1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package br.com.ingenieux.mojo.beanstalk.cmd.env.waitfor;
18
19 import com.google.common.base.Predicate;
20 import com.google.common.base.Predicates;
21 import com.google.common.collect.Collections2;
22
23 import com.amazonaws.regions.Region;
24 import com.amazonaws.services.elasticbeanstalk.model.DescribeEnvironmentsRequest;
25 import com.amazonaws.services.elasticbeanstalk.model.DescribeEventsRequest;
26 import com.amazonaws.services.elasticbeanstalk.model.DescribeEventsResult;
27 import com.amazonaws.services.elasticbeanstalk.model.EnvironmentDescription;
28 import com.amazonaws.services.elasticbeanstalk.model.EventDescription;
29
30 import org.apache.commons.lang.Validate;
31 import org.apache.maven.plugin.MojoExecutionException;
32
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Comparator;
36 import java.util.Date;
37 import java.util.List;
38 import java.util.Set;
39 import java.util.TreeSet;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42
43 import br.com.ingenieux.mojo.beanstalk.AbstractBeanstalkMojo;
44 import br.com.ingenieux.mojo.beanstalk.cmd.BaseCommand;
45 import br.com.ingenieux.mojo.beanstalk.util.EnvironmentHostnameUtil;
46
47 import static java.lang.String.format;
48 import static org.apache.commons.lang.StringUtils.defaultString;
49 import static org.apache.commons.lang.StringUtils.isNotBlank;
50
51 public class WaitForEnvironmentCommand extends BaseCommand<WaitForEnvironmentContext, EnvironmentDescription> {
52
53
54
55
56 public static final long POLL_INTERVAL = 30 * 1000;
57
58
59
60 private static final long MINS_TO_MSEC = 60 * 1000;
61
62
63
64
65
66
67 public WaitForEnvironmentCommand(AbstractBeanstalkMojo parentMojo) throws MojoExecutionException {
68 super(parentMojo);
69 }
70
71 public Collection<EnvironmentDescription> lookupInternal(WaitForEnvironmentContext context) {
72 List<Predicate<EnvironmentDescription>> envPredicates = getEnvironmentDescriptionPredicate(context);
73
74 DescribeEnvironmentsRequest req = new DescribeEnvironmentsRequest().withApplicationName(context.getApplicationName()).withIncludeDeleted(true);
75
76 final List<EnvironmentDescription> envs = parentMojo.getService().describeEnvironments(req).getEnvironments();
77
78 return Collections2.filter(envs, Predicates.and(envPredicates));
79 }
80
81 protected List<Predicate<EnvironmentDescription>> getEnvironmentDescriptionPredicate(WaitForEnvironmentContext context) {
82
83
84 final String environmentRef = context.getEnvironmentRef();
85 final String statusToWaitFor = defaultString(context.getStatusToWaitFor(), "!Terminated");
86 final String healthToWaitFor = context.getHealth();
87
88
89 Validate.isTrue(isNotBlank(environmentRef), "EnvironmentRef is blank or null", environmentRef);
90
91
92
93 final boolean negated = statusToWaitFor.startsWith("!");
94
95
96
97 List<Predicate<EnvironmentDescription>> result = new ArrayList<Predicate<EnvironmentDescription>>();
98
99 Matcher hostMatcher = EnvironmentHostnameUtil.PATTERN_HOSTNAME.matcher(environmentRef);
100
101 if (environmentRef.matches("e-\\p{Alnum}{10}")) {
102 result.add(
103 new Predicate<EnvironmentDescription>() {
104 @Override
105 public boolean apply(EnvironmentDescription t) {
106 return t.getEnvironmentId().equals(environmentRef);
107 }
108 });
109
110 info("... with environmentId equal to '%s'", environmentRef);
111 } else if (hostMatcher.matches()) {
112 final Region region = parentMojo.getRegion();
113 final String cnamePrefix = hostMatcher.group("cnamePrefix");
114
115 final Predicate<EnvironmentDescription> predicate = EnvironmentHostnameUtil.getHostnamePredicate(region, cnamePrefix);
116
117 result.add(predicate);
118
119 info(predicate.toString());
120 } else {
121 String tmpRE = Pattern.quote(environmentRef);
122
123 if (environmentRef.endsWith("*")) {
124 tmpRE = format("^\\Q%s\\E.*", environmentRef.substring(0, -1 + environmentRef.length()));
125 }
126
127 final String environmentRefNameRE = tmpRE;
128
129 result.add(
130 new Predicate<EnvironmentDescription>() {
131 @Override
132 public boolean apply(EnvironmentDescription t) {
133 return t.getEnvironmentName().matches(environmentRefNameRE);
134 }
135 });
136
137 info("... with environmentName matching re '%s'", environmentRefNameRE);
138 }
139
140 {
141
142
143 final int offset = negated ? 1 : 0;
144 final String vStatusToWaitFor = statusToWaitFor.substring(offset);
145
146 result.add(
147 new Predicate<EnvironmentDescription>() {
148 public boolean apply(EnvironmentDescription t) {
149
150 boolean result = vStatusToWaitFor.equals(t.getStatus());
151
152 if (negated) {
153 result = !result;
154 }
155
156 debug("testing status '%s' as equal as '%s' (negated? %s, offset: %d): %s", vStatusToWaitFor, t.getStatus(), negated, offset, result);
157
158 return result;
159 }
160 });
161
162 info("... with status %s set to '%s'", (negated ? "*NOT*" : " "), vStatusToWaitFor);
163 }
164
165 {
166 if (isNotBlank(healthToWaitFor)) {
167 result.add(
168 new Predicate<EnvironmentDescription>() {
169 @Override
170 public boolean apply(EnvironmentDescription t) {
171 return t.getHealth().equals(healthToWaitFor);
172 }
173 });
174
175 info("... with health equal to '%s'", healthToWaitFor);
176 }
177 }
178 return result;
179 }
180
181 public EnvironmentDescription executeInternal(WaitForEnvironmentContext context) throws Exception {
182
183 long timeoutMins = context.getTimeoutMins();
184
185 Date expiresAt = new Date(System.currentTimeMillis() + MINS_TO_MSEC * timeoutMins);
186 Date lastMessageRecord = new Date();
187
188 info("Environment Lookup");
189
190 List<Predicate<EnvironmentDescription>> envPredicates = getEnvironmentDescriptionPredicate(context);
191 Predicate<EnvironmentDescription> corePredicate = envPredicates.get(0);
192 Predicate<EnvironmentDescription> fullPredicate = Predicates.and(envPredicates);
193
194 do {
195 DescribeEnvironmentsRequest req = new DescribeEnvironmentsRequest().withApplicationName(context.getApplicationName()).withIncludeDeleted(true);
196
197 final List<EnvironmentDescription> envs = parentMojo.getService().describeEnvironments(req).getEnvironments();
198
199 Collection<EnvironmentDescription> validEnvironments = Collections2.filter(envs, fullPredicate);
200
201 debug("There are %d environments", validEnvironments.size());
202
203 if (1 == validEnvironments.size()) {
204 EnvironmentDescription foundEnvironment = validEnvironments.iterator().next();
205
206 debug("Found environment %s", foundEnvironment);
207
208 return foundEnvironment;
209 } else {
210 debug("Found %d environments. No good. Ignoring.", validEnvironments.size());
211
212 for (EnvironmentDescription d : validEnvironments) {
213 debug(" ... %s", d);
214 }
215
216
217
218 Collection<EnvironmentDescription> foundEnvironments = Collections2.filter(envs, corePredicate);
219
220 if (1 == foundEnvironments.size()) {
221 EnvironmentDescription foundEnvironment = foundEnvironments.iterator().next();
222
223 DescribeEventsResult events =
224 service.describeEvents(
225 new DescribeEventsRequest()
226 .withApplicationName(foundEnvironment.getApplicationName())
227 .withStartTime(new Date(1000 + lastMessageRecord.getTime()))
228 .withEnvironmentId(foundEnvironment.getEnvironmentId())
229 .withSeverity("TRACE"));
230
231 Set<EventDescription> eventList = new TreeSet<EventDescription>(new EventDescriptionComparator());
232
233 eventList.addAll(events.getEvents());
234
235 for (EventDescription d : eventList) {
236 info("%s %s %s", d.getSeverity(), d.getEventDate(), d.getMessage());
237
238 if (d.getSeverity().equals(("ERROR"))) {
239 throw new MojoExecutionException("Something went wrong in while waiting for the environment setup to complete : " + d.getMessage());
240 }
241 lastMessageRecord = d.getEventDate();
242 }
243 }
244 }
245
246 sleepInterval(POLL_INTERVAL);
247 } while (!timedOutP(expiresAt));
248
249 throw new MojoExecutionException("Timed out");
250 }
251
252 boolean timedOutP(Date expiresAt) throws MojoExecutionException {
253 return expiresAt.before(new Date(System.currentTimeMillis()));
254 }
255
256 public void sleepInterval(long pollInterval) {
257 debug("Sleeping for %d seconds", pollInterval / 1000);
258 try {
259 Thread.sleep(pollInterval);
260 } catch (InterruptedException e) {
261 }
262 }
263
264 static class EventDescriptionComparator implements Comparator<EventDescription> {
265
266 @Override
267 public int compare(EventDescription o1, EventDescription o2) {
268 return o1.getEventDate().compareTo(o2.getEventDate());
269 }
270 }
271 }