View Javadoc
1   /*
2    * Copyright (c) 2016 ingenieux Labs
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package br.com.ingenieux.mojo.beanstalk.bg;
18  
19  import com.google.common.base.Function;
20  import com.google.common.collect.Collections2;
21  
22  import com.amazonaws.services.elasticbeanstalk.model.EnvironmentDescription;
23  import com.amazonaws.services.elasticbeanstalk.model.SwapEnvironmentCNAMEsRequest;
24  
25  import org.apache.maven.plugins.annotations.Mojo;
26  import org.apache.maven.plugins.annotations.Parameter;
27  
28  import java.util.Collection;
29  
30  import br.com.ingenieux.mojo.beanstalk.AbstractNeedsEnvironmentMojo;
31  import br.com.ingenieux.mojo.beanstalk.cmd.env.update.UpdateEnvironmentCommand;
32  import br.com.ingenieux.mojo.beanstalk.cmd.env.update.UpdateEnvironmentContextBuilder;
33  import br.com.ingenieux.mojo.beanstalk.cmd.env.waitfor.WaitForEnvironmentCommand;
34  import br.com.ingenieux.mojo.beanstalk.cmd.env.waitfor.WaitForEnvironmentContextBuilder;
35  
36  import static java.lang.String.format;
37  
38  /**
39   * <p>Implements Bluegreen Deployment</p>
40   *
41   * @since 1.3.0
42   */
43  @Mojo(name = "blue-green")
44  public class BluegreenDeploymentMojo extends AbstractNeedsEnvironmentMojo {
45  
46    /**
47     * environmentNamePrefix - Matches all environment names prefixed with
48     */
49    @Parameter(property = "beanstalk.environmentNamePrefix", required = true, defaultValue = "${beanstalk.environmentName}")
50    protected String environmentNamePrefix;
51    /**
52     * Version Label to use. Defaults to Project Version
53     */
54    @Parameter(property = "beanstalk.versionLabel", defaultValue = "${project.version}-${maven.build.timestamp}")
55    String versionLabel;
56  
57    @Override
58    protected Object executeInternal() throws Exception {
59      versionLabel = lookupVersionLabel(applicationName, versionLabel);
60  
61      getLog().info(format("Using version %s", versionLabel));
62  
63      Collection<EnvironmentDescription> envs =
64          new WaitForEnvironmentCommand(this)
65              .lookupInternal(
66                  new WaitForEnvironmentContextBuilder().withApplicationName(applicationName).withEnvironmentRef(environmentNamePrefix + "*").build());
67  
68      if (envs.size() > 2) {
69        final Collection<String> environmentList =
70            Collections2.transform(
71                envs,
72                new Function<EnvironmentDescription, String>() {
73                  @Override
74                  public String apply(EnvironmentDescription input) {
75                    return format("%s[%s]", input.getEnvironmentId(), input.getEnvironmentName());
76                  }
77                });
78  
79        String message = "Ooops. There are multiple environments matching the lookup spec: " + environmentList;
80  
81        getLog().warn(message);
82        getLog().warn("Will pick one at random anyway as long as it uses WebServer tier name");
83      }
84  
85      String otherEnvId = null;
86      for (EnvironmentDescription e : envs) {
87        if (!e.getEnvironmentId().equals(curEnv.getEnvironmentId()) && "WebServer".equals(e.getTier().getName())) {
88          otherEnvId = e.getEnvironmentId();
89          break;
90        }
91      }
92  
93      getLog().info(format("(Green) Environment with environmentId['%s'] will be prepared once its ready to update", curEnv.getEnvironmentId()));
94  
95      new WaitForEnvironmentCommand(this)
96          .execute(
97              new WaitForEnvironmentContextBuilder()
98                  .withStatusToWaitFor("Ready")
99                  .withApplicationName(applicationName)
100                 .withEnvironmentRef(curEnv.getEnvironmentId())
101                 .build());
102 
103     getLog().info(format("(Blue) Environment with environmentId['%s'] will be prepared once its ready to update", otherEnvId));
104 
105     new WaitForEnvironmentCommand(this)
106         .execute(
107             new WaitForEnvironmentContextBuilder().withStatusToWaitFor("Ready").withApplicationName(applicationName).withEnvironmentRef(otherEnvId).build());
108 
109     getLog().info(format("(Blue) Updating environmentId to version %s", versionLabel));
110 
111     new UpdateEnvironmentCommand(this).execute(new UpdateEnvironmentContextBuilder().withEnvironmentId(otherEnvId).withVersionLabel(versionLabel).build());
112 
113     getLog().info(format("(Blue) Waiting for environmentId['%s'] to get ready and green prior to switching", otherEnvId));
114 
115     new WaitForEnvironmentCommand(this)
116         .execute(
117             new WaitForEnvironmentContextBuilder()
118                 .withStatusToWaitFor("Ready")
119                 .withApplicationName(applicationName)
120                 .withHealth("Green")
121                 .withEnvironmentRef(otherEnvId)
122                 .build());
123 
124     getLog().info(format("Ok. Switching"));
125 
126     getService()
127         .swapEnvironmentCNAMEs(new SwapEnvironmentCNAMEsRequest().withDestinationEnvironmentId(curEnv.getEnvironmentId()).withSourceEnvironmentId(otherEnvId));
128 
129     getLog().info(format("Done."));
130 
131     return null; //To change body of implemented methods use File | Settings | File Templates.
132   }
133 }