You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/02/08 18:37:51 UTC

[1/4] brooklyn-library git commit: See https://issues.apache.org/jira/browse/BROOKLYN-217.

Repository: brooklyn-library
Updated Branches:
  refs/heads/master c15409b3f -> d78795eeb


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
new file mode 100644
index 0000000..5f5069a
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt.impl;
+
+import com.google.common.reflect.TypeToken;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.yaml.Yamls;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility for setting a Salt highstate description on entity sensors.
+ */
+public class SaltHighstate {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SaltHighstate.class);
+
+    public static final String HIGHSTATE_SENSOR_PREFIX = "salt.state";
+
+    public static TypeToken<Map<String, Object>> STATE_FUNCTION_TYPE =
+        new TypeToken<Map<String, Object>>() {};
+
+    private SaltHighstate() {}
+
+    public static void applyHighstate(String contents, Entity entity) {
+
+        final String adaptedYaml = adaptForSaltYamlTypes(contents);
+        LOG.debug("Parsing Salt highstate yaml:\n{}", adaptedYaml);
+        final Iterable<Object> objects = Yamls.parseAll(adaptedYaml);
+
+        for (Object entry: objects) {
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> scopeMap = Yamls.getAs(entry, Map.class);
+            applyStatesInScope(entity, scopeMap);
+        }
+    }
+
+    private static void applyStatesInScope(Entity entity, Map<String, Object> scopeMap) {
+        for (String scope: scopeMap.keySet()) {
+            @SuppressWarnings("unchecked")
+            final Map<String, Object> stateMap = Yamls.getAs(scopeMap.get(scope), Map.class);
+            for (String id: stateMap.keySet()) {
+                applyStateSensor(id, stateMap.get(id), entity);
+            }
+        }
+    }
+
+
+    private static String adaptForSaltYamlTypes(String description) {
+        return description.replaceAll("!!python/unicode", "!!java.lang.String");
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void applyStateSensor(String id, Object stateData, Entity entity) {
+        if (isSaltInternal(id)) {
+            return;
+        }
+        addStateSensor(id, entity);
+        try {
+            Map<String, List<Object>> stateInfo = (Map<String, List<Object>>)stateData;
+            for (String stateModule : stateInfo.keySet()) {
+                addStateModuleValue(id, entity, stateInfo, stateModule);
+            }
+        } catch (ClassCastException e) {
+            LOG.info("Unexpected structure for {} state, skipping ({})", id, e.getMessage());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void addStateModuleValue(String id, Entity entity, Map<String, List<Object>> stateInfo,
+        String stateModule) {
+
+        if (isSaltInternal(stateModule)) {
+            return;
+        }
+        try {
+            final List<Object> stateEntries = stateInfo.get(stateModule);
+            String stateFunction = "";
+            Map<String, Object> moduleSettings = MutableMap.of();
+            for (Object entry : stateEntries) {
+                if (entry instanceof Map) {
+                    moduleSettings.putAll((Map<String, Object>)entry);
+                } else {
+                    stateFunction = entry.toString();
+                }
+            }
+
+            final String name = sensorName(id, stateModule, stateFunction);
+            final AttributeSensor<Map<String, Object>> newSensor =
+                Sensors.newSensor(STATE_FUNCTION_TYPE, HIGHSTATE_SENSOR_PREFIX + "." + name, name);
+            entity.sensors().set(newSensor, moduleSettings);
+
+            LOG.debug("Sensor set for: {}", moduleSettings);
+        } catch (ClassCastException e) {
+            LOG.info("Unexpected structure for state module {}, skipping ({})", id + "." + stateModule, e.getMessage());
+        }
+    }
+
+    private static String sensorName(String... parts) {
+        return Strings.join(parts, ".");
+    }
+
+
+    private static void addStateSensor(String state, Entity entity) {
+        List<String> states = entity.sensors().get(SaltEntityImpl.STATES);
+        if (null == states || !states.contains(state)) {
+            if (null == states) {
+                states = MutableList.of();
+            }
+            states.add(state);
+            entity.sensors().set(SaltEntityImpl.STATES, states);
+        }
+    }
+
+    private static boolean isSaltInternal(String module) {
+        return module.startsWith("__");
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java
new file mode 100644
index 0000000..871caea
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltLifecycleEffectorTasks.java
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt.impl;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.mgmt.TaskAdaptable;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
+import org.apache.brooklyn.entity.cm.salt.SaltConfig;
+import org.apache.brooklyn.core.effector.Effectors;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters;
+import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.TaskBuilder;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static java.util.regex.Pattern.DOTALL;
+import static java.util.regex.Pattern.MULTILINE;
+import static org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.ALWAYS;
+import static org.apache.brooklyn.entity.software.base.SoftwareProcess.StopSoftwareParameters.StopMode.NEVER;
+
+@Beta
+public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks implements SaltConfig {
+    private static final Logger LOG = LoggerFactory.getLogger(SaltLifecycleEffectorTasks.class);
+
+    @Override
+    protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) {
+        SaltMode mode = detectSaltMode(entity());
+        final MachineLocation machine = machineS.get();
+        LOG.info("Starting salt in '{}' mode at '{}'", mode, machine.getDisplayName());
+        if (mode == SaltMode.MASTERLESS) {
+            startWithSshAsync();
+        } else {
+            // TODO: implement MASTER and MINION
+            throw new IllegalStateException("Unknown salt mode: " + mode.name());
+        }
+        return "salt tasks submitted (" + mode + ")";
+    }
+
+
+    protected static SaltMode detectSaltMode(Entity entity) {
+        SaltMode mode = entity.getConfig(SaltConfig.SALT_MODE);
+        Preconditions.checkNotNull(mode, "Required config " + SaltConfig.SALT_MODE + " not provided for entity: " + entity);
+        return mode;
+    }
+
+    protected void startWithSshAsync() {
+
+        final Set<? extends String> startStates = entity().getConfig(SaltConfig.START_STATES);
+        final Set<? extends String> formulas = entity().getConfig(SaltConfig.SALT_FORMULAS);
+        final Set<? extends String> pillars = entity().getConfig(SaltConfig.SALT_PILLARS);
+        final Set<? extends String> pillarUrls = entity().getConfig(SaltConfig.SALT_PILLAR_URLS);
+        final String entityId = entity().getConfig(BrooklynCampConstants.PLAN_ID);
+
+        final ProcessTaskWrapper<Integer> installedAlready = queueAndBlock(SaltSshTasks.isSaltInstalled(false));
+
+        if (0 != installedAlready.getExitCode()) {
+            DynamicTasks.queue("install", new Runnable() {
+                public void run() {
+                    DynamicTasks.queue(
+                        SaltSshTasks.installSalt(false),
+                        SaltSshTasks.installSaltUtilities(false),
+                        SaltSshTasks.configureForMasterlessOperation(false),
+                        SaltSshTasks.installTopFile(startStates, false));
+
+                    if (Strings.isNonBlank(entityId)) {
+                        DynamicTasks.queue(SaltSshTasks.setMinionId(entityId));
+                    }
+                    installFormulas(formulas);
+                    installPillars(pillars, pillarUrls);
+                }
+            });
+        }
+
+        startSalt();
+
+        connectSensors();
+    }
+
+
+    private static final Pattern FAILURES = Pattern.compile(".*^Failed:\\s+(\\d+)$.*", MULTILINE | DOTALL);
+    private static final String ZERO = "0";
+
+    private void startSalt() {
+        String name = "apply top states";
+        final ProcessTaskWrapper<Integer> topStates = queueAndBlock(SaltSshTasks.applyTopStates(false).summary(name));
+
+        // Salt apply returns exit code 0 even upon failure so check the stdout.
+        final Matcher failCount = FAILURES.matcher(topStates.getStdout());
+        if (!failCount.matches() || !ZERO.equals(failCount.group(1))) {
+            LOG.warn("Encountered error in applying Salt top states: {}", topStates.getStdout());
+            throw new RuntimeException(
+                "Encountered error in applying Salt top states, see '" + name + "' in activities for details");
+        }
+    }
+
+    private void installFormulas(Set<? extends String> formulas) {
+        if (formulas.size() > 0) {
+            DynamicTasks.queue(SaltSshTasks.enableFileRoots(false));
+
+            final TaskBuilder<Object> formulaTasks = TaskBuilder.builder().displayName("install formulas");
+            for (String url : formulas) {
+                formulaTasks.add(SaltSshTasks.installSaltFormula(url, false).newTask());
+            }
+            DynamicTasks.queue(formulaTasks.build());
+        }
+    }
+
+    private void installPillars(Set<? extends String> pillars, Set<? extends String> pillarUrls) {
+        if (pillarUrls.size() > 0) {
+            final TaskBuilder<Object> pillarTasks = TaskBuilder.builder().displayName("install pillars");
+            pillarTasks.add(SaltSshTasks.invokeSaltUtility("init_pillar_config", null, false)
+                .summary("init pillar config").newTask());
+            for (String pillar : pillars) {
+                pillarTasks.add(SaltSshTasks.addPillarToTop(pillar, false).newTask());
+            }
+            for (String url : pillarUrls) {
+                pillarTasks.add(SaltSshTasks.installSaltPillar(url, false).newTask());
+            }
+            DynamicTasks.queue(pillarTasks.build());
+        }
+    }
+
+    private void connectSensors() {
+        final ProcessTaskWrapper<String> retrieveHighstate = SaltSshTasks.retrieveHighstate();
+        final ProcessTaskWrapper<String> highstate = DynamicTasks.queue(retrieveHighstate).block();
+        String stateDescription = highstate.get();
+
+        SaltHighstate.applyHighstate(stateDescription, entity());
+    }
+
+
+    protected void postStartCustom() {
+        // TODO: check for package installed?
+        entity().sensors().set(SoftwareProcess.SERVICE_UP, true);
+    }
+
+
+    @Override
+    protected String stopProcessesAtMachine() {
+        final Set<? extends String> stopStates = entity().getConfig(SaltConfig.STOP_STATES);
+        LOG.debug("Executing Salt stopProcessesAtMachine with states {}", stopStates);
+        if (stopStates.isEmpty()) {
+            stopBasedOnStartStates();
+        } else {
+            applyStates(stopStates);
+        }
+        return null;
+    }
+
+    private void applyStates(Set<? extends String> states) {
+        for (String state : states) {
+            DynamicTasks.queue(SaltSshTasks.applyState(state, false).summary("apply state " + state));
+        }
+    }
+
+    private void stopBasedOnStartStates() {
+        final Set<? extends String> startStates = entity().getConfig(SaltConfig.START_STATES);
+        final MutableSet<String> stopStates = addSuffix(startStates, ".stop");
+        final ProcessTaskWrapper<Integer> checkStops =
+            queueAndBlock(SaltSshTasks.verifyStates(stopStates, false).summary("check stop states"));
+        if (0 != checkStops.getExitCode()) {
+            throw new RuntimeException("No stop_states configured and not all start_states have matching stop states");
+        } else {
+            applyStates(stopStates);
+        }
+    }
+
+    public void restart(ConfigBag parameters) {
+        ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING);
+
+        try {
+            final Set<? extends String> restartStates = entity().getConfig(SaltConfig.RESTART_STATES);
+            LOG.debug("Executing Salt restart with states {}", restartStates);
+            if (restartStates.isEmpty()) {
+                restartBasedOnStartStates();
+            } else {
+                applyStates(restartStates);
+            }
+            ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING);
+        } catch (Exception e) {
+            entity().sensors().set(ServiceStateLogic.SERVICE_NOT_UP_DIAGNOSTICS,
+                ImmutableMap.<String, Object>of("restart", e.getMessage()));
+            ServiceStateLogic.setExpectedState(entity(), Lifecycle.ON_FIRE);
+        }
+    }
+
+    private void restartBasedOnStartStates() {
+        final Set<? extends String> startStates = entity().getConfig(SaltConfig.START_STATES);
+        final MutableSet<String> restartStates = addSuffix(startStates, ".restart");
+        final ProcessTaskWrapper<Integer> queued =
+            queueAndBlock(SaltSshTasks.findStates(restartStates, false).summary("check restart states"));
+        final String stdout = queued.getStdout();
+        String[] foundStates = Strings.isNonBlank(stdout) ? stdout.split("\\n") : null;
+
+        if (restartStates.size() > 0 && foundStates != null && (restartStates.size() == foundStates.length)) {
+            // each state X listed in start_states has a matching state of the form X.restart;  we apply them.
+            LOG.debug("All start_states have matching restart states, applying these");
+            applyStates(restartStates);
+
+        } else if (foundStates != null && foundStates.length > 0) {
+            // only *some* of the states have a matching restart; we treat this as a fail
+            LOG.debug("Only some start_states have matching restart states, treating as restart failure") ;
+            throw new RuntimeException("unable to find restart state for all applied states");
+
+        } else {
+            // else we apply "stop" effector (with parameters to stop processes not machine) then "start"
+            // (and in that effector we'd fail if stop was not well-defined)
+            LOG.debug("No stop states available, invoking stop and start effectors");
+            invokeEffector(Startable.STOP, ConfigBag.newInstance()
+                .configure(StopSoftwareParameters.STOP_PROCESS_MODE, ALWAYS)
+                .configure(StopSoftwareParameters.STOP_MACHINE_MODE, NEVER));
+            invokeEffector(Startable.START, ConfigBag.EMPTY);
+        }
+    }
+
+    private ProcessTaskWrapper<Integer> queueAndBlock(ProcessTaskFactory<Integer> taskFactory) {
+        final ProcessTaskWrapper<Integer> queued = DynamicTasks.queue(taskFactory);
+        queued.asTask().blockUntilEnded();
+        return queued;
+    }
+
+    private void invokeEffector(Effector<Void> effector, ConfigBag config) {
+        final TaskAdaptable<Void> stop = Entities.submit(entity(), Effectors.invocation(entity(), effector, config));
+        stop.asTask().blockUntilEnded();
+    }
+
+    private MutableSet<String> addSuffix(Set<? extends String> names, String suffix) {
+        final MutableSet<String> suffixed = MutableSet.of();
+        for (String name : names) {
+            suffixed.add(name + suffix);
+        }
+        return suffixed;
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java
new file mode 100644
index 0000000..cd9cb38
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshDriver.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt.impl;
+
+
+public class SaltSshDriver {
+
+    private SaltSshDriver() {
+        // Utility class
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
new file mode 100644
index 0000000..177843f
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt.impl;
+
+import org.apache.brooklyn.api.mgmt.TaskAdaptable;
+import org.apache.brooklyn.api.mgmt.TaskFactory;
+import org.apache.brooklyn.core.effector.EffectorTasks;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
+import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.SshEffectorTaskFactory;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.core.file.ArchiveTasks;
+import org.apache.brooklyn.util.core.task.TaskBuilder;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.ssh.SshPutTaskFactory;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskFactory;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.apache.brooklyn.util.ssh.BashCommands;
+import org.apache.brooklyn.util.text.Identifiers;
+import org.apache.brooklyn.util.text.Strings;
+
+import java.util.List;
+import java.util.Set;
+
+import static org.apache.brooklyn.core.effector.ssh.SshEffectorTasks.ssh;
+import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
+
+
+public class SaltSshTasks {
+
+    private static final String UTILITY_SCRIPT = "salt_utilities.sh";
+
+    private SaltSshTasks() {
+        // Utility class
+    }
+
+    public static TaskFactory<?> installSalt(boolean force) {
+        // TODO: ignore force?
+        return sshCommands(
+            BashCommands.commandToDownloadUrlAs("https://bootstrap.saltstack.com", "install_salt.sh"),
+            sudo("sh install_salt.sh")
+        )
+        .summary("install salt");
+    }
+
+    public static SshEffectorTaskFactory<Integer> isSaltInstalled(boolean force) {
+        return invokeSaltUtility("salt_installed", null, true).summary("check installed");
+    }
+
+    public static TaskFactory<?> configureForMasterlessOperation(boolean force) {
+        // TODO: ignore force?
+        return ssh(sudo("sed -i '/^#file_client/c file_client: local' /etc/salt/minion"))
+            .summary("configure masterless");
+    }
+
+
+    public static TaskFactory<?> enableFileRoots(boolean force) {
+        return sshCommands(
+                "grep ^file_roots /etc/salt/minion || {",
+                "cat /etc/salt/minion > /tmp/minion.update",
+                "cat >> /tmp/minion.update  << BROOKLYN_EOF",
+                "file_roots:",
+                "  base:",
+                "    - /srv/salt/",
+                "BROOKLYN_EOF",
+                sudo("mv /tmp/minion.update /etc/salt/minion"),
+                "}"
+            )
+            .requiringExitCodeZero()
+            .summary("enable file_roots");
+    }
+
+
+    public static TaskFactory<?>  addPillarToTop(String pillar, boolean b) {
+        return invokeSaltUtility("add_pillar_to_top", pillar, false)
+            .summary("Add pillar " + pillar + " to top");
+    }
+
+    public static TaskFactory<?>  installSaltPillar(final String pillarUrl, boolean force) {
+        return new TaskFactory<TaskAdaptable<?>>() {
+            @Override
+            public TaskAdaptable<?> newTask() {
+                TaskBuilder<Void> tb = Tasks.<Void>builder().displayName(pillarUrl);
+                String tempDownloadDir = "/tmp/download-" + Identifiers.makeRandomId(12);
+
+                tb.add(ArchiveTasks.deploy(null,null,pillarUrl,EffectorTasks.findSshMachine(),
+                    tempDownloadDir,false,null,null).newTask());
+
+                tb.add(ssh("cd " + tempDownloadDir + " ; " + sudo("mv * /srv/pillar")).newTask());
+
+                return tb.build();
+            }
+        };
+    }
+
+    public static TaskFactory<?> installSaltFormula(final String formulaUrl, boolean force) {
+        return new TaskFactory<TaskAdaptable<?>>() {
+            @Override
+            public TaskAdaptable<?> newTask() {
+                TaskBuilder<Void> tb = Tasks.<Void>builder().displayName(formulaUrl);
+
+                String tempDirectoryForUnpack = "/tmp/download-" + Identifiers.makeRandomId(12);
+
+                tb.add(ArchiveTasks.deploy(null, null, formulaUrl, EffectorTasks.findSshMachine(),
+                    tempDirectoryForUnpack, false, null, null).newTask());
+
+                // TODO move this into salt_utilities.sh
+                String installCmd = BashCommands.chain(
+                    "cd "+tempDirectoryForUnpack,
+                    "EXPANDED_DIR=`ls`",
+                    BashCommands.requireTest("`ls | wc -w` -eq 1",
+                        "The deployed archive "+ formulaUrl +" must contain exactly one directory"),
+                    "sudo mkdir -p /srv/formula",
+                    "sudo mv $EXPANDED_DIR /srv/formula/",
+                    // sed command below relies on enableFileRoots behaviour of append file_roots to end of config file
+                    "sudo sed -i \"$ a\\    - /srv/formula/$EXPANDED_DIR\" /etc/salt/minion",
+                    "cd ..",
+                    "rm -rf '"+tempDirectoryForUnpack+"'");
+                tb.add(ssh(installCmd).summary("installing " + formulaUrl + " states to /srv/formula")
+                    .requiringExitCodeZero().newTask());
+
+                return tb.build();
+            }
+        };
+    }
+
+    public static TaskFactory<?> installTopFile(final Set<? extends String> runList, boolean force) {
+        // TODO: ignore force?
+        // TODO: move this into salt_utilities.sh
+        final MutableList.Builder<String> topBuilder = MutableList.<String>builder()
+            .add("cat > /tmp/top.sls << BROOKLYN_EOF")
+            .add("base:")
+            .add("  '*':");
+        for (String stateName: runList) {
+            topBuilder.add("    - " + stateName);
+        }
+        topBuilder.add("BROOKLYN_EOF");
+        List<String> createTempTopFile = topBuilder.build();
+
+        List<String> commands = MutableList.<String>builder()
+            .add(sudo("mkdir -p /srv/salt"))
+            .add(Strings.join(createTempTopFile, "\n"))
+            .add(sudo("mv /tmp/top.sls /srv/salt"))
+            .build();
+        return ssh(commands).summary("create top.sls file");
+    }
+
+    public static ProcessTaskFactory<Integer> applyTopStates(boolean force) {
+        return  saltCall("state.apply");
+    }
+
+    public static SshEffectorTaskFactory<Integer> applyState(String state, boolean force) {
+        return saltCall("state.apply " + state);
+    }
+
+    public static SshEffectorTaskFactory<Integer> saltCall(String command) {
+        return ssh(sudo("salt-call --local " + command)).allowingNonZeroExitCode();
+    }
+
+    public static ProcessTaskWrapper<String> retrieveHighstate() {
+        return saltCall("state.show_highstate --out=yaml")
+            .summary("retrieve highstate")
+            .requiringZeroAndReturningStdout()
+            .newTask();
+    }
+
+    public static TaskFactory<?> installSaltUtilities(boolean force) {
+        return new TaskFactory<TaskAdaptable<?>>() {
+            @Override
+            public TaskAdaptable<?> newTask() {
+                final TaskBuilder<Void> builder = Tasks.<Void>builder()
+                    .displayName("install salt utilities")
+                    .add(installScript(UTILITY_SCRIPT, "install salt shell utils").newTask())
+                    .add(ssh(sudo("mv /tmp/" + UTILITY_SCRIPT + " /etc/salt")).newTask());
+                return builder.build();
+            }
+        };
+    }
+
+    private static SshPutTaskFactory installScript(String name, String description) {
+        return SshEffectorTasks.put("/tmp/" + name)
+                .contents(ResourceUtils.create().getResourceFromUrl("classpath:" + name))
+                .summary(description);
+    }
+
+    public static SshEffectorTaskFactory<Integer> verifyStates(Set<String> states, boolean force) {
+        return invokeSaltUtility("verify_states", Strings.join(states, " "), true);
+    }
+
+    public static SshEffectorTaskFactory<Integer> findStates(Set<String> states, boolean force) {
+        return invokeSaltUtility("find_states", Strings.join(states, " "), true);
+    }
+
+    // Simple invocation of a function from salt_utilities.sh, optionally allowing it to fail.
+    // Uses single quoted bash command, so args mustn't contain single quotes.
+    public static SshEffectorTaskFactory<Integer> invokeSaltUtility(String functionName, String args, boolean permitFailure) {
+
+        final SshEffectorTaskFactory<Integer> taskFactory =
+            ssh(sudo("/bin/bash -c '. /etc/salt/salt_utilities.sh ; " + functionName + " " + args + "'"));
+
+        if (permitFailure) {
+            taskFactory.allowingNonZeroExitCode();
+        } else {
+            taskFactory.requiringExitCodeZero();
+        }
+        return taskFactory;
+
+    }
+
+    public static SshEffectorTaskFactory<Integer> sshCommands(String line, String... lines) {
+        final MutableList.Builder<String> builder = MutableList.<String>builder()
+            .add(line);
+        builder.addAll(lines);
+        return ssh(Strings.join(builder.build(), "\n"));
+    }
+
+    public static SshEffectorTaskFactory<Integer> setMinionId(final String entityId) {
+        return invokeSaltUtility("set_minion_id", entityId, true);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java
new file mode 100644
index 0000000..29761f4
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt.impl;
+
+import org.apache.brooklyn.api.location.LocationDefinition;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+
+public class SaltUtils {
+
+    private SaltUtils() {
+        // Utility class
+    }
+
+    public static ManagementContext.PropertiesReloadListener propertiesReloadListener(
+            ManagementContext mc, LocationDefinition definition) {
+
+        return new ManagementContext.PropertiesReloadListener() {
+            private static final long serialVersionUID = 1L;
+            public void reloaded() {
+                // TODO: implement properties reload logic
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/resources/salt_utilities.sh
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/resources/salt_utilities.sh b/software/cm/salt/src/main/resources/salt_utilities.sh
new file mode 100644
index 0000000..7dc2ae6
--- /dev/null
+++ b/software/cm/salt/src/main/resources/salt_utilities.sh
@@ -0,0 +1,79 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+function edit_pillar_roots () {
+    cat <<EOI
+1
+/^#.*srv.pillar
+a
+base:
+  - /srv/pillar
+
+.
+wq
+EOI
+}
+
+function init_pillar_top () {
+cat > /srv/pillar/top.sls <<EOI
+base:
+  '*':
+EOI
+}
+
+function add_pillar_to_top () {
+    local pillar=$1
+    { cat /srv/pillar/top.sls ; echo "    - $pillar" ; } > /tmp/top.sls
+    mv /tmp/top.sls /srv/pillar/top.sls
+}
+
+function init_pillar_config () {
+    mkdir -p /srv/pillar
+    edit_pillar_roots | ed /etc/salt/minion
+    init_pillar_top
+}
+
+function set_minion_id () {
+    sed "s/^#id:/id: $1/" /etc/salt/minion > /tmp/minion
+    mv /tmp/minion /etc/salt/minion
+}
+
+function salt_installed () {
+    [ -f install_salt.sh ]
+}
+
+function state_exists () {
+  salt-call --local state.sls test=True $1 2>/dev/null | grep Succeeded >/dev/null 2>&1
+}
+
+function verify_states () {
+    echo verifying states "$@"
+    for state in "$@" ; do
+      state_exists $state || { >&2 echo state $state not found ; exit 1 ; }
+    done
+}
+
+function find_states () {
+    for state in "$@" ; do
+      if state_exists $state ; then
+        echo $state
+      fi
+    done
+}
+

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
new file mode 100644
index 0000000..6163262
--- /dev/null
+++ b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.entity.cm.salt.impl.SaltHighstate;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.util.core.ResourceUtils;
+import org.apache.brooklyn.util.stream.Streams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test Highstate utility.
+ */
+public class HighstateTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(HighstateTest.class);
+
+    private TestApplication app = null;
+    private SaltEntity entity = null;
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() {
+        if ( app != null) {
+            Entities.destroyAll(app.getManagementContext());
+            app = null;
+        }
+    }
+
+    @Test
+    public void shouldSetSensorsOnEntity() throws Exception {
+        String contents = getTestYaml();
+        TestApplication app = TestApplication.Factory.newManagedInstanceForTests();
+        entity = app.createAndManageChild(EntitySpec.create(SaltEntity.class)
+            .configure(SaltEntity.START_STATES, ImmutableSet.of("apache")));
+
+        SaltHighstate.applyHighstate(contents, entity);
+
+        final List<String> states = entity.sensors().get(SaltEntity.STATES);
+        assertThat(states)
+            .contains("apache")
+            .contains("apache-reload")
+            .contains("apache-restart");
+
+        final Map<String, Object> apachePkgInstalled =
+            entity.sensors().get(Sensors.newSensor(SaltHighstate.STATE_FUNCTION_TYPE, "salt.state.apache.pkg.installed", ""));
+        assertThat(apachePkgInstalled).isNotNull();
+        assertThat(apachePkgInstalled.get("name")).isEqualTo("apache2");
+        assertThat(apachePkgInstalled.get("order")).isEqualTo(10000);
+
+        final Map<String, Object> apacheServiceRunning =
+            entity.sensors().get(Sensors.newSensor(SaltHighstate.STATE_FUNCTION_TYPE, "salt.state.apache.service.running", ""));
+        assertThat(apacheServiceRunning).isNotNull();
+        assertThat(apacheServiceRunning.get("name")).isEqualTo("apache2");
+        assertThat(apacheServiceRunning.get("order")).isEqualTo(10001);
+        assertThat(apacheServiceRunning.get("enable"));
+    }
+
+
+    private String getTestYaml() {
+        final ResourceUtils resourceUtils = ResourceUtils.create();
+        final InputStream yaml = resourceUtils.getResourceFromUrl("classpath://test-highstate.yaml");
+        return Streams.readFullyString(yaml);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/SaltConfigsTest.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/SaltConfigsTest.java b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/SaltConfigsTest.java
new file mode 100644
index 0000000..89b63f3
--- /dev/null
+++ b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/SaltConfigsTest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class SaltConfigsTest {
+
+    private TestApplication app = null;
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() {
+        if ( app != null) {
+        	Entities.destroyAll(app.getManagementContext());
+            app = null;
+        }
+    }
+
+    @Test
+    public void testAddToRunList() {
+        TestApplication app = TestApplication.Factory.newManagedInstanceForTests();
+        SaltConfigs.addToRunList(app, "a", "b");
+        Set<? extends String> runs = app.getConfig(SaltConfig.START_STATES);
+        Assert.assertEquals(runs, ImmutableSet.of("a", "b"));
+    }
+
+    @Test
+    public void testAddLaunchAttributes() {
+        TestApplication app = TestApplication.Factory.newManagedInstanceForTests();
+        SaltConfigs.addLaunchAttributes(app, ImmutableMap.of("k1", "v1"));
+        Map<String, Object> attribs = app.getConfig(SaltConfig.SALT_SSH_LAUNCH_ATTRIBUTES);
+        Assert.assertEquals(attribs, ImmutableMap.of("k1", "v1"));
+    }
+
+    @Test
+    public void testAddToFormulas() {
+        TestApplication app = TestApplication.Factory.newManagedInstanceForTests();
+        SaltConfigs.addToFormulas(app, "v1");
+        SaltConfigs.addToFormulas(app, "v2");
+        final Set<? extends String> formulas = app.getConfig(SaltConfig.SALT_FORMULAS);
+        Assert.assertEquals(formulas, ImmutableSet.of("v1", "v2"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/SaltIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/SaltIntegrationTest.java b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/SaltIntegrationTest.java
new file mode 100644
index 0000000..d48e523
--- /dev/null
+++ b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/SaltIntegrationTest.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.entity.drivers.DriverDependentEntity;
+import org.apache.brooklyn.api.entity.drivers.EntityDriver;
+import org.apache.brooklyn.api.entity.drivers.EntityDriverManager;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.MachineLocation;
+import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.drivers.BasicEntityDriverManager;
+import org.apache.brooklyn.core.entity.drivers.ReflectiveEntityDriverFactory.AbstractDriverInferenceRule;
+import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.location.SimulatedLocation;
+import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+
+public class SaltIntegrationTest extends BrooklynAppLiveTestSupport {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(SaltIntegrationTest.class);
+    private static final boolean USE_SIMULATED_LOCATION = true;
+
+    private Location testLocation;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws Exception {
+        super.setUp();
+
+        testLocation = USE_SIMULATED_LOCATION ? app.newSimulatedLocation() : getByonLocation();
+        EntityDriverManager edm = mgmt.getEntityDriverManager();
+        if (edm != null && edm instanceof BasicEntityDriverManager) {
+        	BasicEntityDriverManager bedm = (BasicEntityDriverManager) edm;
+        	bedm.getReflectiveDriverFactory().addRule(
+        			DriverInferenceForSimulatedLocation.DEFAULT_IDENTIFIER, 
+        			new DriverInferenceForSimulatedLocation());
+        }
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void tearDown() throws Exception {
+        if (mgmt != null) {
+        	Entities.destroyAll(mgmt);
+        }
+    }
+
+    /**
+     * Test that the broker starts up and sets SERVICE_UP correctly.
+     */
+    @Test(groups = "Integration")
+    public void canStartupAndShutdown() throws Exception {
+    	SaltEntity salt = app.createAndManageChild(EntitySpec.create(SaltEntity.class));
+
+    	salt.start(ImmutableList.of(testLocation));
+    	salt.stop();
+    	
+    	Assert.assertFalse(salt.getAttribute(Startable.SERVICE_UP));
+    }
+
+    public Location getByonLocation() throws NoMachinesAvailableException {
+    	BrooklynProperties brooklynProperties = mgmt.getBrooklynProperties();
+        brooklynProperties.put("brooklyn.location.byon.user", "hadrian");
+        brooklynProperties.put("brooklyn.location.byon.password", "secret");
+        String spec = "byon";
+        Map<String, ?> flags = ImmutableMap.of(
+            "hosts", ImmutableList.of("10.1.1.10", "10.1.1.11"),
+            "osFamily", "linux");
+
+        @SuppressWarnings("unchecked")
+		MachineProvisioningLocation<MachineLocation> provisioner = 
+        	(MachineProvisioningLocation<MachineLocation>) mgmt.getLocationRegistry().resolve(spec, flags);
+        return provisioner.obtain(ImmutableMap.of());
+
+    }
+    public static class DriverInferenceForSimulatedLocation extends AbstractDriverInferenceRule {
+
+        public static final String DEFAULT_IDENTIFIER = "simulated-location-driver-inference-rule";
+
+        @Override
+        public <D extends EntityDriver> String inferDriverClassName(DriverDependentEntity<D> entity, Class<D> driverInterface, Location location) {
+            String driverInterfaceName = driverInterface.getName();
+            if (!(location instanceof SimulatedLocation)) {
+            	return null;
+            }
+            if (!driverInterfaceName.endsWith("Driver")) {
+                throw new IllegalArgumentException(String.format("Driver name [%s] doesn't end with 'Driver'; cannot auto-detect SshDriver class name", driverInterfaceName));
+            }
+            return Strings.removeFromEnd(driverInterfaceName, "Driver") + "SimulatedDriver";
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/test/resources/test-highstate.yaml
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/test/resources/test-highstate.yaml b/software/cm/salt/src/test/resources/test-highstate.yaml
new file mode 100644
index 0000000..9291310
--- /dev/null
+++ b/software/cm/salt/src/test/resources/test-highstate.yaml
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+local:
+  apache:
+    pkg:
+    - name: apache2
+    - installed
+    - order: 10000
+    service:
+    - name: apache2
+    - enable: true
+    - running
+    - order: 10001
+    __sls__: !!python/unicode apache
+    __env__: base
+  apache-reload:
+    module:
+    - name: service.reload
+    - m_name: apache2
+    - wait
+    - order: 10002
+    __sls__: !!python/unicode apache
+    __env__: base
+  apache-restart:
+    module:
+    - name: service.restart
+    - m_name: apache2
+    - wait
+    - order: 10003
+    __sls__: !!python/unicode apache
+    __env__: base
\ No newline at end of file


[2/4] brooklyn-library git commit: See https://issues.apache.org/jira/browse/BROOKLYN-217.

Posted by he...@apache.org.
See https://issues.apache.org/jira/browse/BROOKLYN-217.

This commit merges code from incubator-brooklyn, following the move to the new repos
after graduation. As such, it unfortunately loses the commit history, but the code
is based on the work done by Hadrian Zbarcea. See the Jira link above for history
and discussion.


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

Branch: refs/heads/master
Commit: a122c5e6ee86a6929cc913b043b0ea4bd8f43e74
Parents: 02abbab
Author: Geoff Macartney <ge...@cloudsoftcorp.com>
Authored: Thu Feb 4 13:44:16 2016 +0000
Committer: Geoff Macartney <ge...@cloudsoftcorp.com>
Committed: Thu Feb 4 13:44:16 2016 +0000

----------------------------------------------------------------------
 pom.xml                                         |   1 +
 .../brooklyn/entity/salt/SaltBashCommands.java  |  91 -------
 .../apache/brooklyn/entity/salt/SaltConfig.java | 101 -------
 .../brooklyn/entity/salt/SaltConfigs.java       |  89 ------
 .../entity/salt/SaltLifecycleEffectorTasks.java | 220 ---------------
 .../brooklyn/entity/salt/SaltStackMaster.java   |  72 -----
 .../entity/salt/SaltStackMasterDriver.java      |  25 --
 .../entity/salt/SaltStackMasterImpl.java        |  55 ----
 .../entity/salt/SaltStackMasterSshDriver.java   |  96 -------
 .../apache/brooklyn/entity/salt/SaltTasks.java  | 145 ----------
 .../org/apache/brooklyn/entity/salt/master      |  65 -----
 .../org/apache/brooklyn/entity/salt/masterless  |  53 ----
 .../org/apache/brooklyn/entity/salt/minion      |  52 ----
 .../brooklyn/entity/salt/SaltConfigsTest.java   |  70 -----
 .../entity/salt/SaltLiveTestSupport.java        |  68 -----
 software/cm/pom.xml                             |  80 ++++++
 software/cm/salt/pom.xml                        |  89 ++++++
 .../brooklyn/entity/cm/salt/SaltAttributes.java |  40 +++
 .../brooklyn/entity/cm/salt/SaltConfig.java     |  76 ++++++
 .../brooklyn/entity/cm/salt/SaltConfigs.java    |  77 ++++++
 .../brooklyn/entity/cm/salt/SaltEntity.java     |  56 ++++
 .../entity/cm/salt/SaltEntityDriver.java        |  25 ++
 .../entity/cm/salt/impl/SaltEntityImpl.java     |  81 ++++++
 .../cm/salt/impl/SaltEntitySshDriver.java       |  58 ++++
 .../entity/cm/salt/impl/SaltHighstate.java      | 142 ++++++++++
 .../salt/impl/SaltLifecycleEffectorTasks.java   | 271 +++++++++++++++++++
 .../entity/cm/salt/impl/SaltSshDriver.java      |  28 ++
 .../entity/cm/salt/impl/SaltSshTasks.java       | 236 ++++++++++++++++
 .../brooklyn/entity/cm/salt/impl/SaltUtils.java |  41 +++
 .../salt/src/main/resources/salt_utilities.sh   |  79 ++++++
 .../brooklyn/entity/cm/salt/HighstateTest.java  |  93 +++++++
 .../entity/cm/salt/SaltConfigsTest.java         |  70 +++++
 .../entity/cm/salt/SaltIntegrationTest.java     | 123 +++++++++
 .../salt/src/test/resources/test-highstate.yaml |  47 ++++
 34 files changed, 1713 insertions(+), 1202 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 2b6ba8d..3bc2888 100644
--- a/pom.xml
+++ b/pom.xml
@@ -102,6 +102,7 @@
         <!-- <module>sandbox/nosql</module> -->
         <!-- <module>sandbox/web-acceptance</module> -->
 
+        <module>software/cm</module>
         <module>software/network</module>
         <module>software/osgi</module>
         <module>software/database</module>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltBashCommands.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltBashCommands.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltBashCommands.java
deleted file mode 100644
index 7b06a1e..0000000
--- a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltBashCommands.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import static org.apache.brooklyn.util.ssh.BashCommands.downloadToStdout;
-
-import javax.annotation.Nullable;
-
-import org.apache.commons.io.FilenameUtils;
-
-import org.apache.brooklyn.util.ssh.BashCommands;
-import org.apache.brooklyn.util.text.Identifiers;
-import org.apache.brooklyn.util.text.Strings;
-
-import com.google.common.annotations.Beta;
-import com.google.common.io.Files;
-
-/**
- * BASH commands useful for setting up SaltStack.
- */
-@Beta
-public class SaltBashCommands {
-
-    /**
-     * SaltStack formulas can be found at {@code https://github.com/saltstack-formulas} as repositories.
-     * <p>
-     * This assumes the download is an archive containing a single directory on the root which will
-     * be renamed to {@code formulaName}. if that directory already has the correct name {@code formulaName}
-     * can be null, but if taking from a GitHub tarball it will typically be of the form {@code formulaName-master/}
-     * hence the renaming.
-     */
-    // TODO support installing from classpath, and using the repository (tie in with those methods)
-    public static final String downloadAndExpandFormula(String source, @Nullable String formulaName, boolean force) {
-        String dl = downloadAndExpandFormula(source);
-        if (formulaName==null) return dl;
-        String tmpName = "tmp-"+Strings.makeValidFilename(formulaName)+"-"+Identifiers.makeRandomId(4);
-        String installCmd = BashCommands.chain("mkdir "+tmpName, "cd "+tmpName, dl,
-                BashCommands.requireTest("`ls | wc -w` -eq 1", "The archive must contain exactly one directory"),
-                "FORMULA_EXPANDED_DIR=`ls`",
-                "mv $FORMULA_EXPANDED_DIR '../"+formulaName+"'",
-                "cd ..",
-                "rm -rf "+tmpName);
-        if (!force) return BashCommands.alternatives("ls "+formulaName, installCmd);
-        else return BashCommands.alternatives("rm -rf "+formulaName, installCmd);
-    }
-
-    /**
-     * Same as {@link #downloadAndExpandFormula(String, String)} with no formula name.
-     * <p>
-     * Equivalent to the following command, but substituting the given {@code sourceUrl}.
-     * <pre>{@code
-     * curl -f -L  https://github.com/saltstack-formulas/nginx-formula/archive/master.tar.gz | tar xvz
-     * }</pre>
-     */
-    public static final String downloadAndExpandFormula(String sourceUrl) {
-        String ext = Files.getFileExtension(sourceUrl);
-        if ("tar".equalsIgnoreCase(ext))
-            return downloadToStdout(sourceUrl) + " | tar xv";
-        if ("tgz".equalsIgnoreCase(ext) || sourceUrl.toLowerCase().endsWith(".tar.gz"))
-            return downloadToStdout(sourceUrl) + " | tar xvz";
-
-        String target = FilenameUtils.getName(sourceUrl);
-        if (target==null) target = ""; else target = target.trim();
-        target += "_"+Strings.makeRandomId(4);
-
-        if ("zip".equalsIgnoreCase(ext) || "tar.gz".equalsIgnoreCase(ext))
-            return BashCommands.chain(
-                BashCommands.commandToDownloadUrlAs(sourceUrl, target),
-                "unzip "+target,
-                "rm "+target);
-
-        throw new UnsupportedOperationException("No way to expand "+sourceUrl+" (yet)");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfig.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfig.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfig.java
deleted file mode 100644
index c67eda0..0000000
--- a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfig.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.sensor.AttributeSensor;
-import org.apache.brooklyn.api.mgmt.TaskAdaptable;
-import org.apache.brooklyn.api.mgmt.TaskFactory;
-import org.apache.brooklyn.util.core.flags.SetFromFlag;
-
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.config.ConfigKeys;
-import org.apache.brooklyn.core.config.BasicConfigKey;
-import org.apache.brooklyn.core.config.MapConfigKey;
-import org.apache.brooklyn.core.config.SetConfigKey;
-import org.apache.brooklyn.core.sensor.BasicAttributeSensor;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Function;
-import com.google.common.base.Functions;
-import com.google.common.reflect.TypeToken;
-
-/**
- * {@link ConfigKey}s used to configure Salt entities.
- *
- * @see SaltConfigs
- * @see SaltLifecycleEffectorTasks
- */
-@Beta
-public interface SaltConfig {
-
-    MapConfigKey<String> SALT_FORMULAS = new MapConfigKey<String>(String.class,
-            "salt.formulaUrls", "Map of Salt formula URLs (normally GutHub repository archives from the salt-formulas user)");
-    SetConfigKey<String> SALT_RUN_LIST = new SetConfigKey<String>(String.class,
-            "salt.runList", "Set of Salt states to install from the formula URLs");
-    MapConfigKey<Object> SALT_LAUNCH_ATTRIBUTES = new MapConfigKey<Object>(Object.class, "salt.launch.attributes", "TODO");
-
-    @SetFromFlag("master")
-    ConfigKey<SaltStackMaster> MASTER = ConfigKeys.newConfigKey(SaltStackMaster.class,
-            "salt.master.entity", "The Salt master server");
-
-    AttributeSensor<String> MINION_ID = new BasicAttributeSensor<String>(String.class,
-            "salt.minionId", "The ID for a Salt minion");
-
-    @SetFromFlag("masterless")
-    ConfigKey<Boolean> MASTERLESS_MODE = ConfigKeys.newBooleanConfigKey(
-            "salt.masterless", "Salt masterless, minion only configuration (default uses master and minion)",
-            Boolean.FALSE);
-
-    @SetFromFlag("masterConfigUrl")
-    ConfigKey<String> MASTER_CONFIGURATION_URL = ConfigKeys.newStringConfigKey(
-            "salt.master.templateUrl", "The Salt master configuration file template URL",
-            "classpath://org/apache/brooklyn/entity/salt/master");
-
-    @SetFromFlag("minionConfigUrl")
-    ConfigKey<String> MINION_CONFIGURATION_URL = ConfigKeys.newStringConfigKey(
-            "salt.minion.templateUrl", "The Salt minion configuration file template URL",
-            "classpath://org/apache/brooklyn/entity/salt/minion");
-
-    @SetFromFlag("masterlessConfigUrl")
-    ConfigKey<String> MASTERLESS_CONFIGURATION_URL = ConfigKeys.newStringConfigKey(
-            "salt.masterless.templateUrl", "The Salt minion masterless configuration file template URL",
-            "classpath://org/apache/brooklyn/entity/salt/masterless");
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    @SetFromFlag("minionIdFunction")
-    ConfigKey<Function<Entity, String>> MINION_ID_FUNCTION = new BasicConfigKey(Function.class,
-            "salt.minionId.function", "Function to generate the ID of a Salt minion for an entity", Functions.toStringFunction());
-
-    @SuppressWarnings("serial")
-    ConfigKey<TaskFactory<? extends TaskAdaptable<Boolean>>> IS_RUNNING_TASK = ConfigKeys.newConfigKey(
-            new TypeToken<TaskFactory<? extends TaskAdaptable<Boolean>>>() {}, 
-            "salt.driver.isRunningTask");
-
-    @SuppressWarnings("serial")
-    ConfigKey<TaskFactory<?>> STOP_TASK = ConfigKeys.newConfigKey(
-            new TypeToken<TaskFactory<?>>() {}, 
-            "salt.driver.stopTask");
-
-    /**
-     * The {@link SaltStackMaster master} entity.
-     */
-    SaltStackMaster getMaster();
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfigs.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfigs.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfigs.java
deleted file mode 100644
index 4f7d610..0000000
--- a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfigs.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import java.util.Map;
-
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.EntitySpec;
-import org.apache.brooklyn.core.config.MapConfigKey.MapModifications;
-import org.apache.brooklyn.core.config.SetConfigKey.SetModifications;
-import org.apache.brooklyn.core.entity.EntityInternal;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Preconditions;
-
-/**
- * Conveniences for configuring brooklyn Salt entities 
- *
- * @since 0.6.0
- */
-@Beta
-public class SaltConfigs {
-
-    public static void addToRunList(EntitySpec<?> entity, String...states) {
-        for (String state : states) {
-            entity.configure(SaltConfig.SALT_RUN_LIST, SetModifications.addItem(state));
-        }
-    }
-
-    public static void addToRunList(EntityInternal entity, String...states) {
-        for (String state : states) {
-            entity.config().set(SaltConfig.SALT_RUN_LIST, SetModifications.addItem(state));
-        }
-    }
-
-    public static void addToFormuals(EntitySpec<?> entity, String formulaName, String formulaUrl) {
-        entity.configure(SaltConfig.SALT_FORMULAS.subKey(formulaName), formulaUrl);
-    }
-
-    public static void addToFormulas(EntityInternal entity, String formulaName, String formulaUrl) {
-        entity.config().set(SaltConfig.SALT_FORMULAS.subKey(formulaName), formulaUrl);
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static void addLaunchAttributes(EntitySpec<?> entity, Map<? extends Object,? extends Object> attributesMap) {
-        entity.configure(SaltConfig.SALT_LAUNCH_ATTRIBUTES, MapModifications.add((Map)attributesMap));
-    }
-    
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public static void addLaunchAttributes(EntityInternal entity, Map<? extends Object,? extends Object> attributesMap) {
-        entity.config().set(SaltConfig.SALT_LAUNCH_ATTRIBUTES, MapModifications.add((Map)attributesMap));
-    }
-    
-    /** replaces the attributes underneath the rootAttribute parameter with the given value;
-     * see {@link #addLaunchAttributesMap(EntitySpec, Map)} for richer functionality */
-    public static void setLaunchAttribute(EntitySpec<?> entity, String rootAttribute, Object value) {
-        entity.configure(SaltConfig.SALT_LAUNCH_ATTRIBUTES.subKey(rootAttribute), value);
-    }
-    
-    /** replaces the attributes underneath the rootAttribute parameter with the given value;
-     * see {@link #addLaunchAttributesMap(EntitySpec, Map)} for richer functionality */
-    public static void setLaunchAttribute(EntityInternal entity, String rootAttribute, Object value) {
-        entity.config().set(SaltConfig.SALT_LAUNCH_ATTRIBUTES.subKey(rootAttribute), value);
-    }
-
-    public static <T> T getRequiredConfig(Entity entity, ConfigKey<T> key) {
-        return Preconditions.checkNotNull(
-                Preconditions.checkNotNull(entity, "Entity must be supplied").getConfig(key), 
-                "Key "+key+" is required on "+entity);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
deleted file mode 100644
index 6f3e4f5..0000000
--- a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.location.MachineLocation;
-import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
-import org.apache.brooklyn.core.entity.Attributes;
-import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
-import org.apache.brooklyn.core.server.BrooklynServerConfig;
-import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
-import org.apache.brooklyn.util.core.task.DynamicTasks;
-import org.apache.brooklyn.util.core.task.Tasks;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.brooklyn.util.net.Urls;
-import org.apache.brooklyn.util.ssh.BashCommands;
-import org.apache.brooklyn.util.time.Duration;
-import org.apache.brooklyn.util.time.Time;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.Supplier;
-
-/**
- * Creates effectors to start, restart, and stop processes using SaltStack.
- * <p>
- * Instances of this should use the {@link SaltConfig} config attributes to configure startup,
- * and invoke {@link #usePidFile(String)} or {@link #useService(String)} to determine check-running and stop behaviour.
- * Alternatively this can be subclassed and {@link #postStartCustom()} and {@link #stopProcessesAtMachine()} overridden.
- *
- * @since 0.6.0
- */
-@Beta
-public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks implements SaltConfig {
-
-    private static final Logger log = LoggerFactory.getLogger(SaltLifecycleEffectorTasks.class);
-
-    protected SaltStackMaster master = null;
-    protected String pidFile, serviceName, windowsServiceName;
-
-    public SaltLifecycleEffectorTasks() {
-    }
-
-    public SaltLifecycleEffectorTasks usePidFile(String pidFile) {
-        this.pidFile = pidFile;
-        return this;
-    }
-    public SaltLifecycleEffectorTasks useService(String serviceName) {
-        this.serviceName = serviceName;
-        return this;
-    }
-    public SaltLifecycleEffectorTasks useWindowsService(String serviceName) {
-        this.windowsServiceName = serviceName;
-        return this;
-    }
-    public SaltLifecycleEffectorTasks master(SaltStackMaster master) {
-        this.master = master;
-        return this;
-    }
-
-    @Override
-    public void attachLifecycleEffectors(Entity entity) {
-        if (pidFile==null && serviceName==null && getClass().equals(SaltLifecycleEffectorTasks.class)) {
-            // warn on incorrect usage
-            log.warn("Uses of "+getClass()+" must define a PID file or a service name (or subclass and override {start,stop} methods as per javadoc) " +
-                    "in order for check-running and stop to work");
-        }
-
-        super.attachLifecycleEffectors(entity);
-    }
-
-    @Override
-    protected String startProcessesAtMachine(Supplier<MachineLocation> machineS) {
-        startMinionAsync();
-        return "salt start tasks submitted";
-    }
-
-    protected void startMinionAsync() {
-        // TODO make directories more configurable (both for ssh-drivers and for this)
-        String installDir = Urls.mergePaths(BrooklynServerConfig.getMgmtBaseDir(entity().getManagementContext()), "salt-install");
-        String runDir = Urls.mergePaths(BrooklynServerConfig.getMgmtBaseDir(entity().getManagementContext()),
-                "apps/"+entity().getApplicationId()+"/salt-entities/"+entity().getId());
-
-        Boolean masterless = entity().getConfig(SaltConfig.MASTERLESS_MODE);
-        if (masterless) {
-            DynamicTasks.queue(
-                    SaltTasks.installFormulas(installDir, SaltConfigs.getRequiredConfig(entity(), SALT_FORMULAS), false),
-                    SaltTasks.buildSaltFile(runDir,
-                            SaltConfigs.getRequiredConfig(entity(), SALT_RUN_LIST),
-                            entity().getConfig(SALT_LAUNCH_ATTRIBUTES)),
-                    SaltTasks.installSaltMinion(entity(), runDir, installDir, false),
-                    SaltTasks.runSalt(runDir));
-        } else {
-            throw new UnsupportedOperationException("Salt master mode not yet supported for minions");
-        }
-    }
-
-    @Override
-    protected void postStartCustom() {
-        boolean result = false;
-        result |= tryCheckStartPid();
-        result |= tryCheckStartService();
-        result |= tryCheckStartWindowsService();
-        if (!result) {
-            throw new IllegalStateException("The process for "+entity()+" appears not to be running (no way to check!)");
-        }
-    }
-
-    protected boolean tryCheckStartPid() {
-        if (pidFile==null) return false;
-
-        // if it's still up after 5s assume we are good (default behaviour)
-        Time.sleep(Duration.FIVE_SECONDS);
-        if (!DynamicTasks.queue(SshEffectorTasks.isPidFromFileRunning(pidFile).runAsRoot()).get()) {
-            throw new IllegalStateException("The process for "+entity()+" appears not to be running (pid file "+pidFile+")");
-        }
-
-        // and set the PID
-        entity().sensors().set(Attributes.PID,
-                Integer.parseInt(DynamicTasks.queue(SshEffectorTasks.ssh("cat "+pidFile).runAsRoot()).block().getStdout().trim()));
-        return true;
-    }
-
-    protected boolean tryCheckStartService() {
-        if (serviceName==null) return false;
-
-        // if it's still up after 5s assume we are good (default behaviour)
-        Time.sleep(Duration.FIVE_SECONDS);
-        if (!((Integer)0).equals(DynamicTasks.queue(SshEffectorTasks.ssh("/etc/init.d/"+serviceName+" status").runAsRoot()).get())) {
-            throw new IllegalStateException("The process for "+entity()+" appears not to be running (service "+serviceName+")");
-        }
-
-        return true;
-    }
-
-    protected boolean tryCheckStartWindowsService() {
-        if (windowsServiceName==null) return false;
-
-        // if it's still up after 5s assume we are good (default behaviour)
-        Time.sleep(Duration.FIVE_SECONDS);
-        if (!((Integer)0).equals(DynamicTasks.queue(SshEffectorTasks.ssh("sc query \""+serviceName+"\" | find \"RUNNING\"").runAsCommand()).get())) {
-            throw new IllegalStateException("The process for "+entity()+" appears not to be running (windowsService "+windowsServiceName+")");
-        }
-
-        return true;
-    }
-
-    @Override
-    protected String stopProcessesAtMachine() {
-        boolean result = false;
-        result |= tryStopService();
-        result |= tryStopPid();
-        if (!result) {
-            throw new IllegalStateException("The process for "+entity()+" appears could not be stopped (no impl!)");
-        }
-        return "stopped";
-    }
-
-    protected boolean tryStopService() {
-        if (serviceName==null) return false;
-        int result = DynamicTasks.queue(SshEffectorTasks.ssh("/etc/init.d/"+serviceName+" stop").runAsRoot()).get();
-        if (0==result) return true;
-        if (entity().getAttribute(Attributes.SERVICE_STATE)!=Lifecycle.RUNNING)
-            return true;
-
-        throw new IllegalStateException("The process for "+entity()+" appears could not be stopped (exit code "+result+" to service stop)");
-    }
-
-    protected boolean tryStopPid() {
-        Integer pid = entity().getAttribute(Attributes.PID);
-        if (pid==null) {
-            if (entity().getAttribute(Attributes.SERVICE_STATE)==Lifecycle.RUNNING && pidFile==null)
-                log.warn("No PID recorded for "+entity()+" when running, with PID file "+pidFile+"; skipping kill in "+Tasks.current());
-            else
-                if (log.isDebugEnabled())
-                    log.debug("No PID recorded for "+entity()+"; skipping ("+entity().getAttribute(Attributes.SERVICE_STATE)+" / "+pidFile+")");
-            return false;
-        }
-
-        // allow non-zero exit as process may have already been killed
-        DynamicTasks.queue(SshEffectorTasks.ssh(
-                "kill "+pid, "sleep 5", BashCommands.ok("kill -9 "+pid)).allowingNonZeroExitCode().runAsRoot()).block();
-
-        if (DynamicTasks.queue(SshEffectorTasks.isPidRunning(pid).runAsRoot()).get()) {
-            throw new IllegalStateException("Process for "+entity()+" in "+pid+" still running after kill");
-        }
-        entity().sensors().set(Attributes.PID, null);
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @return the Salt master entity if it exists.
-     * @see #master(SaltStackMaster)
-     * @see SaltConfig#MASTERLESS_MODE
-     */
-    @Override
-    public SaltStackMaster getMaster() {
-        return master;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMaster.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMaster.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMaster.java
deleted file mode 100644
index 5b4a085..0000000
--- a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMaster.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import java.util.List;
-
-import org.apache.brooklyn.api.catalog.Catalog;
-import org.apache.brooklyn.api.entity.ImplementedBy;
-import org.apache.brooklyn.api.sensor.AttributeSensor;
-import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.util.core.flags.SetFromFlag;
-
-
-import org.apache.brooklyn.core.config.ConfigKeys;
-import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
-import org.apache.brooklyn.core.sensor.BasicAttributeSensor;
-import org.apache.brooklyn.core.sensor.PortAttributeSensorAndConfigKey;
-import org.apache.brooklyn.entity.software.base.SoftwareProcess;
-
-import com.google.common.reflect.TypeToken;
-
-@ImplementedBy(SaltStackMasterImpl.class)
-@Catalog(name="SaltStack Master", description="The Salt master server")
-public interface SaltStackMaster extends SoftwareProcess {
-
-    @SetFromFlag("version")
-    ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(BrooklynConfigKeys.SUGGESTED_VERSION, "stable");
-
-    @SetFromFlag("bootstrapUrl")
-    ConfigKey<String> BOOTSTRAP_URL = ConfigKeys.newStringConfigKey(
-            "salt.bootstrap.url", "The URL that returns the Salt boostrap commands",
-            "http://bootstrap.saltstack.org/");
-
-    @SetFromFlag("masterUser")
-    ConfigKey<String> MASTER_USER = ConfigKeys.newStringConfigKey(
-            "salt.master.user", "The user that runs the Salt master daemon process",
-            "root");
-
-    @SetFromFlag("masterConfigTemplate")
-    ConfigKey<String> MASTER_CONFIG_TEMPLATE_URL = ConfigKeys.newStringConfigKey(
-            "salt.master.config.templateUrl", "The template for the Salt master configuration (URL)",
-            "classpath://org/apache/brooklyn/entity/salt/master");
-
-    @SetFromFlag("saltPort")
-    PortAttributeSensorAndConfigKey SALT_PORT = new PortAttributeSensorAndConfigKey(
-            "salt.port", "Port used for communication between Salt master and minion processes", "4506+");
-
-    @SetFromFlag("publishPort")
-    PortAttributeSensorAndConfigKey PUBLISH_PORT = new PortAttributeSensorAndConfigKey(
-            "salt.publish.port", "Port used by the Salt master publisher", "4505+");
-
-    @SuppressWarnings("serial")
-    AttributeSensor<List<String>> MINION_IDS = new BasicAttributeSensor<List<String>>(new TypeToken<List<String>>() {},
-            "salt.minions", "List of Salt minion IDs");
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterDriver.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterDriver.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterDriver.java
deleted file mode 100644
index 09c4db1..0000000
--- a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterDriver.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver;
-
-public interface SaltStackMasterDriver extends SoftwareProcessDriver {
-
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterImpl.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterImpl.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterImpl.java
deleted file mode 100644
index 40e38c5..0000000
--- a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterImpl.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.brooklyn.core.feed.ConfigToAttributes;
-import org.apache.brooklyn.entity.software.base.SoftwareProcessImpl;
-
-public class SaltStackMasterImpl extends SoftwareProcessImpl implements SaltStackMaster {
-
-    private static final Logger log = LoggerFactory.getLogger(SaltStackMasterImpl.class);
-
-    public SaltStackMasterImpl() {
-        super();
-    }
-
-    @Override
-    public Class getDriverInterface() {
-        return SaltStackMasterDriver.class;
-    }
-
-    @Override
-    protected void connectSensors() {
-        super.connectSensors();
-
-        // TODO what sensors should we poll?
-        ConfigToAttributes.apply(this);
-
-        connectServiceUpIsRunning();
-    }
-
-    @Override
-    protected void disconnectSensors() {
-        disconnectServiceUpIsRunning();
-
-        super.disconnectSensors();
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterSshDriver.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterSshDriver.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterSshDriver.java
deleted file mode 100644
index 6010484..0000000
--- a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterSshDriver.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import org.apache.brooklyn.location.ssh.SshMachineLocation;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.util.core.task.DynamicTasks;
-
-import org.apache.brooklyn.entity.java.JavaSoftwareProcessSshDriver;
-import org.apache.brooklyn.util.ssh.BashCommands;
-
-import com.google.common.collect.ImmutableMap;
-
-public class SaltStackMasterSshDriver extends JavaSoftwareProcessSshDriver implements SaltStackMasterDriver {
-
-    public SaltStackMasterSshDriver(SaltStackMasterImpl entity, SshMachineLocation machine) {
-        super(entity, machine);
-    }
-
-    @Override
-    public SaltStackMasterImpl getEntity() {
-        return (SaltStackMasterImpl) super.getEntity();
-    }
-
-    @Override
-    protected String getLogFileLocation() {
-        return "master.log";
-    }
-
-    private String getPidFile() {
-        return "master.pid";
-    }
-
-    @Override
-    public void install() {
-        String url = Entities.getRequiredUrlConfig(getEntity(), SaltStackMaster.BOOTSTRAP_URL);
-        copyTemplate(url, "/etc/salt/master");
-
-        // Copy the file contents to the remote machine
-//        DynamicTasks.queue(SshEffectorTasks.put("/tmp/cumulus.yaml").contents(contents)).get();
-
-        // Run Salt bootstrap task to install master
-        DynamicTasks.queue(SaltTasks.installSaltMaster(getEntity(), getRunDir(), true));
-
-
-        newScript("createInstallDir")
-                .body.append("mkdir -p "+getInstallDir())
-                .failOnNonZeroResultCode()
-                .execute();
-
-        newScript(INSTALLING).
-                failOnNonZeroResultCode().
-                body.append("").execute();
-    }
-
-    @Override
-    public void customize() {
-    }
-
-    @Override
-    public void launch() {
-        newScript(ImmutableMap.of("usePidFile", false), LAUNCHING)
-                .body.append(BashCommands.sudo("start salt-master"))
-                .execute();
-    }
-
-    @Override
-    public boolean isRunning() {
-        return newScript(ImmutableMap.of("usePidFile", false), CHECK_RUNNING)
-                .body.append(BashCommands.sudo("status salt-master"))
-                .execute() == 0;
-    }
-
-    @Override
-    public void stop() {
-        newScript(ImmutableMap.of("usePidFile", false), STOPPING)
-                .body.append(BashCommands.sudo("stop salt-master"))
-                .execute();
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltTasks.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltTasks.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltTasks.java
deleted file mode 100644
index f969b44..0000000
--- a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltTasks.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_CURL;
-import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_TAR;
-import static org.apache.brooklyn.util.ssh.BashCommands.INSTALL_UNZIP;
-import static org.apache.brooklyn.util.ssh.BashCommands.downloadToStdout;
-import static org.apache.brooklyn.util.ssh.BashCommands.sudo;
-
-import java.util.Map;
-
-import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.mgmt.TaskFactory;
-import org.apache.brooklyn.core.effector.EffectorTasks;
-import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.util.core.ResourceUtils;
-import org.apache.brooklyn.util.core.task.DynamicTasks;
-import org.apache.brooklyn.util.core.task.Tasks;
-import org.apache.brooklyn.util.core.text.TemplateProcessor;
-import org.apache.brooklyn.util.collections.MutableMap;
-import org.apache.brooklyn.util.net.Urls;
-import org.apache.brooklyn.util.ssh.BashCommands;
-
-import com.google.common.annotations.Beta;
-
-@Beta
-public class SaltTasks {
-
-    public static TaskFactory<?> installSaltMaster(Entity master, String saltDirectory, boolean force) {
-        // TODO check on entity whether it is salt _server_
-        String boostrapUrl = master.getConfig(SaltStackMaster.BOOTSTRAP_URL);
-        String version = master.getConfig(SaltStackMaster.SUGGESTED_VERSION);
-        String installCmd = cdAndRun(saltDirectory,
-                BashCommands.chain(
-                        INSTALL_CURL,
-                        INSTALL_TAR,
-                        INSTALL_UNZIP,
-                        "( "+downloadToStdout(boostrapUrl) + " | " + sudo("sh -s -- -M -N "+version)+" )"));
-        if (!force) installCmd = BashCommands.alternatives("which salt-master", installCmd);
-        return SshEffectorTasks.ssh(installCmd).summary("install salt master");
-    }
-
-    public static TaskFactory<?> installSaltMinion(final Entity minion, final String runDir, final String installDir, final boolean force) {
-        return Tasks.<Void>builder().displayName("install minion").body(
-                new Runnable() {
-                    public void run() {
-                        // Setup bootstrap installation command for minion
-                        String boostrapUrl = minion.getConfig(SaltStackMaster.BOOTSTRAP_URL);
-                        String installCmd = cdAndRun(runDir, BashCommands.chain(
-                                INSTALL_CURL,
-                                INSTALL_TAR,
-                                INSTALL_UNZIP,
-                                "( "+downloadToStdout(boostrapUrl) + " | " + sudo("sh")+" )"));
-                        if (!force) installCmd = BashCommands.alternatives("which salt-minion", installCmd);
-
-                        // Process the minion configuration template
-                        Boolean masterless = minion.getConfig(SaltConfig.MASTERLESS_MODE);
-                        String url = masterless ? Entities.getRequiredUrlConfig(minion, SaltConfig.MASTERLESS_CONFIGURATION_URL)
-                                                : Entities.getRequiredUrlConfig(minion, SaltConfig.MINION_CONFIGURATION_URL);
-                        Map<String, Object> config = MutableMap.<String, Object>builder()
-                                .put("entity", minion)
-                                .put("runDir", runDir)
-                                .put("installDir", installDir)
-                                .put("formulas", minion.getConfig(SaltConfig.SALT_FORMULAS))
-                                .build();
-                        String contents = TemplateProcessor.processTemplateContents(new ResourceUtils(minion).getResourceAsString(url), config);
-
-                        // Copy the file contents to the remote machine and install/start salt-minion
-                        DynamicTasks.queue(
-                                SshEffectorTasks.ssh(installCmd),
-                                SshEffectorTasks.put("/tmp/minion")
-                                        .contents(contents)
-                                        .createDirectory(),
-                                SshEffectorTasks.ssh(sudo("mv /tmp/minion /etc/salt/minion")), // TODO clunky
-                                SshEffectorTasks.ssh(sudo("restart salt-minion"))
-                            );
-                    }
-                }).buildFactory();
-    }
-
-    public static TaskFactory<?> installFormulas(final String installDir, final Map<String,String> formulasAndUrls, final boolean force) {
-        return Tasks.<Void>builder().displayName("install formulas").body(
-                new Runnable() {
-                    public void run() {
-                        Entity e = EffectorTasks.findEntity();
-                        if (formulasAndUrls==null)
-                            throw new IllegalStateException("No formulas defined to install at "+e);
-                        for (String formula: formulasAndUrls.keySet())
-                            DynamicTasks.queue(installFormula(installDir, formula, formulasAndUrls.get(formula), force));
-                    }
-                }).buildFactory();
-    }
-
-    public static TaskFactory<?> installFormula(String installDir, String formula, String url, boolean force) {
-        return SshEffectorTasks.ssh(cdAndRun(installDir, SaltBashCommands.downloadAndExpandFormula(url, formula, force)))
-                .summary("install formula "+formula)
-                .requiringExitCodeZero();
-    }
-
-    protected static String cdAndRun(String targetDirectory, String command) {
-        return BashCommands.chain(
-                "mkdir -p "+targetDirectory,
-                "cd "+targetDirectory,
-                command);
-    }
-
-    public static TaskFactory<?> buildSaltFile(String runDir, Iterable<? extends String> runList, Map<String, Object> attributes) {
-        StringBuilder top =  new StringBuilder()
-                .append("base:\n")
-                .append("    '*':\n");
-        for (String run : runList) {
-            top.append("      - " + run + "\n");
-        }
-
-        return SshEffectorTasks.put(Urls.mergePaths(runDir, "base", "top.sls"))
-                .contents(top.toString())
-                .summary("build salt top file")
-                .createDirectory();
-    }
-
-    public static TaskFactory<?> runSalt(String runDir) {
-        return SshEffectorTasks.ssh(cdAndRun(runDir, BashCommands.sudo("salt-call state.highstate")))
-                .summary("run salt install")
-                .requiringExitCodeZero();
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/master
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/master b/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/master
deleted file mode 100644
index 72d7eb9..0000000
--- a/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/master
+++ /dev/null
@@ -1,65 +0,0 @@
-[#ftl]
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#  http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-# /etc/salt/master
-##
-
-#interface: 0.0.0.0
-#ipv6: False
-#publish_port: ${entity.publishPort,c} # 4505
-
-#user: root
-#max_open_files: 100000
-#worker_threads: 5
-ret_port: ${entity.saltPort,c} # 4506
-
-root_dir: /
-pidfile: ${driver.pidFile}
-pki_dir: ${runDir}/pki
-cachedir: ${runDir}/cache
-log_file: ${driver.logFileLocation}
-key_logfile: ${runDir}/key.log
-
-#verify_env: True
-#keep_jobs: 24
-
-#timeout: 5
-#loop_interval: 60
-
-output: nested
-color: False
-log_level: info
-log_level_logfile: debug # Debugging
-
-#job_cache: True
-#minion_data_cache: True
-
-#open_mode: False
-#auto_accept: False
-#autosign_file: autosign.conf
-#permissive_pki_access: False
-
-fileserver_backend:
-  - git
-
-gitfs_remotes:
-  - git://github.com/saltstack/salt-states.git
-  - git://github.com/saltstack-formulas/postgres-formula.git
-  - ${entity.remoteUrl}
-  # TODO iterate through formula URLs

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/masterless
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/masterless b/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/masterless
deleted file mode 100644
index ba41c6e..0000000
--- a/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/masterless
+++ /dev/null
@@ -1,53 +0,0 @@
-[#ftl]
-##
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#  http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-# SaltStack Masterless Minion Configuration
-#
-# /etc/salt/minion
-##
-
-# Minion configuration
-id: ${entity.id}
-user: root
-backup_mode: minion
-
-# Directory settings
-root_dir: /
-pidfile: ${runDir}/salt-minion.pid
-pki_dir: ${runDir}/pki
-cachedir: ${runDir}/cache
-log_file: ${runDir}/minion.log
-key_logfile: ${runDir}/key.log
-
-file_client: local
-file_roots:
-  base:
-    - ${runDir}/base
-[#list formulas?keys as formulaName]
-    - ${installDir}/${formulaName}
-[/#list]
-
-#verify_env: True
-#cache_jobs: True # Debugging
-
-output: nested
-color: False
-log_level: info
-log_level_logfile: debug # Debugging

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/minion
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/minion b/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/minion
deleted file mode 100644
index cff42c7..0000000
--- a/sandbox/extra/src/main/resources/org/apache/brooklyn/entity/salt/minion
+++ /dev/null
@@ -1,52 +0,0 @@
-[#ftl]
-##
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#  http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-# SaltStack Minion Configuration
-#
-# /etc/salt/minion
-##
-
-# The salt master server
-master: ${entity.master.hostname}
-ipv6: False
-retry_dns: 30
-master_port: ${entity.master.saltPort,c} # 4506
-acceptance_wait_time: 30
-acceptance_wait_time_max: 300
-
-# Minion configuration
-id: ${entity.id}
-user: root
-backup_mode: minion
-
-# Directory settings
-root_dir: /
-pidfile: ${runDir}/salt-minion.pid
-pki_dir: ${runDir}/pki
-cachedir: ${runDir}/cache
-log_file: ${runDir}/minion.log
-key_logfile: ${runDir}/key.log
-
-#verify_env: True
-#cache_jobs: True # Debugging
-
-output: nested
-color: False
-log_level: info
-log_level_logfile: debug # Debugging

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/test/java/org/apache/brooklyn/entity/salt/SaltConfigsTest.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/test/java/org/apache/brooklyn/entity/salt/SaltConfigsTest.java b/sandbox/extra/src/test/java/org/apache/brooklyn/entity/salt/SaltConfigsTest.java
deleted file mode 100644
index 0bbbbcd..0000000
--- a/sandbox/extra/src/test/java/org/apache/brooklyn/entity/salt/SaltConfigsTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.brooklyn.entity.salt.SaltConfig;
-import org.apache.brooklyn.entity.salt.SaltConfigs;
-import org.testng.Assert;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.Test;
-import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.factory.ApplicationBuilder;
-import org.apache.brooklyn.core.test.entity.TestApplication;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-
-public class SaltConfigsTest {
-
-    private TestApplication app = null;
-
-    @AfterMethod(alwaysRun=true)
-    public void tearDown() {
-        if (app!=null) Entities.destroyAll(app.getManagementContext());
-        app = null;
-    }
-
-    @Test
-    public void testAddToRunList() {
-        app = ApplicationBuilder.newManagedApp(TestApplication.class);
-        SaltConfigs.addToRunList(app, "a", "b");
-        Set<? extends String> runs = app.getConfig(SaltConfig.SALT_RUN_LIST);
-        Assert.assertEquals(runs, ImmutableSet.of("a", "b"));
-    }
-
-    @Test
-    public void testAddToFormulas() {
-        app = ApplicationBuilder.newManagedApp(TestApplication.class);
-        SaltConfigs.addToFormulas(app, "k1", "v1");
-        SaltConfigs.addToFormulas(app, "k2", "v2");
-        Map<String, String> formulas = app.getConfig(SaltConfig.SALT_FORMULAS);
-        Assert.assertEquals(formulas, ImmutableMap.of("k1", "v1", "k2", "v2"));
-    }
-
-    @Test
-    public void testAddLaunchAttributes() {
-        app = ApplicationBuilder.newManagedApp(TestApplication.class);
-        SaltConfigs.addLaunchAttributes(app, ImmutableMap.of("k1", "v1"));
-        Map<String, Object> attribs = app.getConfig(SaltConfig.SALT_LAUNCH_ATTRIBUTES);
-        Assert.assertEquals(attribs, ImmutableMap.of("k1", "v1"));
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/sandbox/extra/src/test/java/org/apache/brooklyn/entity/salt/SaltLiveTestSupport.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/test/java/org/apache/brooklyn/entity/salt/SaltLiveTestSupport.java b/sandbox/extra/src/test/java/org/apache/brooklyn/entity/salt/SaltLiveTestSupport.java
deleted file mode 100644
index 13f1b34..0000000
--- a/sandbox/extra/src/test/java/org/apache/brooklyn/entity/salt/SaltLiveTestSupport.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.brooklyn.entity.salt;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.annotations.BeforeMethod;
-
-import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
-import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.MachineProvisioningLocation;
-import org.apache.brooklyn.api.mgmt.ManagementContext;
-import org.apache.brooklyn.location.ssh.SshMachineLocation;
-
-public class SaltLiveTestSupport extends BrooklynAppLiveTestSupport {
-
-    private static final Logger log = LoggerFactory.getLogger(SaltLiveTestSupport.class);
-
-    protected MachineProvisioningLocation<? extends SshMachineLocation> targetLocation;
-
-    @Override
-    @BeforeMethod(alwaysRun=true)
-    public void setUp() throws Exception {
-        super.setUp();
-
-        targetLocation = createLocation();
-    }
-
-    protected MachineProvisioningLocation<? extends SshMachineLocation> createLocation() {
-        return createLocation(mgmt);
-    }
-
-    /**
-     * Convenience for setting up a pre-built or fixed IP machine.
-     * <p>
-     * Useful if you are unable to set up Salt on localhost,
-     * and for ensuring tests against Salt always use the same
-     * configured location.
-     */
-    @SuppressWarnings("unchecked")
-    public static MachineProvisioningLocation<? extends SshMachineLocation> createLocation(ManagementContext mgmt) {
-        Location bestLocation = mgmt.getLocationRegistry().resolveIfPossible("named:SaltTests");
-        if (bestLocation==null) {
-            log.info("using AWS for salt tests because named:SaltTests does not exist");
-            bestLocation = mgmt.getLocationRegistry().resolveIfPossible("jclouds:aws-ec2:us-east-1");
-        }
-        if (bestLocation==null) {
-            throw new IllegalStateException("Need a location called named:SaltTests or AWS configured for these tests");
-        }
-        return (MachineProvisioningLocation<? extends SshMachineLocation>)bestLocation;
-    }
-}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/pom.xml
----------------------------------------------------------------------
diff --git a/software/cm/pom.xml b/software/cm/pom.xml
new file mode 100644
index 0000000..90af5b4
--- /dev/null
+++ b/software/cm/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.brooklyn</groupId>
+        <artifactId>brooklyn-library</artifactId>
+        <version>0.9.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.apache.brooklyn</groupId>
+    <artifactId>brooklyn-software-cm</artifactId>
+    <version>0.9.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
+    <packaging>pom</packaging>
+
+    <name>Brooklyn CM Integration Root</name>
+    <description>
+        Brooklyn Library CM project root, serving as the ancestor POM for CM integration projects --
+        declaring versions, profiles, and the modules to build
+    </description>
+    <url>https://brooklyn.apache.org/</url>
+    <inceptionYear>2012</inceptionYear>
+
+
+    <scm>
+        <connection>scm:git:https://git-wip-us.apache.org/repos/asf/incubator-brooklyn.git</connection>
+        <developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/incubator-brooklyn.git</developerConnection>
+        <url>https://git-wip-us.apache.org/repos/asf?p=incubator-brooklyn.git</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <issueManagement>
+        <system>JIRA</system>
+        <url>https://issues.apache.org/jira/browse/BROOKLYN</url>
+    </issueManagement>
+    <ciManagement>
+        <system>Jenkins</system>
+        <url>https://builds.apache.org/job/incubator-brooklyn-master-build/</url>
+    </ciManagement>
+    <mailingLists>
+        <mailingList>
+            <name>Brooklyn Developer List</name>
+            <subscribe>dev-subscribe@brooklyn.apache.org</subscribe>
+            <unsubscribe>dev-unsubscribe@brooklyn.apache.org</unsubscribe>
+            <post>dev@brooklyn.apache.org</post>
+            <archive>
+                http://mail-archives.apache.org/mod_mbox/brooklyn-dev/
+            </archive>
+        </mailingList>
+    </mailingLists>
+
+
+    <modules>
+
+        <module>salt</module>
+
+    </modules>
+
+</project>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/pom.xml
----------------------------------------------------------------------
diff --git a/software/cm/salt/pom.xml b/software/cm/salt/pom.xml
new file mode 100644
index 0000000..f2ba74d
--- /dev/null
+++ b/software/cm/salt/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+     http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>brooklyn-software-cm-salt</artifactId>
+  <packaging>jar</packaging>
+  <name>Brooklyn CM SaltStack</name>
+  <description>Brooklyn entities for Configuration Management using SaltStack</description>
+
+
+    <parent>
+        <groupId>org.apache.brooklyn</groupId>
+        <artifactId>brooklyn-library</artifactId>
+        <version>0.9.0-SNAPSHOT</version>  <!-- BROOKLYN_VERSION -->
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-software-base</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-software-database</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+         <groupId>org.apache.brooklyn</groupId>
+         <artifactId>brooklyn-camp</artifactId>
+        <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-test-support</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-core</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.brooklyn</groupId>
+      <artifactId>brooklyn-software-base</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+
+      <dependency>
+          <groupId>org.assertj</groupId>
+          <artifactId>assertj-core</artifactId>
+          <version>${assertj.version}</version>
+          <scope>test</scope>
+      </dependency>
+
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltAttributes.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltAttributes.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltAttributes.java
new file mode 100644
index 0000000..388b4bc
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltAttributes.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey;
+import org.apache.brooklyn.core.sensor.Sensors;
+
+public final class SaltAttributes {
+
+    private SaltAttributes() {
+        // Utility class
+    }
+
+    public static final AttributeSensor<Location> INFRA_LOCATION = Sensors.newSensor(Location.class,
+            "infra.location", "The location where the infrastructure is deployed");
+
+    public static final AttributeSensorAndConfigKey<Entity, Entity> SALT_INFRASTRUCTURE = 
+            ConfigKeys.newSensorAndConfigKey(Entity.class, "salt.infrastructure", "The SaltStack infrastructure");
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltConfig.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltConfig.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltConfig.java
new file mode 100644
index 0000000..88eb677
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltConfig.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.MapConfigKey;
+import org.apache.brooklyn.core.config.SetConfigKey;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+/**
+ * {@link ConfigKey}s used to configure Salt entities.
+ *
+ * @see SaltConfigs
+ */
+@Beta
+public interface SaltConfig {
+
+    enum SaltMode {
+        /** Master entity */
+        MASTER, 
+        /** Minion entity */
+        MINION,
+        /** Masterless entity using salt-ssh */
+        MASTERLESS
+    }
+
+    @SetFromFlag("salt.mode")
+    ConfigKey<SaltMode> SALT_MODE = ConfigKeys.newConfigKey(SaltMode.class, "brooklyn.salt.mode",
+            "SaltStack execution mode (master/minion/masterless)", SaltMode.MASTERLESS);
+
+    @SetFromFlag("pillars")
+    SetConfigKey<String> SALT_PILLARS = new SetConfigKey<>(String.class, "brooklyn.salt.pillars",
+            "List of Salt Pillar identifiers for top.sls", ImmutableSet.<String>of());
+
+    @SetFromFlag("pillarUrls")
+    SetConfigKey<String> SALT_PILLAR_URLS = new SetConfigKey<>(String.class, "brooklyn.salt.pillarUrls",
+            "List of Salt Pillar archive URLs", ImmutableSet.<String>of());
+
+    @SetFromFlag("formulas")
+    SetConfigKey<String> SALT_FORMULAS = new SetConfigKey<>(String.class, "brooklyn.salt.formulaUrls",
+            "List of Salt formula URLs", ImmutableSet.<String>of());
+
+    @SetFromFlag("start_states")
+    SetConfigKey<String> START_STATES = new SetConfigKey<>(String.class, "brooklyn.salt.start.states",
+            "Set of Salt states to apply to start entity", ImmutableSet.<String>of());
+
+    @SetFromFlag("stop_states")
+    SetConfigKey<String> STOP_STATES = new SetConfigKey<>(String.class, "brooklyn.salt.stop.states",
+            "Set of Salt states to apply to stop entity", ImmutableSet.<String>of());
+
+    @SetFromFlag("restart_states")
+    SetConfigKey<String> RESTART_STATES = new SetConfigKey<>(String.class, "brooklyn.salt.restart.states",
+            "Set of Salt states to apply to restart entity", ImmutableSet.<String>of());
+
+    MapConfigKey<Object> SALT_SSH_LAUNCH_ATTRIBUTES = new MapConfigKey<>(Object.class, "brooklyn.salt.ssh.launch.attributes", "TODO");
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltConfigs.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltConfigs.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltConfigs.java
new file mode 100644
index 0000000..9bbb0c9
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltConfigs.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt;
+
+import java.util.Map;
+
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.core.config.MapConfigKey.MapModifications;
+import org.apache.brooklyn.core.config.SetConfigKey.SetModifications;
+import org.apache.brooklyn.core.entity.EntityInternal;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+/**
+ * Conveniences for configuring Brooklyn Salt entities 
+ *
+ * @since 0.9.0
+ */
+@Beta
+public class SaltConfigs {
+
+    public static void addToRunList(EntitySpec<?> entity, String...states) {
+        for (String state : states) {
+            entity.configure(SaltConfig.START_STATES, SetModifications.addItem(state));
+        }
+    }
+
+    public static void addToRunList(EntityInternal entity, String...states) {
+        for (String state : states) {
+            entity.config().set(SaltConfig.START_STATES, SetModifications.addItem(state));
+        }
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static void addLaunchAttributes(EntitySpec<?> entity, Map<?,?> attributesMap) {
+        entity.configure(SaltConfig.SALT_SSH_LAUNCH_ATTRIBUTES, MapModifications.add((Map)attributesMap));
+    }
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static void addLaunchAttributes(EntityInternal entity, Map<?,?> attributesMap) {
+        entity.config().set(SaltConfig.SALT_SSH_LAUNCH_ATTRIBUTES, MapModifications.add((Map)attributesMap));
+    }
+
+    public static void addToFormulas(EntitySpec<?> entity, String formulaUrl) {
+        entity.configure(SaltConfig.SALT_FORMULAS, SetModifications.addItem(formulaUrl));
+    }
+
+    public static void addToFormulas(EntityInternal entity, String formulaUrl) {
+        entity.config().set(SaltConfig.SALT_FORMULAS, SetModifications.addItem(formulaUrl));
+    }
+
+    public static <T> T getRequiredConfig(Entity entity, ConfigKey<T> key) {
+        return Preconditions.checkNotNull(
+                Preconditions.checkNotNull(entity, "Entity must be supplied").getConfig(key), 
+                "Key " + key + " is required on " + entity);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
new file mode 100644
index 0000000..00d017d
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt;
+
+import com.google.common.reflect.TypeToken;
+import org.apache.brooklyn.api.catalog.Catalog;
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.entity.cm.salt.impl.SaltEntityImpl;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.annotation.Effector;
+import org.apache.brooklyn.core.annotation.EffectorParam;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.effector.MethodEffector;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
+import org.apache.brooklyn.util.core.flags.SetFromFlag;
+
+import java.util.List;
+
+@ImplementedBy(SaltEntityImpl.class)
+@Catalog(name="SaltEntity", description="Software managed by Salt CM")
+public interface SaltEntity extends SoftwareProcess, SaltConfig {
+
+    @SetFromFlag("version")
+    ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(
+        BrooklynConfigKeys.SUGGESTED_VERSION, "stable");
+
+    AttributeSensor<List<String>> STATES = Sensors.newSensor(new TypeToken<List<String>>() {}, "salt.states",
+        "Salt Highstate states");
+
+    MethodEffector<String> SALT_CALL = new MethodEffector<>(SaltEntity.class, "saltCall");
+
+    @Effector(description = "Invoke a Salt command")
+    String saltCall(
+        @EffectorParam(name = "spec", description = "Name and optional arguments of a Salt command") String spec);
+
+}
+

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java
new file mode 100644
index 0000000..19a88ed
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntityDriver.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt;
+
+import org.apache.brooklyn.entity.software.base.SoftwareProcessDriver;
+
+public interface SaltEntityDriver extends SoftwareProcessDriver {
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java
new file mode 100644
index 0000000..bea2581
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntityImpl.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt.impl;
+
+import com.google.common.annotations.Beta;
+import org.apache.brooklyn.entity.cm.salt.SaltConfig;
+import org.apache.brooklyn.entity.cm.salt.SaltEntity;
+import org.apache.brooklyn.entity.stock.EffectorStartableImpl;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+@Beta
+public class SaltEntityImpl extends EffectorStartableImpl implements SaltEntity {
+    private static final Logger LOG = LoggerFactory.getLogger(SaltEntityImpl.class);
+
+    public SaltEntityImpl() {
+        super();
+    }
+
+    @Override
+    public void init() {
+        super.init();
+
+        final Set<? extends String> runList = getConfig(SaltConfig.START_STATES);
+        if (0 == runList.size()) {
+            throw new IllegalArgumentException("Must have configuration values for 'start_states' ("
+                + SaltConfig.START_STATES + ")");
+        }
+
+        LOG.debug("Run list size is {}", runList.size());
+        for (String state : runList) {
+            LOG.debug("Runlist state: {} ", state);
+        }
+
+        final Set<? extends String> formulas = getConfig(SaltConfig.SALT_FORMULAS);
+        LOG.debug("Formulas size: {}", formulas.size());
+        for (String formula : formulas) {
+            LOG.debug("Formula configured:  {}", formula);
+        }
+
+        SaltConfig.SaltMode mode = getConfig(SaltConfig.SALT_MODE);
+        LOG.debug("Initialize SaltStack {} mode", mode.name());
+        new SaltLifecycleEffectorTasks().attachLifecycleEffectors(this);
+    }
+
+    @Override
+    public void populateServiceNotUpDiagnostics() {
+        // TODO: noop for now;
+    }
+
+    @Override
+    public String saltCall(String spec) {
+        final ProcessTaskWrapper<Integer> command = DynamicTasks.queue(SaltSshTasks.saltCall(spec));
+        command.asTask().blockUntilEnded();
+        if (0 == command.getExitCode()) {
+            return command.getStdout();
+        } else {
+            throw new RuntimeException("Command (" + spec + ")  failed with stderr:\n" + command.getStderr() + "\n");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/a122c5e6/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java
new file mode 100644
index 0000000..7531c27
--- /dev/null
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltEntitySshDriver.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.cm.salt.impl;
+
+import org.apache.brooklyn.entity.cm.salt.SaltEntityDriver;
+import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+
+// TODO: does this belong to the _.impl package?
+public class SaltEntitySshDriver extends AbstractSoftwareProcessSshDriver implements SaltEntityDriver {
+
+    public SaltEntitySshDriver(SaltEntityImpl entity, SshMachineLocation machine) {
+        super(entity, machine);
+    }
+
+    @Override
+    public SaltEntityImpl getEntity() {
+        return (SaltEntityImpl) super.getEntity();
+    }
+
+    @Override
+    public void install() {
+    }
+
+    @Override
+    public void customize() {
+    }
+
+    @Override
+    public void launch() {
+    }
+
+    @Override
+    public boolean isRunning() {
+        return true;
+    }
+
+    @Override
+    public void stop() {
+    }
+
+}


[4/4] brooklyn-library git commit: minor tidies of salt work

Posted by he...@apache.org.
minor tidies of salt work


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

Branch: refs/heads/master
Commit: d78795eeb1fc8f4d9da62c58e4723f4e899796fc
Parents: 20ff02b
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Feb 8 17:28:03 2016 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Feb 8 17:37:36 2016 +0000

----------------------------------------------------------------------
 software/cm/salt/pom.xml                                        | 5 -----
 .../java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java     | 2 ++
 .../org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java  | 2 ++
 .../org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java   | 3 ++-
 .../java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java  | 1 +
 5 files changed, 7 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/d78795ee/software/cm/salt/pom.xml
----------------------------------------------------------------------
diff --git a/software/cm/salt/pom.xml b/software/cm/salt/pom.xml
index f2ba74d..40d150d 100644
--- a/software/cm/salt/pom.xml
+++ b/software/cm/salt/pom.xml
@@ -46,11 +46,6 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
-      <groupId>org.apache.brooklyn</groupId>
-      <artifactId>brooklyn-software-database</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
          <groupId>org.apache.brooklyn</groupId>
          <artifactId>brooklyn-camp</artifactId>
         <version>${project.version}</version>

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/d78795ee/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
index 00d017d..4428658 100644
--- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/SaltEntity.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.entity.cm.salt;
 
 import com.google.common.reflect.TypeToken;
+
 import org.apache.brooklyn.api.catalog.Catalog;
 import org.apache.brooklyn.api.entity.ImplementedBy;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
@@ -43,6 +44,7 @@ public interface SaltEntity extends SoftwareProcess, SaltConfig {
     ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(
         BrooklynConfigKeys.SUGGESTED_VERSION, "stable");
 
+    @SuppressWarnings("serial")
     AttributeSensor<List<String>> STATES = Sensors.newSensor(new TypeToken<List<String>>() {}, "salt.states",
         "Salt Highstate states");
 

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/d78795ee/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
index 5f5069a..e48c0b2 100644
--- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltHighstate.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.entity.cm.salt.impl;
 
 import com.google.common.reflect.TypeToken;
+
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.core.sensor.Sensors;
@@ -41,6 +42,7 @@ public class SaltHighstate {
 
     public static final String HIGHSTATE_SENSOR_PREFIX = "salt.state";
 
+    @SuppressWarnings("serial")
     public static TypeToken<Map<String, Object>> STATE_FUNCTION_TYPE =
         new TypeToken<Map<String, Object>>() {};
 

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/d78795ee/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
index 177843f..a8e1af6 100644
--- a/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
+++ b/software/cm/salt/src/main/java/org/apache/brooklyn/entity/cm/salt/impl/SaltSshTasks.java
@@ -212,7 +212,8 @@ public class SaltSshTasks {
     public static SshEffectorTaskFactory<Integer> invokeSaltUtility(String functionName, String args, boolean permitFailure) {
 
         final SshEffectorTaskFactory<Integer> taskFactory =
-            ssh(sudo("/bin/bash -c '. /etc/salt/salt_utilities.sh ; " + functionName + " " + args + "'"));
+            ssh(sudo("/bin/bash -c '. /etc/salt/salt_utilities.sh ; " + functionName + " " + args + "'"))
+            .summary(functionName);
 
         if (permitFailure) {
             taskFactory.allowingNonZeroExitCode();

http://git-wip-us.apache.org/repos/asf/brooklyn-library/blob/d78795ee/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
----------------------------------------------------------------------
diff --git a/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
index 6163262..4f9ef1f 100644
--- a/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
+++ b/software/cm/salt/src/test/java/org/apache/brooklyn/entity/cm/salt/HighstateTest.java
@@ -65,6 +65,7 @@ public class HighstateTest {
         SaltHighstate.applyHighstate(contents, entity);
 
         final List<String> states = entity.sensors().get(SaltEntity.STATES);
+        LOG.info("Test states are: "+states);
         assertThat(states)
             .contains("apache")
             .contains("apache-reload")


[3/4] brooklyn-library git commit: This closes #1

Posted by he...@apache.org.
This closes #1


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

Branch: refs/heads/master
Commit: 20ff02bba00b6a3acd60ab523ee83ebcd515c159
Parents: c15409b a122c5e
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Feb 8 16:53:08 2016 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Feb 8 16:53:08 2016 +0000

----------------------------------------------------------------------
 pom.xml                                         |   1 +
 .../brooklyn/entity/salt/SaltBashCommands.java  |  91 -------
 .../apache/brooklyn/entity/salt/SaltConfig.java | 101 -------
 .../brooklyn/entity/salt/SaltConfigs.java       |  89 ------
 .../entity/salt/SaltLifecycleEffectorTasks.java | 220 ---------------
 .../brooklyn/entity/salt/SaltStackMaster.java   |  72 -----
 .../entity/salt/SaltStackMasterDriver.java      |  25 --
 .../entity/salt/SaltStackMasterImpl.java        |  55 ----
 .../entity/salt/SaltStackMasterSshDriver.java   |  96 -------
 .../apache/brooklyn/entity/salt/SaltTasks.java  | 145 ----------
 .../org/apache/brooklyn/entity/salt/master      |  65 -----
 .../org/apache/brooklyn/entity/salt/masterless  |  53 ----
 .../org/apache/brooklyn/entity/salt/minion      |  52 ----
 .../brooklyn/entity/salt/SaltConfigsTest.java   |  70 -----
 .../entity/salt/SaltLiveTestSupport.java        |  68 -----
 software/cm/pom.xml                             |  80 ++++++
 software/cm/salt/pom.xml                        |  89 ++++++
 .../brooklyn/entity/cm/salt/SaltAttributes.java |  40 +++
 .../brooklyn/entity/cm/salt/SaltConfig.java     |  76 ++++++
 .../brooklyn/entity/cm/salt/SaltConfigs.java    |  77 ++++++
 .../brooklyn/entity/cm/salt/SaltEntity.java     |  56 ++++
 .../entity/cm/salt/SaltEntityDriver.java        |  25 ++
 .../entity/cm/salt/impl/SaltEntityImpl.java     |  81 ++++++
 .../cm/salt/impl/SaltEntitySshDriver.java       |  58 ++++
 .../entity/cm/salt/impl/SaltHighstate.java      | 142 ++++++++++
 .../salt/impl/SaltLifecycleEffectorTasks.java   | 271 +++++++++++++++++++
 .../entity/cm/salt/impl/SaltSshDriver.java      |  28 ++
 .../entity/cm/salt/impl/SaltSshTasks.java       | 236 ++++++++++++++++
 .../brooklyn/entity/cm/salt/impl/SaltUtils.java |  41 +++
 .../salt/src/main/resources/salt_utilities.sh   |  79 ++++++
 .../brooklyn/entity/cm/salt/HighstateTest.java  |  93 +++++++
 .../entity/cm/salt/SaltConfigsTest.java         |  70 +++++
 .../entity/cm/salt/SaltIntegrationTest.java     | 123 +++++++++
 .../salt/src/test/resources/test-highstate.yaml |  47 ++++
 34 files changed, 1713 insertions(+), 1202 deletions(-)
----------------------------------------------------------------------