View Javadoc
1   package org.kuali.common.devops.logic;
2   
3   import static com.google.common.base.Optional.absent;
4   import static com.google.common.base.Optional.fromNullable;
5   import static com.google.common.base.Preconditions.checkArgument;
6   import static com.google.common.collect.Lists.newArrayList;
7   import static com.google.common.collect.Sets.newTreeSet;
8   import static java.lang.String.format;
9   import static org.apache.commons.io.FileUtils.writeLines;
10  import static org.apache.commons.lang.StringUtils.trimToNull;
11  import static org.kuali.common.util.Encodings.UTF8;
12  import static org.kuali.common.util.base.Exceptions.illegalState;
13  import static org.kuali.common.util.base.Precondition.checkNotBlank;
14  import static org.kuali.common.util.base.Precondition.checkNotNull;
15  import static org.kuali.common.util.log.Loggers.newLogger;
16  
17  import java.io.File;
18  import java.io.IOException;
19  import java.util.Collections;
20  import java.util.List;
21  import java.util.Properties;
22  import java.util.Set;
23  import java.util.SortedMap;
24  import java.util.SortedSet;
25  
26  import org.apache.commons.io.FileUtils;
27  import org.kuali.common.aws.ec2.api.EC2Service;
28  import org.kuali.common.aws.ec2.impl.DefaultEC2Service;
29  import org.kuali.common.aws.ec2.model.EC2ServiceContext;
30  import org.kuali.common.devops.logic.function.ToCsvFunction;
31  import org.kuali.common.devops.logic.function.ToListFunction;
32  import org.kuali.common.devops.metadata.model.EC2Instance;
33  import org.kuali.common.devops.metadata.model.EC2Tag;
34  import org.kuali.common.devops.table.TableCellDescriptor;
35  import org.kuali.common.devops.table.Tables;
36  import org.kuali.common.util.Encodings;
37  import org.kuali.common.util.LocationUtils;
38  import org.kuali.common.util.PropertyUtils;
39  import org.kuali.common.util.Str;
40  import org.kuali.common.util.base.Exceptions;
41  import org.kuali.common.util.file.CanonicalFile;
42  import org.kuali.common.util.wait.DefaultWaitService;
43  import org.kuali.common.util.wait.WaitService;
44  import org.slf4j.Logger;
45  
46  import com.amazonaws.auth.AWSCredentials;
47  import com.amazonaws.services.ec2.model.Instance;
48  import com.amazonaws.services.ec2.model.Tag;
49  import com.google.common.base.Optional;
50  import com.google.common.collect.ImmutableList;
51  import com.google.common.collect.Maps;
52  import com.google.common.collect.Table;
53  
54  public class Instances {
55  
56  	private static final File CACHE_DIR = new CanonicalFile("./target/cache/servers/aws/ec2");
57  	private static final Logger logger = newLogger();
58  	private static final String EC2_NAME_TAG_KEY = "Name";
59  
60  	/**
61  	 * Produces a map containing all EC2 server instances for all Kuali accounts.
62  	 */
63  	public static SortedMap<String, List<EC2Instance>> getInstances(boolean refresh) {
64  		SortedMap<String, List<EC2Instance>> map = Maps.newTreeMap();
65  		Set<String> accounts = Auth.getAwsAccountNames();
66  		for (String account : accounts) {
67  			map.put(account, getInstances(account, refresh));
68  		}
69  		return map;
70  	}
71  
72  	public static void updateTags(String account) {
73  		String location = "classpath:org/kuali/" + account + "/tags.properties";
74  		checkArgument(LocationUtils.exists(location), "Location [%s] does not exist", location);
75  		Properties props = PropertyUtils.load(location);
76  		AWSCredentials creds = Auth.getAwsCredentials(account);
77  		WaitService ws = new DefaultWaitService();
78  		EC2ServiceContext context = EC2ServiceContext.create(creds);
79  		EC2Service service = new DefaultEC2Service(context, ws);
80  		SortedSet<String> keys = newTreeSet(props.stringPropertyNames());
81  		for (String key : keys) {
82  			updateTag(account, key, props.getProperty(key), service);
83  		}
84  	}
85  
86  	public static void updateTag(String account, String key, String value, EC2Service service) {
87  		String location = "classpath:org/kuali/" + account + "/" + value;
88  		checkArgument(LocationUtils.exists(location), "Location [%s] does not exist", location);
89  		Properties props = PropertyUtils.load(location);
90  		List<Instance> instances = service.getInstances();
91  		for (Instance instance : instances) {
92  			Optional<String> name = getTagValue(instance, EC2_NAME_TAG_KEY);
93  			if (name.isPresent()) {
94  				Optional<String> tagValue = fromNullable(props.getProperty(name.get()));
95  				if (tagValue.isPresent()) {
96  					logger.info(format("tagging -> %s  [%s]", key, Str.flatten(tagValue.get())));
97  					Tag tag = new Tag(key, tagValue.get());
98  					service.tag(instance.getInstanceId(), ImmutableList.of(tag));
99  				}
100 			}
101 		}
102 	}
103 
104 	protected static List<EC2Instance> getInstances(String account, boolean refresh) {
105 		File cache = getCacheFile(account);
106 		if (refresh || !cache.exists()) {
107 			AWSCredentials creds = Auth.getAwsCredentials(account);
108 			List<EC2Instance> instances = queryAmazon(creds);
109 			store(cache, instances);
110 			return instances;
111 		} else {
112 			return load(cache);
113 		}
114 	}
115 
116 	protected static File getCacheFile(String account) {
117 		return new CanonicalFile(CACHE_DIR, account + ".txt");
118 	}
119 
120 	protected static List<EC2Instance> load(File file) {
121 		try {
122 			logger.info(format("loading -> [%s]", file));
123 			List<String> lines = FileUtils.readLines(file, Encodings.UTF8);
124 			Table<Integer, String, TableCellDescriptor<String>> table = Tables.getTableFromCSV(lines, EC2Instance.class);
125 			ToListFunction<Integer, String, EC2Instance> function = new ToListFunction.Builder<Integer, String, EC2Instance>().targetType(EC2Instance.class).build();
126 			return function.apply(table);
127 		} catch (IOException e) {
128 			throw Exceptions.illegalState(e, "unexpected io error -> [%s]", file);
129 		}
130 	}
131 
132 	public static List<EC2Instance> queryAmazon(AWSCredentials creds) {
133 		WaitService ws = new DefaultWaitService();
134 		EC2ServiceContext context = EC2ServiceContext.create(creds);
135 		EC2Service service = new DefaultEC2Service(context, ws);
136 		List<EC2Instance> instances = convert(service.getInstances());
137 		Collections.sort(instances);
138 		return instances;
139 	}
140 
141 	protected static void store(File file, List<EC2Instance> instances) {
142 		Table<Integer, String, TableCellDescriptor<Object>> table = Tables.getTable(instances, EC2Instance.class);
143 		ToCsvFunction<Integer, String> function = new ToCsvFunction<Integer, String>();
144 		List<String> lines = function.apply(table);
145 		store(file, lines, UTF8);
146 	}
147 
148 	protected static void store(File file, List<String> lines, String encoding) {
149 		checkNotNull(file, "file");
150 		checkNotNull(lines, "lines");
151 		checkNotBlank(encoding, "encoding");
152 		try {
153 			logger.info(format("creating -> [%s]", file));
154 			writeLines(file, encoding, lines);
155 		} catch (IOException e) {
156 			throw illegalState(e, "unexpected io error -> [%s]", file);
157 		}
158 	}
159 
160 	protected static List<EC2Instance> convert(List<Instance> instances) {
161 		List<EC2Instance> list = newArrayList();
162 		for (Instance instance : instances) {
163 			list.add(convert(instance));
164 		}
165 		return list;
166 	}
167 
168 	protected static EC2Instance convert(Instance instance) {
169 		String id = instance.getInstanceId();
170 		Optional<String> name = getTagValue(instance, EC2_NAME_TAG_KEY);
171 		Optional<String> publicDnsName = fromNullable(trimToNull(instance.getPublicDnsName()));
172 		String type = instance.getInstanceType();
173 		long launchTime = instance.getLaunchTime().getTime();
174 		String ami = instance.getImageId();
175 		String state = instance.getState().getName();
176 		List<EC2Tag> tags = getTags(instance);
177 		return EC2Instance.builder().id(id).name(name).publicDnsName(publicDnsName).type(type).launchTime(launchTime).ami(ami).state(state).tags(tags).build();
178 	}
179 
180 	protected static List<EC2Tag> getTags(Instance instance) {
181 		if (instance.getTags() == null || instance.getTags().isEmpty()) {
182 			return newArrayList();
183 		}
184 		List<EC2Tag> tags = newArrayList();
185 		for (Tag tag : instance.getTags()) {
186 			EC2Tag newTag = EC2Tag.create(tag.getKey(), tag.getValue());
187 			tags.add(newTag);
188 		}
189 		return tags;
190 	}
191 
192 	protected static Optional<String> getTagValue(Instance instance, String tagName) {
193 		Optional<Tag> tag = getTag(instance, tagName);
194 		if (tag.isPresent()) {
195 			return Optional.of(tag.get().getValue());
196 		} else {
197 			return absent();
198 		}
199 	}
200 
201 	protected static Optional<Tag> getTag(Instance instance, String key) {
202 		List<Tag> tags = instance.getTags();
203 		for (Tag tag : tags) {
204 			if (key.equals(tag.getKey())) {
205 				return Optional.of(tag);
206 			}
207 		}
208 		return absent();
209 	}
210 
211 }