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 2021/08/06 13:27:37 UTC

[brooklyn-server] 04/09: ensure scopeRoot resovles to the root node of the blueprint _where the DSL is used_

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

commit 987694403aae28046a94f11d730a55c9c08532f8
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Aug 6 12:45:48 2021 +0100

    ensure scopeRoot resovles to the root node of the blueprint _where the DSL is used_
    
    even for nested (extended) types - do this by replacing the DSL expression _at parse time_
---
 .../camp/brooklyn/spi/creation/CampResolver.java   | 34 ++++++++++++++++++++--
 .../brooklyn/spi/dsl/methods/DslComponent.java     | 18 +++++++++++-
 .../brooklyn/camp/brooklyn/EntitiesYamlTest.java   | 12 ++------
 3 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java
index 8ac1ecb..9d809d9 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java
@@ -22,6 +22,8 @@ import com.google.common.annotations.Beta;
 import java.util.Set;
 
 import java.util.Stack;
+import java.util.function.Consumer;
+import java.util.function.Function;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
@@ -36,8 +38,11 @@ import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
 import org.apache.brooklyn.camp.BasicCampPlatform;
 import org.apache.brooklyn.camp.CampPlatform;
 import org.apache.brooklyn.camp.brooklyn.api.AssemblyTemplateSpecInstantiator;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
 import org.apache.brooklyn.camp.spi.AssemblyTemplate;
 import org.apache.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
+import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
 import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
 import org.apache.brooklyn.core.entity.AbstractEntity;
@@ -176,11 +181,14 @@ class CampResolver {
 
             // above will unwrap but only if it's an Application (and it's permitted); 
             // but it doesn't know whether we need an App or if an Entity is okay  
-            if (!isApplication) return EntityManagementUtils.unwrapEntity(appSpec);
+            EntitySpec<?> result = !isApplication ? EntityManagementUtils.unwrapEntity(appSpec) : appSpec;
             // if we need an App then definitely *don't* unwrap here because
             // the instantiator will have done that, and it knows if the plan
             // specified a wrapped app explicitly (whereas we don't easily know that here!)
-            return appSpec;
+
+            fixScopeRootAtRoot(result);
+
+            return result;
             
         } else {
             if (at.getPlatformComponentTemplates()==null || at.getPlatformComponentTemplates().isEmpty()) {
@@ -191,4 +199,26 @@ class CampResolver {
 
     }
 
+    private static void fixScopeRootAtRoot(EntitySpec<?> node) {
+        node.getConfig().entrySet().forEach(entry -> {
+            fixScopeRoot(entry.getValue(), newValue -> node.configure( (ConfigKey) entry.getKey(), newValue));
+        });
+        node.getFlags().entrySet().forEach(entry -> {
+            fixScopeRoot(entry.getValue(), newValue -> node.configure( entry.getKey(), newValue));
+        });
+    }
+
+    private static void fixScopeRoot(Object value, Consumer<Object> updater) {
+        Function<String,String> fixString = v -> "$brooklyn:self()" + Strings.removeFromStart((String)v, "$brooklyn:scopeRoot()");
+        if (value instanceof String && ((String)value).startsWith("$brooklyn:scopeRoot()")) {
+            value = fixString.apply((String)value);
+            updater.accept(value);
+        } else if (value instanceof DslComponent) {
+            if ( ((DslComponent)value).getScope() == Scope.SCOPE_ROOT ) {
+                value = DslComponent.newInstanceChangingScope(Scope.THIS, (DslComponent) value, fixString);
+                updater.accept(value);
+            }
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
index cf6fa2d..01c68e1 100644
--- a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
+++ b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
@@ -21,6 +21,7 @@ package org.apache.brooklyn.camp.brooklyn.spi.dsl.methods;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.util.Set;
+import java.util.function.Function;
 import static org.apache.brooklyn.camp.brooklyn.spi.dsl.DslUtils.resolved;
 
 import java.util.Collection;
@@ -113,6 +114,20 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements
         }
     }
 
+    public static DslComponent newInstanceChangingScope(Scope scope, DslComponent old, Function<String,String> dslUpdateFn) {
+        DslComponent result;
+        if (old.componentIdSupplier!=null) result = new DslComponent(scope, old.componentIdSupplier);
+        else if (old.componentId!=null) result = new DslComponent(scope, old.componentId);
+        else result = new DslComponent(scope);
+
+        if (dslUpdateFn!=null && old.dsl instanceof String) {
+            result.dsl = dslUpdateFn.apply((String) old.dsl);
+        } else {
+            result.dsl = old.dsl;
+        }
+        return result;
+    }
+
     /**
      * Resolve componentId in the {@link Scope#GLOBAL} scope.
      * 
@@ -1028,7 +1043,8 @@ public class DslComponent extends BrooklynDslDeferredSupplier<Entity> implements
         DESCENDANT,
         ANCESTOR,
         ROOT,
-        /** highest ancestor where all items come from the same catalog item ID */
+        /** root node of blueprint where the the DSL is used; usually the depth in ancestor,
+         *  though specially treated in CampResolver to handle usage within blueprints */
         SCOPE_ROOT,
         THIS;
 
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
index 4ee8a4c..e86ab1a 100644
--- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
@@ -586,7 +586,8 @@ public class EntitiesYamlTest extends AbstractYamlTest {
         Entity e2 = nextChild(e1);
         assertScopes(e2, "APP-grandchild", app, app);
         Entity e3 = nextChild(e2);
-        assertScopes(e3, "APP-greatgrandchild=RP", app, e2, app);
+        // see logic in CampResolver which ensures scopeRoot in a nested blueprint refer to the root of that nested blueprint
+        assertScopes(e3, "APP-greatgrandchild=RP", app, e3, app);
         Entity e4 = nextChild(e3);
         assertScopes(e4, "RP-child", app, e3);
         Entity e5 = nextChild(e4);
@@ -622,14 +623,7 @@ public class EntitiesYamlTest extends AbstractYamlTest {
     private static void assertScopes(Entity entity, String name, Entity root, Entity scopeRoot, Entity scopeRoot2) {
         if (name!=null) assertEquals(entity.getDisplayName(), name);
         assertEquals(entity.config().get(ReferencingYamlTestEntity.TEST_REFERENCE_ROOT), root);
-
-        Entity actualScopeRoot = entity.config().get(ReferencingYamlTestEntity.TEST_REFERENCE_SCOPE_ROOT);
-        if (!actualScopeRoot.equals(scopeRoot) && !actualScopeRoot.equals(scopeRoot2)) {
-            Assert.fail("Wrong scope root; should be either "+scopeRoot+" or "+scopeRoot2+"; but is actually "+actualScopeRoot);
-        }
-        // TODO would be nice if we can capture which blueprint scopeRoot is used in - but this requires introspecting the DSL
-        // currently it will always equal scopeRoot2; if we could convert it to "self()" when the definition is loaded, that would solve it
-
+        assertEquals(entity.config().get(ReferencingYamlTestEntity.TEST_REFERENCE_SCOPE_ROOT), scopeRoot);
         assertEquals(entity.config().get(ReferencingYamlTestEntity.TEST_REFERENCE_SCOPE_ROOT2), scopeRoot2);
     }