You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/12/17 13:54:25 UTC

[4/5] incubator-brooklyn git commit: BrooklynProperties handles externalised config

BrooklynProperties handles externalised config

- Adds DeferredBrooklynProperties
- Adds tests (ExternalConfigBrooklynPropertiesTest)


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/f66a72d2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/f66a72d2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/f66a72d2

Branch: refs/heads/master
Commit: f66a72d2c16b076cfc0b6d902b52754a55b013aa
Parents: ea37bb6
Author: Aled Sage <al...@gmail.com>
Authored: Wed Dec 16 20:44:44 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Dec 17 12:53:03 2015 +0000

----------------------------------------------------------------------
 .../core/location/BasicLocationRegistry.java    |   7 +-
 .../internal/AbstractManagementContext.java     |   4 +-
 .../internal/DeferredBrooklynProperties.java    | 370 +++++++++++++++++++
 .../mgmt/internal/LocalManagementContext.java   |   2 +-
 .../location/byon/ByonLocationResolverTest.java |   4 +-
 docs/guide/ops/brooklyn_properties.md           |  23 ++
 docs/guide/ops/externalized-configuration.md    |  11 +
 .../ExternalConfigBrooklynPropertiesTest.java   | 146 ++++++++
 .../camp/brooklyn/ExternalConfigYamlTest.java   |  16 +
 9 files changed, 577 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f66a72d2/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java b/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
index 202ecf4..9e81382 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
@@ -45,6 +45,7 @@ import org.apache.brooklyn.core.config.ConfigPredicates;
 import org.apache.brooklyn.core.config.ConfigUtils;
 import org.apache.brooklyn.core.location.internal.LocationInternal;
 import org.apache.brooklyn.core.mgmt.internal.LocalLocationManager;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
 import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -494,6 +495,11 @@ public class BasicLocationRegistry implements LocationRegistry {
     }
 
     @VisibleForTesting
