ensure keys saved as value only are if specified as NEVER_INHERITED are not inherited
even if the type is removed from the entity
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
index 943c9b1..e2bd4a2 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigInheritance.java
@@ -127,7 +127,7 @@
@Override protected ConfigInheritance getDelegate() { return DELEGATE; }
}
/** Indicates that a key's value should never be inherited, even if inherited from a value set on a container that does not know the key.
- * (Most usages will prefer {@link #NOT_REINHERITED}.) */
+ * (Many usages will prefer {@link #NOT_REINHERITED}, to allow unknown dynamic keys set in an ancestor accessible from descendants.) */
public static final ConfigInheritance NEVER_INHERITED = new NeverInherited();
/** In case we deserialize an old copy; the last arg (ancestor inherit default) is irrelevant
diff --git a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
index 5d69a4b..80ba2f8 100644
--- a/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/core/config/internal/AbstractConfigMapImpl.java
@@ -27,6 +27,7 @@
import org.apache.brooklyn.api.mgmt.TaskFactory;
import org.apache.brooklyn.api.objs.BrooklynObject;
import org.apache.brooklyn.config.ConfigInheritance;
+import org.apache.brooklyn.config.ConfigInheritance.ConfigInheritanceContext;
import org.apache.brooklyn.config.ConfigInheritances;
import org.apache.brooklyn.config.ConfigInheritances.BasicConfigValueAtContainer;
import org.apache.brooklyn.config.ConfigKey;
@@ -613,11 +614,12 @@
return getSelectedConfigInheritedRaw(null, true);
}
- protected Map<ConfigKey<?>,ReferenceWithError<ConfigValueAtContainer<TContainer,?>>> getSelectedConfigInheritedRaw(Map<ConfigKey<?>,ConfigKey<?>> knownKeys, boolean onlyReinheritable) {
+ protected Map<ConfigKey<?>,ReferenceWithError<ConfigValueAtContainer<TContainer,?>>> getSelectedConfigInheritedRaw(Map<ConfigKey<?>,ConfigKey<?>> knownKeysAtDescendants,
+ /* if true, only returns keys which are intended for inheritance by our descendants */ boolean onlyReinheritable) {
Map<ConfigKey<?>, ConfigKey<?>> knownKeysOnType = MutableMap.of();
for (ConfigKey<?> k: getKeysAtContainer(getContainer())) knownKeysOnType.put(k, k);
- Map<ConfigKey<?>, ConfigKey<?>> knownKeysIncludingDescendants = MutableMap.copyOf(knownKeys);
+ Map<ConfigKey<?>, ConfigKey<?>> knownKeysIncludingDescendants = MutableMap.copyOf(knownKeysAtDescendants);
knownKeysIncludingDescendants.putAll(knownKeysOnType);
Map<ConfigKey<?>,ReferenceWithError<ConfigValueAtContainer<TContainer,?>>> parents = MutableMap.of();
@@ -645,7 +647,15 @@
// if no key on type, we must use any descendant declared key here
// so that the correct descendant conflict resolution strategy is applied
- ConfigInheritance inhHereOrDesc = ConfigInheritances.findInheritance(kTypeOrDescendant, InheritanceContext.RUNTIME_MANAGEMENT, getDefaultRuntimeInheritance());
+ ConfigInheritance inhHereOrDesc = ConfigInheritances.findInheritance(kTypeOrDescendant, InheritanceContext.RUNTIME_MANAGEMENT, null);
+ if (inhHereOrDesc==null) {
+ inhHereOrDesc = kSet.getInheritanceByContext(InheritanceContext.RUNTIME_MANAGEMENT);
+ if (inhHereOrDesc != null) {
+ kOnType = kTypeOrDescendant = kSet; // prefer kset if it has inheritance set (locally by value but not on type, e.g. because key was removed from type while still present)
+ } else {
+ inhHereOrDesc = getDefaultRuntimeInheritance();
+ }
+ }
// however for the purpose of qualifying we must not give any key except what is exactly declared here,
// else reinheritance will be incorrectly deduced
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java b/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
index c7b5532..f30f2fb 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityConfigMap.java
@@ -19,15 +19,20 @@
package org.apache.brooklyn.core.entity.internal;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.mgmt.ExecutionContext;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.config.ConfigInheritance.ConfigInheritanceContext;
import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigInheritance;
import org.apache.brooklyn.core.config.ConfigConstraints;
+import org.apache.brooklyn.core.config.ConfigKeys.InheritanceContext;
import org.apache.brooklyn.core.config.internal.AbstractConfigMapImpl;
+import org.apache.brooklyn.core.entity.AbstractEntity;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.util.guava.Maybe;
import org.slf4j.Logger;
@@ -99,7 +104,20 @@
@Override
protected <T> ConfigKey<?> getKeyAtContainerImpl(Entity container, ConfigKey<T> queryKey) {
if (queryKey==null) return null;
- return container.getEntityType().getConfigKey(queryKey.getName());
+ ConfigKey<?> kOnType = container.getEntityType().getConfigKey(queryKey.getName());
+ if (kOnType!=null) return kOnType;
+ ConfigKey<?> kOnTypeUndeclared;
+ Map<ConfigKey<?>, Object> ownConfig = ((EntityConfigMap) ((EntityInternal) container).config().getInternalConfigMap()).ownConfig;
+ synchronized (ownConfig) {
+ kOnTypeUndeclared = ownConfig.keySet().stream().filter(ck -> Objects.equals(queryKey.getName(), ck.getName())).findAny().orElse(null);
+ }
+ if (kOnTypeUndeclared!=null) {
+ // if a never inherited key is set, but not declared, it should be returned
+ if (BasicConfigInheritance.NEVER_INHERITED.equals(kOnTypeUndeclared.getInheritanceByContext(InheritanceContext.RUNTIME_MANAGEMENT))) {
+ return kOnTypeUndeclared;
+ }
+ }
+ return null;
}
@Override
diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/ConfigEntityInheritanceTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/ConfigEntityInheritanceTest.java
index 3ed0519..07d7059 100644
--- a/core/src/test/java/org/apache/brooklyn/core/entity/ConfigEntityInheritanceTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/entity/ConfigEntityInheritanceTest.java
@@ -20,17 +20,22 @@
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.stream.Collectors;
+import com.google.common.collect.Iterables;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.config.ConfigMap.ConfigMapWithInheritance;
import org.apache.brooklyn.config.ConfigValueAtContainer;
import org.apache.brooklyn.core.config.BasicConfigInheritance;
+import org.apache.brooklyn.core.config.BasicConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.internal.AbstractConfigMapImpl;
import org.apache.brooklyn.core.entity.EntityConfigTest.MyOtherEntity;
import org.apache.brooklyn.core.entity.EntityConfigTest.MyOtherEntityImpl;
+import org.apache.brooklyn.core.entity.internal.EntityConfigMap;
import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey.IntegerAttributeSensorAndConfigKey;
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
@@ -42,8 +47,6 @@
import org.testng.Assert;
import org.testng.annotations.Test;
-import com.google.common.collect.Iterables;
-
public class ConfigEntityInheritanceTest extends BrooklynAppUnitTestSupport {
protected void checkKeys(Entity entity2, Integer value) {
@@ -170,19 +173,81 @@
app.config().set(MyEntityWithPartiallyHeritableConfig.NOT_REINHERITABLE, "maybe");
return app.addChild(EntitySpec.create(MyEntityWithPartiallyHeritableConfig.class));
}
-
+
+ private boolean isKeyInInternalConfigMapInherited(Entity entity, ConfigKey k) {
+ return isKeyInInternalConfigMapInherited(entity, k.getName());
+ }
+ private boolean isKeyInInternalConfigMapInherited(Entity entity, String s) {
+ Map<ConfigKey<?>, Object> cfgMap = ((EntityInternal) entity).config().getInternalConfigMap().getAllConfigInheritedRawValuesIgnoringErrors();
+ Set<String> cfgs = cfgMap.keySet().stream().map(k -> k.getName()).collect(Collectors.toSet());
+ return cfgs.contains(s);
+ }
+
@Test
public void testConfigKeysInheritance() throws Exception {
+ ConfigKey<Object> WAS_NEVER_INHERIT_BUT_NOW_DEFAULT_INHERITANCE = ConfigKeys.builder(Object.class, MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT.getName()).build();
+
Entity child = setupBasicInheritanceTest();
Assert.assertNotNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.HERITABLE_BY_DEFAULT));
Assert.assertNotNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.ALWAYS_HERITABLE));
Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ Assert.assertNull(child.getConfig(WAS_NEVER_INHERIT_BUT_NOW_DEFAULT_INHERITANCE));
// it's reinheritable unless explicitly declared
Assert.assertNotNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NOT_REINHERITABLE));
+
+ Asserts.assertTrue(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.HERITABLE_BY_DEFAULT));
+ Asserts.assertTrue(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.ALWAYS_HERITABLE));
+ Asserts.assertFalse(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ Asserts.assertTrue(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NOT_REINHERITABLE));
+
+ // if reinheritable is _declared_ at the app, it is null at the child
app.getMutableEntityType().addConfigKey(MyEntityWithPartiallyHeritableConfig.NOT_REINHERITABLE);
Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NOT_REINHERITABLE));
+ // and not found
+ Asserts.assertFalse(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NOT_REINHERITABLE));
+
+ // and returns to previous state when reverted
+ app.getMutableEntityType().removeConfigKey(MyEntityWithPartiallyHeritableConfig.NOT_REINHERITABLE);
+ Assert.assertNotNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NOT_REINHERITABLE));
+ Asserts.assertTrue(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NOT_REINHERITABLE));
+
+ // whereas never inherit is not found at child whether declared or not on parent or child
+ app.getMutableEntityType().addConfigKey(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT);
+ Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ Asserts.assertFalse(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+
+ app.getMutableEntityType().removeConfigKey(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT);
+ Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ Asserts.assertFalse(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+
+ ((EntityInternal)child).getMutableEntityType().addConfigKey(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT);
+ Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ Asserts.assertFalse(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ Assert.assertNull(child.getConfig(WAS_NEVER_INHERIT_BUT_NOW_DEFAULT_INHERITANCE));
+ Asserts.assertFalse(isKeyInInternalConfigMapInherited(child, WAS_NEVER_INHERIT_BUT_NOW_DEFAULT_INHERITANCE));
+
+ ((EntityInternal)child).getMutableEntityType().removeConfigKey(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT);
+ // even if removed, if the key is set, it will keep that type
+ Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ ((EntityInternal) child).config().getInternalConfigMap().getAllConfigInheritedRawValuesIgnoringErrors();
+
+ Asserts.assertFalse(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ Assert.assertNull(child.getConfig(WAS_NEVER_INHERIT_BUT_NOW_DEFAULT_INHERITANCE));
+ Asserts.assertFalse(isKeyInInternalConfigMapInherited(child, WAS_NEVER_INHERIT_BUT_NOW_DEFAULT_INHERITANCE));
+
+ // redefining at descendant doesn't change visibility
+ ConfigKey<Object> WAS_NEVER_INHERIT_BUT_NOW_OVERWRITE_AT_CHILD = ConfigKeys.builder(Object.class, MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT.getName())
+ .runtimeInheritance(BasicConfigInheritance.OVERWRITE).build();
+ ((EntityInternal)child).getMutableEntityType().addConfigKey(WAS_NEVER_INHERIT_BUT_NOW_OVERWRITE_AT_CHILD);
+ Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ // (but the key is known, of course)
+ Asserts.assertTrue(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+
+ ((EntityInternal)child).getMutableEntityType().removeConfigKey(WAS_NEVER_INHERIT_BUT_NOW_OVERWRITE_AT_CHILD);
+ Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
+ Asserts.assertFalse(isKeyInInternalConfigMapInherited(child, MyEntityWithPartiallyHeritableConfig.NEVER_INHERIT));
}
@SuppressWarnings("unchecked")