package com.hubspot.singularity.helpers;
import java.util.UUID;
import com.google.common.base.Optional;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.hubspot.singularity.RequestCleanupType;
import com.hubspot.singularity.RequestState;
import com.hubspot.singularity.SingularityCreateResult;
import com.hubspot.singularity.SingularityDeploy;
import com.hubspot.singularity.SingularityPendingRequest;
import com.hubspot.singularity.SingularityPendingRequest.PendingType;
import com.hubspot.singularity.SingularityRequest;
import com.hubspot.singularity.SingularityRequestCleanup;
import com.hubspot.singularity.SingularityRequestDeployState;
import com.hubspot.singularity.SingularityRequestHistory.RequestHistoryType;
import com.hubspot.singularity.api.SingularityBounceRequest;
import com.hubspot.singularity.data.DeployManager;
import com.hubspot.singularity.data.RequestManager;
import com.hubspot.singularity.data.SingularityValidator;
import com.hubspot.singularity.expiring.SingularityExpiringBounce;
import com.hubspot.singularity.smtp.SingularityMailer;
@Singleton
public class RequestHelper {
private final RequestManager requestManager;
private final SingularityMailer mailer;
private final DeployManager deployManager;
private final SingularityValidator validator;
@Inject
public RequestHelper(RequestManager requestManager, SingularityMailer mailer, DeployManager deployManager, SingularityValidator validator) {
this.requestManager = requestManager;
this.mailer = mailer;
this.deployManager = deployManager;
this.validator = validator;
}
public long unpause(SingularityRequest request, Optional<String> user, Optional<String> message, Optional<Boolean> skipHealthchecks) {
mailer.sendRequestUnpausedMail(request, user, message);
Optional<String> maybeDeployId = deployManager.getInUseDeployId(request.getId());
final long now = System.currentTimeMillis();
requestManager.unpause(request, now, user, message);
if (maybeDeployId.isPresent() && !request.isOneOff()) {
requestManager.addToPendingQueue(new SingularityPendingRequest(request.getId(), maybeDeployId.get(), now, user, PendingType.UNPAUSED,
skipHealthchecks, message));
}
return now;
}
private SingularityRequestDeployHolder getDeployHolder(String requestId) {
Optional<SingularityRequestDeployState> requestDeployState = deployManager.getRequestDeployState(requestId);
Optional<SingularityDeploy> activeDeploy = Optional.absent();
Optional<SingularityDeploy> pendingDeploy = Optional.absent();
if (requestDeployState.isPresent()) {
if (requestDeployState.get().getActiveDeploy().isPresent()) {
activeDeploy = deployManager.getDeploy(requestId, requestDeployState.get().getActiveDeploy().get().getDeployId());
}
if (requestDeployState.get().getPendingDeploy().isPresent()) {
pendingDeploy = deployManager.getDeploy(requestId, requestDeployState.get().getPendingDeploy().get().getDeployId());
}
}
return new SingularityRequestDeployHolder(activeDeploy, pendingDeploy);
}
private boolean shouldReschedule(SingularityRequest newRequest, SingularityRequest oldRequest) {
if (newRequest.getInstancesSafe() != oldRequest.getInstancesSafe()) {
return true;
}
if (newRequest.isScheduled() && oldRequest.isScheduled()) {
if (!newRequest.getQuartzScheduleSafe().equals(oldRequest.getQuartzScheduleSafe())) {
return true;
}
}
return false;
}
private void checkReschedule(SingularityRequest newRequest, Optional<SingularityRequest> maybeOldRequest, Optional<String> user, long timestamp, Optional<Boolean> skipHealthchecks, Optional<String> message, Optional<SingularityBounceRequest> maybeBounceRequest) {
if (!maybeOldRequest.isPresent()) {
return;
}
if (shouldReschedule(newRequest, maybeOldRequest.get())) {
Optional<String> maybeDeployId = deployManager.getInUseDeployId(newRequest.getId());
if (maybeDeployId.isPresent()) {
if (maybeBounceRequest.isPresent()) {
Optional<String> actionId = maybeBounceRequest.get().getActionId().or(Optional.of(UUID.randomUUID().toString()));
SingularityCreateResult createResult = requestManager.createCleanupRequest(
new SingularityRequestCleanup(user, maybeBounceRequest.get().getIncremental().or(true) ? RequestCleanupType.INCREMENTAL_BOUNCE : RequestCleanupType.BOUNCE,
System.currentTimeMillis(), Optional.<Boolean> absent(), newRequest.getId(), Optional.of(maybeDeployId.get()), skipHealthchecks, message, actionId, maybeBounceRequest.get().getRunShellCommandBeforeKill()));
if (createResult != SingularityCreateResult.EXISTED) {
requestManager.bounce(newRequest, System.currentTimeMillis(), user, Optional.of("Bouncing due to bounce after scale"));
final SingularityBounceRequest validatedBounceRequest = validator.checkBounceRequest(maybeBounceRequest.get());
requestManager.saveExpiringObject(new SingularityExpiringBounce(newRequest.getId(), maybeDeployId.get(), user, System.currentTimeMillis(), validatedBounceRequest, actionId.get()));
} else {
requestManager.addToPendingQueue(new SingularityPendingRequest(newRequest.getId(), maybeDeployId.get(), timestamp, user, PendingType.UPDATED_REQUEST,
skipHealthchecks, message));
}
} else {
requestManager.addToPendingQueue(new SingularityPendingRequest(newRequest.getId(), maybeDeployId.get(), timestamp, user, PendingType.UPDATED_REQUEST,
skipHealthchecks, message));
}
}
}
}
public void updateRequest(SingularityRequest request, Optional<SingularityRequest> maybeOldRequest, RequestState requestState, Optional<RequestHistoryType> historyType,
Optional<String> user, Optional<Boolean> skipHealthchecks, Optional<String> message, Optional<SingularityBounceRequest> maybeBounceRequest) {
SingularityRequestDeployHolder deployHolder = getDeployHolder(request.getId());
SingularityRequest newRequest = validator.checkSingularityRequest(request, maybeOldRequest, deployHolder.getActiveDeploy(), deployHolder.getPendingDeploy());
final long now = System.currentTimeMillis();
if (requestState == RequestState.FINISHED && maybeOldRequest.isPresent() && shouldReschedule(newRequest, maybeOldRequest.get())) {
requestState = RequestState.ACTIVE;
}
RequestHistoryType historyTypeToSet = null;
if (historyType.isPresent()) {
historyTypeToSet = historyType.get();
} else if (maybeOldRequest.isPresent()) {
historyTypeToSet = RequestHistoryType.UPDATED;
} else {
historyTypeToSet = RequestHistoryType.CREATED;
}
requestManager.save(newRequest, requestState, historyTypeToSet, now, user, message);
checkReschedule(newRequest, maybeOldRequest, user, now, skipHealthchecks, message, maybeBounceRequest);
}
}