All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.nitorcreations.willow.autoscaler.clouds.aws.AWSCloudAdapter Maven / Gradle / Ivy

package com.nitorcreations.willow.autoscaler.clouds.aws;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.codec.binary.Base64;

import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.CreateTagsRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.Filter;
import com.amazonaws.services.ec2.model.IamInstanceProfileSpecification;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceStateChange;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.RunInstancesRequest;
import com.amazonaws.services.ec2.model.RunInstancesResult;
import com.amazonaws.services.ec2.model.Tag;
import com.amazonaws.services.ec2.model.TerminateInstancesRequest;
import com.amazonaws.services.ec2.model.TerminateInstancesResult;
import com.nitorcreations.willow.autoscaler.clouds.CloudAdapter;
import com.nitorcreations.willow.autoscaler.config.AutoScalingGroupConfig;
import com.nitorcreations.willow.autoscaler.deployment.AutoScalingGroupDeploymentStatus;
import com.nitorcreations.willow.autoscaler.metrics.AutoScalingGroupStatus;
import com.nitorcreations.willow.autoscaler.metrics.AutoScalingStatus;

@Named("AWS")
public class AWSCloudAdapter implements CloudAdapter {

  private Logger logger = Logger.getLogger(this.getClass().getCanonicalName());

  @Inject
  private EC2ClientFactory ec2ClientFactory;

  @Inject
  private AutoScalingStatus autoScalingStatus;

  @Inject
  private Random random;

  @Override
  public String getCloudProviderId() {
    return "AWS";
  }

  @Override
  public AutoScalingGroupDeploymentStatus getGroupStatus(String regionId, String groupId) {
    List instances = new LinkedList<>();
    AmazonEC2Client client = ec2ClientFactory.getClient(regionId);
    DescribeInstancesRequest request = new DescribeInstancesRequest();
    request.withFilters(
        new Filter()
            .withName("tag:willow-group")
            .withValues(groupId)
    );

    DescribeInstancesResult result;
    try {
      result = client.describeInstances(request);
    } catch (Exception e) {
      logger.log(Level.SEVERE, "Failed to query AWS instance(s)", e);
      throw e;
    }

    for (Reservation r : result.getReservations()) {
      addLiveInstancesToList(instances, r);
    }
    while (result.getNextToken() != null) {
      DescribeInstancesRequest moreRequest = new DescribeInstancesRequest().withNextToken(result.getNextToken());
      result = client.describeInstances(moreRequest);
      for (Reservation r : result.getReservations()) {
        addLiveInstancesToList(instances, r);
      }
    }
    return new AutoScalingGroupDeploymentStatus(groupId, instances);
  }

  @Override
  public List launchInstances(AutoScalingGroupConfig config, int count) {
    List instanceIds = new ArrayList<>();
    RunInstancesRequest runInstancesRequest = new RunInstancesRequest();

    try {
      logger.info(String.format("starting instance with authorization role %s", config.getAuthorizationRole()));
      runInstancesRequest
          .withImageId(config.getVirtualMachineImage())
          .withInstanceType(config.getInstanceType())
          .withMinCount(count)
          .withMaxCount(count)
          .withKeyName(config.getSshKey())
          .withSecurityGroupIds(config.getSecurityGroups())
          .withSubnetId(config.getSubnet())
          .withUserData(Base64.encodeBase64String(config.getUserData().getBytes("UTF-8")))
          .withIamInstanceProfile(new IamInstanceProfileSpecification().withArn(config.getAuthorizationRole()))
          ;
    } catch (UnsupportedEncodingException e) {
      logger.log(Level.SEVERE, "UTF-8 not supported, all bets are off!", e);
    }

    logger.info("Sending runInstances request to AWS");
    AmazonEC2Client client = ec2ClientFactory.getClient(config.getRegion());
    RunInstancesResult result = null;
    try {
      result = client.runInstances(runInstancesRequest);
    } catch (Exception e) {
      logger.log(Level.SEVERE, "Failed to launch AWS instance(s)", e);
      return instanceIds;
    }
    logger.info("Sent runInstances request to AWS");

    List instances = result.getReservation().getInstances();
    for (Instance instance : instances) {
      CreateTagsRequest createTagsRequest = new CreateTagsRequest();
      createTagsRequest
          .withResources(instance.getInstanceId())
          .withTags(willowTagsToAWSTags(config.getTags()));
      logger.info("Tagging instance " + instance.getInstanceId());
      client.createTags(createTagsRequest);
      instanceIds.add(instance.getInstanceId());
    }
    return instanceIds;
  }

  @Override
  public List terminateInstances(AutoScalingGroupConfig config, int count) {
    List instanceIds = new ArrayList<>();
    List idsToTerminate = chooseInstancesToTerminate(config, count);
    TerminateInstancesRequest request = new TerminateInstancesRequest();
    request.withInstanceIds(idsToTerminate);

    AmazonEC2Client client = ec2ClientFactory.getClient(config.getRegion());
    TerminateInstancesResult result;
    try {
      result = client.terminateInstances(request);
    } catch (Exception e) {
      logger.log(Level.SEVERE, "Failed to terminate AWS instance(s)", e);
      return instanceIds;
    }

    for (InstanceStateChange i : result.getTerminatingInstances()) {
      instanceIds.add(i.getInstanceId());
    }
    return instanceIds;
  }

  private List chooseInstancesToTerminate(AutoScalingGroupConfig config, int count) {
    Set idsToTerminate = new HashSet<>();
    AutoScalingGroupStatus groupStatus = autoScalingStatus.getStatus(config.getName());
    if (groupStatus != null && groupStatus.getDeploymentStatus() != null) {
      List instances =
          groupStatus.getDeploymentStatus().getInstances();
      assert instances.size() >= count;
      while (idsToTerminate.size() < count) {
        int index = random.nextInt(instances.size());
        String instanceId = instances.get(index).getInstanceId();
        idsToTerminate.add(instanceId);
        logger.info(String.format("Chose instance %s to terminate", instanceId));
      }
    }
    return new ArrayList<>(idsToTerminate);
  }


  private List willowTagsToAWSTags(List wTags) {
    List tags = new ArrayList<>();
    for (com.nitorcreations.willow.autoscaler.config.Tag t : wTags) {
      tags.add(new Tag(t.name, t.value));
    }
    return tags;
  }

  private void addLiveInstancesToList(List instances, Reservation r) {
    for (Instance instance : r.getInstances()) {
      String state = instance.getState().getName();
      if ("running".equalsIgnoreCase(state) || "pending".equalsIgnoreCase(state)) {
        instances.add(
            new com.nitorcreations.willow.autoscaler.deployment.Instance(instance.getInstanceId())
                .setInstanceType(instance.getInstanceType())
                .setPrivateHostname(instance.getPrivateDnsName())
                .setPrivateIp(instance.getPrivateIpAddress())
                .setPublicHostname(instance.getPublicDnsName())
                .setPublicIp(instance.getPublicIpAddress())
                .setTags(convertToWillowTags(instance.getTags())));
      }
    }
  }

  private List convertToWillowTags(List awsTags) {
    List willowTags = new ArrayList<>();
    for (Tag tag : awsTags) {
      willowTags.add(new com.nitorcreations.willow.autoscaler.config.Tag(tag.getKey(), tag.getValue()));
    }
    return willowTags;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy