Package br.com.ingenieux.mojo.beanstalk.dns

Source Code of br.com.ingenieux.mojo.beanstalk.dns.BindDomainsMojo$BindDomainContext

package br.com.ingenieux.mojo.beanstalk.dns;

/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import static java.lang.String.format;
import static org.apache.commons.lang.StringUtils.join;
import static org.apache.commons.lang.StringUtils.strip;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import br.com.ingenieux.mojo.beanstalk.AbstractNeedsEnvironmentMojo;

import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.elasticbeanstalk.model.DescribeEnvironmentResourcesRequest;
import com.amazonaws.services.elasticloadbalancing.AmazonElasticLoadBalancing;
import com.amazonaws.services.elasticloadbalancing.AmazonElasticLoadBalancingClient;
import com.amazonaws.services.elasticloadbalancing.model.DescribeLoadBalancersRequest;
import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerDescription;
import com.amazonaws.services.route53.AmazonRoute53;
import com.amazonaws.services.route53.AmazonRoute53Client;
import com.amazonaws.services.route53.model.AliasTarget;
import com.amazonaws.services.route53.model.Change;
import com.amazonaws.services.route53.model.ChangeAction;
import com.amazonaws.services.route53.model.ChangeBatch;
import com.amazonaws.services.route53.model.ChangeResourceRecordSetsRequest;
import com.amazonaws.services.route53.model.HostedZone;
import com.amazonaws.services.route53.model.ListResourceRecordSetsRequest;
import com.amazonaws.services.route53.model.ListResourceRecordSetsResult;
import com.amazonaws.services.route53.model.RRType;
import com.amazonaws.services.route53.model.ResourceRecordSet;

/**
* <p>
* Binds an Elastic Beanstalk Environment into a set of Route53 records
* </p>
*
* <p>
* NOTE: THIS IS HIGHLY EXPERIMENTAL CODE
* </p>
*
* @since 0.2.9
*/
@Mojo(name = "bind-domains")
public class BindDomainsMojo extends AbstractNeedsEnvironmentMojo {
  /**
   * <p>List of Domains</p>
   *
   * <p>Could be set as either:</p>
   * <ul>
   * <li>fqdn:hostedZoneId (e.g. "services.modafocas.org:Z3DJ4DL0DIEEJA")</li>
   * <li>hosted zone name - will be set to root. (e.g., "modafocas.org")</li>
   * </ul>
   */
  @Parameter(property = "beanstalk.domains")
  String[] domains;

  @Override
  protected Object executeInternal() throws Exception {
    Map<String, String> recordsToAssign = new LinkedHashMap<String, String>();
    /**
     * Step #1: AWS Client Config
     */
    BindDomainContext ctx = new BindDomainContext();

    /**
     * Step #2: Validate Parameters
     */
    {
      for (String domain : domains) {
        String key = formatDomain(domain);
        String value = null;
       
        /*
         * Handle Entries in the form <record>:<zoneid>
         */
        if (-1 != key.indexOf(':')) {
          String[] pair = key.split(":", 2);
         
          key = formatDomain(pair[0]);
          value = strip(pair[1], ".");
        }
       
        recordsToAssign.put(key, value);
      }

      Validate.isTrue(recordsToAssign.size() > 0, "No Domains Supplied!");

      if (getLog().isInfoEnabled()) {
        getLog().info(format("Domains to Map to Environment (cnamePrefix='%s')", curEnv.getCNAME()));

        for (Entry<String, String> entry : recordsToAssign.entrySet()) {
          String key = entry.getKey();
          String zoneId = entry.getValue();
         
          String message = format(" * Domain: %s", key);
         
          if (null != zoneId)
            message += " (and using zoneId " + zoneId + ")";
         
          getLog().info(message);
        }
      }
    }

    /**
     * Step #3: Lookup Domains on Route53
     */
    Map<String, HostedZone> hostedZoneMapping = new LinkedHashMap<String, HostedZone>();
   
    {
      Set<String> unresolvedDomains = new LinkedHashSet<String>();
     
      for (Entry<String, String> entry : recordsToAssign.entrySet()) {
        if (null != entry.getValue())
          continue;
       
        unresolvedDomains.add(entry.getKey());
      }
     
      for (HostedZone hostedZone : ctx.r53.listHostedZones().getHostedZones()) {
        String id = hostedZone.getId();
        String name = hostedZone.getName();
       
        hostedZoneMapping.put(id, hostedZone);

        if (unresolvedDomains.contains(name)) {
          if (getLog().isInfoEnabled())
            getLog().info(format("Mapping Domain %s to R53 Zone Id %s", name, id));

          recordsToAssign.put(name, id);

          unresolvedDomains.remove(name);
        }
      }

      Validate.isTrue(unresolvedDomains.isEmpty(), "Domains not resolved: " + join(unresolvedDomains, "; "));
    }
   
    /**
     * Step #4: Domain Validation
     */
    {
      for (Entry<String, String> entry : recordsToAssign.entrySet()) {
        String record = entry.getKey();
        String zoneId = entry.getValue();
        HostedZone hostedZone = hostedZoneMapping.get(zoneId);
       
        Validate.notNull(hostedZone, format("Unknown Hosted Zone Id: %s for Record: %s", zoneId, record));
        Validate.isTrue(record.endsWith(hostedZone.getName()), format("Record %s does not map to zoneId %s (domain: %s)", record, zoneId, hostedZone.getName()));
      }
    }

    /**
     * Step #5: Get ELB Hosted Zone Id
     */
    {
      String loadBalancerName = getService().describeEnvironmentResources(new DescribeEnvironmentResourcesRequest().withEnvironmentId(curEnv.getEnvironmentId())).getEnvironmentResources().getLoadBalancers().get(0).getName();

      DescribeLoadBalancersRequest req = new DescribeLoadBalancersRequest(
          Arrays.asList(loadBalancerName));

      List<LoadBalancerDescription> loadBalancers = ctx.elb.describeLoadBalancers(req)
          .getLoadBalancerDescriptions();

      Validate.isTrue(1 == loadBalancers.size(), "Unexpected number of Load Balancers returned");

      ctx.elbHostedZoneId = loadBalancers.get(0).getCanonicalHostedZoneNameID();

      if (getLog().isInfoEnabled())
        getLog().info(format("Using ELB Canonical Hosted Zone Name Id %s", ctx.elbHostedZoneId));
    }

    /**
     * Step #6: Apply Change Batch on Each Domain
     */
    for (Entry<String, String> recordEntry : recordsToAssign.entrySet()) {
      assignDomain(ctx, recordEntry.getKey(), recordEntry.getValue());
    }

    return null;
  }

