View Javadoc
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  	 * 1 - Copy the latest ci.slave-xxx-test AMI from us-west-1 to all 3 US regions as ci.slave-xxx-prod<br>
45  	 * 2 - Copy the latest jenkins-master-backup-xxx-test.tar.gz to jenkins-master-backup-xxx-prod.tar.gz<br>
46  	 * 3 - Copy the latest jenkins-master-repo-m2-test.tar.gz to jenkins-master-repo-m2-prod.tar.gz<br>
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 	// private/org/jenkins/jenkins-master-repo/m2/jenkins-master-repo-m2-test-latest.tar.gz
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 }