1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package br.com.ingenieux.mojo.beanstalk;
18
19 import static java.lang.String.format;
20 import static org.apache.commons.lang.StringUtils.defaultString;
21 import static org.apache.commons.lang.StringUtils.isBlank;
22 import static org.apache.commons.lang.StringUtils.isNotBlank;
23
24 import java.text.Collator;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32 import java.util.stream.Collectors;
33
34 import org.apache.commons.collections.ComparatorUtils;
35 import org.apache.commons.collections.comparators.ReverseComparator;
36 import org.apache.commons.lang.Validate;
37 import org.apache.maven.plugin.MojoExecutionException;
38 import org.apache.maven.plugins.annotations.Parameter;
39
40 import com.amazonaws.services.ec2.AmazonEC2;
41 import com.amazonaws.services.ec2.AmazonEC2Client;
42 import com.amazonaws.services.ec2.model.DescribeSecurityGroupsRequest;
43 import com.amazonaws.services.ec2.model.DescribeSecurityGroupsResult;
44 import com.amazonaws.services.ec2.model.SecurityGroup;
45 import com.amazonaws.services.elasticbeanstalk.AWSElasticBeanstalkClient;
46 import com.amazonaws.services.elasticbeanstalk.model.ConfigurationOptionSetting;
47 import com.amazonaws.services.elasticbeanstalk.model.DescribeApplicationsRequest;
48 import com.amazonaws.services.elasticbeanstalk.model.EnvironmentDescription;
49 import com.amazonaws.services.elasticbeanstalk.model.OptionSpecification;
50 import com.amazonaws.services.elasticbeanstalk.model.SolutionStackDescription;
51 import com.google.common.base.Function;
52 import com.google.common.base.Predicate;
53 import com.google.common.collect.Collections2;
54
55 import br.com.ingenieux.mojo.aws.AbstractAWSMojo;
56 import br.com.ingenieux.mojo.aws.util.GlobUtil;
57 import br.com.ingenieux.mojo.beanstalk.cmd.env.waitfor.WaitForEnvironmentCommand;
58 import br.com.ingenieux.mojo.beanstalk.cmd.env.waitfor.WaitForEnvironmentContext;
59 import br.com.ingenieux.mojo.beanstalk.cmd.env.waitfor.WaitForEnvironmentContextBuilder;
60 import br.com.ingenieux.mojo.beanstalk.util.ConfigUtil;
61 import br.com.ingenieux.mojo.beanstalk.util.EnvironmentHostnameUtil;
62
63 public abstract class AbstractBeanstalkMojo extends AbstractAWSMojo<AWSElasticBeanstalkClient> {
64
65 protected List<ConfigurationOptionSetting> getOptionSettings(ConfigurationOptionSetting[] optionSettings) {
66 ConfigurationOptionSetting[] arrOptionSettings = optionSettings;
67
68 if (null == arrOptionSettings || 0 == arrOptionSettings.length) {
69 return Collections.emptyList();
70 }
71
72 return Arrays.asList(arrOptionSettings);
73 }
74
75 protected List<OptionSpecification> getOptionsToRemove(OptionSpecification[] optionSettings) {
76 OptionSpecification[] arrOptionSettings = optionSettings;
77
78 if (null == arrOptionSettings || 0 == arrOptionSettings.length) {
79 return Collections.emptyList();
80 }
81
82 return Arrays.asList(arrOptionSettings);
83 }
84
85 protected EnvironmentDescription lookupEnvironment(String applicationName, String environmentRef) throws MojoExecutionException {
86 final WaitForEnvironmentContext ctx =
87 new WaitForEnvironmentContextBuilder().withApplicationName(applicationName).withEnvironmentRef(environmentRef).build();
88 final Collection<EnvironmentDescription> environments = new WaitForEnvironmentCommand(this).lookupInternal(ctx);
89 return handleResults(environments);
90 }
91
92 protected EnvironmentDescription handleResults(Collection<EnvironmentDescription> environments) throws MojoExecutionException {
93 int len = environments.size();
94
95 if (1 == len) {
96 return environments.iterator().next();
97 }
98
99 handleNonSingle(len);
100
101 return null;
102 }
103
104 protected void handleNonSingle(int len) throws MojoExecutionException {
105 if (0 == len) {
106 throw new MojoExecutionException("No environments found");
107 } else {
108 throw new MojoExecutionException("Multiple environments found matching the supplied parameters (may you file a bug report?)");
109 }
110 }
111
112
113
114
115
116
117
118
119
120
121
122 protected boolean harmfulOptionSettingP(final String environmentId, ConfigurationOptionSetting optionSetting) throws Exception {
123
124 if (ConfigUtil.optionSettingMatchesP(optionSetting, "aws:autoscaling:launchconfiguration", "SecurityGroups")) {
125 final String securityGroup = optionSetting.getValue();
126
127 if (-1 != securityGroup.indexOf(environmentId)) {
128 return true;
129 }
130
131 if (getLog().isInfoEnabled()) {
132 getLog().info("Probing security group '" + securityGroup + "'");
133 }
134
135 Validate.isTrue(securityGroup.matches("^sg-\\p{XDigit}{8,17}(,sg-\\p{XDigit}{8,17})*$"), "Invalid Security Group Spec: " + securityGroup);
136
137 final AmazonEC2 ec2 = this.getClientFactory().getService(AmazonEC2Client.class);
138
139 final DescribeSecurityGroupsResult describeSecurityGroupsResult =
140 ec2.describeSecurityGroups(new DescribeSecurityGroupsRequest().withGroupIds(securityGroup.split(",")));
141
142 if (!describeSecurityGroupsResult.getSecurityGroups().isEmpty()) {
143
144 getLog().info("Checking security groups.");
145
146 final Predicate<SecurityGroup> predicate = new Predicate<SecurityGroup>() {
147 @Override
148 public boolean apply(SecurityGroup input) {
149
150 getLog().info("Checking predicate on security group " + input.getGroupName() + ", index of " + environmentId + " is " + input.getGroupName().indexOf(environmentId));
151
152 return -1 == input.getGroupName().indexOf(environmentId);
153 }
154 };
155
156 boolean flag = Collections2.filter(describeSecurityGroupsResult.getSecurityGroups(), predicate).isEmpty();
157
158
159
160 List<String> customSecurityGroups = describeSecurityGroupsResult.getSecurityGroups().stream().filter(sg -> {
161 return !sg.getGroupName().contains(environmentId);
162 }).map(csg -> csg.getGroupId()).collect(Collectors.toList());
163
164 String securityGroupCsv = String.join(",", customSecurityGroups);
165 getLog().info("Adjusting security group from " + optionSetting.getValue() + " to " + securityGroupCsv);
166
167 getLog().info("returning " + flag + " for security group check.");
168
169 optionSetting.setValue(securityGroupCsv);
170
171 return flag;
172 }
173 }
174
175 boolean bInvalid = isBlank(optionSetting.getValue());
176
177 if (!bInvalid) {
178 bInvalid = (optionSetting.getNamespace().equals("aws:cloudformation:template:parameter") && optionSetting.getOptionName().equals("AppSource"));
179 }
180
181 if (!bInvalid) {
182 bInvalid = (optionSetting.getNamespace().equals("aws:elasticbeanstalk:sns:topics") && optionSetting.getOptionName().equals("Notification Topic ARN"));
183 }
184
185
186
187
188 if (!bInvalid && isNotBlank(environmentId)) {
189 bInvalid = (optionSetting.getValue().contains(environmentId));
190 }
191
192 return bInvalid;
193 }
194
195 public String lookupTemplateName(String applicationName, String templateName) {
196 if (!GlobUtil.hasWildcards(defaultString(templateName))) {
197 return templateName;
198 }
199
200 getLog().info(format("Template Name %s contains wildcards. A Lookup is needed", templateName));
201
202 Collection<String> configurationTemplates = getConfigurationTemplates(applicationName);
203
204 for (String configTemplateName : configurationTemplates) {
205 getLog().debug(format(" * Found Template Name: %s", configTemplateName));
206 }
207
208
209
210
211
212 Pattern templateMask = GlobUtil.globify(templateName);
213
214 for (String s : configurationTemplates) {
215 Matcher m = templateMask.matcher(s);
216 if (m.matches()) {
217 getLog().info(format("Selecting: %s", s));
218 return s;
219 }
220 }
221
222 getLog().info("Not found");
223
224 return null;
225 }
226
227 @SuppressWarnings("unchecked")
228 protected List<String> getConfigurationTemplates(String applicationName) {
229 List<String> configurationTemplates =
230 getService()
231 .describeApplications(new DescribeApplicationsRequest().withApplicationNames(applicationName))
232 .getApplications()
233 .get(0)
234 .getConfigurationTemplates();
235
236 Collections.<String>sort(configurationTemplates, new ReverseComparator(String.CASE_INSENSITIVE_ORDER));
237
238 return configurationTemplates;
239 }
240
241
242 public String ensureSuffixStripped(String cnamePrefix) {
243 return EnvironmentHostnameUtil.ensureSuffixStripped(cnamePrefix);
244 }
245
246
247 @SuppressWarnings("unchecked")
248 protected String lookupSolutionStack(final String solutionStack) {
249 if (!GlobUtil.hasWildcards(solutionStack)) {
250 return solutionStack;
251 }
252
253 getLog().info("Looking up for solution stacks matching '" + solutionStack + "'");
254
255 final Function<SolutionStackDescription, String> stackTransformer =
256 new Function<SolutionStackDescription, String>() {
257 @Override
258 public String apply(SolutionStackDescription input) {
259 return input.getSolutionStackName();
260 }
261 };
262 final List<SolutionStackDescription> stackDetails = getService().listAvailableSolutionStacks().getSolutionStackDetails();
263
264 Collection<String> solStackList = Collections2.transform(stackDetails, stackTransformer);
265
266 final Pattern stackPattern = GlobUtil.globify(solutionStack);
267
268 List<String> matchingStacks =
269 new ArrayList<String>(
270 Collections2.filter(
271 solStackList,
272 new Predicate<String>() {
273 @Override
274 public boolean apply(String input) {
275 return stackPattern.matcher(input).matches();
276 }
277 }));
278
279 Collections.sort(matchingStacks, ComparatorUtils.reversedComparator(Collator.getInstance()));
280
281 if (matchingStacks.isEmpty()) {
282 throw new IllegalStateException("unable to lookup a solution stack matching '" + solutionStack + "'");
283 }
284
285 return matchingStacks.iterator().next();
286 }
287
288
289
290
291 @Parameter(property = "beanstalk.endpointUrl")
292 protected String endpointUrl;
293
294 @Override
295 public AWSElasticBeanstalkClient getService() {
296 final AWSElasticBeanstalkClient service = super.getService();
297
298 if (isNotBlank(endpointUrl)) {
299 service.setEndpoint(endpointUrl);
300 }
301
302 return service;
303 }
304 }