All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hubspot.baragon.service.managers.RequestManager Maven / Gradle / Ivy

There is a newer version: 0.6.2
Show newest version
package com.hubspot.baragon.service.managers;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.hubspot.baragon.data.BaragonAgentResponseDatastore;
import com.hubspot.baragon.data.BaragonLoadBalancerDatastore;
import com.hubspot.baragon.data.BaragonRequestDatastore;
import com.hubspot.baragon.data.BaragonResponseHistoryDatastore;
import com.hubspot.baragon.data.BaragonStateDatastore;
import com.hubspot.baragon.exceptions.InvalidRequestActionException;
import com.hubspot.baragon.exceptions.InvalidUpstreamsException;
import com.hubspot.baragon.exceptions.RequestAlreadyEnqueuedException;
import com.hubspot.baragon.models.BaragonGroup;
import com.hubspot.baragon.models.BaragonRequest;
import com.hubspot.baragon.models.BaragonResponse;
import com.hubspot.baragon.models.BaragonService;
import com.hubspot.baragon.models.InternalRequestStates;
import com.hubspot.baragon.models.InternalStatesMap;
import com.hubspot.baragon.models.QueuedRequestId;
import com.hubspot.baragon.models.RequestAction;
import com.hubspot.baragon.service.config.BaragonConfiguration;

@Singleton
public class RequestManager {
  private static final Logger LOG = LoggerFactory.getLogger(RequestManager.class);
  private final BaragonRequestDatastore requestDatastore;
  private final BaragonLoadBalancerDatastore loadBalancerDatastore;
  private final BaragonStateDatastore stateDatastore;
  private final BaragonAgentResponseDatastore agentResponseDatastore;
  private final BaragonResponseHistoryDatastore responseHistoryDatastore;
  private final BaragonConfiguration configuration;

  @Inject
  public RequestManager(BaragonRequestDatastore requestDatastore,
                        BaragonLoadBalancerDatastore loadBalancerDatastore,
                        BaragonStateDatastore stateDatastore,
                        BaragonAgentResponseDatastore agentResponseDatastore,
                        BaragonResponseHistoryDatastore responseHistoryDatastore,
                        BaragonConfiguration configuration) {
    this.requestDatastore = requestDatastore;
    this.loadBalancerDatastore = loadBalancerDatastore;
    this.stateDatastore = stateDatastore;
    this.agentResponseDatastore = agentResponseDatastore;
    this.responseHistoryDatastore = responseHistoryDatastore;
    this.configuration = configuration;
  }

  public Optional getRequest(String requestId) {
    return requestDatastore.getRequest(requestId);
  }

  public Optional getRequestState(String requestId) {
    return requestDatastore.getRequestState(requestId);
  }

  public void setRequestState(String requestId, InternalRequestStates state) {
    requestDatastore.setRequestState(requestId, state);
  }

  public void setRequestMessage(String requestId, String message) {
    requestDatastore.setRequestMessage(requestId, message);
  }

  public List getQueuedRequestIds() {
    return requestDatastore.getQueuedRequestIds();
  }

  public void removeQueuedRequest(QueuedRequestId queuedRequestId) {
    requestDatastore.removeQueuedRequest(queuedRequestId);
  }

  public List getResponsesForService(String serviceId) {
    List responses = new ArrayList<>();
    for (String requestId : requestDatastore.getAllRequestIds()) {
      Optional maybeRequest = requestDatastore.getRequest(requestId);
      if (maybeRequest.isPresent() && maybeRequest.get().getLoadBalancerService().getServiceId().equals(serviceId)) {
        Optional maybeStatus = requestDatastore.getRequestState(requestId);
        if (maybeStatus.isPresent()) {
          responses.add(new BaragonResponse(requestId, InternalStatesMap.getRequestState(maybeStatus.get()), requestDatastore.getRequestMessage(requestId), Optional.of(agentResponseDatastore.getLastResponses(requestId)), maybeRequest));
        }
      }
    }
    responses.addAll(responseHistoryDatastore.getResponsesForService(serviceId, configuration.getHistoryConfiguration().getMaxResponsesToFetch()));
    return responses;
  }

  public Optional getResponse(String requestId) {
    Optional maybeActiveRequestResponse = getResponseFromActiveRequests(requestId);
    if (maybeActiveRequestResponse.isPresent()) {
      return maybeActiveRequestResponse;
    } else {
      Optional maybeServiceId = responseHistoryDatastore.getServiceIdForRequestId(requestId);
      if (maybeServiceId.isPresent()) {
        return responseHistoryDatastore.getResponse(maybeServiceId.get(), requestId);
      } else {
        return Optional.absent();
      }
    }
  }