  protected void assignDomain(BindDomainContext ctx, String record, String zoneId) {
    ChangeBatch changeBatch = new ChangeBatch();

    /**
     * Look for Existing Resource Record Sets
     */
    {
      ResourceRecordSet resourceRecordSet = null;

      ListResourceRecordSetsResult listResourceRecordSets = ctx.r53
          .listResourceRecordSets(new ListResourceRecordSetsRequest(
              zoneId));

      for (ResourceRecordSet rrs : listResourceRecordSets
          .getResourceRecordSets()) {
        if (!rrs.getName().equals(record))
          continue;

        if (!"A".equals(rrs.getType()))
          continue;

        resourceRecordSet = rrs;
        break;
      }

      if (null != resourceRecordSet) {
        if (getLog().isInfoEnabled())
          getLog().info(format("Excluding resourceRecordSet %s for domain %s", resourceRecordSet, record));
        changeBatch.getChanges().add(new Change(ChangeAction.DELETE,
            resourceRecordSet));
      }
    }

    /**
     * Then Add Ours
     */
    AliasTarget aliasTarget = new AliasTarget();

    aliasTarget.setHostedZoneId(ctx.elbHostedZoneId);
    aliasTarget.setDNSName(curEnv.getEndpointURL());

    ResourceRecordSet resourceRecordSet = new ResourceRecordSet();

    resourceRecordSet.setName(record);
    resourceRecordSet.setType(RRType.A);

    resourceRecordSet.setAliasTarget(aliasTarget);

    if (getLog().isInfoEnabled())
      getLog().info(format("Adding resourceRecordSet %s for domain %s mapped to %s", resourceRecordSet, record, aliasTarget.getDNSName()));

    changeBatch.getChanges().add(new Change(ChangeAction.CREATE, resourceRecordSet));

    ChangeResourceRecordSetsRequest req = new ChangeResourceRecordSetsRequest(
        zoneId, changeBatch);

    ctx.r53.changeResourceRecordSets(req);
  }

  class BindDomainContext {
    String elbHostedZoneId;

    AmazonRoute53 r53;

    AmazonEC2 ec2;

    AmazonElasticLoadBalancing elb;

    public BindDomainContext() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
      this.r53 = clientFactory.getService(AmazonRoute53Client.class);
      this.ec2 = clientFactory.getService(AmazonEC2Client.class);
      this.elb = clientFactory.getService(AmazonElasticLoadBalancingClient.class);
    }

    public void setElbHostedZoneId(String elbHostedZoneId) {
      this.elbHostedZoneId = elbHostedZoneId;
    }

    public String getElbHostedZoneId() {
      return elbHostedZoneId;
    }

  }

  String formatDomain(String d) {
    return strip(d, ".").concat(".");
  }
}
TOP

Related Classes of br.com.ingenieux.mojo.beanstalk.dns.BindDomainsMojo$BindDomainContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.