package com.hubspot.baragon.service.managers; import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.amazonaws.AmazonClientException; import com.amazonaws.services.elasticloadbalancing.model.Instance; import com.google.common.base.Optional; import com.google.inject.Inject; import com.google.inject.Singleton; import com.hubspot.baragon.data.BaragonLoadBalancerDatastore; import com.hubspot.baragon.models.AgentCheckInResponse; import com.hubspot.baragon.models.BaragonAgentMetadata; import com.hubspot.baragon.models.BaragonGroup; import com.hubspot.baragon.models.TrafficSource; import com.hubspot.baragon.models.TrafficSourceState; import com.hubspot.baragon.models.TrafficSourceType; import com.hubspot.baragon.service.config.ElbConfiguration; import com.hubspot.baragon.service.elb.ApplicationLoadBalancer; import com.hubspot.baragon.service.elb.ClassicLoadBalancer; import com.hubspot.baragon.service.elb.ElasticLoadBalancer; import com.hubspot.baragon.service.exceptions.NoMatchingElbForVpcException; @Singleton public class ElbManager { private static final Logger LOG = LoggerFactory.getLogger(ElbManager.class); private final ApplicationLoadBalancer applicationLoadBalancer; private final ClassicLoadBalancer classicLoadBalancer; private final Optional<ElbConfiguration> configuration; private final BaragonLoadBalancerDatastore loadBalancerDatastore; @Inject public ElbManager(ApplicationLoadBalancer applicationLoadBalancer, ClassicLoadBalancer classicLoadBalancer, BaragonLoadBalancerDatastore loadBalancerDatastore, Optional<ElbConfiguration> configuration) { this.applicationLoadBalancer = applicationLoadBalancer; this.classicLoadBalancer = classicLoadBalancer; this.configuration = configuration; this.loadBalancerDatastore = loadBalancerDatastore; } public boolean isElbConfigured() { return (configuration.isPresent() && configuration.get().isEnabled()); } public boolean isActiveAndHealthy(Optional<BaragonGroup> group, BaragonAgentMetadata agent) { for (TrafficSource source : group.get().getTrafficSources()) { if (getLoadBalancer(source.getType()).isInstanceHealthy(agent.getEc2().getInstanceId().get(), source.getName())) { return true; } } return false; } public AgentCheckInResponse attemptRemoveAgent(BaragonAgentMetadata agent, Optional<BaragonGroup> group, String groupName, boolean isStatusCheck) throws AmazonClientException { TrafficSourceState state = TrafficSourceState.DONE; long maxWaitTime = 0L; Optional<String> maybeExceptions = Optional.absent(); if (isElbEnabledAgent(agent, group, groupName)) { for (TrafficSource source : group.get().getTrafficSources()) { Instance instance = new Instance(agent.getEc2().getInstanceId().get()); AgentCheckInResponse response = isStatusCheck ? getLoadBalancer(source.getType()).checkRemovedInstance(instance, source.getName(), agent.getAgentId()) : getLoadBalancer(source.getType()).removeInstance(instance, source.getName(), agent.getAgentId()); if (response.getState().ordinal() > state.ordinal()) { state = response.getState(); } if (response.getExceptionMessage().isPresent()) { maybeExceptions = Optional.of(maybeExceptions.or("") + response.getExceptionMessage().get() + "\n"); } if (response.getWaitTime() > maxWaitTime) { maxWaitTime = response.getWaitTime(); } } } return new AgentCheckInResponse(state, maybeExceptions, maxWaitTime); } public AgentCheckInResponse attemptAddAgent(BaragonAgentMetadata agent, Optional<BaragonGroup> group, String groupName, boolean isStatusCheck) throws AmazonClientException, NoMatchingElbForVpcException { TrafficSourceState state = TrafficSourceState.DONE; Optional<String> maybeVpcException = Optional.absent(); long maxWaitTime = 0L; if (isElbEnabledAgent(agent, group, groupName)) { for (TrafficSource source : group.get().getTrafficSources()) { Instance instance = new Instance(agent.getEc2().getInstanceId().get()); AgentCheckInResponse response = isStatusCheck ? getLoadBalancer(source.getType()).checkRegisteredInstance(instance, source.getName(), agent) : getLoadBalancer(source.getType()).registerInstance(instance, source.getName(), agent); if (response.getExceptionMessage().isPresent()) { maybeVpcException = Optional.of(maybeVpcException.or("") + response.getExceptionMessage().get() + "\n"); } if (response.getState().ordinal() > state.ordinal()) { state = response.getState(); } if (response.getWaitTime() > maxWaitTime) { maxWaitTime = response.getWaitTime(); } } if (maybeVpcException.isPresent() && configuration.get().isFailWhenNoElbForVpc()) { throw new NoMatchingElbForVpcException(maybeVpcException.get()); } } return new AgentCheckInResponse(state, maybeVpcException, maxWaitTime); } public boolean isElbEnabledAgent(BaragonAgentMetadata agent, Optional<BaragonGroup> group, String groupName) { if (group.isPresent()) { if (!group.get().getTrafficSources().isEmpty()) { if (agent.getEc2().getInstanceId().isPresent()) { return true; } else { LOG.debug("No instance id for agent {}, can't add to ELB", agent.getAgentId()); } } else { LOG.debug("No traffic sources for group {}, not adding agent {} to an ELB", group.get().getName(), agent.getAgentId()); } } else { LOG.debug("Group {} not found for agent {}", groupName, agent.getAgentId()); } return false; } public void syncAll() { Collection<BaragonGroup> groups = loadBalancerDatastore.getLoadBalancerGroups(); classicLoadBalancer.syncAll(groups); applicationLoadBalancer.syncAll(groups); } private ElasticLoadBalancer getLoadBalancer(TrafficSourceType type) { switch (type) { case ALB_TARGET_GROUP: return applicationLoadBalancer; case CLASSIC: default: return classicLoadBalancer; } } }