package tw.com.repository; import java.util.LinkedList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tw.com.AwsFacade; import tw.com.entity.ProjectAndEnv; import tw.com.entity.SearchCriteria; import tw.com.exceptions.CfnAssistException; import tw.com.exceptions.TooManyELBException; import tw.com.exceptions.MustHaveBuildNumber; import tw.com.providers.LoadBalancerClient; import com.amazonaws.services.ec2.model.Vpc; import com.amazonaws.services.elasticloadbalancing.model.Instance; import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerDescription; import com.amazonaws.services.elasticloadbalancing.model.Tag; public class ELBRepository { private static final Logger logger = LoggerFactory.getLogger(ELBRepository.class); LoadBalancerClient elbClient; VpcRepository vpcRepository; ResourceRepository cfnRepository; public ELBRepository(LoadBalancerClient elbClient, VpcRepository vpcRepository, ResourceRepository cfnRepository) { this.elbClient = elbClient; this.vpcRepository = vpcRepository; this.cfnRepository = cfnRepository; } public LoadBalancerDescription findELBFor(ProjectAndEnv projAndEnv, String type) throws TooManyELBException { String vpcID = getVpcId(projAndEnv); List<LoadBalancerDescription> foundELBForVPC = new LinkedList<LoadBalancerDescription>(); logger.info(String.format("Searching for load balancers for %s (will use tag %s:%s if >1 found)", vpcID, AwsFacade.TYPE_TAG,type)); List<LoadBalancerDescription> elbs = elbClient.describeLoadBalancers(); for (LoadBalancerDescription elb : elbs) { String dnsName = elb.getDNSName(); logger.debug("Found an ELB: " + dnsName); String possible = elb.getVPCId(); if (possible!=null) { if (possible.equals(vpcID)) { logger.info(String.format("Matched ELB %s for VPC: %s", dnsName, vpcID)); foundELBForVPC.add(elb); } else { logger.info(String.format("Not matched ELB %s as VPC id %s does not match: %s", dnsName, possible, vpcID)); } } else { logger.debug("No VPC ID for ELB " + dnsName); } } if (foundELBForVPC.size()>1) { return checkForMatchingTag(foundELBForVPC, vpcID, type); } if (foundELBForVPC.size()==1) { return foundELBForVPC.get(0); } logger.error("No matching ELB found for " + projAndEnv); return null; // ugly but preserves current api } private LoadBalancerDescription checkForMatchingTag( List<LoadBalancerDescription> descriptions, String vpcID, String type) throws TooManyELBException { List<LoadBalancerDescription> found = new LinkedList<LoadBalancerDescription>(); for(LoadBalancerDescription desc : descriptions) { String loadBalancerName = desc.getLoadBalancerName(); logger.info(String.format("Checking LB for tag %s:%s, ELB name is %s", AwsFacade.TYPE_TAG, type, loadBalancerName)); List<Tag> tags = elbClient.getTagsFor(loadBalancerName); if (containsCorrectTag(tags, type)) { logger.info("LB matched " + loadBalancerName); found.add(desc); } } if (found.size()==1) { return found.get(0); } throw new TooManyELBException(found.size(), String.format("Found too many elbs for vpc (%s) that matched tag %s", vpcID, AwsFacade.TYPE_TAG)); } private boolean containsCorrectTag(List<Tag> tags, String type) { for(Tag tag : tags) { if (tag.getKey().equals(AwsFacade.TYPE_TAG)) { return tag.getValue().equals(type); } } return false; } private String getVpcId(ProjectAndEnv projAndEnv) { Vpc vpc = vpcRepository.getCopyOfVpc(projAndEnv); String vpcID = vpc.getVpcId(); return vpcID; } private List<Instance> addInstancesThatMatchBuildAndType(ProjectAndEnv projAndEnv, String typeTag) throws CfnAssistException { if (!projAndEnv.hasBuildNumber()) { throw new MustHaveBuildNumber(); } LoadBalancerDescription elb = findELBFor(projAndEnv, typeTag); List<Instance> currentInstances = elb.getInstances(); String lbName = elb.getLoadBalancerName(); SearchCriteria criteria = new SearchCriteria(projAndEnv); List<Instance> allMatchingInstances = cfnRepository.getAllInstancesMatchingType(criteria, typeTag); List<Instance> instancesToAdd = filterBy(currentInstances, allMatchingInstances); if (allMatchingInstances.size()==0) { logger.warn(String.format("No instances matched %s and type tag %s (%s)", projAndEnv, typeTag, AwsFacade.TYPE_TAG)); } else { logger.info(String.format("Regsister matching %s instances with the LB %s ", instancesToAdd.size(),lbName)); elbClient.registerInstances(instancesToAdd, lbName); } return instancesToAdd; } private List<Instance> filterBy(List<Instance> currentInstances, List<Instance> allMatchingInstances) { List<Instance> result = new LinkedList<Instance>(); for(Instance candidate : allMatchingInstances) { if (!currentInstances.contains(candidate)) { result.add(candidate); } } return result; } public List<Instance> findInstancesAssociatedWithLB( ProjectAndEnv projAndEnv, String typeTag) throws TooManyELBException { LoadBalancerDescription elb = findELBFor(projAndEnv, typeTag); return elb.getInstances(); } // returns remaining instances private List<Instance> removeInstancesNotMatching(ProjectAndEnv projAndEnv, List<Instance> matchingInstances, String typeTag) throws MustHaveBuildNumber, TooManyELBException { LoadBalancerDescription elb = findELBFor(projAndEnv, typeTag); logger.info("Checking if instances should be removed from ELB " + elb.getLoadBalancerName()); List<Instance> currentInstances = elb.getInstances(); List<Instance> toRemove = new LinkedList<Instance>(); for(Instance current : currentInstances) { String instanceId = current.getInstanceId(); if (matchingInstances.contains(current)) { logger.info("Instance matched project/env/build/type, will not be removed " + instanceId); } else { logger.info("Instance did not match, will be removed from ELB " +instanceId); toRemove.add(new Instance(instanceId)); } } if (toRemove.isEmpty()) { logger.info("No instances to remove from ELB " + elb.getLoadBalancerName()); return new LinkedList<Instance>(); } return removeInstances(elb,toRemove); } private List<Instance> removeInstances(LoadBalancerDescription elb, List<Instance> toRemove) { String loadBalancerName = elb.getLoadBalancerName(); logger.info("Removing instances from ELB " + loadBalancerName); return elbClient.degisterInstancesFromLB(toRemove,loadBalancerName); } public List<Instance> updateInstancesMatchingBuild(ProjectAndEnv projAndEnv, String typeTag) throws CfnAssistException { List<Instance> matchinginstances = addInstancesThatMatchBuildAndType(projAndEnv, typeTag); return removeInstancesNotMatching(projAndEnv, matchinginstances, typeTag); } // TODO filter on the request, but api does not seeem to support that currently public List<LoadBalancerDescription> findELBForVPC(String vpcId) { List<LoadBalancerDescription> result = elbClient.describeLoadBalancers(); // seems to be no filter for vpc on elbs List<LoadBalancerDescription> filtered = new LinkedList<LoadBalancerDescription>(); for(LoadBalancerDescription lb : result) { if (lb.getVPCId()!=null) { if (lb.getVPCId().equals(vpcId)) { filtered.add(lb); } } } return filtered; } }