1 package org.kuali.common.devops.ci;
2
3 import static com.google.common.base.Optional.fromNullable;
4 import static com.google.common.base.Stopwatch.createStarted;
5 import static java.lang.String.format;
6 import static org.junit.Assert.fail;
7 import static org.kuali.common.devops.ci.CreateBuildSlaveAMI.US_REGIONS;
8 import static org.kuali.common.devops.ci.CreateBuildSlaveAMI.cleanupAmis;
9 import static org.kuali.common.devops.ci.UpdateBuildSlaveAMI.getMostRecentAMI;
10 import static org.kuali.common.devops.ci.model.Constants.KUALI_FOUNDATION_ACCOUNT;
11 import static org.kuali.common.devops.logic.Auth.getAwsCredentials;
12 import static org.kuali.common.util.Str.getPath;
13 import static org.kuali.common.util.base.Exceptions.illegalState;
14 import static org.kuali.common.util.log.Loggers.newLogger;
15 import static org.kuali.common.util.nullify.NullUtils.trimToNull;
16
17 import java.util.Set;
18
19 import org.junit.Test;
20 import org.kuali.common.aws.ec2.api.EC2Service;
21 import org.kuali.common.aws.ec2.impl.DefaultEC2Service;
22 import org.kuali.common.aws.ec2.model.ImmutableTag;
23 import org.kuali.common.aws.s3.DefaultS3Service;
24 import org.kuali.common.aws.s3.S3Service;
25 import org.kuali.common.core.system.VirtualSystem;
26 import org.kuali.common.devops.aws.Tags;
27 import org.kuali.common.devops.aws.Tags.Stack;
28 import org.kuali.common.devops.ci.model.CloneJenkinsStackContext;
29 import org.kuali.common.util.FormatUtils;
30 import org.slf4j.Logger;
31
32 import com.amazonaws.auth.AWSCredentials;
33 import com.amazonaws.services.ec2.model.Image;
34 import com.amazonaws.services.ec2.model.Tag;
35 import com.google.common.base.Joiner;
36 import com.google.common.base.Optional;
37 import com.google.common.base.Stopwatch;
38
39 public class CloneJenkinsStack {
40
41 private static final Logger logger = newLogger();
42
43
44
45
46
47
48 @Test
49 public void test() throws Exception {
50 try {
51 Stopwatch sw = createStarted();
52 System.setProperty("ec2.stack.src", "test");
53 System.setProperty("ec2.stack.dst", "prod");
54 System.setProperty("ec2.ami.region.src", "us-west-1");
55 VirtualSystem vs = VirtualSystem.create();
56 CloneJenkinsStackContext context = getCloneJenkinsStackContext(vs);
57 if (!Boolean.getBoolean("ec2.ami.skip")) {
58 copyAMI(context);
59 }
60 copyBackups(context);
61 info("cloning completed - %s", FormatUtils.getTime(sw));
62 } catch (Exception e) {
63 e.printStackTrace();
64 fail(e.getMessage());
65 }
66 }
67
68 protected void copyAMI(CloneJenkinsStackContext context) {
69 AWSCredentials creds = getAwsCredentials(KUALI_FOUNDATION_ACCOUNT);
70 EC2Service service = new DefaultEC2Service(creds, context.getRegion().getName());
71 String ami = getMostRecentAMI(service, context.getSrcStack().getTag(), Tags.Name.SLAVE.getTag());
72 Image image = service.getImage(ami);
73 copyAmi(context.getRegion().getName(), US_REGIONS, image, context.getDstStack().getTag());
74 }
75
76 protected void copyBackups(CloneJenkinsStackContext context) {
77 String region = "us-east-1";
78 String bucket = "maven.kuali.org";
79 AWSCredentials creds = getAwsCredentials(KUALI_FOUNDATION_ACCOUNT);
80 S3Service service = DefaultS3Service.builder(creds).withRegion(region).build();
81 String srcKey1 = getJenkinsMasterBackupKey(context.getVersion(), context.getSrcStack().getTag().getValue(), context.getMode().name().toLowerCase());
82 String dstKey1 = getJenkinsMasterBackupKey(context.getVersion(), context.getDstStack().getTag().getValue(), context.getMode().name().toLowerCase());
83 String srcKey2 = getJenkinsMasterRepoBackupKey(context.getSrcStack().getTag().getValue());
84 String dstKey2 = getJenkinsMasterRepoBackupKey(context.getDstStack().getTag().getValue());
85 info("copy backup objects from '%s' to '%s' [%s :: %s]", context.getSrcStack().getTag().getValue(), context.getDstStack().getTag().getValue(), region, bucket);
86 info("src -> https://%s/%s", bucket, srcKey1);
87 info("dst -> https://%s/%s", bucket, dstKey1);
88 service.copyObject(bucket, srcKey1, dstKey1);
89 info("src -> https://%s/%s", bucket, srcKey2);
90 info("dst -> https://%s/%s", bucket, dstKey2);
91 service.copyObject(bucket, srcKey2, dstKey2);
92 }
93
94 protected void copyAmi(String srcRegion, Set<String> regions, Image ami, Tag stack) {
95 String copiedAmi = null;
96 String copiedRegion = null;
97 for (String dstRegion : regions) {
98 if (!dstRegion.equals(srcRegion)) {
99 copiedAmi = copyAMI(srcRegion, ami.getImageId(), ami.getName(), dstRegion, stack);
100 copiedRegion = dstRegion;
101 }
102 }
103 copyAMI(copiedRegion, copiedAmi, getNewName(ami.getName(), stack.getValue()), srcRegion, stack);
104 }
105
106
107 protected String getJenkinsMasterRepoBackupKey(String stack) {
108 return getJenkinsBackupKey("jenkins-master-repo", "m2", stack, Optional.<String> absent());
109 }
110
111 protected String getJenkinsMasterBackupKey(String version, String stack, String mode) {
112 return getJenkinsBackupKey("jenkins-master-backup", version, stack, Optional.of(mode));
113 }
114
115 protected String getJenkinsBackupKey(String artifactId, String version, String stack, Optional<String> mode) {
116 String prefix = "private";
117 String groupId = "org.jenkins";
118 String classifier = stack + "-latest" + (mode.isPresent() ? "-" + mode.get() : "");
119 String type = "tar.gz";
120 String filename = artifactId + "-" + version + "-" + classifier + "." + type;
121 return Joiner.on('/').join(prefix, getPath(groupId), artifactId, version, filename);
122 }
123
124 protected String getNewName(String amiName, String stack) {
125 int pos = amiName.lastIndexOf("-");
126 return amiName.substring(0, pos) + "-" + stack;
127 }
128
129 protected String copyAMI(String srcRegion, String ami, String amiName, String dstRegion, Tag stack) {
130 String newName = getNewName(amiName, stack.getValue());
131 EC2Service service = new DefaultEC2Service(getAwsCredentials(KUALI_FOUNDATION_ACCOUNT), dstRegion);
132 info("copying [%s] from [%s] to [%s] as [%s]", amiName, srcRegion, dstRegion, newName);
133 String newAmi = service.copyAmi(srcRegion, ami, newName);
134 service.tag(newAmi, stack);
135 service.tag(newAmi, new ImmutableTag(Tags.Name.SLAVE.getTag().getKey(), newName));
136 cleanupAmis(service, stack, 7);
137 return newAmi;
138 }
139
140 private static CloneJenkinsStackContext getCloneJenkinsStackContext(VirtualSystem vs) {
141 Stack src = Stack.valueOf(getRequiredProperty(vs, "ec2.stack.src").toUpperCase());
142 Stack dst = Stack.valueOf(getRequiredProperty(vs, "ec2.stack.dst").toUpperCase());
143 String region = getRequiredProperty(vs, "ec2.ami.region.src");
144 return new CloneJenkinsStackContext.Builder().withDstStack(dst).withSrcStack(src).withRegion(region).build();
145 }
146
147 private static String getRequiredProperty(VirtualSystem vs, String key) {
148 Optional<String> property = fromNullable(trimToNull(vs.getProperties().getProperty(key)));
149 if (!property.isPresent()) {
150 throw illegalState("[%s] is required", key);
151 } else {
152 return property.get();
153 }
154 }
155
156 private static void info(String msg, Object... args) {
157 if (args == null) {
158 logger.info(msg);
159 } else {
160 logger.info(format(msg, args));
161 }
162 }
163
164 }