  public Optional getResponse(String serviceId, String requestId) {
    Optional maybeActiveRequestResponse = getResponseFromActiveRequests(requestId);
    if (maybeActiveRequestResponse.isPresent()) {
      return maybeActiveRequestResponse;
    } else {
      return responseHistoryDatastore.getResponse(serviceId, requestId);
    }
  }

  private Optional getResponseFromActiveRequests(String requestId) {
    if (requestDatastore.activeRequestExists(requestId)) {
      final Optional maybeStatus = requestDatastore.getRequestState(requestId);

      if (!maybeStatus.isPresent()) {
        return Optional.absent();
      }

      final Optional maybeRequest = requestDatastore.getRequest(requestId);
      if (!maybeRequest.isPresent()) {
        return Optional.absent();
      }

      return Optional.of(new BaragonResponse(requestId, InternalStatesMap.getRequestState(maybeStatus.get()), requestDatastore.getRequestMessage(requestId), Optional.of(agentResponseDatastore.getLastResponses(requestId)), maybeRequest));
    } else {
      return Optional.absent();
    }
  }

  public Map getBasePathConflicts(BaragonRequest request) {
    final BaragonService service = request.getLoadBalancerService();
    final Map loadBalancerServiceIds = Maps.newHashMap();

    for (String loadBalancerGroup : service.getLoadBalancerGroups()) {
      Optional maybeGroup = loadBalancerDatastore.getLoadBalancerGroup(loadBalancerGroup);
      for (String path : service.getAllPaths()) {
        final Optional maybeServiceIdForPath = loadBalancerDatastore.getBasePathServiceId(loadBalancerGroup, path);
        if (maybeServiceIdForPath.isPresent() && !maybeServiceIdForPath.get().equals(service.getServiceId())) {
          if (!request.getReplaceServiceId().isPresent() || (request.getReplaceServiceId().isPresent() && !request.getReplaceServiceId().get().equals(maybeServiceIdForPath.get()))) {
            loadBalancerServiceIds.put(loadBalancerGroup, maybeServiceIdForPath.get());
            continue;
          }
        }
        if (!path.startsWith("/")) {
          if (maybeGroup.isPresent() && maybeGroup.get().getDefaultDomain().isPresent() && path.startsWith(maybeGroup.get().getDefaultDomain().get())) {
            Optional maybeServiceForDefaultDomainPath = loadBalancerDatastore.getBasePathServiceId(loadBalancerGroup, path.replace(maybeGroup.get().getDefaultDomain().get(), ""));
            if (maybeServiceForDefaultDomainPath.isPresent() && !maybeServiceForDefaultDomainPath.get().equals(service.getServiceId())) {
              if (!request.getReplaceServiceId().isPresent() || (request.getReplaceServiceId().isPresent() && !request.getReplaceServiceId().get().equals(maybeServiceForDefaultDomainPath.get()))) {
                loadBalancerServiceIds.put(loadBalancerGroup, maybeServiceForDefaultDomainPath.get());
              }
            }
          }
        }
      }
    }

    return loadBalancerServiceIds;
  }

  public void revertBasePath(BaragonRequest request) {
    Optional maybeOriginalService = Optional.absent();
    if (request.getReplaceServiceId().isPresent()) {
      maybeOriginalService = stateDatastore.getService(request.getReplaceServiceId().get());
    }
    if (!maybeOriginalService.isPresent()) {
      maybeOriginalService = stateDatastore.getService(request.getLoadBalancerService().getServiceId());
    }
    // if the request is not in the state datastore (ie. no previous request) clear the base path lock
    if (!maybeOriginalService.isPresent()) {
      for (String loadBalancerGroup : request.getLoadBalancerService().getLoadBalancerGroups()) {
        for (String path : request.getLoadBalancerService().getAllPaths()) {
          loadBalancerDatastore.clearBasePath(loadBalancerGroup, path);
        }
      }
    }

    // if we changed the base path, revert it to the old one
    if (maybeOriginalService.isPresent() && request.getReplaceServiceId().isPresent() && maybeOriginalService.get().getServiceId().equals(request.getReplaceServiceId().get())) {
      lockBasePaths(request.getLoadBalancerService().getLoadBalancerGroups(), request.getLoadBalancerService().getAllPaths(), maybeOriginalService.get().getServiceId());
    }
  }

  public Set getMissingLoadBalancerGroups(BaragonRequest request) {
    final Set groups = new HashSet<>(request.getLoadBalancerService().getLoadBalancerGroups());

    return Sets.difference(groups, loadBalancerDatastore.getLoadBalancerGroupNames());
  }