+    public void putProperties(Map<String, ?> vals) {
+        ((ManagementContextInternal)mgmt).getBrooklynProperties().putAll(vals);
+    }
+
+    @VisibleForTesting
     public static void setupLocationRegistryForTesting(ManagementContext mgmt) {
         // ensure localhost is added (even on windows)
         LocationDefinition l = mgmt.getLocationRegistry().getDefinedLocationByName("localhost");
@@ -502,5 +508,4 @@ public class BasicLocationRegistry implements LocationRegistry {
         
         ((BasicLocationRegistry)mgmt.getLocationRegistry()).disablePersistence();
     }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f66a72d2/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
index cfa1d29..d41e059 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
@@ -151,7 +151,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte
 
     private final AtomicLong totalEffectorInvocationCount = new AtomicLong();
 
-    protected BrooklynProperties configMap;
+    protected DeferredBrooklynProperties configMap;
     protected BasicLocationRegistry locationRegistry;
     protected final BasicBrooklynCatalog catalog;
     protected final BrooklynTypeRegistry typeRegistry;
@@ -184,7 +184,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte
     }
 
     public AbstractManagementContext(BrooklynProperties brooklynProperties, DataGridFactory datagridFactory) {
-        this.configMap = brooklynProperties;
+        this.configMap = new DeferredBrooklynProperties(brooklynProperties, this);
         this.entityDriverManager = new BasicEntityDriverManager();
         this.downloadsManager = BasicDownloadsManager.newDefault(configMap);
         if (datagridFactory == null) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f66a72d2/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java
new file mode 100644
index 0000000..ae0c7a5
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.mgmt.internal;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import org.apache.brooklyn.api.mgmt.ExecutionContext;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.task.DeferredSupplier;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Maps;
+
+/**
+ * Delegates to another {@link BrooklynProperties} implementation, but intercepts all calls to get.
+ * The results are transformed: if they are in the external-config format then they are 
+ * automatically converted to {@link DeferredSupplier}.
+ * 
+ * The external-config format is that same as that for camp-yaml blueprints (i.e. 
+ * {@code $brooklyn:external("myprovider", "mykey")}.
+ */
+public class DeferredBrooklynProperties implements BrooklynProperties {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DeferredBrooklynProperties.class);
+
+    private static final String BROOKLYN_YAML_PREFIX = "$brooklyn:";
+    
+    private final BrooklynProperties delegate;
+    private final ManagementContextInternal mgmt;
+
+    public DeferredBrooklynProperties(BrooklynProperties delegate, ManagementContextInternal mgmt) {
+        this.delegate = checkNotNull(delegate, "delegate");
+        this.mgmt = checkNotNull(mgmt, "mgmt");
+    }
+    
+    private Object transform(ConfigKey<?> key, Object value) {
+        if (value instanceof CharSequence) {
+            String raw = value.toString();
+            if (raw.startsWith(BROOKLYN_YAML_PREFIX)) {
+                CampYamlParser parser = mgmt.getConfig().getConfig(CampYamlParser.YAML_PARSER_KEY);
+                if (parser == null) {
+                    // TODO Should we fail or return the untransformed value?
+                    // Problem is this gets called during initialisation, e.g. by BrooklynFeatureEnablement calling asMapWithStringKeys()
+                    // throw new IllegalStateException("Cannot parse external-config for "+key+" because no camp-yaml parser available");
+                    LOG.debug("Not transforming external-config {}, as no camp-yaml parser available", key);
+                    return value;
+                }
+                return parser.parse(raw);
+            }
+        }
+        return value;
+    }
+    
+    private <T> T resolve(ConfigKey<T> key, Object value) {
+        Object transformed = transform(key, value);
+
+        Object result;
+        if (transformed instanceof DeferredSupplier) {
+            ExecutionContext exec = mgmt.getServerExecutionContext();
+            try {
+                result = Tasks.resolveValue(transformed, key.getType(), exec);
+            } catch (ExecutionException | InterruptedException e) {
+                throw Exceptions.propagate(e);
+            }
+        } else {
+            result = transformed;
+        }
+
+        return TypeCoercions.coerce(result, key.getTypeToken());
+    }
+    
+    @Override
+    public <T> T getConfig(ConfigKey<T> key) {
+        T raw = delegate.getConfig(key);
+        return resolve(key, raw);
+    }
+
+    @Override
+    public <T> T getConfig(HasConfigKey<T> key) {
+        T raw = delegate.getConfig(key);
+        return resolve(key.getConfigKey(), raw);
+    }
+
+    @Override
+    public <T> T getConfig(HasConfigKey<T> key, T defaultValue) {
+        T raw = delegate.getConfig(key, defaultValue);
+        return resolve(key.getConfigKey(), raw);
+    }
+
+    @Override
+    public <T> T getConfig(ConfigKey<T> key, T defaultValue) {
+        T raw = delegate.getConfig(key, defaultValue);
+        return resolve(key, raw);
+    }
+
+    @Deprecated
+    @Override
+    public Object getRawConfig(ConfigKey<?> key) {
+        return transform(key, delegate.getRawConfig(key));
+    }
+    
+    @Override
+    public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) {
+        Maybe<Object> result = delegate.getConfigRaw(key, includeInherited);
+        return (result.isPresent()) ? Maybe.of(transform(key, result.get())) : Maybe.absent();
+    }
+
+    @Override
+    public Map<ConfigKey<?>, Object> getAllConfig() {
+        Map<ConfigKey<?>, Object> raw = delegate.getAllConfig();
+        Map<ConfigKey<?>, Object> result = Maps.newLinkedHashMap();
+        for (Map.Entry<ConfigKey<?>, Object> entry : raw.entrySet()) {
+            result.put(entry.getKey(), transform(entry.getKey(), entry.getValue()));
+        }
+        return result;
+    }
+
+    @Override
+    public Map<String, Object> asMapWithStringKeys() {
+        Map<ConfigKey<?>, Object> raw = delegate.getAllConfig();
+        Map<String, Object> result = Maps.newLinkedHashMap();
+        for (Map.Entry<ConfigKey<?>, Object> entry : raw.entrySet()) {
+            result.put(entry.getKey().getName(), transform(entry.getKey(), entry.getValue()));
+        }
+        return result;
+    }
+
+    /**
+     * Discouraged; returns the String so if it is external config, it will be the 
+     * {@code $brooklyn:external(...)} format.
+     */
+    @Override
+    @SuppressWarnings("rawtypes")
+    @Deprecated
+    public String get(Map flags, String key) {
+        return delegate.get(flags, key);
+    }
+
+    /**
+     * Discouraged; returns the String so if it is external config, it will be the 
+     * {@code $brooklyn:external(...)} format.
+     */
+    @Override
+    public String getFirst(String ...keys) {
+        return delegate.getFirst(keys);
+    }
+    
+    /**
+     * Discouraged; returns the String so if it is external config, it will be the 
+     * {@code $brooklyn:external(...)} format.
+     */
+    @Override
+    @SuppressWarnings("rawtypes")
+    public String getFirst(Map flags, String ...keys) {
+        return delegate.getFirst(flags, keys);
+    }
+
+    @Override
+    public BrooklynProperties submap(Predicate<ConfigKey<?>> filter) {
+        BrooklynProperties submap = delegate.submap(filter);
+        return new DeferredBrooklynProperties(submap, mgmt);
+    }
+
+    @Override
+    public BrooklynProperties addEnvironmentVars() {
+        delegate.addEnvironmentVars();
+        return this;
+    }
+
+    @Override
+    public BrooklynProperties addSystemProperties() {
+        delegate.addSystemProperties();
+        return this;
+    }
+
+    @Override
+    public BrooklynProperties addFrom(ConfigBag cfg) {
+        delegate.addFrom(cfg);
+        return this;
+    }
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public BrooklynProperties addFrom(Map map) {
+        delegate.addFrom(map);
+        return this;
+    }
+
+    @Override
+    public BrooklynProperties addFrom(InputStream i) {
+        delegate.addFrom(i);
+        return this;
+    }
+    
+    @Override
+    public BrooklynProperties addFrom(File f) {
+        delegate.addFrom(f);
+        return this;
+    }
+    
+    @Override
+    public BrooklynProperties addFrom(URL u) {
+        delegate.addFrom(u);
+        return this;
+    }
+
+    @Override
+    public BrooklynProperties addFromUrl(String url) {
+        delegate.addFromUrl(url);
+        return this;
+    }
+
+    @Override
+    public BrooklynProperties addFromUrlProperty(String urlProperty) {
+        delegate.addFromUrlProperty(urlProperty);
+        return this;
+    }
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public BrooklynProperties addFromMap(Map properties) {
+        delegate.addFromMap(properties);
+        return this;
+    }
+
+    @Override
+    public boolean putIfAbsent(String key, Object value) {
+        return delegate.putIfAbsent(key, value);
+    }
+
+    @Override
+    public String toString() {
+        return delegate.toString();
+    }
+
+    @Override
+    public Object put(Object key, Object value) {
+        return delegate.put(key, value);
+    }
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public void putAll(Map vals) {
+        delegate.putAll(vals);
+    }
+    
+    @Override
+    public <T> Object put(HasConfigKey<T> key, T value) {
+        return delegate.put(key, value);
+    }
+
+    @Override
+    public <T> Object put(ConfigKey<T> key, T value) {
+        return delegate.put(key, value);
+    }
+    
+    @Override
+    public <T> boolean putIfAbsent(ConfigKey<T> key, T value) {
+        return delegate.putIfAbsent(key, value);
+    }
+    
+    
+    //////////////////////////////////////////////////////////////////////////////////
+    // Methods below from java.util.LinkedHashMap, which BrooklynProperties extends //
+    //////////////////////////////////////////////////////////////////////////////////
+    
+    @Override
+    public int size() {
+        return delegate.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return delegate.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return delegate.containsValue(value);
+    }
+
+    @Override
+    public Object get(Object key) {
+        return delegate.get(key);
+    }
+
+    @Override
+    public Object remove(Object key) {
+        return delegate.remove(key);
+    }
+
+    @Override
+    public void clear() {
+        delegate.clear();
+    }
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public Set keySet() {
+        return delegate.keySet();
+    }
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public Collection values() {
+        return delegate.values();
+    }
+    
+    @Override
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public Set<Map.Entry> entrySet() {
+        return delegate.entrySet();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return delegate.equals(o);
+    }
+
+    @Override
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+    
+    // put(Object, Object) already overridden
+    //@Override
+    //public Object put(Object key, Object value) {
+
+    // putAll(Map) already overridden
+    //@Override
+    //public void putAll(Map m) {
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f66a72d2/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
index f464d3b..d88a500 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
@@ -380,7 +380,7 @@ public class LocalManagementContext extends AbstractManagementContext {
             log.warn("When reloading, mgmt context "+this+" properties are fixed, so reload will be of limited utility");
         
         BrooklynProperties properties = builder.build();
-        configMap = properties;
+        configMap = new DeferredBrooklynProperties(properties, this);
         if (brooklynAdditionalProperties != null) {
             log.info("Reloading additional brooklyn properties from " + brooklynAdditionalProperties);
             configMap.addFromMap(brooklynAdditionalProperties);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f66a72d2/core/src/test/java/org/apache/brooklyn/location/byon/ByonLocationResolverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/location/byon/ByonLocationResolverTest.java b/core/src/test/java/org/apache/brooklyn/location/byon/ByonLocationResolverTest.java
index edc374d..675125f 100644
--- a/core/src/test/java/org/apache/brooklyn/location/byon/ByonLocationResolverTest.java
+++ b/core/src/test/java/org/apache/brooklyn/location/byon/ByonLocationResolverTest.java
@@ -243,7 +243,7 @@ public class ByonLocationResolverTest {
     @Test
     public void testResolvesUserArg3() throws Exception {
         String spec = "byon(hosts=\"1.1.1.1\")";
-        managementContext.getLocationRegistry().getProperties().putAll(MutableMap.of(
+        ((BasicLocationRegistry)managementContext.getLocationRegistry()).putProperties(MutableMap.of(
                 "brooklyn.location.named.foo", spec,
                 "brooklyn.location.named.foo.user", "bob"));
         ((BasicLocationRegistry)managementContext.getLocationRegistry()).updateDefinedLocations();
@@ -267,7 +267,7 @@ public class ByonLocationResolverTest {
     /** private key should be inherited, so confirm that happens correctly */
     public void testResolvesPrivateKeyArgInheritance() throws Exception {
         String spec = "byon(hosts=\"1.1.1.1\")";
-        managementContext.getLocationRegistry().getProperties().putAll(MutableMap.of(
+        ((BasicLocationRegistry)managementContext.getLocationRegistry()).putProperties(MutableMap.of(
                 "brooklyn.location.named.foo", spec,
                 "brooklyn.location.named.foo.user", "bob",
                 "brooklyn.location.named.foo.privateKeyFile", "/tmp/x"));

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f66a72d2/docs/guide/ops/brooklyn_properties.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/brooklyn_properties.md b/docs/guide/ops/brooklyn_properties.md
index cb00ceb..e8335f1 100644
--- a/docs/guide/ops/brooklyn_properties.md
+++ b/docs/guide/ops/brooklyn_properties.md
@@ -52,6 +52,28 @@ brooklyn.webconsole.security.https.required=true
 More information, including setting up a certificate, is described [further below](#https-configuration).
 
 
+## Camp YAML Expressions
+
+Values in `brooklyn.properties` can use the Camp YAML syntax. Any value starting `$brooklyn:` is 
+parsed as a Camp YAML expression.
+
+This allows [externalized configuration](externalized-configuration.html) to be used from 
+brooklyn.properties. For example:
+
+{% highlight properties %}
+brooklyn.location.jclouds.aws-ec2.identity=$brooklyn:external("vault", "aws-identity")
+brooklyn.location.jclouds.aws-ec2.credential=$brooklyn:external("vault", "aws-credential")
+{% endhighlight %}
+
+If for some reason one requires a literal value that really does start with `$brooklyn:` (i.e.
+for the value to not be parsed), then this can be achieved by using the syntax below. This 
+example returns the property value `$brooklyn:myexample`:
+
+{% highlight properties %}
+example.property=$brooklyn:literal("$brooklyn:myexample")
+{% endhighlight %}
+
+
 ## Locations
 
 Information on defining locations in the `brooklyn.properties` file is available [here](locations/).
@@ -83,6 +105,7 @@ brooklyn.webconsole.security.user.admin.sha256=91e16f94509fa8e3dd21c43d69cadfd7d
 The `users` line should contain a comma-separated list. The special value `*` is accepted to permit all users.
 
 To generate this, the brooklyn CLI can be used:
+
 {% highlight bash %}
 brooklyn generate-password --user admin
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f66a72d2/docs/guide/ops/externalized-configuration.md
----------------------------------------------------------------------
diff --git a/docs/guide/ops/externalized-configuration.md b/docs/guide/ops/externalized-configuration.md
index 3a80499..ea6c63c 100644
--- a/docs/guide/ops/externalized-configuration.md
+++ b/docs/guide/ops/externalized-configuration.md
@@ -111,6 +111,17 @@ brooklyn.config:
   example: $brooklyn:formatString("%s", external("supplier", "key"))
 {% endhighlight %}
 
+
+## Referring to External Configuration in brooklyn.properties
+
+The same blueprint language DSL can be used from `brooklyn.properties`. For example:
+
+{% highlight properties %}
+brooklyn.location.jclouds.aws-ec2.identity=$brooklyn:external("mysupplier", "aws-identity")
+brooklyn.location.jclouds.aws-ec2.credential=$brooklyn:external("mysupplier", "aws-credential")
+{% endhighlight %}
+
+
 ## Suppliers available with Brooklyn
 
 Brooklyn ships with a number of external configuration suppliers ready to use.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f66a72d2/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigBrooklynPropertiesTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigBrooklynPropertiesTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigBrooklynPropertiesTest.java
new file mode 100644
index 0000000..39b444d
--- /dev/null
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigBrooklynPropertiesTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.api.mgmt.ExecutionContext;
+import org.apache.brooklyn.camp.brooklyn.ExternalConfigYamlTest.MyExternalConfigSupplier;
+import org.apache.brooklyn.camp.brooklyn.ExternalConfigYamlTest.MyExternalConfigSupplierWithoutMapArg;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.ConfigPredicates;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.location.jclouds.JcloudsLocation;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.task.DeferredSupplier;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+@Test
+public class ExternalConfigBrooklynPropertiesTest extends AbstractYamlTest {
+
+    @Override
+    protected LocalManagementContext newTestManagementContext() {
+        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        props.put("brooklyn.external.myprovider", MyExternalConfigSupplier.class.getName());
+        props.put("brooklyn.external.myprovider.mykey", "myval");
+        props.put("brooklyn.external.myprovider.mykey2", "myval2");
+        props.put("brooklyn.external.myproviderWithoutMapArg", MyExternalConfigSupplierWithoutMapArg.class.getName());
+        props.put("myproperty", "$brooklyn:external(\"myprovider\", \"mykey\")");
+
+        return LocalManagementContextForTests.builder(true)
+                .useProperties(props)
+                .build();
+    }
+
+    // Yaml parsing support is more generic than just external-config.
+    // Test other parsing here, even though it's not directly related to external-config.
+    @Test
+    public void testYamlLiteralFromPropertiesInLocation() throws Exception {
+        ((ManagementContextInternal)mgmt()).getBrooklynProperties().put(
+                ConfigKeys.newStringConfigKey("myDynamicProperty"), "$brooklyn:literal(\"myliteral\")");
+        
+        String val = mgmt().getConfig().getConfig(ConfigKeys.newStringConfigKey("myDynamicProperty"));
+        assertEquals(val, "myliteral");
+    }
+
+    @Test
+    public void testInvalidYamlExpression() throws Exception {
+        ((ManagementContextInternal)mgmt()).getBrooklynProperties().put(
+                ConfigKeys.newStringConfigKey("myInvalidExternal"), "$brooklyn:external");
+        
+        try {
+            String val = mgmt().getConfig().getConfig(ConfigKeys.newStringConfigKey("myInvalidExternal"));
+            Asserts.shouldHaveFailedPreviously("val="+val);
+        } catch (IllegalArgumentException e) {
+            Asserts.expectedFailureContains(e, "Error evaluating node");
+        }
+    }
+
+    @Test
+    public void testExternalisedConfigFromPropertiesInLocation() throws Exception {
+        BrooklynProperties props = ((ManagementContextInternal)mgmt()).getBrooklynProperties();
+        props.put("brooklyn.location.jclouds.aws-ec2.identity", "$brooklyn:external(\"myprovider\", \"mykey\")");
+        props.put("brooklyn.location.jclouds.aws-ec2.credential", "$brooklyn:external(\"myprovider\", \"mykey2\")");
+        
+        JcloudsLocation loc = (JcloudsLocation) mgmt().getLocationRegistry().resolve("jclouds:aws-ec2:us-east-1");
+        assertEquals(loc.getIdentity(), "myval");
+        assertEquals(loc.getCredential(), "myval2");
+    }
+
+    @Test
+    public void testExternalisedConfigInProperties() throws Exception {
+        runExternalisedConfigGetters("myproperty", "myval");
+    }
+    
+    @Test
+    public void testExternalisedConfigInAddedStringProperty() throws Exception {
+        ((ManagementContextInternal)mgmt()).getBrooklynProperties().put(
+                "myDynamicProperty", "$brooklyn:external(\"myprovider\", \"mykey\")");
+        runExternalisedConfigGetters("myDynamicProperty", "myval");
+    }
+    
+    @Test
+    public void testExternalisedConfigInAddedKeyProperty() throws Exception {
+        ((ManagementContextInternal)mgmt()).getBrooklynProperties().put(
+                ConfigKeys.newStringConfigKey("myDynamicProperty"), "$brooklyn:external(\"myprovider\", \"mykey\")");
+        runExternalisedConfigGetters("myDynamicProperty", "myval");
+    }
+    
+    @Test
+    public void testExternalisedConfigInAddedMapProperty() throws Exception {
+        ((ManagementContextInternal)mgmt()).getBrooklynProperties().addFromMap(
+                ImmutableMap.of("myDynamicProperty", "$brooklyn:external(\"myprovider\", \"mykey\")"));
+        runExternalisedConfigGetters("myDynamicProperty", "myval");
+    }
+
+    protected void runExternalisedConfigGetters(String property, String expectedVal) throws Exception {
+        runExternalisedConfigGetters(((ManagementContextInternal)mgmt()).getBrooklynProperties(), property, expectedVal, true);
+    }
+    
+    protected void runExternalisedConfigGetters(BrooklynProperties props, String property, String expectedVal, boolean testSubMap) throws Exception {
+        ExecutionContext exec = mgmt().getServerExecutionContext();
+
+        String val1 = props.getConfig(ConfigKeys.newStringConfigKey(property));
+        assertEquals(val1, expectedVal);
+        
+        DeferredSupplier<?> val2 = (DeferredSupplier<?>) props.getRawConfig(ConfigKeys.newStringConfigKey(property));
+        assertEquals(Tasks.resolveValue(val2, String.class, exec), expectedVal);
+        
+        DeferredSupplier<?> val3 = (DeferredSupplier<?>) props.getConfigRaw(ConfigKeys.newStringConfigKey(property), false).get();
+        assertEquals(Tasks.resolveValue(val3, String.class, exec), expectedVal);
+
+        DeferredSupplier<?> val4 = (DeferredSupplier<?>) props.getAllConfig().get(ConfigKeys.newStringConfigKey(property));
+        assertEquals(Tasks.resolveValue(val4, String.class, exec), expectedVal);
+        
+        String val5 = props.getFirst(property);
+        assertTrue(val5.startsWith("$brooklyn:external"), "val="+val5);
+        
+        if (testSubMap) {
+            BrooklynProperties submap = props.submap(ConfigPredicates.nameEqualTo(property));
+            runExternalisedConfigGetters(submap, property, expectedVal, false);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f66a72d2/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
index e075080..210f158 100644
--- a/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
+++ b/usage/camp/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
@@ -26,17 +26,22 @@ import java.util.Map;
 
 import com.google.common.collect.Iterables;
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.ExecutionContext;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.config.external.AbstractExternalConfigSupplier;
 import org.apache.brooklyn.core.config.external.ExternalConfigSupplier;
 import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.location.cloud.CloudLocationConfig;
+import org.apache.brooklyn.core.mgmt.internal.CampYamlParser;
 import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.util.core.task.DeferredSupplier;
+import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -61,6 +66,17 @@ public class ExternalConfigYamlTest extends AbstractYamlTest {
     }
 
     @Test
+    public void testCampYamlParserHandlesExternalisedConfig() throws Exception {
+        CampYamlParser parser = mgmt().getConfig().getConfig(CampYamlParser.YAML_PARSER_KEY);
+        
+        DeferredSupplier<?> supplier = (DeferredSupplier<?>) parser.parse("$brooklyn:external(\"myprovider\", \"mykey\")");
+        
+        ExecutionContext exec = mgmt().getServerExecutionContext();
+        String result = Tasks.resolveValue(supplier, String.class, exec);
+        assertEquals(result, "myval");
+    }
+
+    @Test
     public void testExternalisedConfigReferencedFromYaml() throws Exception {
         ConfigKey<String> MY_CONFIG_KEY = ConfigKeys.newStringConfigKey("my.config.key");