![JAR search and dependency download from the Maven repository](/logo.png)
io.takari.orchestra.plugins.ansible.RunPlaybookTask Maven / Gradle / Ivy
package io.takari.orchestra.plugins.ansible;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.takari.bpm.api.BpmnError;
import io.takari.bpm.api.ExecutionContext;
import io.takari.bpm.api.JavaDelegate;
import io.takari.orchestra.common.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Named;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
@Named
public class RunPlaybookTask implements JavaDelegate, Task {
private static final Logger log = LoggerFactory.getLogger(RunPlaybookTask.class);
private static final int SUCCESS_EXIT_CODE = 0;
public static final String PLAYBOOKS_PATH_KEY = "ansiblePlaybooksPath";
private static final String DEFAULT_PLAYBOOKS_PATH = "/opt/orchestra/ansible/playbooks";
public static final String PLAYBOOK_KEY = "ansiblePlaybook";
private static final String DEFAULT_PLAYBOOK = "playbook.yml";
public static final String USER_KEY = "ansibleUser";
private static final String DEFAULT_USER = "ansible";
public static final String USER_PASSWORD_KEY = "ansiblePassword";
public static final String HOSTS_FILE_TEMPLATE_KEY = "ansibleHostsFileTemplate";
private static final String DEFAULT_HOSTS_FILE_TEMPLATE = "[all]\n%s";
public static final String HOSTS_KEY = "ansibleHosts";
public static final String EXTRA_VARS_KEY = "ansibleExtraVars";
@Override
public String getKey() {
return "ansible";
}
@Override
public void execute(ExecutionContext ctx) throws Exception {
Collection hosts = (Collection) getObject(ctx, HOSTS_KEY, null);
if (hosts == null || hosts.isEmpty()) {
log.warn("execution -> no hosts were specified for this task, skipping");
return;
}
String playbooksPath = getString(ctx, PLAYBOOKS_PATH_KEY, DEFAULT_PLAYBOOKS_PATH);
String playbook = getString(ctx, PLAYBOOK_KEY, DEFAULT_PLAYBOOK);
String user = getString(ctx, USER_KEY, DEFAULT_USER);
String hostsFileTemplate = getString(ctx, HOSTS_FILE_TEMPLATE_KEY, DEFAULT_HOSTS_FILE_TEMPLATE);
Collection extraKeys = (Collection) getObject(ctx, EXTRA_VARS_KEY, null);
String extraVars = formatExtraVars(ctx, extraKeys);
createAnsibleCfgFile(System.getProperty("user.home") + "/.ansible.cfg");
// TODO secure store?
PasswordCallback callback = (x) -> ((String) x.getVariable(USER_PASSWORD_KEY)).getBytes();
// TODO prepare a script instead of passing everything as args
String hostsDir = makeHostsDir(hostsFileTemplate, hosts, user, callback.getPassword(ctx));
String[] cmd = formatCmd(hostsDir, playbooksPath, playbook, user, extraVars);
log.info("execution -> cmd: {}", String.join(" ", cmd));
Process p = new ProcessBuilder()
.command(cmd)
.redirectErrorStream(true)
.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
log.info("OUTPUT: {}", line);
}
int code = p.waitFor();
if (code == SUCCESS_EXIT_CODE) {
log.info("execution -> done");
} else {
log.error("execution -> finished with exit code {}", code);
throw new BpmnError("ansibleError", new IllegalStateException("Process finished with with exit code " + code));
}
}
private static String getString(ExecutionContext ctx, String k, String defaultValue) {
String s = (String) ctx.getVariable(k);
if (s == null) {
return defaultValue;
}
return s;
}
private static Object getObject(ExecutionContext ctx, String k, Object defaultValue) {
Object o = ctx.getVariable(k);
if (o == null) {
return defaultValue;
}
return o;
}
private static String formatExtraVars(ExecutionContext ctx, Collection keys) throws IOException {
if (keys == null || keys.isEmpty()) {
return "";
}
Set ks = new HashSet<>(keys);
Map m = new HashMap<>();
for (Map.Entry e : ctx.getVariables().entrySet()) {
String k = e.getKey();
if (!ks.contains(k)) {
continue;
}
Object v = e.getValue();
if (v == null) {
continue;
}
m.put(k, v.toString());
}
return new ObjectMapper().writeValueAsString(m);
}
private static String makeHostsDir(String template, Collection hosts, String user, byte[] passwd) throws IOException {
String p = new String(passwd);
StringBuilder hs = new StringBuilder();
for (String h : hosts) {
hs.append(h).append(" ansible_ssh_user=").append(user).append(" ansible_ssh_pass=").append(p).append('\n');
}
String s = String.format(template.replaceAll("\\n", "\n"), hs.toString());
Path tmpDir = Files.createTempDirectory("inventory");
File f = new File(tmpDir.toFile(), "hosts");
try (FileWriter w = new FileWriter(f)) {
w.append(s);
w.flush();
}
return tmpDir.toAbsolutePath().toString();
}
private static String[] formatCmd(String hostsPath, String playbooksPath, String playbook, String user, String extraVars) {
return new String[]{
"ansible-playbook", "-T", "30", "-i", hostsPath, playbooksPath + "/" + playbook, "-u", user, "-e", extraVars
};
}
private static void createAnsibleCfgFile(String path) throws IOException {
String template = "[defaults]\n" +
"host_key_checking = False\n" +
"[ssh_connection]\n" +
"control_path = %(directory)s/%%h-%%p-%%r\n" +
"pipelining=true";
try (OutputStream out = new FileOutputStream(path)) {
out.write(template.getBytes());
}
log.info("createAnsibleCfgFile ['{}'] -> done", path);
}
private static class Echo implements Appendable {
private StringBuilder buffer = new StringBuilder();
@Override
public Appendable append(CharSequence csq) throws IOException {
buffer.append(csq);
maybePrint();
return this;
}
@Override
public Appendable append(CharSequence csq, int start, int end) throws IOException {
buffer.append(csq, start, end);
maybePrint();
return this;
}
@Override
public Appendable append(char c) throws IOException {
buffer.append(c);
maybePrint();
return this;
}
private void maybePrint() {
int len = buffer.length();
int idx = buffer.indexOf("\n");
if (idx < 0) {
return;
}
log.info("OUTPUT: {}", buffer.subSequence(0, idx));
int start = idx + 1;
if (start < len) {
buffer = new StringBuilder(buffer.subSequence(start, len));
} else {
buffer = new StringBuilder();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy