package com.sequenceiq.cloudbreak.orchestrator;
import java.util.Map;
import java.util.concurrent.Callable;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorCancelledException;
import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorFailedException;
import com.sequenceiq.cloudbreak.orchestrator.exception.CloudbreakOrchestratorTerminateException;
import com.sequenceiq.cloudbreak.orchestrator.state.ExitCriteria;
import com.sequenceiq.cloudbreak.orchestrator.state.ExitCriteriaModel;
public class OrchestratorBootstrapRunner implements Callable<Boolean> {
private static final Logger LOGGER = LoggerFactory.getLogger(OrchestratorBootstrapRunner.class);
private static final int MAX_RETRY_COUNT = 30;
private static final int SLEEP_TIME = 5000;
private static final int MS_IN_SEC = 1000;
private static final int SEC_IN_MIN = 60;
private final OrchestratorBootstrap orchestratorBootstrap;
private final Map<String, String> mdcMap;
private final ExitCriteria exitCriteria;
private final ExitCriteriaModel exitCriteriaModel;
private final int maxRetryCount;
private final int sleepTime;
public OrchestratorBootstrapRunner(OrchestratorBootstrap orchestratorBootstrap, ExitCriteria exitCriteria,
ExitCriteriaModel exitCriteriaModel, Map<String, String> mdcReplica) {
this(orchestratorBootstrap, exitCriteria, exitCriteriaModel, mdcReplica, MAX_RETRY_COUNT, SLEEP_TIME);
}
public OrchestratorBootstrapRunner(OrchestratorBootstrap orchestratorBootstrap, ExitCriteria exitCriteria,
ExitCriteriaModel exitCriteriaModel, Map<String, String> mdcReplica,
int maxRetryCount, int sleepTime) {
this.orchestratorBootstrap = orchestratorBootstrap;
this.mdcMap = mdcReplica;
this.exitCriteria = exitCriteria;
this.exitCriteriaModel = exitCriteriaModel;
this.maxRetryCount = maxRetryCount;
this.sleepTime = sleepTime;
}
@Override
public Boolean call() throws Exception {
if (mdcMap != null) {
MDC.setContextMap(mdcMap);
}
ImmutablePair<Boolean, Exception> result = doCall();
if (!Boolean.TRUE.equals(result.getLeft())) {
String cause = null;
if (result.getRight() != null) {
cause = result.getRight().getMessage();
}
String messageTemplate = result.getLeft() == null
? "Timeout: Orchestrator component failed to finish in %f mins, last message: %s"
: "Failed: Orchestrator component went failed in %f mins, message: %s";
String errorMessage = String.format(messageTemplate, (double) maxRetryCount * SLEEP_TIME / MS_IN_SEC / SEC_IN_MIN, cause);
LOGGER.error(errorMessage);
throw new CloudbreakOrchestratorFailedException(errorMessage);
}
return Boolean.TRUE;
}
private ImmutablePair<Boolean, Exception> doCall() throws CloudbreakOrchestratorCancelledException, InterruptedException {
Boolean success = null;
int retryCount = 1;
Exception actualException = null;
String type = orchestratorBootstrap.getClass().getSimpleName().replace("Bootstrap", "");
long initialStartTime = System.currentTimeMillis();
while (success == null && retryCount <= maxRetryCount) {
if (isExitNeeded()) {
LOGGER.error(exitCriteria.exitMessage());
throw new CloudbreakOrchestratorCancelledException(exitCriteria.exitMessage());
}
long startTime = System.currentTimeMillis();
try {
LOGGER.info("Calling orchestrator bootstrap: {}, additional info: {}", type, orchestratorBootstrap);
orchestratorBootstrap.call();
long elapsedTime = System.currentTimeMillis() - startTime;
long totalElapsedTime = System.currentTimeMillis() - initialStartTime;
success = true;
LOGGER.info("Orchestrator component {} successfully started! Elapsed time: {} ms, Total elapsed time: {} ms, "
+ "additional info: {}", type, elapsedTime, totalElapsedTime, orchestratorBootstrap);
} catch (CloudbreakOrchestratorTerminateException te) {
actualException = te;
long elapsedTime = System.currentTimeMillis() - startTime;
long totalElapsedTime = System.currentTimeMillis() - initialStartTime;
success = false;
LOGGER.info("Failed to execute orchestrator component {}! Elapsed time: {} ms, Total elapsed time: {} ms, "
+ "additional info: {}", type, elapsedTime, totalElapsedTime, orchestratorBootstrap);
} catch (Exception ex) {
actualException = ex;
long elapsedTime = System.currentTimeMillis() - startTime;
long totalElapsedTime = System.currentTimeMillis() - initialStartTime;
LOGGER.warn("Orchestrator component {} failed to start, retrying [{}/{}] Elapsed time: {} ms, "
+ "Total elapsed time: {} ms, Reason: {}, additional info: {}",
type, retryCount, maxRetryCount, elapsedTime, totalElapsedTime, actualException.getMessage(), orchestratorBootstrap);
retryCount++;
if (retryCount <= maxRetryCount) {
Thread.sleep(sleepTime);
}
}
}
return new ImmutablePair<>(success, actualException);
}
private boolean isExitNeeded() {
boolean exitNeeded = false;
if (exitCriteriaModel != null && exitCriteria != null) {
LOGGER.debug("exitCriteriaModel: {}, exitCriteria: {}", exitCriteriaModel, exitCriteria);
exitNeeded = exitCriteria.isExitNeeded(exitCriteriaModel);
}
LOGGER.debug("isExitNeeded: {}", exitNeeded);
return exitNeeded;
}
}