You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2022/08/22 09:06:12 UTC

[brooklyn-server] branch master updated: backwards compatibility for `location` singular to auto-flatten / run as has-element check

This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git


The following commit(s) were added to refs/heads/master by this push:
     new bd7db292cc backwards compatibility for `location` singular to auto-flatten / run as has-element check
bd7db292cc is described below

commit bd7db292cc4eb3ffc45c8fc18669655f4c726371
Author: Alex Heneveld <al...@cloudsoft.io>
AuthorDate: Mon Aug 22 09:43:49 2022 +0100

    backwards compatibility for `location` singular to auto-flatten / run as has-element check
    
    plus same for child, tag
---
 .../brooklyn/spi/dsl/DslPredicateYamlTest.java     | 196 ++++++++++++++++++---
 .../util/core/predicates/DslPredicates.java        |  81 +++++++--
 2 files changed, 244 insertions(+), 33 deletions(-)

diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslPredicateYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslPredicateYamlTest.java
index 4688964004..db25f2433e 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslPredicateYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslPredicateYamlTest.java
@@ -15,41 +15,19 @@
  */
 package org.apache.brooklyn.camp.brooklyn.spi.dsl;
 
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
 import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
-import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslTestObjects.DslTestCallable;
-import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslTestObjects.DslTestSupplierWrapper;
-import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslTestObjects.TestDslSupplier;
-import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslTestObjects.TestDslSupplierValue;
-import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.custom.UserSuppliedPackageType;
-import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
-import org.apache.brooklyn.core.entity.Dumper;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.EntityInternal;
-import org.apache.brooklyn.core.sensor.Sensors;
-import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.core.test.entity.TestEntity;
-import org.apache.brooklyn.entity.group.DynamicCluster;
 import org.apache.brooklyn.entity.stock.BasicApplication;
 import org.apache.brooklyn.entity.stock.BasicEntity;
-import org.apache.brooklyn.entity.stock.BasicStartable;
 import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.predicates.DslPredicates;
-import org.apache.brooklyn.util.core.task.Tasks;
-import org.apache.brooklyn.util.guava.Maybe;
 import org.testng.annotations.Test;
 
-import java.util.concurrent.ExecutionException;
-
-import static org.testng.Assert.assertEquals;
-
 public class DslPredicateYamlTest extends AbstractYamlTest {
 
     @Test
@@ -117,4 +95,176 @@ public class DslPredicateYamlTest extends AbstractYamlTest {
         Asserts.assertTrue( predicate.apply(app) );
     }
 
+    @Test
+    public void testDslTargetLocationRetargets() throws Exception {
+        Entity app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  location: localhost",
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: location",
+                "      tag: yes!");
+        // 'location' can be expanded as list
+        DslPredicates.DslPredicate predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        Asserts.assertFalse( predicate.apply(app) );
+        app.getLocations().iterator().next().tags().addTag("yes!");
+        Asserts.assertTrue( predicate.apply(app) );
+
+        app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  location: localhost",
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: locations",
+                "      tag: yes!");
+        // 'locations' requires has-element, cannot be auto-expanded
+        predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        app.getLocations().iterator().next().tags().addTag("yes!");
+        Asserts.assertFalse( predicate.apply(app) );
+
+        app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  location: localhost",
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: locations",
+                "      has-element:",
+                "        tag: yes!");
+        // 'locations' works if has-element specified
+        predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        Asserts.assertFalse( predicate.apply(app) );
+        app.getLocations().iterator().next().tags().addTag("yes!");
+        Asserts.assertTrue( predicate.apply(app) );
+
+        app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  location: localhost",
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: location",
+                "      has-element:",
+                "        tag: yes!");
+        // 'location' also _accepts_ has-element (skips expanding)
+        predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        Asserts.assertFalse( predicate.apply(app) );
+        app.getLocations().iterator().next().tags().addTag("yes!");
+        Asserts.assertTrue( predicate.apply(app) );
+    }
+
+    @Test
+    public void testDslTargetTagRetargets() throws Exception {
+        Entity app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: tag",
+                "      equals: yes!");
+        // 'tag' can be expanded as list
+        DslPredicates.DslPredicate predicate = app.config().get(TestEntity.CONF_PREDICATE);
+//        Asserts.assertFalse( predicate.apply(app) );
+        app.tags().addTag("yes!");
+        Asserts.assertTrue( predicate.apply(app) );
+
+        app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: tags",
+                "      equals: yes!");
+        // 'tags' requires has-element, cannot be auto-expanded
+        predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        app.tags().addTag("yes!");
+        Asserts.assertFalse( predicate.apply(app) );
+
+        app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: tags",
+                "      has-element:",
+                "        equals: yes!");
+        // 'tags' works if has-element specified
+        predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        Asserts.assertFalse( predicate.apply(app) );
+        app.tags().addTag("yes!");
+        Asserts.assertTrue( predicate.apply(app) );
+
+        app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: tag",
+                "      has-element:",
+                "        equals: yes!");
+        // 'tag' also _accepts_ has-element (skips expanding)
+        predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        Asserts.assertFalse( predicate.apply(app) );
+        app.tags().addTag("yes!");
+        Asserts.assertTrue( predicate.apply(app) );
+    }
+
+    @Test
+    public void testDslTargetChildRetargets() throws Exception {
+        Entity app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: child",
+                "      tag: yes!");
+        // 'child' can be expanded as list
+        DslPredicates.DslPredicate predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        Asserts.assertFalse( predicate.apply(app) );
+        app.addChild(EntitySpec.create(BasicEntity.class).tag("yes!"));
+        Asserts.assertTrue( predicate.apply(app) );
+
+        app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: children",
+                "      tag: yes!");
+        // 'children' requires has-element, cannot be auto-expanded
+        predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        app.addChild(EntitySpec.create(BasicEntity.class).tag("yes!"));
+        Asserts.assertFalse( predicate.apply(app) );
+
+        app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: children",
+                "      has-element:",
+                "        tag: yes!");
+        // 'children' works if has-element specified
+        predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        Asserts.assertFalse( predicate.apply(app) );
+        app.addChild(EntitySpec.create(BasicEntity.class).tag("yes!"));
+        Asserts.assertTrue( predicate.apply(app) );
+
+        app = createAndStartApplication(
+                "services:",
+                "- type: " + BasicApplication.class.getName(),
+                "  brooklyn.config:",
+                "    test.confPredicate:",
+                "      target: child",
+                "      has-element:",
+                "        tag: yes!");
+        // 'child' also _accepts_ has-element (skips expanding)
+        predicate = app.config().get(TestEntity.CONF_PREDICATE);
+        Asserts.assertFalse( predicate.apply(app) );
+        app.addChild(EntitySpec.create(BasicEntity.class).tag("yes!"));
+        Asserts.assertTrue( predicate.apply(app) );
+    }
+
 }
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java b/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java
index d027292fd9..a2f2b72d2e 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/predicates/DslPredicates.java
@@ -21,7 +21,6 @@ package org.apache.brooklyn.util.core.predicates;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.util.TokenBuffer;
@@ -32,6 +31,7 @@ import com.google.common.collect.Iterables;
 import com.google.common.reflect.TypeToken;
 import com.jayway.jsonpath.JsonPath;
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.Configurable;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
@@ -42,14 +42,12 @@ import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.resolve.jackson.BeanWithTypeUtils;
 import org.apache.brooklyn.core.resolve.jackson.BrooklynJacksonSerializationUtils;
 import org.apache.brooklyn.core.resolve.jackson.JsonSymbolDependentDeserializer;