  public void lockBasePaths(BaragonRequest request) {
    for (String loadBalancerGroup : request.getLoadBalancerService().getLoadBalancerGroups()) {
      for (String path : request.getLoadBalancerService().getAllPaths()) {
        loadBalancerDatastore.setBasePathServiceId(loadBalancerGroup, path, request.getLoadBalancerService().getServiceId());
      }
    }
  }

  public void lockBasePaths(Set loadBalancerGroups, List paths, String serviceId) {
    for (String loadBalancerGroup : loadBalancerGroups) {
      for (String path : paths) {
        loadBalancerDatastore.setBasePathServiceId(loadBalancerGroup, path, serviceId);
      }
    }
  }

  public BaragonResponse enqueueRequest(BaragonRequest request) throws RequestAlreadyEnqueuedException, InvalidRequestActionException, InvalidUpstreamsException {
    final Optional maybePreexistingResponse = getResponse(request.getLoadBalancerService().getServiceId(), request.getLoadBalancerRequestId());

    if (maybePreexistingResponse.isPresent()) {
      Optional maybePreexistingRequest = requestDatastore.getRequest(request.getLoadBalancerRequestId());
      if (maybePreexistingRequest.isPresent() && !maybePreexistingRequest.get().equals(request)) {
        throw new RequestAlreadyEnqueuedException(request.getLoadBalancerRequestId(), maybePreexistingResponse.get(), String.format("Request %s is already enqueued with different parameters", request.getLoadBalancerRequestId()));
      } else {
        return maybePreexistingResponse.get();
      }
    }

    if (request.isNoReload() && request.getAction().isPresent() && request.getAction().get().equals(RequestAction.RELOAD)) {
      throw new InvalidRequestActionException("You can not specify 'noReload' on a request with action 'RELOAD'");
    }

    if (!request.getReplaceUpstreams().isEmpty() && (!request.getAddUpstreams().isEmpty() || !request.getRemoveUpstreams().isEmpty())) {
      throw new InvalidUpstreamsException("If overrideUpstreams is specified, addUpstreams and removeUpstreams mustbe empty");
    }

    if (request.getAction().isPresent() && request.getAction().equals(Optional.of(RequestAction.REVERT))) {
      throw new InvalidRequestActionException("The REVERT action may only be used internally by Baragon, you may specify UPDATE, DELETE, RELOAD, or leave the action blank(UPDATE)");
    }

    final QueuedRequestId queuedRequestId = requestDatastore.enqueueRequest(request, InternalRequestStates.PENDING);

    requestDatastore.setRequestMessage(request.getLoadBalancerRequestId(), String.format("Queued as %s", queuedRequestId));

    return getResponse(request.getLoadBalancerService().getServiceId(), request.getLoadBalancerRequestId()).get();
  }

  public Optional cancelRequest(String requestId) {
    final Optional maybeState = getRequestState(requestId);

    if (!maybeState.isPresent() || !InternalStatesMap.isCancelable(maybeState.get())) {
      return maybeState;
    }

    requestDatastore.setRequestState(requestId, InternalRequestStates.CANCELLED_SEND_REVERT_REQUESTS);

    return Optional.of(InternalRequestStates.CANCELLED_SEND_REVERT_REQUESTS);
  }

  public void saveResponseToHistory(BaragonRequest request, InternalRequestStates state) {
    BaragonResponse response = new BaragonResponse(request.getLoadBalancerRequestId(), InternalStatesMap.getRequestState(state), requestDatastore.getRequestMessage(request.getLoadBalancerRequestId()), Optional.of(agentResponseDatastore.getLastResponses(request.getLoadBalancerRequestId())), Optional.of(request));
    responseHistoryDatastore.addResponse(request.getLoadBalancerService().getServiceId(), request.getLoadBalancerRequestId(), response);
  }

  public void deleteRequest(String requestId) {
    requestDatastore.deleteRequest(requestId);
  }

  public synchronized void commitRequest(BaragonRequest request) throws Exception {
    RequestAction action = request.getAction().or(RequestAction.UPDATE);
    Optional maybeOriginalService = getOriginalService(request);

    switch(action) {
      case UPDATE:
      case REVERT:
        updateStateDatastore(request);
        clearChangedBasePaths(request, maybeOriginalService);
        clearBasePathsFromUnusedLbs(request, maybeOriginalService);
        removeOldService(request, maybeOriginalService);
        clearBasePathsWithNoUpstreams(request);
        break;
      case DELETE:
        clearChangedBasePaths(request, maybeOriginalService);
        clearBasePathsFromUnusedLbs(request, maybeOriginalService);
        deleteRemovedServices(request);
        clearBasePathsWithNoUpstreams(request);
        break;
      default:
        LOG.debug(String.format("No updates to commit for request action %s", action));
        break;
    }
    updateLastRequestForGroups(request);
  }

