com.hubspot.baragon.service.managers.AgentManager Maven / Gradle / Ivy
package com.hubspot.baragon.service.managers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import com.hubspot.baragon.BaragonDataModule;
import com.hubspot.baragon.data.BaragonAgentResponseDatastore;
import com.hubspot.baragon.data.BaragonLoadBalancerDatastore;
import com.hubspot.baragon.data.BaragonStateDatastore;
import com.hubspot.baragon.models.AgentRequestType;
import com.hubspot.baragon.models.AgentRequestsStatus;
import com.hubspot.baragon.models.AgentResponse;
import com.hubspot.baragon.models.AgentResponseId;
import com.hubspot.baragon.models.BaragonAgentMetadata;
import com.hubspot.baragon.models.BaragonGroup;
import com.hubspot.baragon.models.BaragonRequest;
import com.hubspot.baragon.models.BaragonService;
import com.hubspot.baragon.models.RequestAction;
import com.hubspot.baragon.service.BaragonServiceModule;
import com.hubspot.baragon.service.config.BaragonConfiguration;
import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
import com.ning.http.client.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class AgentManager {
private static final Logger LOG = LoggerFactory.getLogger(AgentManager.class);
private final BaragonLoadBalancerDatastore loadBalancerDatastore;
private final BaragonStateDatastore stateDatastore;
private final BaragonAgentResponseDatastore agentResponseDatastore;
private final AsyncHttpClient asyncHttpClient;
private final String baragonAgentRequestUriFormat;
private final Integer baragonAgentMaxAttempts;
private final Optional baragonAuthKey;
private final Long baragonAgentRequestTimeout;
private final BaragonConfiguration configuration;
@Inject
public AgentManager(BaragonLoadBalancerDatastore loadBalancerDatastore,
BaragonStateDatastore stateDatastore,
BaragonAgentResponseDatastore agentResponseDatastore,
BaragonConfiguration configuration,
@Named(BaragonServiceModule.BARAGON_SERVICE_HTTP_CLIENT) AsyncHttpClient asyncHttpClient,
@Named(BaragonDataModule.BARAGON_AGENT_REQUEST_URI_FORMAT) String baragonAgentRequestUriFormat,
@Named(BaragonDataModule.BARAGON_AGENT_MAX_ATTEMPTS) Integer baragonAgentMaxAttempts,
@Named(BaragonDataModule.BARAGON_AUTH_KEY) Optional baragonAuthKey,
@Named(BaragonDataModule.BARAGON_AGENT_REQUEST_TIMEOUT_MS) Long baragonAgentRequestTimeout) {
this.loadBalancerDatastore = loadBalancerDatastore;
this.stateDatastore = stateDatastore;
this.agentResponseDatastore = agentResponseDatastore;
this.configuration = configuration;
this.asyncHttpClient = asyncHttpClient;
this.baragonAgentRequestUriFormat = baragonAgentRequestUriFormat;
this.baragonAgentMaxAttempts = baragonAgentMaxAttempts;
this.baragonAuthKey = baragonAuthKey;
this.baragonAgentRequestTimeout = baragonAgentRequestTimeout;
}
private AsyncHttpClient.BoundRequestBuilder buildAgentRequest(String url, AgentRequestType requestType) {
final BoundRequestBuilder builder;
switch (requestType) {
case APPLY:
builder = asyncHttpClient.preparePost(url);
break;
case REVERT:
case CANCEL:
builder = asyncHttpClient.prepareDelete(url);
break;
default:
throw new RuntimeException("Don't know how to send requests for " + requestType);
}
if (baragonAuthKey.isPresent()) {
builder.addQueryParameter("authkey", baragonAuthKey.get());
}
return builder;
}
public void sendRequests(final BaragonRequest request, final AgentRequestType requestType) {
final Optional maybeOriginalService = stateDatastore.getService(request.getLoadBalancerService().getServiceId());
final Set loadBalancerGroupsToUpdate = Sets.newHashSet(request.getLoadBalancerService().getLoadBalancerGroups());
if (maybeOriginalService.isPresent()) {
loadBalancerGroupsToUpdate.addAll(maybeOriginalService.get().getLoadBalancerGroups());
}
final String requestId = request.getLoadBalancerRequestId();
for (final BaragonAgentMetadata agentMetadata : getAgents(loadBalancerGroupsToUpdate)) {
final String baseUrl = agentMetadata.getBaseAgentUri();
// wait until pending request has completed.
Optional maybePendingRequest = agentResponseDatastore.getPendingRequest(requestId, baseUrl);
if (maybePendingRequest.isPresent() && !((System.currentTimeMillis() - maybePendingRequest.get()) > baragonAgentRequestTimeout)) {
LOG.info(String.format("Request has been processing for %s ms", (System.currentTimeMillis() - maybePendingRequest.get())));
continue;
}
final Optional maybeLastResponseId = agentResponseDatastore.getLastAgentResponseId(requestId, requestType, baseUrl);
// don't retry request if we've hit the max attempts, or the request was successful
if (maybeLastResponseId.isPresent() && (maybeLastResponseId.get().getAttempt() > baragonAgentMaxAttempts || maybeLastResponseId.get().isSuccess())) {
continue;
}
agentResponseDatastore.setPendingRequestStatus(requestId, baseUrl, true);
final String url = String.format(baragonAgentRequestUriFormat, baseUrl, requestId);
try {
buildAgentRequest(url, requestType).execute(new AsyncCompletionHandler() {
@Override
public Void onCompleted(Response response) throws Exception {
LOG.info(String.format("Got HTTP %d from %s for %s", response.getStatusCode(), baseUrl, requestId));
final Optional content = Strings.isNullOrEmpty(response.getResponseBody()) ? Optional.absent() : Optional.of(response.getResponseBody());
agentResponseDatastore.addAgentResponse(requestId, requestType, baseUrl, url, Optional.of(response.getStatusCode()), content, Optional.absent());
agentResponseDatastore.setPendingRequestStatus(requestId, baseUrl, false);
return null;
}
@Override
public void onThrowable(Throwable t) {
LOG.info(String.format("Got exception %s when hitting %s for %s", t, baseUrl, requestId));
agentResponseDatastore.addAgentResponse(requestId, requestType, baseUrl, url, Optional.absent(), Optional.absent(), Optional.of(t.getMessage()));
agentResponseDatastore.setPendingRequestStatus(requestId, baseUrl, false);
}
});
} catch (Exception e) {
LOG.info(String.format("Got exception %s when hitting %s for %s", e, baseUrl, requestId));
agentResponseDatastore.addAgentResponse(requestId, requestType, baseUrl, url, Optional.absent(), Optional.absent(), Optional.of(e.getMessage()));
agentResponseDatastore.setPendingRequestStatus(requestId, baseUrl, false);
}
}
}
public AgentRequestsStatus getRequestsStatus(BaragonRequest request, AgentRequestType requestType) {
boolean success = true;
RequestAction action = request.getAction().or(RequestAction.UPDATE);
List missingTemplateExceptions = new ArrayList<>();
for (BaragonAgentMetadata agentMetadata : getAgents(request.getLoadBalancerService().getLoadBalancerGroups())) {
final String baseUrl = agentMetadata.getBaseAgentUri();
Optional maybePendingRequestTime = agentResponseDatastore.getPendingRequest(request.getLoadBalancerRequestId(), baseUrl);
if (maybePendingRequestTime.isPresent()) {
if ((System.currentTimeMillis() - maybePendingRequestTime.get()) > baragonAgentRequestTimeout) {
LOG.info(String.format("Request %s reached maximum pending request time", request.getLoadBalancerRequestId()));
agentResponseDatastore.setPendingRequestStatus(request.getLoadBalancerRequestId(), baseUrl, false);
return AgentRequestsStatus.FAILURE;
} else {
return AgentRequestsStatus.WAITING;
}
}
final Optional maybeAgentResponseId = agentResponseDatastore.getLastAgentResponseId(request.getLoadBalancerRequestId(), requestType, baseUrl);
if (!maybeAgentResponseId.isPresent()) {
return AgentRequestsStatus.RETRY;
}
Optional maybeLastResponse = agentResponseDatastore.getAgentResponse(request.getLoadBalancerRequestId(), requestType, maybeAgentResponseId.get(), baseUrl);
boolean missingTemplate = hasMissingTemplate(maybeLastResponse);
missingTemplateExceptions.add(missingTemplate);
if (!missingTemplate) {
final AgentResponseId agentResponseId = maybeAgentResponseId.get();
if ((agentResponseId.getAttempt() < baragonAgentMaxAttempts - 1) && !agentResponseId.isSuccess()) {
return AgentRequestsStatus.RETRY;
} else {
success = success && agentResponseId.isSuccess();
}
}
}
if (!missingTemplateExceptions.isEmpty() && allTrue(missingTemplateExceptions)) {
return AgentRequestsStatus.INVALID_REQUEST_NOOP;
} else if (success) {
return AgentRequestsStatus.SUCCESS;
} else {
return action.equals(RequestAction.RELOAD) ? AgentRequestsStatus.INVALID_REQUEST_NOOP : AgentRequestsStatus.FAILURE;
}
}
public Map> getAgentResponses(String requestId) {
return agentResponseDatastore.getLastResponses(requestId);
}
public Collection getAgents(Set loadBalancerGroups) {
return loadBalancerDatastore.getAgentMetadata(loadBalancerGroups);
}
public boolean invalidAgentCount(String loadBalancerGroup) {
int agentCount = loadBalancerDatastore.getAgentMetadata(loadBalancerGroup).size();
int targetCount = loadBalancerDatastore.getTargetCount(loadBalancerGroup).or(configuration.getDefaultTargetAgentCount());
return (agentCount == 0 || (configuration.isEnforceTargetAgentCount() && agentCount < targetCount));
}
public boolean hasMissingTemplate(Optional maybeLastResponse) {
return maybeLastResponse.isPresent() && maybeLastResponse.get().getContent().isPresent() && maybeLastResponse.get().getContent().get().contains("MissingTemplateException");
}
public boolean allTrue(List array) {
for (boolean b : array) {
if (!b) {
return false;
}
}
return true;
}
public Set getAllDomainsForGroup(String group) {
Optional maybeGroup = loadBalancerDatastore.getLoadBalancerGroup(group);
Set domains = new HashSet<>();
if (maybeGroup.isPresent()) {
domains.addAll(maybeGroup.get().getDomains());
if (maybeGroup.get().getDefaultDomain().isPresent()) {
domains.add(maybeGroup.get().getDefaultDomain().get());
}
}
return domains;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy