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