001package org.kuali.common.devops.ci;
002
003import static com.google.common.base.Preconditions.checkState;
004import static com.google.common.base.Stopwatch.createStarted;
005import static java.lang.String.format;
006import static java.util.Collections.reverse;
007import static java.util.Collections.sort;
008import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
009import static org.kuali.common.devops.aws.EncryptedAWSCredentials.ENCRYPTED_AWS_CREDENTIALS_FOUNDATION;
010import static org.kuali.common.devops.ci.CreateBuildSlaveAMI.CI_SLAVE_STARTS_WITH_TOKEN;
011import static org.kuali.common.devops.ci.CreateBuildSlaveAMI.CONTEXTS;
012import static org.kuali.common.devops.ci.CreateBuildSlaveAMI.DEVOPS_KEYPAIR;
013import static org.kuali.common.devops.ci.CreateBuildSlaveAMI.getEC2Service;
014import static org.kuali.common.devops.ci.CreateBuildSlaveAMI.getFilteredImages;
015import static org.kuali.common.devops.ci.CreateBuildSlaveAMI.getJenkinsMaster;
016import static org.kuali.common.devops.ci.SpinUpJenkinsMaster.exec;
017import static org.kuali.common.devops.ci.SpinUpJenkinsMaster.getJenkinsContext;
018import static org.kuali.common.devops.ci.SpinUpJenkinsMaster.getResource;
019import static org.kuali.common.devops.ci.SpinUpJenkinsMaster.openSecureChannel;
020import static org.kuali.common.devops.ci.SpinUpJenkinsMaster.publishProject;
021import static org.kuali.common.devops.ci.model.Constants.DISTRO;
022import static org.kuali.common.devops.ci.model.Constants.DISTRO_VERSION;
023import static org.kuali.common.devops.ci.model.Constants.ROOT;
024import static org.kuali.common.devops.project.KualiDevOpsProjectConstants.KUALI_DEVOPS_PROJECT_IDENTIFIER;
025import static org.kuali.common.util.encrypt.Encryption.getDefaultEncryptor;
026import static org.kuali.common.util.log.Loggers.newLogger;
027
028import java.io.IOException;
029import java.util.List;
030
031import org.junit.Test;
032import org.kuali.common.aws.ec2.api.EC2Service;
033import org.kuali.common.core.system.VirtualSystem;
034import org.kuali.common.devops.ci.CreateBuildSlaveAMI.ImageTagsComparator;
035import org.kuali.common.devops.ci.model.JenkinsContext;
036import org.kuali.common.util.FormatUtils;
037import org.kuali.common.util.channel.api.SecureChannel;
038import org.kuali.common.util.encrypt.Encryptor;
039import org.kuali.common.util.project.model.ProjectIdentifier;
040import org.slf4j.Logger;
041
042import com.amazonaws.services.ec2.model.Image;
043import com.amazonaws.services.ec2.model.Tag;
044import com.google.common.base.Stopwatch;
045
046public class UpdateBuildSlaveAMI {
047
048        private static final Logger logger = newLogger();
049
050        private final Stopwatch stopwatch = createStarted();
051        private final Encryptor encryptor = getDefaultEncryptor();
052        private final String kisUsernameEncrypted = "U2FsdGVkX18yas/kI9ymLV41TRC9tcoE8P2YaoQmtOc=";
053        private final String kisPasswordEncrypted = "U2FsdGVkX18M5faj1sGRINZ0p5dNNW3FFEPxM1lx3Gw=";
054        private final ProjectIdentifier PID = KUALI_DEVOPS_PROJECT_IDENTIFIER;
055
056        @Test
057        public void test() throws Exception {
058                VirtualSystem vs = VirtualSystem.build();
059                // Default to quiet mode unless they've supplied -Dec2.quiet=false
060                boolean quiet = equalsIgnoreCase(vs.getProperties().getProperty("ec2.quiet"), "false") ? false : true;
061                JenkinsContext jenkinsContext = getJenkinsContext(vs, CONTEXTS);
062                EC2Service service = getEC2Service(ENCRYPTED_AWS_CREDENTIALS_FOUNDATION, jenkinsContext.getRegion());
063                String ami = getMostRecentAMI(service, jenkinsContext);
064                String privateKey = DEVOPS_KEYPAIR.getPrivateKey();
065                String jenkinsMaster = getJenkinsMaster(jenkinsContext);
066                updateMasterAMI(jenkinsMaster, privateKey, quiet, ami);
067                info("updated %s with ami %s - %s", jenkinsMaster, ami, FormatUtils.getTime(stopwatch));
068        }
069
070        protected void updateMasterAMI(String jenkinsMaster, String privateKey, boolean quiet, String ami) throws IOException {
071                info("updating %s with ami [%s]", jenkinsMaster, ami);
072                String kisUsername = encryptor.decrypt(kisUsernameEncrypted);
073                String kisPassword = encryptor.decrypt(kisPasswordEncrypted);
074                SecureChannel channel = openSecureChannel(ROOT, jenkinsMaster, privateKey, quiet);
075                String basedir = publishProject(channel, PID, ROOT, jenkinsMaster, quiet);
076                String rubyScript = getResource(basedir, PID, DISTRO, DISTRO_VERSION, "jenkins/update_jenkins_ami_headless.rb");
077                exec(channel, "ruby", rubyScript, kisUsername, kisPassword, ami, jenkinsMaster);
078        }
079
080        protected static String getMostRecentAMI(EC2Service service, JenkinsContext context) {
081                return getMostRecentAMI(service, context.getStack().getTag(), context.getName().getTag());
082        }
083
084        protected static String getMostRecentAMI(EC2Service service, Tag stack, Tag name) {
085
086                List<Image> images = service.getMyImages();
087
088                // Extract just the ci.slave AMI's for this stack
089                List<Image> filtered = getFilteredImages(images, stack, name.getKey(), CI_SLAVE_STARTS_WITH_TOKEN);
090
091                // This sorts them by date
092                sort(filtered, new ImageTagsComparator());
093
094                // Most recent images are at the bottom, this brings them to the top
095                reverse(filtered);
096
097                // Make sure we have at least one image
098                checkState(filtered.size() > 0, "need at least one image");
099
100                // Return the most recent one
101                return filtered.get(0).getImageId();
102        }
103
104        private static void info(String msg, Object... args) {
105                if (args == null) {
106                        logger.info(msg);
107                } else {
108                        logger.info(format(msg, args));
109                }
110        }
111
112}