-import org.apache.brooklyn.core.resolve.jackson.WrappedValue;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.util.JavaGroovyEquivalents;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.flags.BrooklynTypeNameResolution;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
-import org.apache.brooklyn.util.core.json.BrooklynObjectsJsonMapper;
 import org.apache.brooklyn.util.core.task.DeferredSupplier;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.core.task.ValueResolver;
@@ -256,6 +254,9 @@ public class DslPredicates {
 
         public boolean apply(T input) {
             Maybe<Object> result = resolveTargetAgainstInput(input);
+            if (result.isPresent() && result.get() instanceof RetargettedPredicateEvaluation) {
+                return ((RetargettedPredicateEvaluation)result.get()).apply(input);
+            }
             return applyToResolved(result);
         }
 
@@ -310,7 +311,7 @@ public class DslPredicates {
                 String jsonpathTidied = jsonpath;
                 if (jsonpathTidied!=null && !jsonpathTidied.startsWith("$")) {
                     if (jsonpathTidied.startsWith("@") || jsonpathTidied.startsWith(".") || jsonpathTidied.startsWith("[")) {
-                        jsonpathTidied = "$" + jsonpathTidied;
+                        jsonpathTidied = '$' + jsonpathTidied;
                     } else {
                         jsonpathTidied = "$." + jsonpathTidied;
                     }
@@ -330,6 +331,8 @@ public class DslPredicates {
             });
         }
 
+        /** returns the resolved, possibly redirected target for this test wrapped in a maybe, or absent if there is no valid value/target.
+         * may also return {@link RetargettedPredicateEvaluation} predicate if a different predicate should be run on the same input */
         protected Maybe<Object> resolveTargetAgainstInput(Object input) {
             Map<String,Function<Object,Maybe<Object>>> specialResolvers = MutableMap.of();
             collectApplicableSpecialFieldTargetResolvers(specialResolvers);
@@ -452,7 +455,7 @@ public class DslPredicates {
     }
 
     @Beta
-    public static class DslPredicateDefault<T2> extends DslPredicateBase<T2> implements DslPredicate<T2> {
+    public static class DslPredicateDefault<T2> extends DslPredicateBase<T2> implements DslPredicate<T2>, Cloneable {
         public DslPredicateDefault() {}
 
         // allow a string or int to be an implicit equality target
@@ -465,6 +468,15 @@ public class DslPredicates {
         public String sensor;
         public DslPredicate tag;
 
+        @Override
+        protected DslPredicateDefault<T2> clone() {
+            try {
+                return (DslPredicateDefault<T2>) super.clone();
+            } catch (CloneNotSupportedException e) {
+                throw Exceptions.propagate(e);
+            }
+        }
+
         protected void collectApplicableSpecialFieldTargetResolvers(Map<String,Function<Object, Maybe<Object>>> resolvers) {
             super.collectApplicableSpecialFieldTargetResolvers(resolvers);
 
@@ -509,14 +521,61 @@ public class DslPredicates {
         }
 
         protected Maybe<Object> resolveTargetStringAgainstInput(String target, Object input) {
-            if ("location".equals(target) && input instanceof Entity) return Maybe.of( Locations.getLocationsCheckingAncestors(null, (Entity)input) );
-            if ("locations".equals(target) && input instanceof Entity) return Maybe.of( Locations.getLocationsCheckingAncestors(null, (Entity)input) );
-            if ("children".equals(target) && input instanceof Entity) return Maybe.of( ((Entity)input).getChildren() );
-            if ("tags".equals(target) && input instanceof BrooklynObject) return Maybe.of( ((BrooklynObject)input).tags().getTags() );
+            Maybe<Object> candidate;
+            candidate = resolvePluralNormallyOrSingularAsHasElementRetargettedPredicate(target, input,
+                    "locations", x -> x instanceof Entity, x -> Maybe.of(Locations.getLocationsCheckingAncestors(null, (Entity) x)),
+                    "location", x -> x instanceof Location);
+            if (candidate!=null) return candidate;
+            candidate = resolvePluralNormallyOrSingularAsHasElementRetargettedPredicate(target, input,
+                    "tags", x -> x instanceof BrooklynObject, x -> Maybe.of( ((BrooklynObject)x).tags().getTags() ),
+                    "tag", x -> false);
+            if (candidate!=null) return candidate;
+            candidate = resolvePluralNormallyOrSingularAsHasElementRetargettedPredicate(target, input,
+                    "children", x -> x instanceof Entity, x -> Maybe.of(((Entity) x).getChildren()),
+                    "child", x -> false);
+            if (candidate!=null) return candidate;
 
             return Maybe.absent("Unsupported target '"+target+"' on input "+input);
         }
 
+        protected Maybe<Object> resolveAsHasElementRetargettedPredicate(String target, Object inputValue, Predicate<Object> checkPredicateRetargettingNotNeeded, Predicate<Object> checkValueRetargettable, Function<Object,Maybe<Object>> retargetValue) {
+            if (inputValue == null || checkPredicateRetargettingNotNeeded.test(inputValue)) {
+                // already processsed
+                return Maybe.of(inputValue);
+            }
+            if (hasElement!=null) {
+                // caller is already asking for a member of this list, don't rewrite
+                return retargetValue.apply(inputValue);
+            }
+            if (!checkValueRetargettable.test(inputValue)) {
+                return Maybe.absent("Target " + target + " not applicable to " + inputValue);
+            }
+
+            // keyword 'location' means to re-run checking any element
+            RetargettedPredicateEvaluation retargetPredicate = new RetargettedPredicateEvaluation();
+            retargetPredicate.target = target;
+            retargetPredicate.hasElement = this.clone();
+            ((DslPredicateDefault)retargetPredicate.hasElement).target = null;
+            return Maybe.of(retargetPredicate);
+        }
+
+        protected Maybe<Object> resolvePluralNormallyOrSingularAsHasElementRetargettedPredicate(String target, Object inputValue, String plural, Predicate<Object> checkValueRetargettable, Function<Object,Maybe<Object>> retargetValue,
+                                                                                                String singular, Predicate<Object> checkSingularTargetPredicateRetargettingNotNeeded) {
+            if (plural.equals(target)) {
+                if (!checkValueRetargettable.test(inputValue)) {
+                    return Maybe.absent("Target " + target + " not applicable to " + inputValue);
+                }
+                return retargetValue.apply(inputValue);
+            }
+
+            if (singular.equals(target)) {
+                return resolveAsHasElementRetargettedPredicate(target, inputValue, checkSingularTargetPredicateRetargettingNotNeeded, checkValueRetargettable, retargetValue);
+            }
+
+            // checks didn't apply
+            return null;
+        }
+
         @Override
         public void applyToResolved(Maybe<Object> result, CheckCounts checker) {
             super.applyToResolved(result, checker);
@@ -543,7 +602,6 @@ public class DslPredicates {
     public static class DslEntityPredicateDefault extends DslPredicateDefault<Entity> implements DslEntityPredicate {
         public DslEntityPredicateDefault() { super(); }
         public DslEntityPredicateDefault(String implicitEquals) { super(implicitEquals); }
-
     }
 
     @Beta
@@ -675,4 +733,7 @@ public class DslPredicates {
         return result;
     }
 
+    static class RetargettedPredicateEvaluation<T> extends DslPredicateDefault<T> {
+    }
+
 }