package core.aws.task.linux; import com.amazonaws.services.ec2.model.Instance; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.SftpException; import core.aws.env.Environment; import core.aws.resource.ec2.KeyPair; import core.aws.util.Files; import core.aws.util.Maps; import core.aws.util.SSH; import core.aws.util.Strings; import core.aws.util.Tarball; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Map; import java.util.Optional; import static java.nio.file.Files.createTempDirectory; import static java.nio.file.Files.createTempFile; import static java.nio.file.Files.delete; /** * @author neo */ public class AnsibleProvisioner { private final Logger logger = LoggerFactory.getLogger(AnsibleProvisioner.class); private final Environment env; private final Instance instance; private final Path playbookPath; private final Optional<Path> packageDir; public Map<String, String> additionalVariables = Maps.newHashMap(); public AnsibleProvisioner(Environment env, Instance instance, Path playbookPath, Optional<Path> packageDir) { this.env = env; this.instance = instance; this.playbookPath = playbookPath; this.packageDir = packageDir; } public void provision() throws IOException, JSchException, InterruptedException, SftpException { logger.info("playbook => {}", playbookPath); logger.info("packageDir => {}", packageDir); try (SSH ssh = new SSH(hostName(instance), "ubuntu", KeyPair.keyFile(instance.getKeyName(), env))) { ssh.executeCommands("sudo apt-get -y -q update", "dpkg -s ansible || sudo apt-get -y install ansible", "sudo DEBIAN_FRONTEND=noninteractive apt-get -y --force-yes -q -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade", // update package before run playbook to make sure ansible is updated in advance "sudo rm -rf /opt/ansible /opt/packages", // clear previous ansible roles and packages if installed "sudo mkdir -p /opt/packages /opt/ansible", "sudo chown ubuntu.ubuntu /opt/packages /opt/ansible"); uploadPackage(ssh); runPlaybook(ssh); } } private void runPlaybook(SSH ssh) throws InterruptedException, JSchException, IOException, SftpException { Path ansibleArchive = packAnsibleRoles(); ssh.put(ansibleArchive, "/tmp/ansible.tar.gz"); ssh.put(playbookPath, "/opt/ansible/localhost.yml"); ssh.executeCommands("tar xzf /tmp/ansible.tar.gz -C /opt/ansible", "ansible-playbook --become /opt/ansible/localhost.yml"); delete(ansibleArchive); } private Path packAnsibleRoles() throws IOException { Path ansiblePath = createTempDirectory("ansible"); for (Path path : env.ansibleRolePaths) { Files.copyDirectory(path, ansiblePath); } Tarball tar = new Tarball(ansiblePath); File tarFile = createTempFile("ansible.tar.gz", null).toFile(); tar.archive(tarFile); Files.deleteDirectory(ansiblePath); return tarFile.toPath(); } private void uploadPackage(SSH ssh) throws IOException, SftpException, JSchException { packageDir.ifPresent(path -> ssh.uploadDir(path, "/opt/packages")); } private String hostName(Instance instance) { String publicDNS = instance.getPublicDnsName(); return Strings.notEmpty(publicDNS) ? publicDNS : instance.getPrivateIpAddress(); } }