package core.aws.resource.elb; import com.amazonaws.services.elasticloadbalancing.model.ListenerDescription; import com.amazonaws.services.elasticloadbalancing.model.LoadBalancerDescription; import core.aws.resource.Resource; import core.aws.resource.ResourceStatus; import core.aws.resource.Resources; import core.aws.resource.ec2.SecurityGroup; import core.aws.resource.s3.Bucket; import core.aws.resource.vpc.Subnet; import core.aws.resource.vpc.SubnetType; import core.aws.task.elb.CreateELBListenerTask; import core.aws.task.elb.CreateELBTask; import core.aws.task.elb.DeleteELBListenerTask; import core.aws.task.elb.DeleteELBTask; import core.aws.task.elb.DescribeELBTask; import core.aws.task.elb.UpdateELBSGTask; import core.aws.util.Asserts; import core.aws.util.Lists; import core.aws.workflow.Tasks; import java.util.List; import java.util.Optional; /** * @author neo */ public class ELB extends Resource { public LoadBalancerDescription remoteELB; public String name; public boolean listenHTTP; public boolean listenHTTPS; public ServerCert cert; public String healthCheckURL; public SecurityGroup securityGroup; public Subnet subnet; public Bucket accessLogBucket; public Optional<String> scheme = Optional.empty(); // currently only allowed value is "internal" public String amazonCertARN; public ELB(String id) { super(id); } @Override public void validate(Resources resources) { if (status == ResourceStatus.LOCAL_ONLY && subnet.type == SubnetType.PRIVATE) { Asserts.isFalse(scheme.isPresent(), "ELB in private subnet doesn't need scheme, it will be internal by default"); } if (status == ResourceStatus.LOCAL_ONLY) { Asserts.isTrue(name.length() <= 32, "max length of elb name is 32"); Asserts.isTrue(name.matches("[a-zA-Z0-9\\-]+"), "elb name can only contain alphanumeric, and '-'"); } if (listenHTTPS && (status == ResourceStatus.LOCAL_ONLY || status == ResourceStatus.LOCAL_REMOTE)) { Asserts.isTrue(amazonCertARN != null || cert != null, "https listener requires cert"); } } @Override protected void createTasks(Tasks tasks) { tasks.add(new CreateELBTask(this)); } @Override protected void updateTasks(Tasks tasks) { if (sgChanged()) { tasks.add(new UpdateELBSGTask(this)); } CreateELBListenerTask createELBListenerTask = null; List<String> addedProtocols = Lists.newArrayList(); if (httpListenerAdded()) addedProtocols.add("HTTP"); if (httpsListenerAdded() || httpsCertChanged()) addedProtocols.add("HTTPS"); if (!addedProtocols.isEmpty()) { createELBListenerTask = new CreateELBListenerTask(this, addedProtocols); tasks.add(createELBListenerTask); } List<String> deletedProtocols = Lists.newArrayList(); if (httpListenerRemoved()) deletedProtocols.add("HTTP"); if (httpsListenerRemoved() || httpsCertChanged()) deletedProtocols.add("HTTPS"); if (!deletedProtocols.isEmpty()) { DeleteELBListenerTask deleteELBListenerTask = new DeleteELBListenerTask(this, deletedProtocols); if (createELBListenerTask != null) createELBListenerTask.dependsOn(deleteELBListenerTask); tasks.add(deleteELBListenerTask); } } @Override protected void describeTasks(Tasks tasks) { tasks.add(new DescribeELBTask(this)); } private boolean sgChanged() { if (securityGroup == null) return false; // no vpc if (securityGroup.remoteSecurityGroup == null) return true; if (remoteELB.getSecurityGroups().isEmpty()) return true; return !remoteELB.getSecurityGroups().get(0).equals(securityGroup.remoteSecurityGroup.getGroupId()); } @Override protected void deleteTasks(Tasks tasks) { tasks.add(new DeleteELBTask(this)); } public boolean httpListenerAdded() { return listenHTTP && !hasRemoteHTTPListener(); } public boolean httpListenerRemoved() { return !listenHTTP && hasRemoteHTTPListener(); } public boolean httpsListenerAdded() { Optional<ListenerDescription> remoteHTTPSListener = findRemoteHTTPSListener(); return listenHTTPS && !remoteHTTPSListener.isPresent(); } public boolean httpsListenerRemoved() { Optional<ListenerDescription> remoteHTTPSListener = findRemoteHTTPSListener(); return !listenHTTPS && remoteHTTPSListener.isPresent(); } public boolean httpsCertChanged() { Optional<ListenerDescription> remoteHTTPSListener = findRemoteHTTPSListener(); if (!listenHTTPS || !remoteHTTPSListener.isPresent()) return false; String remoteCertARN = remoteHTTPSListener.get().getListener().getSSLCertificateId(); if (cert != null) { // cert files if (cert.status == ResourceStatus.LOCAL_ONLY || !cert.remoteCert.getServerCertificateMetadata().getArn().equals(remoteCertARN)) return true; if (cert.changed()) return true; } else if (!remoteCertARN.equals(amazonCertARN)) { return true; } return false; } private boolean hasRemoteHTTPListener() { return remoteELB.getListenerDescriptions().stream().anyMatch(listener -> "HTTP".equalsIgnoreCase(listener.getListener().getProtocol())); } private Optional<ListenerDescription> findRemoteHTTPSListener() { return remoteELB.getListenerDescriptions().stream().filter(listener -> "HTTPS".equalsIgnoreCase(listener.getListener().getProtocol())).findAny(); } }