You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ha...@apache.org on 2015/08/07 01:59:55 UTC
[4/6] incubator-brooklyn git commit: package rename to
org.apache.brooklyn: sandbox
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltConfigs.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltConfigs.java b/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltConfigs.java
deleted file mode 100644
index 2de0e5d..0000000
--- a/sandbox/extra/src/main/java/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 brooklyn.entity.salt;
-
-import java.util.Map;
-
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.Entity;
-import brooklyn.entity.basic.EntityInternal;
-import brooklyn.entity.proxying.EntitySpec;
-import brooklyn.event.basic.MapConfigKey.MapModifications;
-import brooklyn.event.basic.SetConfigKey.SetModifications;
-
-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.setConfig(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.setConfig(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.setConfig(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.setConfig(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/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java b/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
deleted file mode 100644
index 82e1b17..0000000
--- a/sandbox/extra/src/main/java/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 brooklyn.entity.salt;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.BrooklynServerConfig;
-import brooklyn.entity.Entity;
-import brooklyn.entity.basic.Attributes;
-import brooklyn.entity.basic.Lifecycle;
-import brooklyn.entity.software.MachineLifecycleEffectorTasks;
-import brooklyn.entity.software.SshEffectorTasks;
-import brooklyn.location.MachineLocation;
-import brooklyn.util.net.Urls;
-import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.time.Duration;
-import 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().setAttribute(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().setAttribute(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/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMaster.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMaster.java b/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMaster.java
deleted file mode 100644
index 0db1841..0000000
--- a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMaster.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 brooklyn.entity.salt;
-
-import java.util.List;
-
-import org.apache.brooklyn.catalog.Catalog;
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.BrooklynConfigKeys;
-import brooklyn.entity.basic.ConfigKeys;
-import brooklyn.entity.basic.SoftwareProcess;
-import brooklyn.entity.proxying.ImplementedBy;
-import brooklyn.event.AttributeSensor;
-import brooklyn.event.basic.BasicAttributeSensor;
-import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
-import brooklyn.util.flags.SetFromFlag;
-
-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:///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/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterDriver.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterDriver.java b/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterDriver.java
deleted file mode 100644
index 55d46da..0000000
--- a/sandbox/extra/src/main/java/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 brooklyn.entity.salt;
-
-import brooklyn.entity.basic.SoftwareProcessDriver;
-
-public interface SaltStackMasterDriver extends SoftwareProcessDriver {
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterImpl.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterImpl.java b/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterImpl.java
deleted file mode 100644
index 2a6815c..0000000
--- a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterImpl.java
+++ /dev/null
@@ -1,56 +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 brooklyn.entity.salt;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.entity.basic.SoftwareProcessImpl;
-import brooklyn.event.feed.ConfigToAttributes;
-
-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/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterSshDriver.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterSshDriver.java b/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterSshDriver.java
deleted file mode 100644
index 8a326e7..0000000
--- a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltStackMasterSshDriver.java
+++ /dev/null
@@ -1,95 +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 brooklyn.entity.salt;
-
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.java.JavaSoftwareProcessSshDriver;
-import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-
-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/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltTasks.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltTasks.java b/sandbox/extra/src/main/java/brooklyn/entity/salt/SaltTasks.java
deleted file mode 100644
index 5b4c4ee..0000000
--- a/sandbox/extra/src/main/java/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 brooklyn.entity.salt;
-
-import static brooklyn.util.ssh.BashCommands.INSTALL_CURL;
-import static brooklyn.util.ssh.BashCommands.INSTALL_TAR;
-import static brooklyn.util.ssh.BashCommands.INSTALL_UNZIP;
-import static brooklyn.util.ssh.BashCommands.downloadToStdout;
-import static brooklyn.util.ssh.BashCommands.sudo;
-
-import java.util.Map;
-
-import brooklyn.entity.Entity;
-import brooklyn.entity.basic.Entities;
-import brooklyn.entity.effector.EffectorTasks;
-import brooklyn.entity.software.SshEffectorTasks;
-import brooklyn.management.TaskFactory;
-import brooklyn.util.ResourceUtils;
-import brooklyn.util.collections.MutableMap;
-import brooklyn.util.net.Urls;
-import brooklyn.util.ssh.BashCommands;
-import brooklyn.util.task.DynamicTasks;
-import brooklyn.util.task.Tasks;
-import brooklyn.util.text.TemplateProcessor;
-
-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().name("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().name("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/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/java/org/apache/brooklyn/entity/database/postgresql/PostgreSqlNodeSaltImpl.java
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/java/org/apache/brooklyn/entity/database/postgresql/PostgreSqlNodeSaltImpl.java b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/database/postgresql/PostgreSqlNodeSaltImpl.java
new file mode 100644
index 0000000..66c7acb
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/database/postgresql/PostgreSqlNodeSaltImpl.java
@@ -0,0 +1,179 @@
+/*
+ * 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.database.postgresql;
+
+import org.apache.brooklyn.entity.database.postgresql.PostgreSqlNodeSaltImpl;
+import org.apache.brooklyn.entity.salt.SaltConfig;
+import org.apache.brooklyn.entity.salt.SaltConfigs;
+import org.apache.brooklyn.entity.salt.SaltLifecycleEffectorTasks;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.Effector;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.EffectorStartableImpl;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.basic.SoftwareProcess;
+import brooklyn.entity.database.postgresql.PostgreSqlNode;
+import brooklyn.entity.effector.EffectorBody;
+import brooklyn.entity.effector.Effectors;
+import brooklyn.entity.software.SshEffectorTasks;
+import brooklyn.event.basic.DependentConfiguration;
+import brooklyn.event.feed.ssh.SshFeed;
+import brooklyn.event.feed.ssh.SshPollConfig;
+import brooklyn.location.Location;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.config.ConfigBag;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.task.DynamicTasks;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
+public class PostgreSqlNodeSaltImpl extends EffectorStartableImpl implements PostgreSqlNode, SoftwareProcess {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PostgreSqlNodeSaltImpl.class);
+
+ public static final Effector<String> EXECUTE_SCRIPT = Effectors.effector(String.class, "executeScript")
+ .description("invokes a script")
+ .parameter(ExecuteScriptEffectorBody.SCRIPT)
+ .impl(new ExecuteScriptEffectorBody())
+ .build();
+
+ private SshFeed feed;
+
+ @Override
+ public void init() {
+ super.init();
+ new SaltPostgreSqlLifecycle().attachLifecycleEffectors(this);
+ }
+
+ public static class SaltPostgreSqlLifecycle extends SaltLifecycleEffectorTasks {
+ public SaltPostgreSqlLifecycle() {
+ usePidFile("/var/run/postgresql/*.pid");
+ useService("postgresql");
+ }
+
+ @Override
+ protected void startMinionAsync() {
+ Entities.warnOnIgnoringConfig(entity(), SaltConfig.SALT_FORMULAS);
+ Entities.warnOnIgnoringConfig(entity(), SaltConfig.SALT_RUN_LIST);
+ Entities.warnOnIgnoringConfig(entity(), SaltConfig.SALT_LAUNCH_ATTRIBUTES);
+
+ // TODO Set these as defaults, rather than replacing user's value!?
+ SaltConfigs.addToFormulas(entity(), "postgres", "https://github.com/saltstack-formulas/postgres-formula/archive/master.tar.gz");
+ SaltConfigs.addToRunList(entity(), "postgres");
+ SaltConfigs.addLaunchAttributes(entity(), ImmutableMap.<String,Object>builder()
+ .put("port", DependentConfiguration.attributeWhenReady(entity(), PostgreSqlNode.POSTGRESQL_PORT))
+ .put("listen_addresses", "*")
+ .put("pg_hba.type", "host")
+ .put("pg_hba.db", "all")
+ .put("pg_hba.user", "all")
+ .put("pg_hba.addr", "0.0.0.0/0")
+ .put("pg_hba.method", "md5")
+ .build());
+
+ super.startMinionAsync();
+ }
+
+ @Override
+ protected void postStartCustom() {
+ super.postStartCustom();
+
+ // now run the creation script
+ String creationScriptUrl = entity().getConfig(PostgreSqlNode.CREATION_SCRIPT_URL);
+ String creationScript;
+ if (creationScriptUrl != null) {
+ creationScript = new ResourceUtils(entity()).getResourceAsString(creationScriptUrl);
+ } else {
+ creationScript = entity().getConfig(PostgreSqlNode.CREATION_SCRIPT_CONTENTS);
+ }
+ entity().invoke(PostgreSqlNodeSaltImpl.EXECUTE_SCRIPT,
+ ConfigBag.newInstance().configure(ExecuteScriptEffectorBody.SCRIPT, creationScript).getAllConfig()).getUnchecked();
+
+ // and finally connect sensors
+ ((PostgreSqlNodeSaltImpl) entity()).connectSensors();
+ }
+
+ @Override
+ protected void preStopCustom() {
+ ((PostgreSqlNodeSaltImpl) entity()).disconnectSensors();
+ super.preStopCustom();
+ }
+ }
+
+ public static class ExecuteScriptEffectorBody extends EffectorBody<String> {
+ public static final ConfigKey<String> SCRIPT = ConfigKeys.newStringConfigKey("script", "contents of script to run");
+
+ @Override
+ public String call(ConfigBag parameters) {
+ return DynamicTasks.queue(SshEffectorTasks.ssh(
+ BashCommands.pipeTextTo(
+ parameters.get(SCRIPT),
+ BashCommands.sudoAsUser("postgres", "psql --file -")))
+ .requiringExitCodeZero()).getStdout();
+ }
+ }
+
+ protected void connectSensors() {
+ setAttribute(DATASTORE_URL, String.format("postgresql://%s:%s/", getAttribute(HOSTNAME), getAttribute(POSTGRESQL_PORT)));
+
+ Location machine = Iterables.get(getLocations(), 0, null);
+
+ if (machine instanceof SshMachineLocation) {
+ feed = SshFeed.builder()
+ .entity(this)
+ .machine((SshMachineLocation)machine)
+ .poll(new SshPollConfig<Boolean>(SERVICE_UP)
+ .command("ps -ef | grep [p]ostgres")
+ .setOnSuccess(true)
+ .setOnFailureOrException(false))
+ .build();
+ } else {
+ LOG.warn("Location(s) %s not an ssh-machine location, so not polling for status; setting serviceUp immediately", getLocations());
+ }
+ }
+
+ protected void disconnectSensors() {
+ if (feed != null) feed.stop();
+ }
+
+ @Override
+ public Integer getPostgreSqlPort() { return getAttribute(POSTGRESQL_PORT); }
+
+ @Override
+ public String getSharedMemory() { return getConfig(SHARED_MEMORY); }
+
+ @Override
+ public Integer getMaxConnections() { return getConfig(MAX_CONNECTIONS); }
+
+ @Override
+ public String getShortName() {
+ return "PostgreSQL";
+ }
+
+ @Override
+ public String executeScript(String commands) {
+ return Entities.invokeEffector(this, this, EXECUTE_SCRIPT,
+ ConfigBag.newInstance().configure(ExecuteScriptEffectorBody.SCRIPT, commands).getAllConfig()).getUnchecked();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/070b5ca7/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
new file mode 100644
index 0000000..a9a07bf
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltBashCommands.java
@@ -0,0 +1,92 @@
+/*
+ * 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 brooklyn.util.ssh.BashCommands.downloadToStdout;
+
+import javax.annotation.Nullable;
+
+import org.apache.commons.io.FilenameUtils;
+
+import brooklyn.entity.chef.ChefBashCommands;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.text.Identifiers;
+import 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/incubator-brooklyn/blob/070b5ca7/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
new file mode 100644
index 0000000..3b5962f
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfig.java
@@ -0,0 +1,100 @@
+/*
+ * 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 brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.BasicAttributeSensor;
+import brooklyn.event.basic.BasicConfigKey;
+import brooklyn.event.basic.MapConfigKey;
+import brooklyn.event.basic.SetConfigKey;
+import brooklyn.management.TaskAdaptable;
+import brooklyn.management.TaskFactory;
+import brooklyn.util.flags.SetFromFlag;
+
+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/incubator-brooklyn/blob/070b5ca7/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
new file mode 100644
index 0000000..1622d85
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltConfigs.java
@@ -0,0 +1,89 @@
+/*
+ * 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 brooklyn.config.ConfigKey;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.EntityInternal;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.event.basic.MapConfigKey.MapModifications;
+import brooklyn.event.basic.SetConfigKey.SetModifications;
+
+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.setConfig(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.setConfig(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.setConfig(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.setConfig(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/incubator-brooklyn/blob/070b5ca7/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
new file mode 100644
index 0000000..7005db9
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
@@ -0,0 +1,220 @@
+/*
+ * 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 brooklyn.config.BrooklynServerConfig;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.Lifecycle;
+import brooklyn.entity.software.MachineLifecycleEffectorTasks;
+import brooklyn.entity.software.SshEffectorTasks;
+import brooklyn.location.MachineLocation;
+import brooklyn.util.net.Urls;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.task.DynamicTasks;
+import brooklyn.util.task.Tasks;
+import brooklyn.util.time.Duration;
+import 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().setAttribute(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().setAttribute(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/incubator-brooklyn/blob/070b5ca7/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
new file mode 100644
index 0000000..8ce2e3b
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMaster.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.salt;
+
+import java.util.List;
+
+import org.apache.brooklyn.catalog.Catalog;
+import brooklyn.config.ConfigKey;
+import brooklyn.entity.basic.BrooklynConfigKeys;
+import brooklyn.entity.basic.ConfigKeys;
+import brooklyn.entity.basic.SoftwareProcess;
+import brooklyn.entity.proxying.ImplementedBy;
+import brooklyn.event.AttributeSensor;
+import brooklyn.event.basic.BasicAttributeSensor;
+import brooklyn.event.basic.PortAttributeSensorAndConfigKey;
+import brooklyn.util.flags.SetFromFlag;
+
+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/incubator-brooklyn/blob/070b5ca7/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
new file mode 100644
index 0000000..703213a
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterDriver.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.salt;
+
+import brooklyn.entity.basic.SoftwareProcessDriver;
+
+public interface SaltStackMasterDriver extends SoftwareProcessDriver {
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/070b5ca7/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
new file mode 100644
index 0000000..1adfd1a
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterImpl.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.salt;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.basic.SoftwareProcessImpl;
+import brooklyn.event.feed.ConfigToAttributes;
+
+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/incubator-brooklyn/blob/070b5ca7/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
new file mode 100644
index 0000000..4b88acc
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltStackMasterSshDriver.java
@@ -0,0 +1,95 @@
+/*
+ * 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 brooklyn.entity.basic.Entities;
+import brooklyn.entity.java.JavaSoftwareProcessSshDriver;
+import brooklyn.location.basic.SshMachineLocation;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.task.DynamicTasks;
+
+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/incubator-brooklyn/blob/070b5ca7/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
new file mode 100644
index 0000000..5cec099
--- /dev/null
+++ b/sandbox/extra/src/main/java/org/apache/brooklyn/entity/salt/SaltTasks.java
@@ -0,0 +1,145 @@
+/*
+ * 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 brooklyn.util.ssh.BashCommands.INSTALL_CURL;
+import static brooklyn.util.ssh.BashCommands.INSTALL_TAR;
+import static brooklyn.util.ssh.BashCommands.INSTALL_UNZIP;
+import static brooklyn.util.ssh.BashCommands.downloadToStdout;
+import static brooklyn.util.ssh.BashCommands.sudo;
+
+import java.util.Map;
+
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.effector.EffectorTasks;
+import brooklyn.entity.software.SshEffectorTasks;
+import brooklyn.management.TaskFactory;
+import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.net.Urls;
+import brooklyn.util.ssh.BashCommands;
+import brooklyn.util.task.DynamicTasks;
+import brooklyn.util.task.Tasks;
+import brooklyn.util.text.TemplateProcessor;
+
+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().name("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().name("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/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/resources/brooklyn/entity/salt/master
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/resources/brooklyn/entity/salt/master b/sandbox/extra/src/main/resources/brooklyn/entity/salt/master
deleted file mode 100644
index 72d7eb9..0000000
--- a/sandbox/extra/src/main/resources/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/incubator-brooklyn/blob/070b5ca7/sandbox/extra/src/main/resources/brooklyn/entity/salt/masterless
----------------------------------------------------------------------
diff --git a/sandbox/extra/src/main/resources/brooklyn/entity/salt/masterless b/sandbox/extra/src/main/resources/brooklyn/entity/salt/masterless
deleted file mode 100644
index ba41c6e..0000000
--- a/sandbox/extra/src/main/resources/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