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 2014/06/26 12:39:38 UTC
[01/11] git commit: env vars for ssh commands on task and in gui
Repository: incubator-brooklyn
Updated Branches:
refs/heads/master 1bec9cce1 -> 51f47ed5f
env vars for ssh commands on task and in gui
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/eb7da4fb
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/eb7da4fb
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/eb7da4fb
Branch: refs/heads/master
Commit: eb7da4fbe1eeb2cf6db0e1988041e0e4f9c65599
Parents: 6976d59
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Jun 2 19:02:57 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jun 24 21:53:28 2014 +0100
----------------------------------------------------------------------
.../brooklyn/entity/basic/BrooklynTaskTags.java | 16 ++++++++++++++++
.../system/internal/AbstractProcessTaskFactory.java | 7 +++++--
.../basic/AbstractSoftwareProcessSshDriver.java | 4 ++++
.../entity/basic/lifecycle/ScriptHelper.java | 8 ++++++++
.../src/main/java/brooklyn/util/text/Strings.java | 1 +
5 files changed, 34 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb7da4fb/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
index efe635a..32d10ef 100644
--- a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
+++ b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
@@ -3,6 +3,7 @@ package brooklyn.entity.basic;
import java.io.ByteArrayOutputStream;
import java.util.Collection;
import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
@@ -16,6 +17,7 @@ import brooklyn.util.stream.Streams;
import brooklyn.util.task.TaskTags;
import brooklyn.util.task.Tasks;
import brooklyn.util.text.Strings;
+import brooklyn.util.text.StringEscapes.BashStringEscapes;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
@@ -162,6 +164,8 @@ public class BrooklynTaskTags extends TaskTags {
public static final String STREAM_STDIN = "stdin";
public static final String STREAM_STDOUT = "stdout";
public static final String STREAM_STDERR = "stderr";
+ /** not a stream, but inserted with the same mechanism */
+ public static final String STREAM_ENV = "env";
/** creates a tag suitable for marking a stream available on a task */
public static WrappedStream tagForStream(String streamType, ByteArrayOutputStream stream) {
@@ -172,6 +176,18 @@ public class BrooklynTaskTags extends TaskTags {
public static WrappedStream tagForStream(String streamType, Supplier<String> contents, Supplier<Integer> size) {
return new WrappedStream(streamType, contents, size);
}
+
+ /** creates a tag suitable for attaching a snapshot of an environment var map as a "stream" on a task;
+ * mainly for use with STREAM_ENV */
+ public static WrappedStream tagForEnvStream(String streamEnv, Map<?, ?> env) {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<?,?> kv: env.entrySet()) {
+ Object val = kv.getValue();
+ sb.append(kv.getKey()+"=" +
+ (val!=null ? BashStringEscapes.wrapBash(val.toString()) : "") + "\n");
+ }
+ return BrooklynTaskTags.tagForStream(BrooklynTaskTags.STREAM_ENV, Streams.byteArrayOfString(sb.toString()));
+ }
/** returns the set of tags indicating the streams available on a task */
public static Set<WrappedStream> streams(Task<?> task) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb7da4fb/core/src/main/java/brooklyn/util/task/system/internal/AbstractProcessTaskFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/task/system/internal/AbstractProcessTaskFactory.java b/core/src/main/java/brooklyn/util/task/system/internal/AbstractProcessTaskFactory.java
index 32dea7d..296d447 100644
--- a/core/src/main/java/brooklyn/util/task/system/internal/AbstractProcessTaskFactory.java
+++ b/core/src/main/java/brooklyn/util/task/system/internal/AbstractProcessTaskFactory.java
@@ -142,8 +142,10 @@ public abstract class AbstractProcessTaskFactory<T extends AbstractProcessTaskFa
@Override
public T environmentVariables(Map<String,String> vars) {
- markDirty();
- shellEnvironment.putAll(vars);
+ if (vars!=null) {
+ markDirty();
+ shellEnvironment.putAll(vars);
+ }
return self();
}
@@ -153,6 +155,7 @@ public abstract class AbstractProcessTaskFactory<T extends AbstractProcessTaskFa
tb.tag(BrooklynTaskTags.tagForStream(BrooklynTaskTags.STREAM_STDIN,
Streams.byteArrayOfString(Strings.join(commands, "\n"))));
+ tb.tag(BrooklynTaskTags.tagForEnvStream(BrooklynTaskTags.STREAM_ENV, shellEnvironment));
return tb;
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb7da4fb/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
index a1fc8d6..be41005 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
@@ -254,6 +254,10 @@ public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareP
flags.putAll(getSshFlags());
flags.putAll(flags2);
Map<String, String> environment = (Map<String, String>) ((flags.get("env") != null) ? flags.get("env") : getShellEnvironment());
+ if (environment!=null && Tasks.current()!=null) {
+ // attach tags here, as well as in ScriptHelper, because they may have just been read from the driver
+ Tasks.addTagDynamically(BrooklynTaskTags.tagForEnvStream(BrooklynTaskTags.STREAM_ENV, environment));
+ }
if (!flags.containsKey("logPrefix")) flags.put("logPrefix", ""+entity.getId()+"@"+getLocation().getDisplayName());
return getMachine().execScript(flags, summaryForLogging, script, environment);
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb7da4fb/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java b/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java
index 64fb212..af672ed 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/lifecycle/ScriptHelper.java
@@ -252,6 +252,14 @@ public class ScriptHelper {
} catch (IOException e) {
log.warn("Error registering stream "+BrooklynTaskTags.STREAM_STDIN+" on "+tb+": "+e, e);
}
+
+ Map<?,?> env = (Map<?,?>) flags.get("env");
+ if (env!=null) {
+ // if not explicitly set, env will come from getShellEnv in AbstractSoftwareProcessSshDriver.execute,
+ // which will also update this tag appropriately
+ tb.tag(BrooklynTaskTags.tagForEnvStream(BrooklynTaskTags.STREAM_ENV, env));
+ }
+
if (gatherOutput) {
stdout = new ByteArrayOutputStream();
tb.tag(BrooklynTaskTags.tagForStream(BrooklynTaskTags.STREAM_STDOUT, stdout));
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/eb7da4fb/utils/common/src/main/java/brooklyn/util/text/Strings.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/text/Strings.java b/utils/common/src/main/java/brooklyn/util/text/Strings.java
index ab5a4d9..0ee44db 100644
--- a/utils/common/src/main/java/brooklyn/util/text/Strings.java
+++ b/utils/common/src/main/java/brooklyn/util/text/Strings.java
@@ -711,6 +711,7 @@ public class Strings {
/** converts a map of any objects to a map of strings, preserving nulls and invoking toString where needed */
public static Map<String, String> toStringMap(Map<?,?> map) {
+ if (map==null) return null;
Map<String,String> result = MutableMap.<String, String>of();
for (Map.Entry<?,?> e: map.entrySet()) {
result.put(String.valueOf(e.getKey()), String.valueOf(e.getValue()));
[10/11] git commit: tidy for ssh effectors,
following @aledsage code review
Posted by al...@apache.org.
tidy for ssh effectors, following @aledsage code review
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/2f595365
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/2f595365
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/2f595365
Branch: refs/heads/master
Commit: 2f595365dc7237180c60cc53e1847a0896ee2527
Parents: 6605118
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Jun 26 02:41:27 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Jun 26 02:42:29 2014 +0100
----------------------------------------------------------------------
.../brooklyn/entity/basic/BrooklynTaskTags.java | 8 +--
.../java/brooklyn/entity/basic/ConfigKeys.java | 2 +-
.../brooklyn/entity/effector/AddEffector.java | 6 +--
.../brooklyn/entity/effector/EffectorTasks.java | 10 +++-
.../brooklyn/event/feed/ssh/SshPollConfig.java | 8 ++-
.../entity/effector/EffectorTaskTest.java | 45 +++++++++++++++++
.../event/feed/ssh/SshFeedIntegrationTest.java | 51 ++++----------------
.../entity/software/ssh/SshCommandEffector.java | 2 +-
8 files changed, 80 insertions(+), 52 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f595365/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
index 32d10ef..e82c89b 100644
--- a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
+++ b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
@@ -238,11 +238,13 @@ public class BrooklynTaskTags extends TaskTags {
while (t!=null) {
Set<Object> tags = t.getTags();
if (tags.contains(EFFECTOR_TAG)) {
+ boolean match = true;
if (entity!=null && !entity.equals(getTargetOrContextEntity(t)))
- return false;
+ match = false;
if (effector!=null && !tags.contains(tagForEffectorName(effector.getName())))
- return false;
- return true;
+ match = false;
+ if (match) return true;
+ if (!allowNestedEffectorCalls) return false;
}
t = t.getSubmittedByTask();
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f595365/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java b/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
index 27a845b..d4a2978 100644
--- a/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
+++ b/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
@@ -219,7 +219,7 @@ public class ConfigKeys {
public static ConfigKey<?> newNamedInstance(String name, Map<?,?> keyDefs) {
ConfigBag defs = ConfigBag.newInstance(keyDefs);
String oldName = defs.put(NAME, name);
- if (!Strings.isBlank(oldName) && !oldName.equals(name))
+ if (oldName!=null && !oldName.equals(name))
log.warn("Dynamic key '"+oldName+"' being overridden as key '"+name+"' in "+keyDefs);
return newInstance(defs);
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f595365/core/src/main/java/brooklyn/entity/effector/AddEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/AddEffector.java b/core/src/main/java/brooklyn/entity/effector/AddEffector.java
index 3953354..3f42d07 100644
--- a/core/src/main/java/brooklyn/entity/effector/AddEffector.java
+++ b/core/src/main/java/brooklyn/entity/effector/AddEffector.java
@@ -38,9 +38,9 @@ public class AddEffector implements EntityInitializer {
((EntityInternal)entity).getMutableEntityType().addEffector(effector);
}
- public static EffectorBuilder<String> newEffectorBuilder(ConfigBag params) {
- String name = Preconditions.checkNotNull(params.get(EFFECTOR_NAME), "name must be supplied when defining an effector");
- EffectorBuilder<String> eff = Effectors.effector(String.class, name);
+ public static <T> EffectorBuilder<T> newEffectorBuilder(Class<T> type, ConfigBag params) {
+ String name = Preconditions.checkNotNull(params.get(EFFECTOR_NAME), "name must be supplied when defining an effector: %s", params);
+ EffectorBuilder<T> eff = Effectors.effector(type, name);
eff.description(params.get(EFFECTOR_DESCRIPTION));
Map<String, Object> paramDefs = params.get(EFFECTOR_PARAMETER_DEFS);
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f595365/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java b/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
index a084901..ec5f33b 100644
--- a/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
+++ b/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
@@ -81,8 +81,14 @@ public class EffectorTasks {
}
}
- /** wrapper for {@link EffectorTaskFactory} which ensures effector task tags are applied */
- public static class EffectorMarkingTaskFactory<T> implements EffectorTaskFactory<T> {
+ /** wrapper for {@link EffectorTaskFactory} which ensures effector task tags are applied to it if needed
+ * (wrapping in a task if needed); without this, {@link EffectorBody}-based effectors get it by
+ * virtue of the call to {@link #getFlagsForTaskInvocationAt(Entity, Effector)} therein
+ * but {@link EffectorTaskFactory}-based effectors generate a task without the right tags
+ * to be able to tell using {@link BrooklynTaskTags} the effector-context of the task
+ * <p>
+ * this gets applied automatically so marked as package-private */
+ static class EffectorMarkingTaskFactory<T> implements EffectorTaskFactory<T> {
private final EffectorTaskFactory<T> effectorTaskFactory;
public EffectorMarkingTaskFactory(EffectorTaskFactory<T> effectorTaskFactory) {
this.effectorTaskFactory = effectorTaskFactory;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f595365/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java b/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
index 58acbbb..65fb405 100644
--- a/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
+++ b/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
@@ -11,6 +11,7 @@ import brooklyn.event.feed.PollConfig;
import brooklyn.util.collections.MutableList;
import brooklyn.util.collections.MutableMap;
+import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
@@ -83,8 +84,12 @@ public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<
return env(Collections.singletonMap(key, val));
}
- /** add the given env params; sequence is as per {@link #env(Supplier)} */
+ /** add the given env params; sequence is as per {@link #env(Supplier)}.
+ * behaviour is undefined if the map supplied here is subsequently changed.
+ * <p>
+ * if a map's contents might change, use {@link #env(Supplier)} */
public SshPollConfig<T> env(Map<String,String> val) {
+ if (val==null) return this;
return env(Suppliers.ofInstance(val));
}
@@ -99,6 +104,7 @@ public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<
* in which they are computed and applied.
**/
public SshPollConfig<T> env(Supplier<Map<String,String>> val) {
+ Preconditions.checkNotNull(val);
dynamicEnvironmentSupplier.add(val);
return this;
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f595365/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java b/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
index e7f2d5f..92f80e3 100644
--- a/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
+++ b/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
@@ -26,6 +26,7 @@ import brooklyn.util.config.ConfigBag;
import brooklyn.util.task.DynamicSequentialTask;
import brooklyn.util.task.DynamicTasks;
import brooklyn.util.task.TaskBuilder;
+import brooklyn.util.task.Tasks;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
@@ -124,6 +125,50 @@ public class EffectorTaskTest {
checkTags(t, app, DOUBLE_2, true);
}
+ public static final Effector<Integer> DOUBLE_CALL_ABSTRACT = Effectors.effector(Integer.class, "double_call")
+ .description("doubles the given number")
+ .parameter(Integer.class, "numberToDouble")
+ .buildAbstract();
+ public static final Effector<Integer> DOUBLE_CALL = Effectors.effector(DOUBLE_CALL_ABSTRACT)
+ .impl(new EffectorBody<Integer>() {
+ @Override
+ public Integer call(ConfigBag parameters) {
+ final Entity parent = entity();
+ final Entity child = Iterables.getOnlyElement(entity().getChildren());
+
+ final Effector<Integer> DOUBLE_CHECK_ABSTRACT = Effectors.effector(Integer.class, "double_check")
+ .description("doubles the given number and checks tags, assuming double exists as an effector here")
+ .parameter(Integer.class, "numberToDouble")
+ .buildAbstract();
+ Effector<Integer> DOUBLE_CHECK = Effectors.effector(DOUBLE_CHECK_ABSTRACT)
+ .impl(new EffectorBody<Integer>() {
+ @Override
+ public Integer call(ConfigBag parameters) {
+ Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(Tasks.current(), child, null, false));
+ Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(Tasks.current(), child, DOUBLE_CHECK_ABSTRACT, false));
+ Assert.assertFalse(BrooklynTaskTags.isInEffectorTask(Tasks.current(), child, DOUBLE_1, false));
+ Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(Tasks.current(), parent, null, true));
+ Assert.assertFalse(BrooklynTaskTags.isInEffectorTask(Tasks.current(), parent, null, false));
+ Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(Tasks.current(), parent, DOUBLE_CALL_ABSTRACT, true));
+ Assert.assertFalse(BrooklynTaskTags.isInEffectorTask(Tasks.current(), parent, DOUBLE_1, true));
+
+ return entity().invoke(DOUBLE_1, parameters.getAllConfig()).getUnchecked();
+ }
+ }).build();
+
+ return child.invoke(DOUBLE_CHECK, parameters.getAllConfig()).getUnchecked();
+ }
+ }).build();
+
+
+ @Test
+ // also assert it works when the effector is defined on an entity
+ public void testNestedEffectorTag() throws Exception {
+ app.createAndManageChild(EntitySpec.create(Entity.class, DoublingEntity.class));
+ Assert.assertEquals(app.invoke(DOUBLE_CALL, MutableMap.of("numberToDouble", 3)).get(), (Integer)6);
+ }
+
+
private void checkTags(Task<Integer> t, Entity entity, Effector<?> eff, boolean shouldHaveChild) {
Assert.assertEquals(BrooklynTaskTags.getContextEntity(t), app);
Assert.assertTrue(t.getTags().contains(BrooklynTaskTags.EFFECTOR_TAG), "missing effector tag; had: "+t.getTags());
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f595365/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java b/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
index 4a6d0e8..de46391 100644
--- a/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
+++ b/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
@@ -1,7 +1,5 @@
package brooklyn.event.feed.ssh;
-import static org.testng.Assert.assertTrue;
-
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@@ -22,7 +20,6 @@ import brooklyn.event.AttributeSensor;
import brooklyn.event.basic.Sensors;
import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.test.Asserts;
import brooklyn.test.EntityTestUtils;
import brooklyn.test.entity.LocalManagementContextForTests;
import brooklyn.test.entity.TestApplication;
@@ -30,6 +27,8 @@ import brooklyn.test.entity.TestEntity;
import brooklyn.util.collections.MutableMap;
import brooklyn.util.exceptions.Exceptions;
import brooklyn.util.stream.Streams;
+import brooklyn.util.text.StringFunctions;
+import brooklyn.util.text.StringPredicates;
import brooklyn.util.time.Duration;
import brooklyn.util.time.Time;
@@ -82,12 +81,7 @@ public class SshFeedIntegrationTest {
.onSuccess(SshValueFunctions.stdout()))
.build();
- Asserts.succeedsEventually(new Runnable() {
- public void run() {
- String val = entity2.getAttribute(SENSOR_STRING);
- assertTrue(val != null);
- }});
-
+ EntityTestUtils.assertAttributeEventuallyNonNull(entity2, SENSOR_STRING);
String val = entity2.getAttribute(SENSOR_STRING);
Assert.assertTrue(val.contains("hello"), "val="+val);
Assert.assertEquals(val.trim(), "hello");
@@ -117,11 +111,8 @@ public class SshFeedIntegrationTest {
.onSuccess(SshValueFunctions.stdout()))
.build();
- Asserts.succeedsEventually(new Runnable() {
- public void run() {
- String val = entity.getAttribute(SENSOR_STRING);
- assertTrue(val != null && val.contains("hello"), "val="+val);
- }});
+ EntityTestUtils.assertAttributeEventually(entity, SENSOR_STRING,
+ Predicates.compose(Predicates.equalTo("hello"), StringFunctions.trim()));
}
@Test(groups="Integration")
@@ -136,11 +127,7 @@ public class SshFeedIntegrationTest {
.onFailure(SshValueFunctions.stderr()))
.build();
- Asserts.succeedsEventually(new Runnable() {
- public void run() {
- String val = entity.getAttribute(SENSOR_STRING);
- assertTrue(val != null && val.contains(cmd), "val="+val);
- }});
+ EntityTestUtils.assertAttributeEventually(entity, SENSOR_STRING, StringPredicates.containsLiteral(cmd));
}
@Test(groups="Integration")
@@ -157,11 +144,7 @@ public class SshFeedIntegrationTest {
}}))
.build();
- Asserts.succeedsEventually(new Runnable() {
- public void run() {
- String val = entity.getAttribute(SENSOR_STRING);
- assertTrue(val != null && val.contains("Exit status 123"), "val=" + val);
- }});
+ EntityTestUtils.assertAttributeEventually(entity, SENSOR_STRING, StringPredicates.containsLiteral("Exit status 123"));
}
@Test(groups="Integration")
@@ -188,11 +171,7 @@ public class SshFeedIntegrationTest {
Assert.assertEquals(entity2.getAttribute(SENSOR_STRING), null);
entity2.setAttribute(Attributes.SERVICE_UP, true);
- Asserts.succeedsEventually(new Runnable() {
- public void run() {
- String val = entity2.getAttribute(SENSOR_STRING);
- assertTrue(val != null && val.contains("hello"), "val="+val);
- }});
+ EntityTestUtils.assertAttributeEventually(entity2, SENSOR_STRING, StringPredicates.containsLiteral("hello"));
}
@@ -222,20 +201,10 @@ public class SshFeedIntegrationTest {
.onSuccess(SshValueFunctions.stdout()))
.build();
- Asserts.succeedsEventually(new Runnable() {
- public void run() {
- String val = entity2.getAttribute(SENSOR_STRING);
- assertTrue(val!=null);
- }});
-
+ EntityTestUtils.assertAttributeEventuallyNonNull(entity, SENSOR_STRING);
final String val1 = assertDifferentOneInOutput(entity2);
- Asserts.succeedsEventually(new Runnable() {
- public void run() {
- String val = entity2.getAttribute(SENSOR_STRING);
- Assert.assertFalse(val1.equals(val));
- }});
-
+ EntityTestUtils.assertAttributeEventually(entity2, SENSOR_STRING, Predicates.not(Predicates.equalTo(val1)));
final String val2 = assertDifferentOneInOutput(entity2);
log.info("vals from dynamic sensors are: "+val1.trim()+" and "+val2.trim());
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2f595365/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
index d119252..51fea06 100644
--- a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
+++ b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
@@ -32,7 +32,7 @@ public final class SshCommandEffector extends AddEffector {
}
public static EffectorBuilder<String> newEffectorBuilder(ConfigBag params) {
- EffectorBuilder<String> eff = AddEffector.newEffectorBuilder(params);
+ EffectorBuilder<String> eff = AddEffector.newEffectorBuilder(String.class, params);
eff.impl(new Body(eff.buildAbstract(), params));
return eff;
}
[03/11] git commit: add support for SshFeed to take a dynamic
supplier of command and environment so that these can change (eg
incorporating parameters server-side or setting shell env vars to latest
sensor values), plus test
Posted by al...@apache.org.
add support for SshFeed to take a dynamic supplier of command and environment so that these can change (eg incorporating parameters server-side or setting shell env vars to latest sensor values), plus test
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/4c9aae43
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/4c9aae43
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/4c9aae43
Branch: refs/heads/master
Commit: 4c9aae430895a39ed1d3f69b05d32cf692d67998
Parents: 0f3d6ea
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Tue Jun 3 15:16:35 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jun 24 21:59:14 2014 +0100
----------------------------------------------------------------------
.../java/brooklyn/event/feed/ssh/SshFeed.java | 13 ++--
.../brooklyn/event/feed/ssh/SshPollConfig.java | 68 +++++++++++++++----
.../event/feed/ssh/SshFeedIntegrationTest.java | 70 ++++++++++++++++++++
3 files changed, 131 insertions(+), 20 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4c9aae43/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
index 87dc79f..8aee368 100644
--- a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
@@ -22,7 +22,6 @@ import brooklyn.event.feed.Poller;
import brooklyn.location.basic.Locations;
import brooklyn.location.basic.Machines;
import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.collections.MutableMap;
import brooklyn.util.config.ConfigBag;
import brooklyn.util.internal.ssh.SshTool;
import brooklyn.util.time.Duration;
@@ -140,10 +139,10 @@ public class SshFeed extends AbstractFeed {
}
private static class SshPollIdentifier {
- final String command;
- final Map<String, String> env;
+ final Supplier<String> command;
+ final Supplier<Map<String, String>> env;
- private SshPollIdentifier(String command, Map<String, String> env) {
+ private SshPollIdentifier(Supplier<String> command, Supplier<Map<String, String>> env) {
this.command = checkNotNull(command, "command");
this.env = checkNotNull(env, "env");
}
@@ -191,9 +190,7 @@ public class SshFeed extends AbstractFeed {
@SuppressWarnings({ "unchecked", "rawtypes" })
SshPollConfig<?> configCopy = new SshPollConfig(config);
if (configCopy.getPeriod() < 0) configCopy.period(builder.period, builder.periodUnits);
- String command = config.getCommand();
- Map<String, String> env = config.getEnv();
- polls.put(new SshPollIdentifier(command, env), configCopy);
+ polls.put(new SshPollIdentifier(config.getCommandSupplier(), config.getEnvSupplier()), configCopy);
}
}
@@ -212,7 +209,7 @@ public class SshFeed extends AbstractFeed {
getPoller().scheduleAtFixedRate(
new Callable<SshPollValue>() {
public SshPollValue call() throws Exception {
- return exec(pollInfo.command, pollInfo.env);
+ return exec(pollInfo.command.get(), pollInfo.env.get());
}},
new DelegatingPollHandler<SshPollValue>(handlers),
minPeriod);
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4c9aae43/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java b/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
index 9c76c70..9e86f5b 100644
--- a/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
+++ b/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
@@ -2,20 +2,26 @@ package brooklyn.event.feed.ssh;
import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import brooklyn.event.AttributeSensor;
import brooklyn.event.feed.PollConfig;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import com.google.common.collect.Maps;
public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<T>> {
- private String command;
- private Map<String,String> env = Maps.newLinkedHashMap();
+ private Supplier<String> commandSupplier;
+ private List<Supplier<Map<String,String>>> dynamicEnvironmentSupplier = MutableList.of();
+ private Map<String,String> fixedEnvironment = Maps.newLinkedHashMap();
public static final Predicate<SshPollValue> DEFAULT_SUCCESS = new Predicate<SshPollValue>() {
@Override
@@ -30,32 +36,70 @@ public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<
public SshPollConfig(SshPollConfig<T> other) {
super(other);
- command = other.command;
- env = other.env;
+ commandSupplier = other.commandSupplier;
+ fixedEnvironment = other.fixedEnvironment;
}
+ /** @deprecated since 0.7.0; use {@link #getCommandSupplier()} and resolve just-in-time */
public String getCommand() {
- return command;
+ return getCommandSupplier().get();
+ }
+ public Supplier<String> getCommandSupplier() {
+ return commandSupplier;
}
+ /** @deprecated since 0.7.0; use {@link #getEnvSupplier()} and resolve just-in-time */
public Map<String, String> getEnv() {
- return env;
+ return getEnvSupplier().get();
+ }
+ public Supplier<Map<String,String>> getEnvSupplier() {
+ return new Supplier<Map<String,String>>() {
+ @Override
+ public Map<String, String> get() {
+ Map<String,String> result = MutableMap.of();
+ result.putAll(fixedEnvironment);
+ for (Supplier<Map<String, String>> envS: dynamicEnvironmentSupplier) {
+ if (envS!=null) {
+ Map<String, String> envM = envS.get();
+ if (envM!=null) {
+ // TODO deeply additive?
+ result.putAll(envM);
+ }
+ }
+ }
+ return result;
+ }
+ };
}
+
- public SshPollConfig<T> command(String val) {
- this.command = val;
+ public SshPollConfig<T> command(String val) { return command(Suppliers.ofInstance(val)); }
+ public SshPollConfig<T> command(Supplier<String> val) {
+ this.commandSupplier = val;
return this;
}
+ /** add the given env param */
public SshPollConfig<T> env(String key, String val) {
- env.put(checkNotNull(key, "key"), checkNotNull(val, "val"));
+ fixedEnvironment.put(checkNotNull(key, "key"), checkNotNull(val, "val"));
return this;
}
+ /** add the given env params; sequence is as per {@link #env(Supplier)} */
public SshPollConfig<T> env(Map<String,String> val) {
- for (Map.Entry<String, String> entry : checkNotNull(val, "map").entrySet()) {
- env(entry.getKey(), entry.getValue());
- }
+ return env(Suppliers.ofInstance(val));
+ }
+
+ /** adds the given dynamic env supplier.
+ * these are put into the result in the order they are applied, with fixed values
+ * set via {@link #env(String, String)} preceding all these.
+ * <p>
+ * currently addition is not deep, in the case of two identical top-level keys
+ * the latter one kills anything from the former.
+ * this behaviour may change in future. */
+ public SshPollConfig<T> env(Supplier<Map<String,String>> val) {
+ dynamicEnvironmentSupplier.add(val);
return this;
}
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4c9aae43/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java b/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
index 535bd5a..4a6d0e8 100644
--- a/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
+++ b/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
@@ -2,6 +2,11 @@ package brooklyn.event.feed.ssh;
import static org.testng.Assert.assertTrue;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
@@ -22,16 +27,21 @@ import brooklyn.test.EntityTestUtils;
import brooklyn.test.entity.LocalManagementContextForTests;
import brooklyn.test.entity.TestApplication;
import brooklyn.test.entity.TestEntity;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
import brooklyn.util.stream.Streams;
import brooklyn.util.time.Duration;
import brooklyn.util.time.Time;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
public class SshFeedIntegrationTest {
+ private static final Logger log = LoggerFactory.getLogger(SshFeedIntegrationTest.class);
+
final static AttributeSensor<String> SENSOR_STRING = Sensors.newStringSensor("aString", "");
final static AttributeSensor<Integer> SENSOR_INT = Sensors.newIntegerSensor("aLong", "");
@@ -185,4 +195,64 @@ public class SshFeedIntegrationTest {
}});
}
+
+ @Test(groups="Integration")
+ public void testDynamicEnvAndCommandSupplier() throws Exception {
+ final TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class).location(machine));
+
+ final AtomicInteger count = new AtomicInteger();
+ Supplier<Map<String, String>> envSupplier = new Supplier<Map<String,String>>() {
+ @Override
+ public Map<String, String> get() {
+ return MutableMap.of("COUNT", ""+count.incrementAndGet());
+ }
+ };
+ Supplier<String> cmdSupplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ return "echo count-"+count.incrementAndGet()+"-$COUNT";
+ }
+ };
+
+ feed = SshFeed.builder()
+ .entity(entity2)
+ .poll(new SshPollConfig<String>(SENSOR_STRING)
+ .env(envSupplier)
+ .command(cmdSupplier)
+ .onSuccess(SshValueFunctions.stdout()))
+ .build();
+
+ Asserts.succeedsEventually(new Runnable() {
+ public void run() {
+ String val = entity2.getAttribute(SENSOR_STRING);
+ assertTrue(val!=null);
+ }});
+
+ final String val1 = assertDifferentOneInOutput(entity2);
+
+ Asserts.succeedsEventually(new Runnable() {
+ public void run() {
+ String val = entity2.getAttribute(SENSOR_STRING);
+ Assert.assertFalse(val1.equals(val));
+ }});
+
+ final String val2 = assertDifferentOneInOutput(entity2);
+ log.info("vals from dynamic sensors are: "+val1.trim()+" and "+val2.trim());
+ }
+
+ private String assertDifferentOneInOutput(final TestEntity entity2) {
+ String val = entity2.getAttribute(SENSOR_STRING);
+ Assert.assertTrue(val.startsWith("count"), "val="+val);
+ try {
+ String[] fields = val.trim().split("-");
+ int field1 = Integer.parseInt(fields[1]);
+ int field2 = Integer.parseInt(fields[2]);
+ Assert.assertEquals(Math.abs(field2-field1), 1, "expected difference of 1");
+ } catch (Throwable t) {
+ Exceptions.propagateIfFatal(t);
+ Assert.fail("Wrong output from sensor, got '"+val.trim()+"', giving error: "+t);
+ }
+ return val;
+ }
+
}
[02/11] git commit: ensure effector tasks are always tagged
appropriately,
fixing bug where some direct invocations did not get tagged correctly and
would be GC'd immediately on completion, and adding test
Posted by al...@apache.org.
ensure effector tasks are always tagged appropriately, fixing bug where some direct invocations did not get tagged correctly and would be GC'd immediately on completion, and adding test
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/6976d59f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/6976d59f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/6976d59f
Branch: refs/heads/master
Commit: 6976d59fd70e0b7be7c74160fa7f782237495d2a
Parents: 985951c
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Jun 2 14:18:34 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jun 24 21:53:28 2014 +0100
----------------------------------------------------------------------
.../brooklyn/entity/basic/BrooklynTaskTags.java | 44 ++++++++++++++++++++
.../brooklyn/entity/effector/EffectorTasks.java | 27 ++++++++++++
.../brooklyn/entity/effector/Effectors.java | 3 +-
.../management/internal/EffectorUtils.java | 4 +-
.../entity/effector/EffectorTaskTest.java | 37 +++++++++++++---
5 files changed, 108 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6976d59f/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
index 1682dea..efe635a 100644
--- a/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
+++ b/core/src/main/java/brooklyn/entity/basic/BrooklynTaskTags.java
@@ -8,6 +8,7 @@ import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import brooklyn.entity.Effector;
import brooklyn.entity.Entity;
import brooklyn.management.ExecutionManager;
import brooklyn.management.Task;
@@ -201,4 +202,47 @@ public class BrooklynTaskTags extends TaskTags {
addTagDynamically(task, INESSENTIAL_TASK);
}
+ // ------ effector tags
+
+ public static String tagForEffectorName(String name) {
+ return EFFECTOR_TAG+":"+name;
+ }
+
+ /**
+ * checks if the given task is part of the given effector call on the given entity;
+ * @param task the task to check (false if null)
+ * @param entity the entity where this effector task should be running, or any entity if null
+ * @param effector the effector (matching name) where this task should be running, or any effector if null
+ * @param allowNestedEffectorCalls whether to match ancestor effector calls, e.g. if eff1 calls eff2,
+ * and we are checking eff2, whether to match eff1
+ * @return whether the given task is part of the given effector
+ */
+ public static boolean isInEffectorTask(Task<?> task, Entity entity, Effector<?> effector, boolean allowNestedEffectorCalls) {
+ Task<?> t = task;
+ while (t!=null) {
+ Set<Object> tags = t.getTags();
+ if (tags.contains(EFFECTOR_TAG)) {
+ if (entity!=null && !entity.equals(getTargetOrContextEntity(t)))
+ return false;
+ if (effector!=null && !tags.contains(tagForEffectorName(effector.getName())))
+ return false;
+ return true;
+ }
+ t = t.getSubmittedByTask();
+ }
+ return false;
+ }
+
+ public static String getEffectorName(Task<?> task) {
+ Task<?> t = task;
+ while (t!=null) {
+ for (Object tag: task.getTags()) {
+ if (tag instanceof String && ((String)tag).startsWith(EFFECTOR_TAG+":"))
+ return Strings.removeFromStart((String)tag, EFFECTOR_TAG+":");
+ }
+ t = t.getSubmittedByTask();
+ }
+ return null;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6976d59f/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java b/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
index 91995e2..a084901 100644
--- a/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
+++ b/core/src/main/java/brooklyn/entity/effector/EffectorTasks.java
@@ -41,6 +41,8 @@ public class EffectorTasks {
public abstract TaskAdaptable<T> newTask(Entity entity, Effector<T> effector, ConfigBag parameters);
}
+ /** wrapper for {@link EffectorBody} which simply runs that body on each invocation;
+ * the body must be thread safe and ideally stateless */
public static class EffectorBodyTaskFactory<T> implements EffectorTaskFactory<T> {
private final EffectorBody<T> effectorBody;
public EffectorBodyTaskFactory(EffectorBody<T> effectorBody) {
@@ -79,6 +81,31 @@ public class EffectorTasks {
}
}
+ /** wrapper for {@link EffectorTaskFactory} which ensures effector task tags are applied */
+ public static class EffectorMarkingTaskFactory<T> implements EffectorTaskFactory<T> {
+ private final EffectorTaskFactory<T> effectorTaskFactory;
+ public EffectorMarkingTaskFactory(EffectorTaskFactory<T> effectorTaskFactory) {
+ this.effectorTaskFactory = effectorTaskFactory;
+ }
+
+ @Override
+ public Task<T> newTask(final Entity entity, final brooklyn.entity.Effector<T> effector, final ConfigBag parameters) {
+ if (effectorTaskFactory instanceof EffectorBodyTaskFactory)
+ return effectorTaskFactory.newTask(entity, effector, parameters).asTask();
+ // if we're in an effector context for this effector already, then also pass through
+ if (BrooklynTaskTags.isInEffectorTask(Tasks.current(), entity, effector, false))
+ return effectorTaskFactory.newTask(entity, effector, parameters).asTask();
+ // otherwise, create the task inside an appropriate effector body so tags, name, etc are set correctly
+ return new EffectorBodyTaskFactory<T>(new EffectorBody<T>() {
+ @Override
+ public T call(ConfigBag parameters) {
+ TaskAdaptable<T> t = DynamicTasks.queue(effectorTaskFactory.newTask(entity, effector, parameters));
+ return t.asTask().getUnchecked();
+ }
+ }).newTask(entity, effector, parameters);
+ }
+ }
+
public static <T> ConfigKey<T> asConfigKey(ParameterType<T> t) {
return ConfigKeys.newConfigKey(t.getParameterClass(), t.getName());
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6976d59f/core/src/main/java/brooklyn/entity/effector/Effectors.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/Effectors.java b/core/src/main/java/brooklyn/entity/effector/Effectors.java
index 72cedd6..3a28f4e 100644
--- a/core/src/main/java/brooklyn/entity/effector/Effectors.java
+++ b/core/src/main/java/brooklyn/entity/effector/Effectors.java
@@ -20,6 +20,7 @@ import brooklyn.entity.basic.Entities;
import brooklyn.entity.basic.EntityInternal;
import brooklyn.entity.basic.EntityLocal;
import brooklyn.entity.effector.EffectorTasks.EffectorBodyTaskFactory;
+import brooklyn.entity.effector.EffectorTasks.EffectorMarkingTaskFactory;
import brooklyn.entity.effector.EffectorTasks.EffectorTaskFactory;
import brooklyn.management.TaskAdaptable;
import brooklyn.util.collections.MutableMap;
@@ -71,7 +72,7 @@ public class Effectors {
return this;
}
public EffectorBuilder<T> impl(EffectorTaskFactory<T> taskFactory) {
- this.impl = taskFactory;
+ this.impl = new EffectorMarkingTaskFactory<T>(taskFactory);
return this;
}
public EffectorBuilder<T> impl(EffectorBody<T> effectorBody) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6976d59f/core/src/main/java/brooklyn/management/internal/EffectorUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/EffectorUtils.java b/core/src/main/java/brooklyn/management/internal/EffectorUtils.java
index 344b173..2cadccf 100644
--- a/core/src/main/java/brooklyn/management/internal/EffectorUtils.java
+++ b/core/src/main/java/brooklyn/management/internal/EffectorUtils.java
@@ -302,7 +302,9 @@ public class EffectorUtils {
return MutableMap.builder()
.put("description", "Invoking effector "+effector.getName()+" on "+entity.getDisplayName())
.put("displayName", effector.getName())
- .put("tags", MutableList.of(ManagementContextInternal.EFFECTOR_TAG,
+ .put("tags", MutableList.of(
+ BrooklynTaskTags.EFFECTOR_TAG,
+ BrooklynTaskTags.tagForEffectorName(effector.getName()),
BrooklynTaskTags.tagForTargetEntity(entity)))
.build();
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6976d59f/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java b/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
index eef05b2..e7f2d5f 100644
--- a/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
+++ b/core/src/test/java/brooklyn/entity/effector/EffectorTaskTest.java
@@ -11,14 +11,12 @@ import brooklyn.entity.Effector;
import brooklyn.entity.Entity;
import brooklyn.entity.basic.AbstractEntity;
import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.BrooklynTaskTags;
import brooklyn.entity.basic.Entities;
import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.effector.EffectorBody;
-import brooklyn.entity.effector.EffectorTasks;
-import brooklyn.entity.effector.EffectorWithBody;
-import brooklyn.entity.effector.Effectors;
import brooklyn.entity.effector.EffectorTasks.EffectorTaskFactory;
import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
import brooklyn.management.HasTaskChildren;
import brooklyn.management.Task;
import brooklyn.test.entity.TestApplication;
@@ -72,7 +70,14 @@ public class EffectorTaskTest {
}
@Test
- // also assert it works when the entity is defined on an entity
+ public void testSyntaxOneTaggedCorrectly() throws Exception {
+ Task<Integer> t = app.invoke(DOUBLE_1, MutableMap.of("numberToDouble", 3));
+ t.get();
+ checkTags(t, app, DOUBLE_1, false);
+ }
+
+ @Test
+ // also assert it works when the effector is defined on an entity
public void testSimpleEffectorOnEntity() throws Exception {
Entity doubler = app.createAndManageChild(EntitySpec.create(Entity.class, DoublingEntity.class));
@@ -112,6 +117,28 @@ public class EffectorTaskTest {
Assert.assertEquals(app.invoke(DOUBLE_2, MutableMap.of("numberToDouble", 3)).get(), (Integer)6);
}
+ @Test
+ public void testEffectorImplTaggedCorrectly() throws Exception {
+ Task<Integer> t = app.invoke(DOUBLE_2, MutableMap.of("numberToDouble", 3));
+ t.get();
+ checkTags(t, app, DOUBLE_2, true);
+ }
+
+ private void checkTags(Task<Integer> t, Entity entity, Effector<?> eff, boolean shouldHaveChild) {
+ Assert.assertEquals(BrooklynTaskTags.getContextEntity(t), app);
+ Assert.assertTrue(t.getTags().contains(BrooklynTaskTags.EFFECTOR_TAG), "missing effector tag; had: "+t.getTags());
+ Assert.assertTrue(t.getDescription().contains(eff.getName()), "description missing effector name: "+t.getDescription());
+ Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(t, entity, eff, false));
+ Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(t, null, null, false));
+ Assert.assertFalse(BrooklynTaskTags.isInEffectorTask(t, entity, Startable.START, false));
+
+ if (shouldHaveChild) {
+ Task<?> subtask = ((HasTaskChildren)t).getChildren().iterator().next();
+ Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(subtask, entity, eff, false));
+ Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(subtask, null, null, false));
+ }
+ }
+
// TEST parameter task missing
// ----------------- syntax for more complex -- an effector using subtasks
[11/11] git commit: This closes #15
Posted by al...@apache.org.
This closes #15
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/51f47ed5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/51f47ed5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/51f47ed5
Branch: refs/heads/master
Commit: 51f47ed5fc3c863130bedb4d001bf1e4d70e65a3
Parents: 1bec9cc 2f59536
Author: Aled Sage <al...@gmail.com>
Authored: Thu Jun 26 11:38:51 2014 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Jun 26 11:38:51 2014 +0100
----------------------------------------------------------------------
.../entity/proxying/EntityInitializer.java | 9 ++
.../brooklyn/entity/basic/BrooklynTaskTags.java | 62 ++++++++
.../java/brooklyn/entity/basic/ConfigKeys.java | 47 +++++++
.../brooklyn/entity/effector/AddEffector.java | 68 +++++++++
.../brooklyn/entity/effector/AddSensor.java | 44 ++++++
.../brooklyn/entity/effector/EffectorTasks.java | 33 +++++
.../brooklyn/entity/effector/Effectors.java | 22 ++-
.../java/brooklyn/event/feed/AbstractFeed.java | 7 +-
.../java/brooklyn/event/feed/FeedConfig.java | 9 ++
.../main/java/brooklyn/event/feed/Poller.java | 16 ++-
.../event/feed/function/FunctionFeed.java | 11 +-
.../java/brooklyn/event/feed/http/HttpFeed.java | 8 +-
.../brooklyn/event/feed/shell/ShellFeed.java | 5 +-
.../java/brooklyn/event/feed/ssh/SshFeed.java | 69 +++++----
.../brooklyn/event/feed/ssh/SshPollConfig.java | 83 ++++++++---
.../management/internal/EffectorUtils.java | 4 +-
.../java/brooklyn/util/flags/TypeCoercions.java | 4 +-
.../internal/AbstractProcessTaskFactory.java | 7 +-
.../entity/effector/EffectorTaskTest.java | 82 ++++++++++-
.../java/brooklyn/event/feed/PollerTest.java | 2 +-
.../event/feed/ssh/SshFeedIntegrationTest.java | 141 ++++++++++++++++---
.../defining-applications/creating-yaml.md | 47 ++++++-
.../vanilla-bash-netcat-restarter.yaml | 3 +-
.../vanilla-bash-netcat-w-client.yaml | 72 ++++++++++
.../defining-applications/yaml-reference.md | 29 ++--
.../basic/AbstractSoftwareProcessSshDriver.java | 4 +
.../entity/basic/lifecycle/ScriptHelper.java | 8 ++
.../brooklyn/entity/chef/ChefAttributeFeed.java | 8 +-
.../entity/software/ssh/SshCommandEffector.java | 83 +++++++++++
.../entity/software/ssh/SshCommandSensor.java | 87 ++++++++++++
.../java/brooklyn/event/feed/jmx/JmxFeed.java | 14 +-
.../event/feed/jmx/JmxOperationPollConfig.java | 1 +
.../software/ssh/SshCommandIntegrationTest.java | 108 ++++++++++++++
.../brooklyn/VanillaBashNetcatYamlTest.java | 95 +++++++++++++
.../resources/vanilla-bash-netcat-w-client.yaml | 74 ++++++++++
.../main/java/brooklyn/util/text/Strings.java | 1 +
36 files changed, 1260 insertions(+), 107 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/51f47ed5/utils/common/src/main/java/brooklyn/util/text/Strings.java
----------------------------------------------------------------------
[08/11] git commit: docs for entity intializers,
ssh sensors and effectors, and example with nice integration test
Posted by al...@apache.org.
docs for entity intializers, ssh sensors and effectors, and example with nice integration test
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/380eff6b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/380eff6b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/380eff6b
Branch: refs/heads/master
Commit: 380eff6bbb84f8016206f13a8c7d465de38aa8d1
Parents: b10f836
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Tue Jun 3 16:34:59 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jun 24 21:59:15 2014 +0100
----------------------------------------------------------------------
.../brooklyn/entity/effector/Effectors.java | 2 +-
.../defining-applications/creating-yaml.md | 47 ++++++++--
.../vanilla-bash-netcat-restarter.yaml | 3 +-
.../vanilla-bash-netcat-w-client.yaml | 72 +++++++++++++++
.../defining-applications/yaml-reference.md | 29 ++++--
.../brooklyn/VanillaBashNetcatYamlTest.java | 95 ++++++++++++++++++++
.../resources/vanilla-bash-netcat-w-client.yaml | 74 +++++++++++++++
7 files changed, 306 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/380eff6b/core/src/main/java/brooklyn/entity/effector/Effectors.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/Effectors.java b/core/src/main/java/brooklyn/entity/effector/Effectors.java
index 684bcbe..46ae767 100644
--- a/core/src/main/java/brooklyn/entity/effector/Effectors.java
+++ b/core/src/main/java/brooklyn/entity/effector/Effectors.java
@@ -79,7 +79,7 @@ public class Effectors {
}
/** returns the effector, with an implementation (required); @see {@link #buildAbstract()} */
public Effector<T> build() {
- Preconditions.checkNotNull(impl, "Cannot create effector {} with no impl (did you forget impl? or did you mean to buildAbstract?)", effectorName);
+ Preconditions.checkNotNull(impl, "Cannot create effector %s with no impl (did you forget impl? or did you mean to buildAbstract?)", effectorName);
return new EffectorAndBody<T>(effectorName, returnType, ImmutableList.copyOf(parameters.values()), description, impl);
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/380eff6b/docs/use/guide/defining-applications/creating-yaml.md
----------------------------------------------------------------------
diff --git a/docs/use/guide/defining-applications/creating-yaml.md b/docs/use/guide/defining-applications/creating-yaml.md
index 087f136..1ccd2bd 100644
--- a/docs/use/guide/defining-applications/creating-yaml.md
+++ b/docs/use/guide/defining-applications/creating-yaml.md
@@ -323,10 +323,12 @@ we can instead write our example as:
The one requirement of the script is that it store the process ID (PID) in the file
pointed to by `$PID_FILE`, hence the second line of the script.
This is because Brooklyn wants to monitor the services under management.
-(There are other options, as documented on the Javadoc of the `VanillaSoftwareProcess` class.)
+(There are other options; you can set `checkRunning.command` and `stop.command` instead,
+as documented on the Javadoc of the `VanillaSoftwareProcess` class,
+and those scripts will be used instead of checking and stopping the process whose PID is in `$PID_FILE`.)
+
And indeed, once you've run one `telnet` to the server, you'll see that the
service has gone "on fire" in Brooklyn -- because the `nc` process has stopped.
-
Besides detecting this failure, Brooklyn policies can be added to the YAML to take appropriate
action. A simple recovery here might just be to restart the process:
@@ -343,13 +345,48 @@ This makes it easy to configure various aspects, such as to delay to see if the
Running with this blueprint, you'll see that the service shows as on fire for 15s after a `telnet`,
before the policy restarts it.
-This of course is a simple example, but it shows many of the building blocks used in real-world blueprints,
-and how often they can be easily described and combined in Brooklyn YAML blueprints.
+For an even more interesting way to test it, look at the blueprint defining
+[a netcat server and client](example_yaml/vanilla-bash-netcat-w-client.yaml).
+This uses `initializers` to define an effector to `sayHiNetcat` on the `Simple Pinger` client,
+using `env` variables to inject the `netcat-server` location and
+`parameters` to pass in per-effector data:
+
+ env:
+ TARGET_HOSTNAME: $brooklyn:component("netcat-server").attributeWhenReady("host.name")
+ brooklyn.initializers:
+ - type: brooklyn.entity.software.ssh.SshCommandEffector
+ brooklyn.config:
+ name: sayHiNetcat
+ description: Echo a small hello string to the netcat entity
+ command: |
+ echo $message | nc $TARGET_HOSTNAME 4321
+ parameters:
+ message:
+ description: The string to pass to netcat
+ defaultValue: hi netcat
+
+This blueprint also uses initializers to define sensors on the `netcat-server` entity
+so that the `$message` we passed above gets logged and reported back:
+
+ launch.command: |
+ echo hello | nc -l 4321 >> server-input &
+ echo $! > $PID_FILE
+ brooklyn.initializers:
+ - type: brooklyn.entity.software.ssh.SshCommandSensor
+ brooklyn.config:
+ name: output.last
+ command: tail -1 server-input
+
+This is still a simple example, but worth going through carefully.
+It shows many of the building blocks used in real-world blueprints,
+and how they can often be easily described and combined in Brooklyn YAML blueprints.
+Next, if you need to drive off-piste, or you want to write tests against these blueprints,
+have a look at, for example, `VanillaBashNetcatYamlTest.java` in the Brooklyn codebase,
+or follow the other references below.
<!--
TODO building up children entities
-TODO adding sensors and effectors
-->
<!--
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/380eff6b/docs/use/guide/defining-applications/example_yaml/vanilla-bash-netcat-restarter.yaml
----------------------------------------------------------------------
diff --git a/docs/use/guide/defining-applications/example_yaml/vanilla-bash-netcat-restarter.yaml b/docs/use/guide/defining-applications/example_yaml/vanilla-bash-netcat-restarter.yaml
index c469b01..2d8cb6e 100644
--- a/docs/use/guide/defining-applications/example_yaml/vanilla-bash-netcat-restarter.yaml
+++ b/docs/use/guide/defining-applications/example_yaml/vanilla-bash-netcat-restarter.yaml
@@ -1,7 +1,8 @@
-name: Simple Netcat Server with Restart
+name: Simple Netcat Example with Restarter Policy
location: localhost
services:
- type: brooklyn.entity.basic.VanillaSoftwareProcess
+ id: netcat-server
name: Simple Netcat Server
launch.command: |
echo hello | nc -l 4321 &
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/380eff6b/docs/use/guide/defining-applications/example_yaml/vanilla-bash-netcat-w-client.yaml
----------------------------------------------------------------------
diff --git a/docs/use/guide/defining-applications/example_yaml/vanilla-bash-netcat-w-client.yaml b/docs/use/guide/defining-applications/example_yaml/vanilla-bash-netcat-w-client.yaml
new file mode 100644
index 0000000..50051db
--- /dev/null
+++ b/docs/use/guide/defining-applications/example_yaml/vanilla-bash-netcat-w-client.yaml
@@ -0,0 +1,72 @@
+name: Simple Netcat with Client
+
+location: localhost
+
+services:
+
+# the netcat server instance, running in listener mode (-l)
+- type: brooklyn.entity.basic.VanillaSoftwareProcess
+ id: netcat-server
+ name: Simple Netcat Server
+ launch.command: |
+ echo hello | nc -l 4321 >> server-input &
+ echo $! > $PID_FILE
+
+ brooklyn.policies:
+ # restarter and
+ - policyType: brooklyn.policy.ha.ServiceFailureDetector
+ brooklyn.config:
+ # wait 15s after service fails before propagating failure
+ serviceFailedStabilizationDelay: 15s
+ - policyType: brooklyn.policy.ha.ServiceRestarter
+ brooklyn.config:
+ # repeated failures in a time window can cause the restarter to abort,
+ # propagating the failure; a time window of 0 will mean it always restarts!
+ failOnRecurringFailuresInThisDuration: 0
+
+ brooklyn.initializers:
+ # two sensors, recording the data sent to this netcat server:
+
+ - type: brooklyn.entity.software.ssh.SshCommandSensor
+ brooklyn.config:
+ name: output.last
+ command: tail -1 server-input
+
+ - type: brooklyn.entity.software.ssh.SshCommandSensor
+ brooklyn.config:
+ name: output.all
+ command: cat server-input
+
+# a client to hit netcat
+- type: brooklyn.entity.basic.VanillaSoftwareProcess
+ name: Simple Pinger
+
+ # set the hostname of the netcat instance as an env var for the scripts
+ env:
+ TARGET_HOSTNAME: $brooklyn:component("netcat-server").attributeWhenReady("host.name")
+
+ # start/check/stop are no-op
+ launch.command: ""
+ checkRunning.command: ""
+ stop.command: ""
+
+ brooklyn.initializers:
+ # but there is a sample effector which runs nc in client mode
+
+ - type: brooklyn.entity.software.ssh.SshCommandEffector
+ brooklyn.config:
+ name: sayHiNetcat
+ description: Echo a small hello string to the netcat entity
+ command: |
+ echo $message | nc $TARGET_HOSTNAME 4321
+ parameters:
+ message:
+ description: The string to pass to netcat
+ defaultValue: hi netcat
+
+# and add an enricher at the root so all sensors from netcat-server are visible on the root
+brooklyn.enrichers:
+- enricherType: brooklyn.enricher.basic.Propagator
+ brooklyn.config:
+ enricher.producer: $brooklyn:component("netcat-server")
+ enricher.propagating.propagatingAll: true
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/380eff6b/docs/use/guide/defining-applications/yaml-reference.md
----------------------------------------------------------------------
diff --git a/docs/use/guide/defining-applications/yaml-reference.md b/docs/use/guide/defining-applications/yaml-reference.md
index 18e41e0..3ca1157 100644
--- a/docs/use/guide/defining-applications/yaml-reference.md
+++ b/docs/use/guide/defining-applications/yaml-reference.md
@@ -22,7 +22,9 @@ defining types:
* `io.brooklyn.package.JavaEntityClass`
* `java:io.brooklyn.package.JavaEntityClass`
-* *OSGi and YAML references are TODO*
+* *OSGi and YAML references are coming soon!*
+
+A reference of some of the common service `type` instances used is included in a section below.
Within the `ServiceSpecification`, other key-value pairs can be supplied to customize
the entity being defined, with these being the most common:
@@ -44,11 +46,22 @@ the entity being defined, with these being the most common:
An `EntityInitiailzer` can perform arbitrary customization to an entity whilst it is being constructed,
such as adding dynamic sensors and effectors. These classes must expose a public constructor taking
a single `Map` where the `brooklyn.config` is passed in.
-
-Entities may accept additional key-value pairs,
-usually documented on the entity.
-These typically consist of the config keys or flags (indicated by `@SetFromFlag`) declared on the entity class itself.
-Declared flags and config keys may be passed in at the root of the `ServiceSpecification` or in `brooklyn.config`.
+ Some common initializers are:
+
+ * `brooklyn.entity.software.ssh.SshCommandEffector`: takes a `name` and `command`,
+ and optionally a map of named `parameters` to their `description` and `defaultValue`,
+ to define an effector with the given name implemented by the given SSH command
+ (on an entity which as an ssh-able machine)
+
+ * `brooklyn.entity.software.ssh.SshCommandSensor`: takes a `name` and `command`,
+ and optionally a `period`, to create a sensor feed which populates the sensor with
+ the given name by running the given command (on an entity which as an ssh-able machine)
+
+Entities, policies, and initializers may accept additional key-value pairs,
+usually documented in their documentation (e.g. javadoc), or in the case of Java
+often as static fields in the underlying Java class.
+Often there are config keys or flags (indicated by `@SetFromFlag`) declared on the class;
+these declared flags and config keys may be passed in at the root of the `ServiceSpecification` or in `brooklyn.config`.
(Undeclared config is only accepted in the `brooklyn.config` map.)
@@ -160,7 +173,7 @@ All entities support configuration via YAML, but these entities in particular
have been designed for general purpose use from YAML. Consult the Javadoc for these
elements for more information:
-* **Vanilla Software** in `VanillaSoftareProcess`: makes it very easy to build entities
+* **Vanilla Software** in `VanillaSoftwareProcess`: makes it very easy to build entities
which use `bash` commands to install and the PID to stop and restart
* **Chef** in `ChefSoftwareProcess`: makes it easy to use Chef cookbooks to build entities,
either with recipes following conventions or with configuration in the `ServiceSpecification`
@@ -168,5 +181,3 @@ elements for more information:
* `DynamicCluster`: provides resizable clusters given a `memberSpec` set with `$brooklyn.entitySpec(Map)` as described above
* `DynamicFabric`: provides a set of homogeneous instances started in different locations,
with an effector to `addLocation`, i.e. add a new instance in a given location, at runtime
-
-
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/380eff6b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/VanillaBashNetcatYamlTest.java
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/VanillaBashNetcatYamlTest.java b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/VanillaBashNetcatYamlTest.java
new file mode 100644
index 0000000..1273d6c
--- /dev/null
+++ b/usage/camp/src/test/java/io/brooklyn/camp/brooklyn/VanillaBashNetcatYamlTest.java
@@ -0,0 +1,95 @@
+package io.brooklyn.camp.brooklyn;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.Effector;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityPredicates;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.effector.Effectors;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.Sensors;
+import brooklyn.management.Task;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.net.Networking;
+import brooklyn.util.text.StringPredicates;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+@Test
+public class VanillaBashNetcatYamlTest extends AbstractYamlTest {
+
+ private static final Logger log = LoggerFactory.getLogger(VanillaBashNetcatYamlTest.class);
+
+ private static final AttributeSensor<String> SENSOR_OUTPUT_ALL = Sensors.newStringSensor("output.all");
+ final static Effector<String> EFFECTOR_SAY_HI = Effectors.effector(String.class, "sayHiNetcat").buildAbstract();
+
+ @Test(groups="Integration")
+ public void testInvocationSensorAndEnricher() throws Exception {
+ Preconditions.checkArgument(Networking.isPortAvailable(4321), "port 4321 must not be in use (no leaked nc instances) for this test to succeed!");
+
+ Entity app = createAndStartApplication("vanilla-bash-netcat-w-client.yaml");
+ waitForApplicationTasks(app);
+
+ log.info("App started:");
+ Entities.dumpInfo(app);
+
+ Assert.assertEquals(app.getDisplayName(), "Simple Netcat with Client");
+
+ // comparing by plan ID is one common way
+ Iterable<Entity> netcatI = Iterables.filter(app.getChildren(), EntityPredicates.configEqualTo(BrooklynCampConstants.PLAN_ID, "netcat-server"));
+ Assert.assertTrue(netcatI.iterator().hasNext(), "no 'netcat-server' child of app: "+app.getChildren());
+ Entity netcat = Iterables.getOnlyElement(netcatI);
+
+ // make sure netcat is running
+ EntityTestUtils.assertAttributeEventually(netcat, Attributes.SERVICE_STATE, Predicates.equalTo(Lifecycle.RUNNING));
+
+ // find the pinger, now comparing by name
+ Iterable<Entity> pingerI = Iterables.filter(app.getChildren(), EntityPredicates.displayNameEqualTo("Simple Pinger"));
+ Assert.assertTrue(pingerI.iterator().hasNext(), "no 'Simple Pinger' child of app: "+app.getChildren());
+ Entity pinger = Iterables.getOnlyElement(pingerI);
+
+ // invoke effector
+ Task<String> ping;
+ ping = pinger.invoke(EFFECTOR_SAY_HI, MutableMap.<String,Object>of());
+ Assert.assertEquals(ping.get().trim(), "hello");
+ // and check we get the right result
+ EntityTestUtils.assertAttributeEventually(netcat, SENSOR_OUTPUT_ALL, StringPredicates.containsLiteral("hi netcat"));
+ log.info("invoked ping from "+pinger+" to "+netcat+", 'all' sensor shows:\n"+
+ netcat.getAttribute(SENSOR_OUTPUT_ALL));
+
+ // netcat should now fail and restart
+ EntityTestUtils.assertAttributeEventually(netcat, Attributes.SERVICE_STATE, Predicates.not(Predicates.equalTo(Lifecycle.RUNNING)));
+ log.info("detected failure, state is: "+netcat.getAttribute(Attributes.SERVICE_STATE));
+ EntityTestUtils.assertAttributeEventually(netcat, Attributes.SERVICE_STATE, Predicates.equalTo(Lifecycle.RUNNING));
+ log.info("detected recovery, state is: "+netcat.getAttribute(Attributes.SERVICE_STATE));
+
+ // invoke effector again, now with a parameter
+ ping = pinger.invoke(EFFECTOR_SAY_HI, MutableMap.<String,Object>of("message", "yo yo yo"));
+ Assert.assertEquals(ping.get().trim(), "hello");
+ // checking right result
+ EntityTestUtils.assertAttributeEventually(netcat, SENSOR_OUTPUT_ALL, StringPredicates.containsLiteral("yo yo yo"));
+ log.info("invoked ping again from "+pinger+" to "+netcat+", 'all' sensor shows:\n"+
+ netcat.getAttribute(SENSOR_OUTPUT_ALL));
+
+ // and it's propagated to the app
+ EntityTestUtils.assertAttributeEventually(app, Sensors.newStringSensor("output.last"), StringPredicates.containsLiteral("yo yo yo"));
+
+ log.info("after all is said and done, app is:");
+ Entities.dumpInfo(app);
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return log;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/380eff6b/usage/camp/src/test/resources/vanilla-bash-netcat-w-client.yaml
----------------------------------------------------------------------
diff --git a/usage/camp/src/test/resources/vanilla-bash-netcat-w-client.yaml b/usage/camp/src/test/resources/vanilla-bash-netcat-w-client.yaml
new file mode 100644
index 0000000..273c23c
--- /dev/null
+++ b/usage/camp/src/test/resources/vanilla-bash-netcat-w-client.yaml
@@ -0,0 +1,74 @@
+# should be the same as what's in the docs, apart from this line
+
+name: Simple Netcat with Client
+
+location: localhost
+
+services:
+
+# the netcat server instance, running in listener mode (-l)
+- type: brooklyn.entity.basic.VanillaSoftwareProcess
+ id: netcat-server
+ name: Simple Netcat Server
+ launch.command: |
+ echo hello | nc -l 4321 >> server-input &
+ echo $! > $PID_FILE
+
+ brooklyn.policies:
+ # restarter and
+ - policyType: brooklyn.policy.ha.ServiceFailureDetector
+ brooklyn.config:
+ # wait 15s after service fails before propagating failure
+ serviceFailedStabilizationDelay: 15s
+ - policyType: brooklyn.policy.ha.ServiceRestarter
+ brooklyn.config:
+ # repeated failures in a time window can cause the restarter to abort,
+ # propagating the failure; a time window of 0 will mean it always restarts!
+ failOnRecurringFailuresInThisDuration: 0
+
+ brooklyn.initializers:
+ # two sensors, recording the data sent to this netcat server:
+
+ - type: brooklyn.entity.software.ssh.SshCommandSensor
+ brooklyn.config:
+ name: output.last
+ command: tail -1 server-input
+
+ - type: brooklyn.entity.software.ssh.SshCommandSensor
+ brooklyn.config:
+ name: output.all
+ command: cat server-input
+
+# a client to hit netcat
+- type: brooklyn.entity.basic.VanillaSoftwareProcess
+ name: Simple Pinger
+
+ # set the hostname of the netcat instance as an env var for the scripts
+ env:
+ TARGET_HOSTNAME: $brooklyn:component("netcat-server").attributeWhenReady("host.name")
+
+ # start/check/stop are no-op
+ launch.command: ""
+ checkRunning.command: ""
+ stop.command: ""
+
+ brooklyn.initializers:
+ # but there is a sample effector which runs nc in client mode
+
+ - type: brooklyn.entity.software.ssh.SshCommandEffector
+ brooklyn.config:
+ name: sayHiNetcat
+ description: Echo a small hello string to the netcat entity
+ command: |
+ echo $message | nc $TARGET_HOSTNAME 4321
+ parameters:
+ message:
+ description: The string to pass to netcat
+ defaultValue: hi netcat
+
+# and add an enricher at the root so all sensors from netcat-server are visible on the root
+brooklyn.enrichers:
+- enricherType: brooklyn.enricher.basic.Propagator
+ brooklyn.config:
+ enricher.producer: $brooklyn:component("netcat-server")
+ enricher.propagating.propagatingAll: true
[09/11] git commit: comment tidy and remove some warnings
Posted by al...@apache.org.
comment tidy and remove some warnings
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/6605118c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/6605118c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/6605118c
Branch: refs/heads/master
Commit: 6605118c5b23aa5274fc5dfc41e7453e4cc091e0
Parents: 380eff6
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Jun 9 08:23:00 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jun 24 21:59:55 2014 +0100
----------------------------------------------------------------------
.../brooklyn/entity/effector/AddSensor.java | 2 +-
.../event/feed/function/FunctionFeed.java | 3 +-
.../brooklyn/event/feed/shell/ShellFeed.java | 5 +--
.../java/brooklyn/event/feed/ssh/SshFeed.java | 2 +-
.../brooklyn/event/feed/ssh/SshPollConfig.java | 35 ++++++++++----------
.../defining-applications/yaml-reference.md | 2 +-
.../entity/software/ssh/SshCommandSensor.java | 6 ----
.../java/brooklyn/event/feed/jmx/JmxFeed.java | 14 ++++----
.../event/feed/jmx/JmxOperationPollConfig.java | 1 +
9 files changed, 35 insertions(+), 35 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6605118c/core/src/main/java/brooklyn/entity/effector/AddSensor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/AddSensor.java b/core/src/main/java/brooklyn/entity/effector/AddSensor.java
index 165e1a6..ccb82c2 100644
--- a/core/src/main/java/brooklyn/entity/effector/AddSensor.java
+++ b/core/src/main/java/brooklyn/entity/effector/AddSensor.java
@@ -15,7 +15,7 @@ import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
/** Entity initializer which adds a sensor to an entity's type definition.
- * TODO this does not add the feed.
+ * Subclasses must add the feed; this class does not do that.
* @since 0.7.0 */
@Beta
public class AddSensor<RT,T extends Sensor<RT>> implements EntityInitializer {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6605118c/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java b/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java
index c40a7ba..bb8dbc2 100644
--- a/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java
@@ -135,6 +135,7 @@ public class FunctionFeed extends AbstractFeed {
super(builder.entity, builder.onlyIfServiceUp);
for (FunctionPollConfig<?,?> config : builder.polls) {
+ @SuppressWarnings({ "rawtypes", "unchecked" })
FunctionPollConfig<?,?> configCopy = new FunctionPollConfig(config);
if (configCopy.getPeriod() < 0) configCopy.period(builder.period, builder.periodUnits);
Callable<?> job = config.getCallable();
@@ -142,7 +143,7 @@ public class FunctionFeed extends AbstractFeed {
}
}
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected void preStart() {
for (final FunctionPollIdentifier pollInfo : polls.keySet()) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6605118c/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java b/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java
index 1f2b37a..9619fbd 100644
--- a/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/shell/ShellFeed.java
@@ -160,6 +160,7 @@ public class ShellFeed extends AbstractFeed {
super(builder.entity);
for (ShellPollConfig<?> config : builder.polls) {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
ShellPollConfig<?> configCopy = new ShellPollConfig(config);
if (configCopy.getPeriod() < 0) configCopy.period(builder.period, builder.periodUnits);
String command = config.getCommand();
@@ -178,7 +179,7 @@ public class ShellFeed extends AbstractFeed {
for (final ShellPollIdentifier pollInfo : polls.keySet()) {
Set<ShellPollConfig<?>> configs = polls.get(pollInfo);
long minPeriod = Integer.MAX_VALUE;
- Set<AttributePollHandler<SshPollValue>> handlers = Sets.newLinkedHashSet();
+ Set<AttributePollHandler<? super SshPollValue>> handlers = Sets.newLinkedHashSet();
for (ShellPollConfig<?> config : configs) {
handlers.add(new AttributePollHandler<SshPollValue>(config, entity, this));
@@ -198,7 +199,7 @@ public class ShellFeed extends AbstractFeed {
Optional<Integer> exitCode = Optional.fromNullable(taskWrapper.getExitCode());
return new SshPollValue(null, exitCode.or(-1), taskWrapper.getStdout(), taskWrapper.getStderr());
}},
- new DelegatingPollHandler(handlers),
+ new DelegatingPollHandler<SshPollValue>(handlers),
minPeriod);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6605118c/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
index 8aee368..7492f75 100644
--- a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
@@ -211,7 +211,7 @@ public class SshFeed extends AbstractFeed {
public SshPollValue call() throws Exception {
return exec(pollInfo.command.get(), pollInfo.env.get());
}},
- new DelegatingPollHandler<SshPollValue>(handlers),
+ new DelegatingPollHandler<SshPollValue>(handlers),
minPeriod);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6605118c/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java b/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
index 9e86f5b..58acbbb 100644
--- a/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
+++ b/core/src/main/java/brooklyn/event/feed/ssh/SshPollConfig.java
@@ -1,7 +1,6 @@
package brooklyn.event.feed.ssh;
-import static com.google.common.base.Preconditions.checkNotNull;
-
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -15,13 +14,11 @@ import brooklyn.util.collections.MutableMap;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
-import com.google.common.collect.Maps;
public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<T>> {
private Supplier<String> commandSupplier;
private List<Supplier<Map<String,String>>> dynamicEnvironmentSupplier = MutableList.of();
- private Map<String,String> fixedEnvironment = Maps.newLinkedHashMap();
public static final Predicate<SshPollValue> DEFAULT_SUCCESS = new Predicate<SshPollValue>() {
@Override
@@ -37,7 +34,6 @@ public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<
public SshPollConfig(SshPollConfig<T> other) {
super(other);
commandSupplier = other.commandSupplier;
- fixedEnvironment = other.fixedEnvironment;
}
/** @deprecated since 0.7.0; use {@link #getCommandSupplier()} and resolve just-in-time */
@@ -57,13 +53,11 @@ public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<
@Override
public Map<String, String> get() {
Map<String,String> result = MutableMap.of();
- result.putAll(fixedEnvironment);
for (Supplier<Map<String, String>> envS: dynamicEnvironmentSupplier) {
if (envS!=null) {
Map<String, String> envM = envS.get();
if (envM!=null) {
- // TODO deeply additive?
- result.putAll(envM);
+ mergeEnvMaps(envM, result);
}
}
}
@@ -72,6 +66,11 @@ public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<
};
}
+ protected void mergeEnvMaps(Map<String,String> supplied, Map<String,String> target) {
+ if (supplied==null) return;
+ // as the value is a string there is no need to look at deep merge behaviour
+ target.putAll(supplied);
+ }
public SshPollConfig<T> command(String val) { return command(Suppliers.ofInstance(val)); }
public SshPollConfig<T> command(Supplier<String> val) {
@@ -79,10 +78,9 @@ public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<
return this;
}
- /** add the given env param */
+ /** add the given env param; sequence is as per {@link #env(Supplier)} */
public SshPollConfig<T> env(String key, String val) {
- fixedEnvironment.put(checkNotNull(key, "key"), checkNotNull(val, "val"));
- return this;
+ return env(Collections.singletonMap(key, val));
}
/** add the given env params; sequence is as per {@link #env(Supplier)} */
@@ -90,13 +88,16 @@ public class SshPollConfig<T> extends PollConfig<SshPollValue, T, SshPollConfig<
return env(Suppliers.ofInstance(val));
}
- /** adds the given dynamic env supplier.
- * these are put into the result in the order they are applied, with fixed values
- * set via {@link #env(String, String)} preceding all these.
+ /**
+ * adds the given dynamic supplier of environment variables.
+ * <p>
+ * use of a supplier allows env vars to be computed on each execution,
+ * for example to take the most recent sensor values.
* <p>
- * currently addition is not deep, in the case of two identical top-level keys
- * the latter one kills anything from the former.
- * this behaviour may change in future. */
+ * in the case of multiple map suppliers, static maps, or static {@link #env(String, String)}
+ * key value pairs, the order in which they are specified here is the order
+ * in which they are computed and applied.
+ **/
public SshPollConfig<T> env(Supplier<Map<String,String>> val) {
dynamicEnvironmentSupplier.add(val);
return this;
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6605118c/docs/use/guide/defining-applications/yaml-reference.md
----------------------------------------------------------------------
diff --git a/docs/use/guide/defining-applications/yaml-reference.md b/docs/use/guide/defining-applications/yaml-reference.md
index 3ca1157..49dde18 100644
--- a/docs/use/guide/defining-applications/yaml-reference.md
+++ b/docs/use/guide/defining-applications/yaml-reference.md
@@ -22,7 +22,7 @@ defining types:
* `io.brooklyn.package.JavaEntityClass`
* `java:io.brooklyn.package.JavaEntityClass`
-* *OSGi and YAML references are coming soon!*
+* *OSGi and YAML references are coming soon!* <!-- TODO -->
A reference of some of the common service `type` instances used is included in a section below.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6605118c/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
index 8fe42a1..5a8323b 100644
--- a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
+++ b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
@@ -42,12 +42,6 @@ public final class SshCommandSensor<T extends String> extends AddSensor<String,A
public void apply(final EntityLocal entity) {
super.apply(entity);
-// TODO:
-// parameters for effectors <- test
-// text for new example,
-// and test for new example
-// javadoc for this class
-
Supplier<Map<String,String>> envSupplier = new Supplier<Map<String,String>>() {
@Override
public Map<String, String> get() {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6605118c/software/base/src/main/java/brooklyn/event/feed/jmx/JmxFeed.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/event/feed/jmx/JmxFeed.java b/software/base/src/main/java/brooklyn/event/feed/jmx/JmxFeed.java
index f591bb9..17ff762 100644
--- a/software/base/src/main/java/brooklyn/event/feed/jmx/JmxFeed.java
+++ b/software/base/src/main/java/brooklyn/event/feed/jmx/JmxFeed.java
@@ -149,11 +149,13 @@ public class JmxFeed extends AbstractFeed {
this.jmxConnectionTimeout = builder.jmxConnectionTimeout;
for (JmxAttributePollConfig<?> config : builder.attributePolls) {
+ @SuppressWarnings({ "rawtypes", "unchecked" })
JmxAttributePollConfig<?> configCopy = new JmxAttributePollConfig(config);
if (configCopy.getPeriod() < 0) configCopy.period(builder.period, builder.periodUnits);
attributePolls.put(configCopy.getObjectName().getCanonicalName() + configCopy.getAttributeName(), configCopy);
}
for (JmxOperationPollConfig<?> config : builder.operationPolls) {
+ @SuppressWarnings({ "rawtypes", "unchecked" })
JmxOperationPollConfig<?> configCopy = new JmxOperationPollConfig(config);
if (configCopy.getPeriod() < 0) configCopy.period(builder.period, builder.periodUnits);
operationPolls.put(configCopy.buildOperationIdentity(), configCopy);
@@ -252,7 +254,7 @@ public class JmxFeed extends AbstractFeed {
* Registers to poll a jmx-operation for an ObjectName, where all the given configs are for the same ObjectName + operation + parameters.
*/
private void registerOperationPoller(Set<JmxOperationPollConfig<?>> configs) {
- Set<AttributePollHandler<Object>> handlers = Sets.newLinkedHashSet();
+ Set<AttributePollHandler<? super Object>> handlers = Sets.newLinkedHashSet();
long minPeriod = Integer.MAX_VALUE;
final ObjectName objectName = Iterables.get(configs, 0).getObjectName();
@@ -276,14 +278,14 @@ public class JmxFeed extends AbstractFeed {
}
}
},
- new DelegatingPollHandler(handlers), minPeriod);
+ new DelegatingPollHandler<Object>(handlers), minPeriod);
}
/**
* Registers to poll a jmx-attribute for an ObjectName, where all the given configs are for that same ObjectName + attribute.
*/
private void registerAttributePoller(Set<JmxAttributePollConfig<?>> configs) {
- Set<AttributePollHandler<Object>> handlers = Sets.newLinkedHashSet();
+ Set<AttributePollHandler<? super Object>> handlers = Sets.newLinkedHashSet();
long minPeriod = Integer.MAX_VALUE;
final ObjectName objectName = Iterables.get(configs, 0).getObjectName();
@@ -302,14 +304,14 @@ public class JmxFeed extends AbstractFeed {
return helper.getAttribute(objectName, jmxAttributeName);
}
},
- new DelegatingPollHandler(handlers), minPeriod);
+ new DelegatingPollHandler<Object>(handlers), minPeriod);
}
/**
* Registers to subscribe to notifications for an ObjectName, where all the given configs are for that same ObjectName + filter.
*/
private NotificationListener registerNotificationListener(Set<JmxNotificationSubscriptionConfig<?>> configs) {
- final List<AttributePollHandler<javax.management.Notification>> handlers = Lists.newArrayList();
+ final List<AttributePollHandler<? super javax.management.Notification>> handlers = Lists.newArrayList();
final ObjectName objectName = Iterables.get(configs, 0).getObjectName();
final NotificationFilter filter = Iterables.get(configs, 0).getNotificationFilter();
@@ -329,7 +331,7 @@ public class JmxFeed extends AbstractFeed {
};
handlers.add(handler);
}
- final PollHandler<javax.management.Notification> compoundHandler = new DelegatingPollHandler(handlers);
+ final PollHandler<javax.management.Notification> compoundHandler = new DelegatingPollHandler<javax.management.Notification>(handlers);
NotificationListener listener = new NotificationListener() {
@Override public void handleNotification(Notification notification, Object handback) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6605118c/software/base/src/main/java/brooklyn/event/feed/jmx/JmxOperationPollConfig.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/event/feed/jmx/JmxOperationPollConfig.java b/software/base/src/main/java/brooklyn/event/feed/jmx/JmxOperationPollConfig.java
index 1dc60ca..4220455 100644
--- a/software/base/src/main/java/brooklyn/event/feed/jmx/JmxOperationPollConfig.java
+++ b/software/base/src/main/java/brooklyn/event/feed/jmx/JmxOperationPollConfig.java
@@ -21,6 +21,7 @@ public class JmxOperationPollConfig<T> extends PollConfig<Object, T, JmxOperatio
private List<String> signature = Collections.emptyList();
private List<?> params = Collections.emptyList();
+ @SuppressWarnings({ "unchecked", "rawtypes" })
public JmxOperationPollConfig(AttributeSensor<T> sensor) {
super(sensor);
onSuccess((Function)Functions.identity());
[07/11] git commit: add an "only if service up" mode to several
sensor feeds (the ones most likely to be configured ahead of time),
and just-in-time inferene for ssh-feeds with test
Posted by al...@apache.org.
add an "only if service up" mode to several sensor feeds (the ones most likely to be configured ahead of time),
and just-in-time inferene for ssh-feeds with test
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/4e3415f9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/4e3415f9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/4e3415f9
Branch: refs/heads/master
Commit: 4e3415f9014b44b002b433031bfb732720d9fdfb
Parents: eb7da4f
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Jun 2 19:50:53 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jun 24 21:59:14 2014 +0100
----------------------------------------------------------------------
.../java/brooklyn/event/feed/AbstractFeed.java | 7 +-
.../java/brooklyn/event/feed/FeedConfig.java | 9 +++
.../main/java/brooklyn/event/feed/Poller.java | 16 ++++-
.../event/feed/function/FunctionFeed.java | 8 ++-
.../java/brooklyn/event/feed/http/HttpFeed.java | 8 ++-
.../java/brooklyn/event/feed/ssh/SshFeed.java | 50 +++++++++-----
.../java/brooklyn/event/feed/PollerTest.java | 2 +-
.../event/feed/ssh/SshFeedIntegrationTest.java | 68 +++++++++++++++++++-
.../brooklyn/entity/chef/ChefAttributeFeed.java | 8 ++-
9 files changed, 150 insertions(+), 26 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e3415f9/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/AbstractFeed.java b/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
index 498409f..066f65f 100644
--- a/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/AbstractFeed.java
@@ -26,13 +26,16 @@ public abstract class AbstractFeed {
protected final EntityLocal entity;
protected final Poller<?> poller;
- private volatile Long lastConnectionTime;
private volatile boolean activated, suspended;
private final Object pollerStateMutex = new Object();
public AbstractFeed(EntityLocal entity) {
+ this(entity, false);
+ }
+
+ public AbstractFeed(EntityLocal entity, boolean onlyIfServiceUp) {
this.entity = checkNotNull(entity, "entity");
- this.poller = new Poller<Object>(entity);
+ this.poller = new Poller<Object>(entity, onlyIfServiceUp);
}
/** true if everything has been _started_ (or it is starting) but not stopped,
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e3415f9/core/src/main/java/brooklyn/event/feed/FeedConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/FeedConfig.java b/core/src/main/java/brooklyn/event/feed/FeedConfig.java
index 34178e9..fce0ac9 100644
--- a/core/src/main/java/brooklyn/event/feed/FeedConfig.java
+++ b/core/src/main/java/brooklyn/event/feed/FeedConfig.java
@@ -82,6 +82,15 @@ public class FeedConfig<V, T, F extends FeedConfig<V, T, F>> {
this.checkSuccess = checkNotNull(val, "checkSuccess");
return self();
}
+ /** as {@link #checkSuccess(Predicate)} */
+ public F checkSuccess(final Function<? super V,Boolean> val) {
+ return checkSuccess(new Predicate<V>() {
+ @Override
+ public boolean apply(V input) {
+ return val.apply(input);
+ }
+ });
+ }
public F onSuccess(Function<? super V,T> val) {
this.onsuccess = checkNotNull(val, "onSuccess");
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e3415f9/core/src/main/java/brooklyn/event/feed/Poller.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/Poller.java b/core/src/main/java/brooklyn/event/feed/Poller.java
index 9065c1c..2ee112c 100644
--- a/core/src/main/java/brooklyn/event/feed/Poller.java
+++ b/core/src/main/java/brooklyn/event/feed/Poller.java
@@ -7,6 +7,7 @@ import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import brooklyn.entity.basic.Attributes;
import brooklyn.entity.basic.BrooklynTaskTags;
import brooklyn.entity.basic.Entities;
import brooklyn.entity.basic.EntityInternal;
@@ -32,6 +33,7 @@ public class Poller<V> {
public static final Logger log = LoggerFactory.getLogger(Poller.class);
private final EntityLocal entity;
+ private final boolean onlyIfServiceUp;
private final Set<Callable<?>> oneOffJobs = new LinkedHashSet<Callable<?>>();
private final Set<PollJob<V>> pollJobs = new LinkedHashSet<PollJob<V>>();
private final Set<Task<?>> oneOffTasks = new LinkedHashSet<Task<?>>();
@@ -74,8 +76,14 @@ public class Poller<V> {
}
}
+ /** @deprecated since 0.7.0, pass in whether should run onlyIfServiceUp */
+ @Deprecated
public Poller(EntityLocal entity) {
+ this(entity, false);
+ }
+ public Poller(EntityLocal entity, boolean onlyIfServiceUp) {
this.entity = entity;
+ this.onlyIfServiceUp = onlyIfServiceUp;
}
/** Submits a one-off poll job; recommended that callers supply to-String so that task has a decent description */
@@ -121,7 +129,13 @@ public class Poller<V> {
Callable<Task<?>> pollingTaskFactory = new Callable<Task<?>>() {
public Task<?> call() {
DynamicSequentialTask<Void> task = new DynamicSequentialTask<Void>(MutableMap.of("displayName", scheduleName, "entity", entity),
- new Callable<Void>() { public Void call() { pollJob.wrappedJob.run(); return null; } } );
+ new Callable<Void>() { public Void call() {
+ if (onlyIfServiceUp && !Boolean.TRUE.equals(entity.getAttribute(Attributes.SERVICE_UP))) {
+ return null;
+ }
+ pollJob.wrappedJob.run();
+ return null;
+ } } );
BrooklynTaskTags.setTransient(task);
return task;
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e3415f9/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java b/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java
index 5f9f3b5..c40a7ba 100644
--- a/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/function/FunctionFeed.java
@@ -68,6 +68,7 @@ public class FunctionFeed extends AbstractFeed {
public static class Builder {
private EntityLocal entity;
+ private boolean onlyIfServiceUp = false;
private long period = 500;
private TimeUnit periodUnits = TimeUnit.MILLISECONDS;
private List<FunctionPollConfig<?,?>> polls = Lists.newArrayList();
@@ -77,6 +78,11 @@ public class FunctionFeed extends AbstractFeed {
this.entity = val;
return this;
}
+ public Builder onlyIfServiceUp() { return onlyIfServiceUp(true); }
+ public Builder onlyIfServiceUp(boolean onlyIfServiceUp) {
+ this.onlyIfServiceUp = onlyIfServiceUp;
+ return this;
+ }
public Builder period(Duration d) {
return period(d.toMilliseconds(), TimeUnit.MILLISECONDS);
}
@@ -126,7 +132,7 @@ public class FunctionFeed extends AbstractFeed {
private final SetMultimap<FunctionPollIdentifier, FunctionPollConfig<?,?>> polls = HashMultimap.<FunctionPollIdentifier,FunctionPollConfig<?,?>>create();
protected FunctionFeed(Builder builder) {
- super(builder.entity);
+ super(builder.entity, builder.onlyIfServiceUp);
for (FunctionPollConfig<?,?> config : builder.polls) {
FunctionPollConfig<?,?> configCopy = new FunctionPollConfig(config);
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e3415f9/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java b/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java
index 42a69d8..a26afb5 100644
--- a/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/http/HttpFeed.java
@@ -92,6 +92,7 @@ public class HttpFeed extends AbstractFeed {
public static class Builder {
private EntityLocal entity;
+ private boolean onlyIfServiceUp = false;
private Supplier<URI> baseUriProvider;
private Duration period = Duration.millis(500);
private List<HttpPollConfig<?>> polls = Lists.newArrayList();
@@ -106,6 +107,11 @@ public class HttpFeed extends AbstractFeed {
this.entity = val;
return this;
}
+ public Builder onlyIfServiceUp() { return onlyIfServiceUp(true); }
+ public Builder onlyIfServiceUp(boolean onlyIfServiceUp) {
+ this.onlyIfServiceUp = onlyIfServiceUp;
+ return this;
+ }
public Builder baseUri(Supplier<URI> val) {
if (baseUri!=null && val!=null)
throw new IllegalStateException("Builder cannot take both a URI and a URI Provider");
@@ -233,7 +239,7 @@ public class HttpFeed extends AbstractFeed {
private final SetMultimap<HttpPollIdentifier, HttpPollConfig<?>> polls = HashMultimap.<HttpPollIdentifier,HttpPollConfig<?>>create();
protected HttpFeed(Builder builder) {
- super(builder.entity);
+ super(builder.entity, builder.onlyIfServiceUp);
Map<String,String> baseHeaders = ImmutableMap.copyOf(checkNotNull(builder.headers, "headers"));
for (HttpPollConfig<?> config : builder.polls) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e3415f9/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
index 74e6d16..55816a6 100644
--- a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
@@ -19,17 +19,17 @@ import brooklyn.event.feed.AbstractFeed;
import brooklyn.event.feed.AttributePollHandler;
import brooklyn.event.feed.DelegatingPollHandler;
import brooklyn.event.feed.Poller;
-import brooklyn.location.Location;
+import brooklyn.location.basic.Locations;
import brooklyn.location.basic.Machines;
import brooklyn.location.basic.SshMachineLocation;
import brooklyn.util.collections.MutableMap;
-import brooklyn.util.guava.Maybe;
import brooklyn.util.time.Duration;
import com.google.common.base.Objects;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
@@ -78,7 +78,8 @@ public class SshFeed extends AbstractFeed {
public static class Builder {
private EntityLocal entity;
- private SshMachineLocation machine;
+ private boolean onlyIfServiceUp = false;
+ private Supplier<SshMachineLocation> machine;
private long period = 500;
private TimeUnit periodUnits = TimeUnit.MILLISECONDS;
private List<SshPollConfig<?>> polls = Lists.newArrayList();
@@ -89,7 +90,15 @@ public class SshFeed extends AbstractFeed {
this.entity = val;
return this;
}
- public Builder machine(SshMachineLocation val) {
+ public Builder onlyIfServiceUp() { return onlyIfServiceUp(true); }
+ public Builder onlyIfServiceUp(boolean onlyIfServiceUp) {
+ this.onlyIfServiceUp = onlyIfServiceUp;
+ return this;
+ }
+ /** optional, to force a machine; otherwise it is inferred from the entity */
+ public Builder machine(SshMachineLocation val) { return machine(Suppliers.ofInstance(val)); }
+ /** optional, to force a machine; otherwise it is inferred from the entity */
+ public Builder machine(Supplier<SshMachineLocation> val) {
this.machine = val;
return this;
}
@@ -153,24 +162,31 @@ public class SshFeed extends AbstractFeed {
}
}
- private final SshMachineLocation machine;
+ private final Supplier<SshMachineLocation> machine;
private final boolean execAsCommand;
// Treat as immutable once built
private final SetMultimap<SshPollIdentifier, SshPollConfig<?>> polls = HashMultimap.<SshPollIdentifier,SshPollConfig<?>>create();
+ /** @deprecated since 0.7.0, use static convenience on {@link Locations} */
+ @Deprecated
public static SshMachineLocation getMachineOfEntity(Entity entity) {
- Maybe<SshMachineLocation> maybe = Machines.findUniqueSshMachineLocation(entity.getLocations());
- if (maybe.isAbsentOrNull()) return null;
- return maybe.get();
+ return Machines.findUniqueSshMachineLocation(entity.getLocations()).orNull();
}
- protected SshFeed(Builder builder) {
- super(builder.entity);
- machine = checkNotNull(builder.machine != null ? builder.machine : getMachineOfEntity(builder.entity), "machine");
+ protected SshFeed(final Builder builder) {
+ super(builder.entity, builder.onlyIfServiceUp);
+ machine = builder.machine != null ? builder.machine :
+ new Supplier<SshMachineLocation>() {
+ @Override
+ public SshMachineLocation get() {
+ return Locations.findUniqueSshMachineLocation(entity.getLocations()).get();
+ }
+ };
execAsCommand = builder.execAsCommand;
for (SshPollConfig<?> config : builder.polls) {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
SshPollConfig<?> configCopy = new SshPollConfig(config);
if (configCopy.getPeriod() < 0) configCopy.period(builder.period, builder.periodUnits);
String command = config.getCommand();
@@ -184,7 +200,7 @@ public class SshFeed extends AbstractFeed {
for (final SshPollIdentifier pollInfo : polls.keySet()) {
Set<SshPollConfig<?>> configs = polls.get(pollInfo);
long minPeriod = Integer.MAX_VALUE;
- Set<AttributePollHandler<SshPollValue>> handlers = Sets.newLinkedHashSet();
+ Set<AttributePollHandler<? super SshPollValue>> handlers = Sets.newLinkedHashSet();
for (SshPollConfig<?> config : configs) {
handlers.add(new AttributePollHandler<SshPollValue>(config, entity, this));
@@ -196,7 +212,7 @@ public class SshFeed extends AbstractFeed {
public SshPollValue call() throws Exception {
return exec(pollInfo.command, pollInfo.env);
}},
- new DelegatingPollHandler(handlers),
+ new DelegatingPollHandler<SshPollValue>(handlers),
minPeriod);
}
}
@@ -213,13 +229,13 @@ public class SshFeed extends AbstractFeed {
int exitStatus;
if (execAsCommand) {
- exitStatus = machine.execCommands(MutableMap.<String,Object>of("out", stdout, "err", stderr),
+ exitStatus = machine.get().execCommands(MutableMap.<String,Object>of("out", stdout, "err", stderr),
"ssh-feed", ImmutableList.of(command), env);
} else {
- exitStatus = machine.execScript(MutableMap.<String,Object>of("out", stdout, "err", stderr),
+ exitStatus = machine.get().execScript(MutableMap.<String,Object>of("out", stdout, "err", stderr),
"ssh-feed", ImmutableList.of(command), env);
}
- return new SshPollValue(machine, exitStatus, new String(stdout.toByteArray()), new String(stderr.toByteArray()));
+ return new SshPollValue(machine.get(), exitStatus, new String(stdout.toByteArray()), new String(stderr.toByteArray()));
}
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e3415f9/core/src/test/java/brooklyn/event/feed/PollerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/event/feed/PollerTest.java b/core/src/test/java/brooklyn/event/feed/PollerTest.java
index cfea7e9..0396869 100644
--- a/core/src/test/java/brooklyn/event/feed/PollerTest.java
+++ b/core/src/test/java/brooklyn/event/feed/PollerTest.java
@@ -34,7 +34,7 @@ public class PollerTest {
public void setUp() throws Exception {
app = ApplicationBuilder.newManagedApp(TestApplication.class);
entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
- poller = new Poller<Integer>(entity);
+ poller = new Poller<Integer>(entity, false);
}
@AfterMethod(alwaysRun=true)
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e3415f9/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java b/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
index d1eaeeb..535bd5a 100644
--- a/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
+++ b/core/src/test/java/brooklyn/event/feed/ssh/SshFeedIntegrationTest.java
@@ -2,13 +2,16 @@ package brooklyn.event.feed.ssh;
import static org.testng.Assert.assertTrue;
+import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Attributes;
import brooklyn.entity.basic.Entities;
import brooklyn.entity.basic.EntityLocal;
+import brooklyn.entity.proxying.EntityInitializer;
import brooklyn.entity.proxying.EntitySpec;
import brooklyn.event.AttributeSensor;
import brooklyn.event.basic.Sensors;
@@ -16,9 +19,12 @@ import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
import brooklyn.location.basic.SshMachineLocation;
import brooklyn.test.Asserts;
import brooklyn.test.EntityTestUtils;
+import brooklyn.test.entity.LocalManagementContextForTests;
import brooklyn.test.entity.TestApplication;
import brooklyn.test.entity.TestEntity;
import brooklyn.util.stream.Streams;
+import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
@@ -37,9 +43,9 @@ public class SshFeedIntegrationTest {
@BeforeMethod(alwaysRun=true)
public void setUp() throws Exception {
- loc = new LocalhostMachineProvisioningLocation();
+ app = ApplicationBuilder.newManagedApp(TestApplication.class, new LocalManagementContextForTests());
+ loc = app.newLocalhostProvisioningLocation();
machine = loc.obtain();
- app = ApplicationBuilder.newManagedApp(TestApplication.class);
entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
app.start(ImmutableList.of(loc));
}
@@ -51,6 +57,32 @@ public class SshFeedIntegrationTest {
if (loc != null) Streams.closeQuietly(loc);
}
+ /** this is one of the most common pattern */
+ @Test(groups="Integration")
+ public void testReturnsSshStdoutAndInfersMachine() throws Exception {
+ final TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+ // inject the machine location, because the app was started with a provisioning location
+ // and TestEntity doesn't provision
+ .location(machine));
+
+ feed = SshFeed.builder()
+ .entity(entity2)
+ .poll(new SshPollConfig<String>(SENSOR_STRING)
+ .command("echo hello")
+ .onSuccess(SshValueFunctions.stdout()))
+ .build();
+
+ Asserts.succeedsEventually(new Runnable() {
+ public void run() {
+ String val = entity2.getAttribute(SENSOR_STRING);
+ assertTrue(val != null);
+ }});
+
+ String val = entity2.getAttribute(SENSOR_STRING);
+ Assert.assertTrue(val.contains("hello"), "val="+val);
+ Assert.assertEquals(val.trim(), "hello");
+ }
+
@Test(groups="Integration")
public void testReturnsSshExitStatus() throws Exception {
feed = SshFeed.builder()
@@ -121,4 +153,36 @@ public class SshFeedIntegrationTest {
assertTrue(val != null && val.contains("Exit status 123"), "val=" + val);
}});
}
+
+ @Test(groups="Integration")
+ public void testAddedEarly() throws Exception {
+ final TestEntity entity2 = app.addChild(EntitySpec.create(TestEntity.class)
+ .location(machine)
+ .addInitializer(new EntityInitializer() {
+ @Override
+ public void apply(EntityLocal entity) {
+ SshFeed.builder()
+ .entity(entity)
+ .onlyIfServiceUp()
+ .poll(new SshPollConfig<String>(SENSOR_STRING)
+ .command("echo hello")
+ .onSuccess(SshValueFunctions.stdout()))
+ .build();
+ }
+ }));
+ Time.sleep(Duration.seconds(2));
+ // would be nice to hook in and assert no errors
+ Assert.assertEquals(entity2.getAttribute(SENSOR_STRING), null);
+ Entities.manage(entity2);
+ Time.sleep(Duration.seconds(2));
+ Assert.assertEquals(entity2.getAttribute(SENSOR_STRING), null);
+ entity2.setAttribute(Attributes.SERVICE_UP, true);
+
+ Asserts.succeedsEventually(new Runnable() {
+ public void run() {
+ String val = entity2.getAttribute(SENSOR_STRING);
+ assertTrue(val != null && val.contains("hello"), "val="+val);
+ }});
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/4e3415f9/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java b/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java
index be6c7e3..47670c7 100644
--- a/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java
+++ b/software/base/src/main/java/brooklyn/entity/chef/ChefAttributeFeed.java
@@ -83,6 +83,7 @@ public class ChefAttributeFeed extends AbstractFeed {
@SuppressWarnings("rawtypes")
public static class Builder {
private EntityLocal entity;
+ private boolean onlyIfServiceUp = false;
private String nodeName;
private Map<String, AttributeSensor<?>> sensors = Maps.newHashMap();
private long period = 30;
@@ -93,6 +94,11 @@ public class ChefAttributeFeed extends AbstractFeed {
this.entity = checkNotNull(val, "entity");
return this;
}
+ public Builder onlyIfServiceUp() { return onlyIfServiceUp(true); }
+ public Builder onlyIfServiceUp(boolean onlyIfServiceUp) {
+ this.onlyIfServiceUp = onlyIfServiceUp;
+ return this;
+ }
public Builder nodeName(String nodeName) {
this.nodeName = checkNotNull(nodeName, "nodeName");
return this;
@@ -147,7 +153,7 @@ public class ChefAttributeFeed extends AbstractFeed {
private final KnifeTaskFactory<String> knifeTaskFactory;
protected ChefAttributeFeed(Builder builder) {
- super(checkNotNull(builder.entity, "builder.entity"));
+ super(checkNotNull(builder.entity, "builder.entity"), builder.onlyIfServiceUp);
entity = builder.entity;
nodeName = checkNotNull(builder.nodeName, "builder.nodeName");
period = builder.period;
[04/11] git commit: trim strings before coercing to numbers
Posted by al...@apache.org.
trim strings before coercing to numbers
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/0f3d6ea6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/0f3d6ea6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/0f3d6ea6
Branch: refs/heads/master
Commit: 0f3d6ea61b1aba59a95768993ba7abc0663c90f1
Parents: 2c8410c
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Jun 2 23:00:30 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jun 24 21:59:14 2014 +0100
----------------------------------------------------------------------
core/src/main/java/brooklyn/util/flags/TypeCoercions.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0f3d6ea6/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
index 93fe775..4ce94bc 100644
--- a/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
+++ b/core/src/main/java/brooklyn/util/flags/TypeCoercions.java
@@ -515,7 +515,7 @@ public class TypeCoercions {
});
registerAdapter(String.class, AtomicLong.class, new Function<String,AtomicLong>() {
@Override public AtomicLong apply(final String input) {
- return new AtomicLong(Long.parseLong(input));
+ return new AtomicLong(Long.parseLong(input.trim()));
}
});
registerAdapter(Integer.class, AtomicInteger.class, new Function<Integer,AtomicInteger>() {
@@ -525,7 +525,7 @@ public class TypeCoercions {
});
registerAdapter(String.class, AtomicInteger.class, new Function<String,AtomicInteger>() {
@Override public AtomicInteger apply(final String input) {
- return new AtomicInteger(Integer.parseInt(input));
+ return new AtomicInteger(Integer.parseInt(input.trim()));
}
});
/** This always returns a {@link Double}, cast as a {@link Number};
[05/11] git commit: entity-initializers for ssh commands (sensors and
effectors) and framework for others we might want to set up,
including parameters and period and env set for sensors
Posted by al...@apache.org.
entity-initializers for ssh commands (sensors and effectors) and framework for others we might want to set up, including parameters and period and env set for sensors
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/b10f8363
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/b10f8363
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/b10f8363
Branch: refs/heads/master
Commit: b10f8363026d8c6ec80d550f883cbabbec4cd267
Parents: 4c9aae4
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Tue Jun 3 15:20:10 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jun 24 21:59:14 2014 +0100
----------------------------------------------------------------------
.../entity/proxying/EntityInitializer.java | 9 ++
.../java/brooklyn/entity/basic/ConfigKeys.java | 47 ++++++++
.../brooklyn/entity/effector/AddEffector.java | 68 ++++++++++++
.../brooklyn/entity/effector/AddSensor.java | 44 ++++++++
.../brooklyn/entity/effector/Effectors.java | 17 ++-
.../entity/software/ssh/SshCommandEffector.java | 83 ++++++++++++++
.../entity/software/ssh/SshCommandSensor.java | 93 ++++++++++++++++
.../software/ssh/SshCommandIntegrationTest.java | 108 +++++++++++++++++++
8 files changed, 465 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b10f8363/api/src/main/java/brooklyn/entity/proxying/EntityInitializer.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/entity/proxying/EntityInitializer.java b/api/src/main/java/brooklyn/entity/proxying/EntityInitializer.java
index bed93ed..f27161c 100644
--- a/api/src/main/java/brooklyn/entity/proxying/EntityInitializer.java
+++ b/api/src/main/java/brooklyn/entity/proxying/EntityInitializer.java
@@ -1,7 +1,16 @@
package brooklyn.entity.proxying;
+import java.util.Map;
+
import brooklyn.entity.basic.EntityLocal;
+/**
+ * Instances of this class supply logic which can be used to initialize entities.
+ * These can be added to an {@link EntitySpec} programmatically, or declared as part
+ * of YAML recipes in a <code>brooklyn.initializers</code> section.
+ * In the case of the latter, implementing classes should define a no-arg constructor
+ * or a {@link Map} constructor so that YAML parameters can be supplied.
+ **/
public interface EntityInitializer {
/** Applies initialization logic to a just-built entity.
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b10f8363/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java b/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
index c052d70..27a845b 100644
--- a/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
+++ b/core/src/main/java/brooklyn/entity/basic/ConfigKeys.java
@@ -1,13 +1,20 @@
package brooklyn.entity.basic;
+import java.util.Map;
+
import javax.annotation.Nonnull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import brooklyn.config.ConfigKey;
import brooklyn.event.basic.AttributeSensorAndConfigKey;
import brooklyn.event.basic.BasicAttributeSensorAndConfigKey;
import brooklyn.event.basic.BasicConfigKey;
import brooklyn.event.basic.BasicConfigKey.BasicConfigKeyOverwriting;
import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.text.Strings;
import com.google.common.base.CaseFormat;
import com.google.common.base.Preconditions;
@@ -19,6 +26,8 @@ import com.google.common.reflect.TypeToken;
*/
public class ConfigKeys {
+ private static final Logger log = LoggerFactory.getLogger(ConfigKeys.class);
+
public static <T> ConfigKey<T> newConfigKey(Class<T> type, String name) {
return new BasicConfigKey<T>(type, name);
}
@@ -179,4 +188,42 @@ public class ConfigKeys {
public static ConfigKey<Boolean> newBooleanConfigKey(String name, String description, Boolean defaultValue) {
return newConfigKey(Boolean.class, name, description, defaultValue);
}
+
+ public static class DynamicKeys {
+
+ // TODO see below
+// public static final ConfigKey<String> TYPE = ConfigKeys.newStringConfigKey("type");
+ public static final ConfigKey<String> NAME = ConfigKeys.newStringConfigKey("name");
+ public static final ConfigKey<String> DESCRIPTION = ConfigKeys.newStringConfigKey("description");
+ public static final ConfigKey<Object> DEFAULT_VALUE = ConfigKeys.newConfigKey(Object.class, "defaultValue");
+
+ public static ConfigKey<?> newInstance(ConfigBag keyDefs) {
+ // TODO dynamic typing - see TYPE key commented out above
+ String typeName = Strings.toString(keyDefs.getStringKey("type"));
+ if (Strings.isNonBlank(typeName))
+ log.warn("Setting 'type' is not currently supported for dynamic config keys; ignoring in definition of "+keyDefs);
+
+ Class<Object> type = Object.class;
+ String name = keyDefs.get(NAME);
+ String description = keyDefs.get(DESCRIPTION);
+ Object defaultValue = keyDefs.get(DEFAULT_VALUE);
+ return newConfigKey(type, name, description, defaultValue);
+ }
+
+ /** creates a new {@link ConfigKey} given a map describing it */
+ public static ConfigKey<?> newInstance(Map<?,?> keyDefs) {
+ return newInstance(ConfigBag.newInstance(keyDefs));
+ }
+
+ /** creates a new {@link ConfigKey} given a name (e.g. as a key in a larger map) and a map of other definition attributes */
+ public static ConfigKey<?> newNamedInstance(String name, Map<?,?> keyDefs) {
+ ConfigBag defs = ConfigBag.newInstance(keyDefs);
+ String oldName = defs.put(NAME, name);
+ if (!Strings.isBlank(oldName) && !oldName.equals(name))
+ log.warn("Dynamic key '"+oldName+"' being overridden as key '"+name+"' in "+keyDefs);
+ return newInstance(defs);
+ }
+
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b10f8363/core/src/main/java/brooklyn/entity/effector/AddEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/AddEffector.java b/core/src/main/java/brooklyn/entity/effector/AddEffector.java
new file mode 100644
index 0000000..3953354
--- /dev/null
+++ b/core/src/main/java/brooklyn/entity/effector/AddEffector.java
@@ -0,0 +1,68 @@
+package brooklyn.entity.effector;
+
+import java.util.Collections;
+import java.util.Map;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Effector;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.entity.effector.Effectors.EffectorBuilder;
+import brooklyn.entity.proxying.EntityInitializer;
+import brooklyn.event.basic.MapConfigKey;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.text.Strings;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+/** Entity initializer which adds an effector to an entity
+ * @since 0.7.0 */
+@Beta
+public class AddEffector implements EntityInitializer {
+
+ public static final ConfigKey<String> EFFECTOR_NAME = ConfigKeys.newStringConfigKey("name");
+ public static final ConfigKey<String> EFFECTOR_DESCRIPTION = ConfigKeys.newStringConfigKey("description");
+
+ public static final ConfigKey<Map<String,Object>> EFFECTOR_PARAMETER_DEFS = new MapConfigKey<Object>(Object.class, "parameters");
+
+ final Effector<?> effector;
+
+ public AddEffector(Effector<?> effector) {
+ this.effector = Preconditions.checkNotNull(effector, "effector");
+ }
+
+ @Override
+ public void apply(EntityLocal entity) {
+ ((EntityInternal)entity).getMutableEntityType().addEffector(effector);
+ }
+
+ public static EffectorBuilder<String> newEffectorBuilder(ConfigBag params) {
+ String name = Preconditions.checkNotNull(params.get(EFFECTOR_NAME), "name must be supplied when defining an effector");
+ EffectorBuilder<String> eff = Effectors.effector(String.class, name);
+ eff.description(params.get(EFFECTOR_DESCRIPTION));
+
+ Map<String, Object> paramDefs = params.get(EFFECTOR_PARAMETER_DEFS);
+ if (paramDefs!=null) {
+ for (Map.Entry<String, Object> paramDef: paramDefs.entrySet()){
+ if (paramDef!=null) {
+ String paramName = paramDef.getKey();
+ Object value = paramDef.getValue();
+ if (value==null) value = Collections.emptyMap();
+ if (!(value instanceof Map)) {
+ if (value instanceof CharSequence && Strings.isBlank((CharSequence) value))
+ value = Collections.emptyMap();
+ }
+ if (!(value instanceof Map))
+ throw new IllegalArgumentException("Illegal argument of type "+value.getClass()+" value '"+value+"' supplied as parameter definition "
+ + "'"+paramName);
+ eff.parameter(ConfigKeys.DynamicKeys.newNamedInstance(paramName, (Map<?, ?>) value));
+ }
+ }
+ }
+
+ return eff;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b10f8363/core/src/main/java/brooklyn/entity/effector/AddSensor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/AddSensor.java b/core/src/main/java/brooklyn/entity/effector/AddSensor.java
new file mode 100644
index 0000000..165e1a6
--- /dev/null
+++ b/core/src/main/java/brooklyn/entity/effector/AddSensor.java
@@ -0,0 +1,44 @@
+package brooklyn.entity.effector;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.entity.proxying.EntityInitializer;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.Sensor;
+import brooklyn.event.basic.Sensors;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+/** Entity initializer which adds a sensor to an entity's type definition.
+ * TODO this does not add the feed.
+ * @since 0.7.0 */
+@Beta
+public class AddSensor<RT,T extends Sensor<RT>> implements EntityInitializer {
+ protected final T sensor;
+
+ public static final ConfigKey<String> SENSOR_NAME = ConfigKeys.newStringConfigKey("name");
+ public static final ConfigKey<Duration> SENSOR_PERIOD = ConfigKeys.newConfigKey(Duration.class, "period", "Period, including units e.g. 1m or 5s or 200ms");
+
+ // TODO
+// public static final ConfigKey<String> SENSOR_TYPE = ConfigKeys.newStringConfigKey("targetType");
+
+ public AddSensor(T sensor) {
+ this.sensor = Preconditions.checkNotNull(sensor, "sensor");
+ }
+
+ @Override
+ public void apply(EntityLocal entity) {
+ ((EntityInternal)entity).getMutableEntityType().addSensor(sensor);
+ }
+
+ public static <T> AttributeSensor<T> newSensor(Class<T> type, ConfigBag params) {
+ String name = Preconditions.checkNotNull(params.get(SENSOR_NAME), "name must be supplied when defining a sensor");
+ return Sensors.newSensor(type, name);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b10f8363/core/src/main/java/brooklyn/entity/effector/Effectors.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/effector/Effectors.java b/core/src/main/java/brooklyn/entity/effector/Effectors.java
index 3a28f4e..684bcbe 100644
--- a/core/src/main/java/brooklyn/entity/effector/Effectors.java
+++ b/core/src/main/java/brooklyn/entity/effector/Effectors.java
@@ -16,6 +16,7 @@ import brooklyn.entity.Effector;
import brooklyn.entity.Entity;
import brooklyn.entity.ParameterType;
import brooklyn.entity.basic.BasicParameterType;
+import brooklyn.entity.basic.ConfigKeys;
import brooklyn.entity.basic.Entities;
import brooklyn.entity.basic.EntityInternal;
import brooklyn.entity.basic.EntityLocal;
@@ -60,11 +61,8 @@ public class Effectors {
public <V> EffectorBuilder<T> parameter(Class<V> paramType, String paramName, String paramDescription, V defaultValue) {
return parameter(new BasicParameterType<V>(paramName, paramType, paramDescription, defaultValue));
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
public <V> EffectorBuilder<T> parameter(ConfigKey<V> key) {
- return parameter(key.hasDefaultValue()
- ? new BasicParameterType<V>(key.getName(), (Class)key.getType(), key.getDescription(), key.getDefaultValue())
- : new BasicParameterType<V>(key.getName(), (Class)key.getType(), key.getDescription()) );
+ return parameter(asParameterType(key));
}
public EffectorBuilder<T> parameter(ParameterType<?> p) {
// allow redeclaring, e.g. for the case where we are overriding an existing effector
@@ -139,6 +137,17 @@ public class Effectors {
throw new UnsupportedOperationException("No implementation registered for effector "+eff+" on "+entity);
}
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static <V> ParameterType<V> asParameterType(ConfigKey<V> key) {
+ return key.hasDefaultValue()
+ ? new BasicParameterType<V>(key.getName(), (Class)key.getType(), key.getDescription(), key.getDefaultValue())
+ : new BasicParameterType<V>(key.getName(), (Class)key.getType(), key.getDescription());
+ }
+
+ public static <V> ConfigKey<V> asConfigKey(ParameterType<V> paramType) {
+ return ConfigKeys.newConfigKey(paramType.getParameterClass(), paramType.getName(), paramType.getDescription(), paramType.getDefaultValue());
+ }
+
/** returns an unsubmitted task which will invoke the given effector on the given entities;
* return type is Task<List<T>> (but haven't put in the blood sweat toil and tears to make the generics work) */
public static TaskAdaptable<List<?>> invocation(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b10f8363/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
new file mode 100644
index 0000000..d119252
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandEffector.java
@@ -0,0 +1,83 @@
+package brooklyn.entity.software.ssh;
+
+import java.util.Map;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Effector;
+import brooklyn.entity.ParameterType;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.SoftwareProcess;
+import brooklyn.entity.effector.AddEffector;
+import brooklyn.entity.effector.EffectorBody;
+import brooklyn.entity.effector.Effectors;
+import brooklyn.entity.effector.Effectors.EffectorBuilder;
+import brooklyn.entity.software.SshEffectorTasks;
+import brooklyn.entity.software.SshEffectorTasks.SshEffectorTaskFactory;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.text.Strings;
+
+import com.google.common.base.Preconditions;
+
+public final class SshCommandEffector extends AddEffector {
+
+ public static final ConfigKey<String> EFFECTOR_COMMAND = ConfigKeys.newStringConfigKey("command");
+
+ public SshCommandEffector(ConfigBag params) {
+ super(newEffectorBuilder(params).build());
+ }
+
+ public SshCommandEffector(Map<String,String> params) {
+ this(ConfigBag.newInstance(params));
+ }
+
+ public static EffectorBuilder<String> newEffectorBuilder(ConfigBag params) {
+ EffectorBuilder<String> eff = AddEffector.newEffectorBuilder(params);
+ eff.impl(new Body(eff.buildAbstract(), params));
+ return eff;
+ }
+
+
+ protected static class Body extends EffectorBody<String> {
+ private Effector<?> effector;
+ private String command;
+
+ public Body(Effector<?> eff, ConfigBag params) {
+ this.effector = eff;
+ this.command = Preconditions.checkNotNull(params.get(EFFECTOR_COMMAND), "command must be supplied when defining an effector");
+ // TODO could take a custom "env" aka effectorShellEnv
+ }
+
+ @Override
+ public String call(ConfigBag params) {
+ String command = this.command;
+
+ String runDir = entity().getAttribute(SoftwareProcess.RUN_DIR);
+ if (runDir!=null) command = "cd '"+runDir+"'\n"+command;
+
+ MutableMap<String, String> env = MutableMap.of();
+ // first set all declared parameters, including default values
+ for (ParameterType<?> param: effector.getParameters()) {
+ env.addIfNotNull(param.getName(), Strings.toString( params.get(Effectors.asConfigKey(param)) ));
+ }
+
+ // then set things from the entities defined shell environment, if applicable
+ env.putAll(Strings.toStringMap(entity().getConfig(SoftwareProcess.SHELL_ENVIRONMENT)));
+
+ // if we wanted to resolve the surrounding environment in real time -- see above
+// Map<String,Object> paramsResolved = (Map<String, Object>) Tasks.resolveDeepValue(effectorShellEnv, Map.class, entity().getExecutionContext());
+
+ // finally set the parameters we've been passed; this will repeat declared parameters but to no harm,
+ // it may pick up additional values (could be a flag defining whether this is permitted or not)
+ env.putAll(Strings.toStringMap(params.getAllConfig()));
+
+ SshEffectorTaskFactory<String> t = SshEffectorTasks.ssh(command)
+ .requiringZeroAndReturningStdout()
+ .summary("effector "+effector.getName())
+ .environmentVariables(env);
+ return queue(t).get();
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b10f8363/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
new file mode 100644
index 0000000..8fe42a1
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/software/ssh/SshCommandSensor.java
@@ -0,0 +1,93 @@
+package brooklyn.entity.software.ssh;
+
+import java.util.Map;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.entity.basic.SoftwareProcess;
+import brooklyn.entity.effector.AddSensor;
+import brooklyn.entity.proxying.EntityInitializer;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.feed.ssh.SshFeed;
+import brooklyn.event.feed.ssh.SshPollConfig;
+import brooklyn.event.feed.ssh.SshValueFunctions;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
+
+import com.google.common.base.Functions;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+
+/**
+ * Configurable {@link EntityInitializer} which adds an SSH sensor feed running the <code>command</code> supplied
+ * in order to populate the sensor with the indicated <code>name</code>.
+ */
+// generics introduced here because we might support a configurable 'targetType` parameter in future,
+// with automatic casting (e.g. for ints); this way it remains compatible
+public final class SshCommandSensor<T extends String> extends AddSensor<String,AttributeSensor<String>> {
+
+ public static final ConfigKey<String> SENSOR_COMMAND = ConfigKeys.newStringConfigKey("command");
+
+ String command;
+
+ public SshCommandSensor(ConfigBag params) {
+ super(newSensor(String.class, params));
+ command = Preconditions.checkNotNull(params.get(SENSOR_COMMAND), SENSOR_COMMAND);
+ }
+
+ @Override
+ public void apply(final EntityLocal entity) {
+ super.apply(entity);
+
+// TODO:
+// parameters for effectors <- test
+// text for new example,
+// and test for new example
+// javadoc for this class
+
+ Supplier<Map<String,String>> envSupplier = new Supplier<Map<String,String>>() {
+ @Override
+ public Map<String, String> get() {
+ Map<String, String> result = MutableMap.of();
+ result.putAll(Strings.toStringMap(entity.getConfig(SoftwareProcess.SHELL_ENVIRONMENT)));
+ // TODO any custom env values?
+ return result;
+ }
+ };
+
+ Supplier<String> commandSupplier = new Supplier<String>() {
+ @Override
+ public String get() {
+ String finalCommand = command;
+ String runDir = entity.getAttribute(SoftwareProcess.RUN_DIR);
+ if (runDir!=null) finalCommand = "cd '"+runDir+"'\n"+finalCommand;
+ return finalCommand;
+ }
+ };
+
+ Duration period = entity.getConfig(SENSOR_PERIOD);
+
+ SshPollConfig<String> pollConfig = new SshPollConfig<String>(sensor)
+ .env(envSupplier)
+ .command(commandSupplier)
+ .checkSuccess(SshValueFunctions.exitStatusEquals(0))
+ .onFailureOrException(Functions.constant((String)null))
+ .onSuccess(SshValueFunctions.stdout());
+
+ if (period!=null) pollConfig.period(period);
+
+ SshFeed.builder().entity(entity)
+ .onlyIfServiceUp()
+ .poll(pollConfig)
+ .build();
+ }
+
+ public SshCommandSensor(Map<String,String> params) {
+ this(ConfigBag.newInstance(params));
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b10f8363/software/base/src/test/java/brooklyn/entity/software/ssh/SshCommandIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/software/ssh/SshCommandIntegrationTest.java b/software/base/src/test/java/brooklyn/entity/software/ssh/SshCommandIntegrationTest.java
new file mode 100644
index 0000000..595788b
--- /dev/null
+++ b/software/base/src/test/java/brooklyn/entity/software/ssh/SshCommandIntegrationTest.java
@@ -0,0 +1,108 @@
+package brooklyn.entity.software.ssh;
+
+import static org.testng.Assert.assertTrue;
+
+import java.io.File;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.Effector;
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.EntityLocal;
+import brooklyn.entity.effector.Effectors;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.Sensors;
+import brooklyn.location.Location;
+import brooklyn.test.Asserts;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.test.entity.TestEntity;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+
+import com.google.common.collect.ImmutableList;
+
+public class SshCommandIntegrationTest {
+
+ final static AttributeSensor<String> SENSOR_STRING = Sensors.newStringSensor("aString", "");
+ final static AttributeSensor<Integer> SENSOR_INT = Sensors.newIntegerSensor("aLong", "");
+ final static Effector<String> EFFECTOR_SAY_HI = Effectors.effector(String.class, "sayHi").buildAbstract();
+
+ private TestApplication app;
+ private EntityLocal entity;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ app = ApplicationBuilder.newManagedApp(TestApplication.class);
+ entity = app.createAndManageChild(EntitySpec.create(TestEntity.class).location(app.newLocalhostProvisioningLocation().obtain()));
+ app.start(ImmutableList.<Location>of());
+ }
+
+ @AfterMethod(alwaysRun=true)
+ public void tearDown() throws Exception {
+ if (app != null) Entities.destroyAll(app.getManagementContext());
+ }
+
+ @Test(groups="Integration")
+ public void testSshSensor() throws Exception {
+ File tempFile = File.createTempFile("testSshCommand", "txt");
+ tempFile.deleteOnExit();
+ new SshCommandSensor<String>(ConfigBag.newInstance()
+ .configure(SshCommandSensor.SENSOR_NAME, SENSOR_STRING.getName())
+ .configure(SshCommandSensor.SENSOR_COMMAND, "echo foo > "+tempFile.getAbsolutePath()+"\n"
+ + "wc "+tempFile.getAbsolutePath()))
+ .apply(entity);
+ entity.setAttribute(Attributes.SERVICE_UP, true);
+
+ Asserts.succeedsEventually(new Runnable() {
+ public void run() {
+ String val = entity.getAttribute(SENSOR_STRING);
+ assertTrue(val != null);
+ }});
+ String val = entity.getAttribute(SENSOR_STRING);
+ assertTrue(val.contains("1"), "val="+val);
+ String[] counts = val.trim().split("\\s+");
+ Assert.assertEquals(counts.length, 4, "val="+val);
+ Assert.assertEquals(counts[0], "1", "val="+val);
+ }
+
+ @Test(groups="Integration")
+ public void testSshEffector() throws Exception {
+ File tempFile = File.createTempFile("testSshCommand", "txt");
+ tempFile.deleteOnExit();
+ new SshCommandEffector(ConfigBag.newInstance()
+ .configure(SshCommandEffector.EFFECTOR_NAME, "sayHi")
+ .configure(SshCommandEffector.EFFECTOR_COMMAND, "echo hi"))
+ .apply(entity);
+
+ String val = entity.invoke(EFFECTOR_SAY_HI, MutableMap.<String,String>of()).get();
+ Assert.assertEquals(val.trim(), "hi", "val="+val);
+ }
+
+ @Test(groups="Integration")
+ public void testSshEffectorWithParameters() throws Exception {
+ File tempFile = File.createTempFile("testSshCommand", "txt");
+ tempFile.deleteOnExit();
+ new SshCommandEffector(ConfigBag.newInstance()
+ .configure(SshCommandEffector.EFFECTOR_NAME, "sayHi")
+ .configure(SshCommandEffector.EFFECTOR_COMMAND, "echo $foo")
+ .configure(SshCommandEffector.EFFECTOR_PARAMETER_DEFS,
+ MutableMap.<String,Object>of("foo", MutableMap.of("defaultValue", "hi"))))
+ .apply(entity);
+
+ String val;
+ // explicit value
+ val = entity.invoke(EFFECTOR_SAY_HI, MutableMap.<String,String>of("foo", "bar")).get();
+ Assert.assertEquals(val.trim(), "bar", "val="+val);
+
+ // default value
+ val = entity.invoke(EFFECTOR_SAY_HI, MutableMap.<String,String>of()).get();
+ Assert.assertEquals(val.trim(), "hi", "val="+val);
+ }
+
+}
[06/11] git commit: don't include extra output in SshFeed calls
Posted by al...@apache.org.
don't include extra output in SshFeed calls
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/2c8410c9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/2c8410c9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/2c8410c9
Branch: refs/heads/master
Commit: 2c8410c93b9f1972395e041ce03cd085ce3a3b3a
Parents: 4e3415f
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Jun 2 22:59:57 2014 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Tue Jun 24 21:59:14 2014 +0100
----------------------------------------------------------------------
core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2c8410c9/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
index 55816a6..87dc79f 100644
--- a/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
+++ b/core/src/main/java/brooklyn/event/feed/ssh/SshFeed.java
@@ -23,6 +23,8 @@ import brooklyn.location.basic.Locations;
import brooklyn.location.basic.Machines;
import brooklyn.location.basic.SshMachineLocation;
import brooklyn.util.collections.MutableMap;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.internal.ssh.SshTool;
import brooklyn.util.time.Duration;
import com.google.common.base.Objects;
@@ -228,11 +230,15 @@ public class SshFeed extends AbstractFeed {
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
int exitStatus;
+ ConfigBag flags = ConfigBag.newInstance()
+ .configure(SshTool.PROP_NO_EXTRA_OUTPUT, true)
+ .configure(SshTool.PROP_OUT_STREAM, stdout)
+ .configure(SshTool.PROP_ERR_STREAM, stderr);
if (execAsCommand) {
- exitStatus = machine.get().execCommands(MutableMap.<String,Object>of("out", stdout, "err", stderr),
+ exitStatus = machine.get().execCommands(flags.getAllConfig(),
"ssh-feed", ImmutableList.of(command), env);
} else {
- exitStatus = machine.get().execScript(MutableMap.<String,Object>of("out", stdout, "err", stderr),
+ exitStatus = machine.get().execScript(flags.getAllConfig(),
"ssh-feed", ImmutableList.of(command), env);
}