You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by ri...@apache.org on 2014/05/28 18:05:21 UTC
[05/21] git commit: Modified SaltStack to use masterless minion
configuration initially
Modified SaltStack to use masterless minion configuration initially
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/b67d8bd0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/b67d8bd0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/b67d8bd0
Branch: refs/heads/master
Commit: b67d8bd0a09e218042b4f025dbe44965b84d42c8
Parents: c21a7bc
Author: Andrew Kennedy <an...@cloudsoftcorp.com>
Authored: Wed Oct 23 14:26:20 2013 +0100
Committer: Andrew Kennedy <an...@cloudsoftcorp.com>
Committed: Wed May 28 15:12:53 2014 +0100
----------------------------------------------------------------------
.../brooklyn/entity/salt/SaltBashCommands.java | 57 ++++++-----
.../java/brooklyn/entity/salt/SaltConfig.java | 15 ++-
.../java/brooklyn/entity/salt/SaltConfigs.java | 14 +--
.../entity/salt/SaltLifecycleEffectorTasks.java | 75 ++++++++------
.../java/brooklyn/entity/salt/SaltTasks.java | 102 +++++++++++--------
.../main/resources/brooklyn/entity/salt/master | 11 +-
.../resources/brooklyn/entity/salt/masterless | 37 +++++++
.../main/resources/brooklyn/entity/salt/minion | 4 +-
.../main/resources/brooklyn/entity/salt/top.sls | 1 +
.../postgresql/PostgreSqlNodeSaltImpl.java | 31 +++---
10 files changed, 219 insertions(+), 128 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b67d8bd0/software/base/src/main/java/brooklyn/entity/salt/SaltBashCommands.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/salt/SaltBashCommands.java b/software/base/src/main/java/brooklyn/entity/salt/SaltBashCommands.java
index 1d37afc..4ee4b4a 100644
--- a/software/base/src/main/java/brooklyn/entity/salt/SaltBashCommands.java
+++ b/software/base/src/main/java/brooklyn/entity/salt/SaltBashCommands.java
@@ -1,10 +1,6 @@
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 static brooklyn.util.ssh.BashCommands.*;
import javax.annotation.Nullable;
@@ -27,11 +23,11 @@ import com.google.common.io.Files;
public class SaltBashCommands {
/**
- * SaltStack formulas are normally found at https://github.com/saltstack-formulas as repositories.
- *
- * this assumes the download is an archive containing a single directory on the root which will be renamed to "cookbookName";
- * if that directory already has the correct name cookbookName can be null,
- * but if e.g. taking from a github tarball it will typically be of the form cookbookName-master/
+ * 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)
@@ -39,9 +35,8 @@ public class SaltBashCommands {
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 downloaded archive must contain exactly one directory; contained"),
+ 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 ..",
@@ -49,27 +44,33 @@ public class SaltBashCommands {
if (!force) return BashCommands.alternatives("ls "+formulaName, installCmd);
else return BashCommands.alternatives("rm -rf "+formulaName, installCmd);
}
-
- /** as {@link #downloadAndExpandFormula(String, String)} with no formula name */
- public static final String downloadAndExpandFormula(String source) {
-// curl -f -L https://github.com/saltstack-formulas/nginx-formula/archive/master.tar.gz | tar xvz
- String ext = Files.getFileExtension(source);
+
+ /**
+ * 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(source) + " | tar xv";
- if ("tgz".equalsIgnoreCase(ext) || source.toLowerCase().endsWith(".tar.gz"))
- return downloadToStdout(source) + " | tar xvz";
-
- String target = FilenameUtils.getName(source);
+ 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(source, target),
+ BashCommands.commandToDownloadUrlAs(sourceUrl, target),
"unzip "+target,
"rm "+target);
-
- throw new UnsupportedOperationException("No way to expand "+source+" (yet)");
+
+ throw new UnsupportedOperationException("No way to expand "+sourceUrl+" (yet)");
}
-
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b67d8bd0/software/base/src/main/java/brooklyn/entity/salt/SaltConfig.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/salt/SaltConfig.java b/software/base/src/main/java/brooklyn/entity/salt/SaltConfig.java
index 5c48487..7d2fbb3 100644
--- a/software/base/src/main/java/brooklyn/entity/salt/SaltConfig.java
+++ b/software/base/src/main/java/brooklyn/entity/salt/SaltConfig.java
@@ -18,7 +18,9 @@ import com.google.common.base.Functions;
public interface SaltConfig {
MapConfigKey<String> SALT_FORMULAS = new MapConfigKey<String>(String.class,
- "salt.formulaUrls", "Map of Salt formulaURLs (normally GutHub repositories from the salt-formulas user)");
+ "salt.formulaUrls", "Map of Salt formulaURLs (normally GutHub repository archives from the salt-formulas user)");
+ SetConfigKey<String> SALT_RUN_LIST = new SetConfigKey<String>(String.class, "salt.runList", "TODO");
+ MapConfigKey<Object> SALT_LAUNCH_ATTRIBUTES = new MapConfigKey<Object>(Object.class, "salt.launch.attributes", "TODO");
@SetFromFlag("master")
ConfigKey<SaltStackMaster> MASTER = ConfigKeys.newConfigKey(SaltStackMaster.class,
@@ -27,6 +29,17 @@ public interface SaltConfig {
AttributeSensor<String> MINION_ID = new BasicAttributeSensor<String>(String.class,
"salt.minion.id", "The ID for a Salt minion");
+ @SetFromFlag("masterConfigUrl")
+ ConfigKey<String> MASTER_CONFIGURATION_URL = ConfigKeys.newStringConfigKey(
+ "salt.master.config.template.url", "The Salt master configuration file template URL",
+ "classpath://brooklyn/entity/salt/master");
+
+ @SetFromFlag("minionConfigUrl")
+ ConfigKey<String> MINION_CONFIGURATION_URL = ConfigKeys.newStringConfigKey(
+ "salt.minion.config.template.url", "The Salt minion configuration file template URL",
+ "classpath://brooklyn/entity/salt/masterless");
+ // TODO allow choice between this and "classpath://brooklyn/entity/salt/minion" template
+
@SuppressWarnings({ "unchecked", "rawtypes" })
@SetFromFlag("minionIdFunction")
ConfigKey<Function<Entity, String>> MINION_ID_FUNCTION = new BasicConfigKey(Function.class,
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b67d8bd0/software/base/src/main/java/brooklyn/entity/salt/SaltConfigs.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/salt/SaltConfigs.java b/software/base/src/main/java/brooklyn/entity/salt/SaltConfigs.java
index ef3aec6..637eb56 100644
--- a/software/base/src/main/java/brooklyn/entity/salt/SaltConfigs.java
+++ b/software/base/src/main/java/brooklyn/entity/salt/SaltConfigs.java
@@ -17,14 +17,16 @@ import com.google.common.base.Preconditions;
@Beta
public class SaltConfigs {
- public static void addToRunList(EntitySpec<?> entity, String ...recipes) {
- for (String recipe: recipes)
- entity.configure(SaltConfig.SALT_RUN_LIST, SetModifications.addItem(recipe));
+ 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 ...recipes) {
- for (String recipe: recipes)
- entity.setConfig(SaltConfig.SALT_RUN_LIST, SetModifications.addItem(recipe));
+ 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) {
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b67d8bd0/software/base/src/main/java/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java b/software/base/src/main/java/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
index 9ca26ca..908c2e3 100644
--- a/software/base/src/main/java/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/salt/SaltLifecycleEffectorTasks.java
@@ -20,25 +20,26 @@ 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.
+/**
+ * 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;
@@ -51,39 +52,41 @@ public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks im
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");
+ "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(AbstractSoftwareProcessSshDriver.BROOKLYN_HOME_DIR, "salt-install");
- String runDir = Urls.mergePaths(AbstractSoftwareProcessSshDriver.BROOKLYN_HOME_DIR,
+ String runDir = Urls.mergePaths(AbstractSoftwareProcessSshDriver.BROOKLYN_HOME_DIR,
"apps/"+entity().getApplicationId()+"/salt-entities/"+entity().getId());
-
+
DynamicTasks.queue(
- SaltTasks.installSaltMinion(installDir, false),
- SaltTasks.installFormulas(installDir, SaltConfigs.getRequiredConfig(entity(), SALT_FORMULAS), false));
-
- DynamicTasks.queue(SaltTasks.buildSaltFile(runDir, installDir, "launch",
- SaltConfigs.getRequiredConfig(entity(), SALT_RUN_LIST),
- entity().getConfig(SALT_LAUNCH_ATTRIBUTES)));
-
- DynamicTasks.queue(SaltTasks.runSalt(runDir, "launch"));
+ 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));
}
@Override
@@ -96,10 +99,10 @@ public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks im
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()) {
@@ -107,14 +110,14 @@ public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks im
}
// and set the PID
- entity().setAttribute(Attributes.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())) {
@@ -126,7 +129,7 @@ public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks im
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())) {
@@ -146,14 +149,14 @@ public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks im
}
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)");
}
@@ -162,16 +165,16 @@ public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks im
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
+ 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");
}
@@ -179,4 +182,14 @@ public class SaltLifecycleEffectorTasks extends MachineLifecycleEffectorTasks im
return true;
}
+ /**
+ * Get the Salt master entity if it exists.
+ * <p>
+ * Returns {@literal null} by default.
+ */
+ @Override
+ public SaltStackMaster getMaster() {
+ return master;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b67d8bd0/software/base/src/main/java/brooklyn/entity/salt/SaltTasks.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/salt/SaltTasks.java b/software/base/src/main/java/brooklyn/entity/salt/SaltTasks.java
index 19e814e..39ac3c0 100644
--- a/software/base/src/main/java/brooklyn/entity/salt/SaltTasks.java
+++ b/software/base/src/main/java/brooklyn/entity/salt/SaltTasks.java
@@ -5,19 +5,19 @@ import static brooklyn.util.ssh.BashCommands.*;
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;
-import com.google.common.collect.ImmutableList;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
@Beta
public class SaltTasks {
@@ -31,24 +31,48 @@ public class SaltTasks {
INSTALL_CURL,
INSTALL_TAR,
INSTALL_UNZIP,
- "( "+downloadToStdout(boostrapUrl) + " | " + sudo("sudo sh -s -- -M -N "+version)+" )"));
+ "( "+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(Entity minion, String saltDirectory, boolean force) {
- String boostrapUrl = minion.getConfig(SaltStackMaster.BOOTSTRAP_URL);
- String installCmd = cdAndRun(saltDirectory,
- BashCommands.chain(
+ 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("sudo sh")+" )"));
- if (!force) installCmd = BashCommands.alternatives("which salt-minion", installCmd);
- return SshEffectorTasks.ssh(installCmd).summary("install salt minion");
+ "( "+downloadToStdout(boostrapUrl) + " | " + sudo("sh")+" )"));
+ if (!force) installCmd = BashCommands.alternatives("which salt-minion", installCmd);
+
+ // Process the minion configuration template
+ String 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 saltDirectory, final Map<String,String> formulasAndUrls, final boolean force) {
+ 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() {
@@ -56,52 +80,42 @@ public class SaltTasks {
if (formulasAndUrls==null)
throw new IllegalStateException("No formulas defined to install at "+e);
for (String formula: formulasAndUrls.keySet())
- DynamicTasks.queue(installFormula(saltDirectory, formula, formulasAndUrls.get(formula), force));
+ DynamicTasks.queue(installFormula(installDir, formula, formulasAndUrls.get(formula), force));
}
}).buildFactory();
}
- public static TaskFactory<?> installFormula(String saltDirectory, String formula, String url, boolean force) {
- // TODO if it's server, try knife first
- // TODO support downloads from classpath / local server
- return SshEffectorTasks.ssh(cdAndRun(saltDirectory, SaltBashCommands.downloadAndExpandFormula(url, formula, force))).
- summary("install formula "+formula).requiringExitCodeZero();
+ 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,
+ return BashCommands.chain(
+ "mkdir -p "+targetDirectory,
"cd "+targetDirectory,
command);
}
- public static TaskFactory<?> buildSaltFile(String runDirectory, String saltDirectory, String phase, Iterable<? extends String> runList,
- Map<String, Object> optionalAttributes) {
- // TODO if it's server, try knife first
- // TODO configure add'l properties
- String phaseRb =
- "root = File.absolute_path(File.dirname(__FILE__))\n"+
- "\n"+
- "file_cache_path root\n"+
-// "formula_path root + '/formulas'\n";
- "formula_path '"+saltDirectory+"'\n";
-
- Map<String,Object> phaseJsonMap = MutableMap.of();
- if (optionalAttributes!=null)
- phaseJsonMap.putAll(optionalAttributes);
- if (runList!=null)
- phaseJsonMap.put("run_list", ImmutableList.copyOf(runList));
- Gson json = new GsonBuilder().create();
- String phaseJson = json.toJson(phaseJsonMap);
+ 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 Tasks.sequential("build salt files for "+phase,
- SshEffectorTasks.put(Urls.mergePaths(runDirectory)+"/"+phase+".rb").contents(phaseRb).createDirectory(),
- SshEffectorTasks.put(Urls.mergePaths(runDirectory)+"/"+phase+".json").contents(phaseJson));
+ return SshEffectorTasks.put(Urls.mergePaths(runDir, "base", "top.sls"))
+ .contents(top.toString())
+ .summary("build salt top file")
+ .createDirectory();
}
- public static TaskFactory<?> runSalt(String runDir, String phase) {
- // TODO salt server
- return SshEffectorTasks.ssh(cdAndRun(runDir, "sudo salt-solo -c "+phase+".rb -j "+phase+".json -ldebug")).
- summary("run salt for "+phase).requiringExitCodeZero();
+ public static TaskFactory<?> runSalt(String runDir) {
+ return SshEffectorTasks.ssh(cdAndRun(runDir, BashCommands.sudo("salt-call --local state.highstate")))
+ .summary("run salt install")
+ .requiringExitCodeZero();
}
}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b67d8bd0/software/base/src/main/resources/brooklyn/entity/salt/master
----------------------------------------------------------------------
diff --git a/software/base/src/main/resources/brooklyn/entity/salt/master b/software/base/src/main/resources/brooklyn/entity/salt/master
index 1b3a95e..ce07663 100644
--- a/software/base/src/main/resources/brooklyn/entity/salt/master
+++ b/software/base/src/main/resources/brooklyn/entity/salt/master
@@ -38,4 +38,13 @@ log_level_logfile: debug # Debugging
#open_mode: False
#auto_accept: False
#autosign_file: autosign.conf
-#permissive_pki_access: False
\ No newline at end of file
+#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/b67d8bd0/software/base/src/main/resources/brooklyn/entity/salt/masterless
----------------------------------------------------------------------
diff --git a/software/base/src/main/resources/brooklyn/entity/salt/masterless b/software/base/src/main/resources/brooklyn/entity/salt/masterless
new file mode 100644
index 0000000..03200d9
--- /dev/null
+++ b/software/base/src/main/resources/brooklyn/entity/salt/masterless
@@ -0,0 +1,37 @@
+[#ftl]
+##
+# SaltStack Masterless Minion Configuration
+# /etc/salt/minion
+##
+
+#default_include: minion.d/*.conf
+
+# Minion configuration
+id: ${entity.id}
+user: root
+backup_mode: minion
+
+# Directory settings
+root_dir: ${runDir}/
+pidfile: salt-minion.pid
+pki_dir: pki
+cachedir: cache
+sock_dir: sock
+log_file: minion.log
+key_logfile: key.log
+
+file_client: local
+file_roots:
+ base:
+ - ${installDir}/base
+[#list formulas.entrySet() as entry]
+ - ${installDir}/base/${entry.key}
+[/#list]
+
+#verify_env: True
+#cache_jobs: True # Debugging
+
+output: nested
+color: False
+log_level: info
+log_level_logfile: debug # Debugging
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b67d8bd0/software/base/src/main/resources/brooklyn/entity/salt/minion
----------------------------------------------------------------------
diff --git a/software/base/src/main/resources/brooklyn/entity/salt/minion b/software/base/src/main/resources/brooklyn/entity/salt/minion
index 16c01f4..119c885 100644
--- a/software/base/src/main/resources/brooklyn/entity/salt/minion
+++ b/software/base/src/main/resources/brooklyn/entity/salt/minion
@@ -16,8 +16,8 @@ acceptance_wait_time_max: 300
# Minion configuration
id: ${entity.id}
-#user: root
-#backup_mode: minion
+user: root
+backup_mode: minion
# Directory settings
root_dir: ${runDir}/
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b67d8bd0/software/base/src/main/resources/brooklyn/entity/salt/top.sls
----------------------------------------------------------------------
diff --git a/software/base/src/main/resources/brooklyn/entity/salt/top.sls b/software/base/src/main/resources/brooklyn/entity/salt/top.sls
new file mode 100644
index 0000000..bc36359
--- /dev/null
+++ b/software/base/src/main/resources/brooklyn/entity/salt/top.sls
@@ -0,0 +1 @@
+[#ftl]
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/b67d8bd0/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeSaltImpl.java
----------------------------------------------------------------------
diff --git a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeSaltImpl.java b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeSaltImpl.java
index 339a7c8..66d5b85 100644
--- a/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeSaltImpl.java
+++ b/software/database/src/main/java/brooklyn/entity/database/postgresql/PostgreSqlNodeSaltImpl.java
@@ -36,7 +36,7 @@ public class PostgreSqlNodeSaltImpl extends EffectorStartableImpl implements Pos
.description("invokes a script")
.parameter(ExecuteScriptEffectorBody.SCRIPT)
.impl(new ExecuteScriptEffectorBody()).build();
-
+
private SshFeed feed;
@Override
@@ -44,34 +44,35 @@ public class PostgreSqlNodeSaltImpl extends EffectorStartableImpl implements Pos
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(), "postgresql", "https://github.com/saltstack-formulas/postgres-formula");
+ 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.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();
@@ -82,33 +83,33 @@ public class PostgreSqlNodeSaltImpl extends EffectorStartableImpl implements Pos
if (creationScriptUrl != null)
creationScript = new ResourceUtils(entity()).getResourceAsString(creationScriptUrl);
else creationScript = entity().getConfig(PostgreSqlNode.CREATION_SCRIPT_CONTENTS);
- entity().invoke(PostgreSqlNodeSaltImpl.EXECUTE_SCRIPT,
+ 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 -")))
+ parameters.get(SCRIPT),
+ BashCommands.sudoAsUser("postgres", "psql --file -")))
.requiringExitCodeZero()).getStdout();
}
}
-
+
protected void connectSensors() {
setAttribute(DB_URL, String.format("postgresql://%s:%s/", getAttribute(HOSTNAME), getAttribute(POSTGRESQL_PORT)));