  private void updateLastRequestForGroups(BaragonRequest request) {
    for (String loadBalancerGroup : request.getLoadBalancerService().getLoadBalancerGroups()) {
      loadBalancerDatastore.setLastRequestId(loadBalancerGroup, request.getLoadBalancerRequestId());
    }
  }

  private Optional getOriginalService(BaragonRequest request) {
    Optional maybeOriginalService = Optional.absent();
    if (request.getReplaceServiceId().isPresent()) {
      maybeOriginalService = stateDatastore.getService(request.getReplaceServiceId().get());
    }
    if (!maybeOriginalService.isPresent()) {
      maybeOriginalService = stateDatastore.getService(request.getLoadBalancerService().getServiceId());
    }
    return maybeOriginalService;
  }

  private void deleteRemovedServices(BaragonRequest request) {
    stateDatastore.removeService(request.getLoadBalancerService().getServiceId());
    if (request.getReplaceServiceId().isPresent() && stateDatastore.getService(request.getReplaceServiceId().get()).isPresent()) {
      stateDatastore.removeService(request.getReplaceServiceId().get());
    }
    stateDatastore.incrementStateVersion();
  }

  private void updateStateDatastore(BaragonRequest request) throws Exception {
    stateDatastore.updateService(request);
    try {
      stateDatastore.incrementStateVersion();
    } catch (Exception e) {
      LOG.error("Error updating state datastore", e);
    }
  }

  private void removeOldService(BaragonRequest request, Optional maybeOriginalService) {
    if (maybeOriginalService.isPresent() && !maybeOriginalService.get().getServiceId().equals(request.getLoadBalancerService().getServiceId())) {
      stateDatastore.removeService(maybeOriginalService.get().getServiceId());
    }
  }

  private void clearBasePathsWithNoUpstreams(BaragonRequest request) {
    try {
      if (stateDatastore.getUpstreams(request.getLoadBalancerService().getServiceId()).isEmpty()) {
        for (String loadBalancerGroup : request.getLoadBalancerService().getLoadBalancerGroups()) {
          for (String path : request.getLoadBalancerService().getAllPaths()) {
            if (loadBalancerDatastore.getBasePathServiceId(loadBalancerGroup, path).or("").equals(request.getLoadBalancerService().getServiceId())) {
              loadBalancerDatastore.clearBasePath(loadBalancerGroup, path);
            }
          }
        }
      }
    } catch (Exception e) {
      LOG.error("Error clearing base path", e);
    }
  }

  private void clearChangedBasePaths(BaragonRequest request, Optional maybeOriginalService) {
    if (maybeOriginalService.isPresent()) {
      try {
        List newPaths = request.getLoadBalancerService().getAllPaths();
        for (String oldPath : maybeOriginalService.get().getAllPaths()) {
          if (!newPaths.contains(oldPath)) {
            for (String loadBalancerGroup : maybeOriginalService.get().getLoadBalancerGroups()) {
              if (loadBalancerDatastore.getBasePathServiceId(loadBalancerGroup, oldPath).or("").equals(maybeOriginalService.get().getServiceId())) {
                loadBalancerDatastore.clearBasePath(loadBalancerGroup, oldPath);
              }
            }
          }
        }
      } catch (Exception e) {
        LOG.error("Error clearing base path", e);
      }
    }
  }

  private void clearBasePathsFromUnusedLbs(BaragonRequest request, Optional maybeOriginalService) {
    if (maybeOriginalService.isPresent()) {
      Set removedLbGroups = maybeOriginalService.get().getLoadBalancerGroups();
      removedLbGroups.removeAll(request.getLoadBalancerService().getLoadBalancerGroups());
      if (!removedLbGroups.isEmpty()) {
        try {
          for (String loadBalancerGroup : removedLbGroups) {
            for (String path : maybeOriginalService.get().getAllPaths()) {
              if (loadBalancerDatastore.getBasePathServiceId(loadBalancerGroup, path).or("").equals(maybeOriginalService.get().getServiceId())) {
                loadBalancerDatastore.clearBasePath(loadBalancerGroup, path);
              }
            }
          }
        } catch (Exception e) {
          LOG.error("Error clearing base path", e);
        }
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy