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