package core.aws.resource.ec2; import core.aws.resource.Resource; import core.aws.resource.ResourceStatus; import core.aws.resource.Resources; import core.aws.resource.ServerResource; import core.aws.resource.elb.ELB; import core.aws.resource.image.AMI; import core.aws.resource.vpc.Subnet; import core.aws.task.ec2.CreateInstanceTask; import core.aws.task.ec2.DeleteInstanceTask; import core.aws.task.ec2.DescribeInstanceTask; import core.aws.task.ec2.ProvisionInstanceTask; import core.aws.task.ec2.RunCommandTask; import core.aws.task.ec2.StartInstanceTask; import core.aws.task.ec2.StopInstanceTask; import core.aws.task.ec2.UploadTask; import core.aws.util.Asserts; import core.aws.util.ToStringHelper; import core.aws.workflow.Tasks; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * @author neo */ public class Instance extends Resource implements ServerResource { public final List<com.amazonaws.services.ec2.model.Instance> remoteInstances = new ArrayList<>(); public EBS ebs; public KeyPair keyPair; public AMI ami; public String instanceType; public SecurityGroup securityGroup; public int count = 1; public Subnet subnet; public ELB elb; public InstanceProfile instanceProfile; public Instance(String id) { super(id); } @Override public void validate(Resources resources) { if (status == ResourceStatus.LOCAL_REMOTE && !remoteInstances.isEmpty()) { for (com.amazonaws.services.ec2.model.Instance remoteInstance : remoteInstances) { Asserts.equals(remoteInstance.getVpcId(), resources.vpc.remoteVPC.getVpcId(), "remote instance is in different vpc"); } } } @Override protected void describeTasks(Tasks tasks) { tasks.add(new DescribeInstanceTask(this)); } @Override public void commandTasks(Tasks tasks) { tasks.add(new RunCommandTask(this)); } @Override public void uploadTasks(Tasks tasks) { tasks.add(new UploadTask(this)); } @Override public void startTasks(Tasks tasks) { if (!stoppedInstanceIds().isEmpty()) { tasks.add(new StartInstanceTask(this)); } } @Override public void stopTasks(Tasks tasks) { if (!runningInstanceIds().isEmpty()) { tasks.add(new StopInstanceTask(this)); } } @Override public void provisionTasks(Tasks tasks) { tasks.add(new ProvisionInstanceTask(this)); } @Override protected void createTasks(Tasks tasks) { tasks.add(new CreateInstanceTask(this, count, false)); } @Override public void deployTasks(Tasks tasks) { updateTasks(tasks, true); } @Override protected void updateTasks(Tasks tasks) { updateTasks(tasks, false); } private void updateTasks(Tasks tasks, boolean isDeployment) { CreateInstanceTask createInstanceTask = null; DeleteInstanceTask deleteInstanceTask = null; List<com.amazonaws.services.ec2.model.Instance> deletedInstances = remoteInstances.stream() .filter(this::changed).collect(Collectors.toList()); remoteInstances.removeAll(deletedInstances); if (count > remoteInstances.size()) { createInstanceTask = tasks.add(new CreateInstanceTask(this, count - remoteInstances.size(), isDeployment)); } else if (count < remoteInstances.size()) { List<com.amazonaws.services.ec2.model.Instance> redundantInstances = new ArrayList<>(remoteInstances.subList(0, remoteInstances.size() - count)); remoteInstances.removeAll(redundantInstances); deletedInstances.addAll(redundantInstances); } if (!deletedInstances.isEmpty()) deleteInstanceTask = tasks.add(new DeleteInstanceTask(this, deletedInstances)); if (isDeployment && createInstanceTask != null && deleteInstanceTask != null) { deleteInstanceTask.dependsOn(createInstanceTask); } } @Override protected void deleteTasks(Tasks tasks) { tasks.add(new DeleteInstanceTask(this, remoteInstances)); } private boolean changed(com.amazonaws.services.ec2.model.Instance instance) { if (!instance.getInstanceType().equals(instanceType)) return true; if (!instance.getImageId().equals(ami.imageId())) return true; // check if instance profile added or removed boolean localHasInstanceProfile = instanceProfile != null; boolean remoteHasInstanceProfile = instance.getIamInstanceProfile() != null; if (localHasInstanceProfile != remoteHasInstanceProfile) return true; return false; } public List<String> stoppedInstanceIds() { return remoteInstances.stream() .filter(remoteInstance -> InstanceState.STOPPED.equalsTo(remoteInstance.getState())) .map(com.amazonaws.services.ec2.model.Instance::getInstanceId) .collect(Collectors.toList()); } public List<String> runningInstanceIds() { return remoteInstances.stream() .filter(remoteInstance -> InstanceState.RUNNING.equalsTo(remoteInstance.getState())) .map(com.amazonaws.services.ec2.model.Instance::getInstanceId) .collect(Collectors.toList()); } @Override public String toString() { return new ToStringHelper(this) .add(id) .add(status) .add("ami", ami) .add("instanceType", instanceType) .add("count", count) .add("subnet", subnet) .addIfNotNull("instanceProfile", instanceProfile) .addIfNotNull("elb", elb) .toString(); } }