
com.hubspot.singularity.data.AbstractMachineManager Maven / Gradle / Ivy
package com.hubspot.singularity.data;
import java.util.List;
import java.util.Map;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.utils.ZKPaths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.MetricRegistry;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hubspot.singularity.MachineState;
import com.hubspot.singularity.SingularityCreateResult;
import com.hubspot.singularity.SingularityDeleteResult;
import com.hubspot.singularity.SingularityMachineAbstraction;
import com.hubspot.singularity.SingularityMachineStateHistoryUpdate;
import com.hubspot.singularity.config.SingularityConfiguration;
import com.hubspot.singularity.data.transcoders.Transcoder;
import com.hubspot.singularity.expiring.SingularityExpiringMachineState;
public abstract class AbstractMachineManager> extends CuratorAsyncManager {
private static final Logger LOG = LoggerFactory.getLogger(AbstractMachineManager.class);
private static final String HISTORY_PATH = "history";
private static final String EXPIRING_PATH = "expiring";
private final Transcoder transcoder;
private final Transcoder historyTranscoder;
private final Transcoder expiringMachineStateTranscoder;
public AbstractMachineManager(CuratorFramework curator, SingularityConfiguration configuration, MetricRegistry metricRegistry, Transcoder transcoder,
Transcoder historyTranscoder, Transcoder expiringMachineStateTranscoder) {
super(curator, configuration, metricRegistry);
this.transcoder = transcoder;
this.historyTranscoder = historyTranscoder;
this.expiringMachineStateTranscoder = expiringMachineStateTranscoder;
}
protected abstract String getRoot();
private String getHistoryPath(String objectId) {
return ZKPaths.makePath(getObjectPath(objectId), HISTORY_PATH);
}
public List getHistory(String objectId) {
return getAsyncChildren(getHistoryPath(objectId), historyTranscoder);
}
public List getObjects() {
return getObjects(getRoot());
}
public int getNumObjectsAtState(MachineState state) {
return getObjectsFiltered(state).size();
}
public Map getObjectsByIdForState(MachineState state) {
List filteredObjects = getObjectsFiltered(state);
Map filteredObjectIds = Maps.newHashMapWithExpectedSize(filteredObjects.size());
for (T filteredObject : filteredObjects) {
filteredObjectIds.put(filteredObject.getId(), filteredObject);
}
return filteredObjectIds;
}
public List getObjectsFiltered(MachineState state) {
return getObjectsFiltered(Optional.of(state));
}
public List getObjectsFiltered(Optional state) {
List objects = getObjects();
if (!state.isPresent()) {
return objects;
}
return getObjectsFiltered(objects, state.get());
}
private List getObjectsFiltered(List objects, MachineState state) {
List filtered = Lists.newArrayListWithCapacity(objects.size());
for (T object : objects) {
if (object.getCurrentState().getState() == state) {
filtered.add(object);
}
}
return filtered;
}
private String getObjectPath(String objectId) {
return ZKPaths.makePath(getRoot(), objectId);
}
public Optional getObject(String objectId) {
return getData(getObjectPath(objectId), transcoder);
}
protected List getObjects(String root) {
return getAsyncChildren(root, transcoder);
}
public SingularityDeleteResult removed(String objectId) {
return delete(getObjectPath(objectId));
}
public enum StateChangeResult {
FAILURE_NOT_FOUND, FAILURE_ALREADY_AT_STATE, FAILURE_ILLEGAL_TRANSITION, SUCCESS;
}
public StateChangeResult changeState(String objectId, MachineState newState, Optional message, Optional user) {
Optional maybeObject = getObject(objectId);
if (!maybeObject.isPresent()) {
return StateChangeResult.FAILURE_NOT_FOUND;
}
final T object = maybeObject.get();
return changeState(object, newState, message, user);
}
public StateChangeResult changeState(T object, MachineState newState, Optional message, Optional user) {
Optional maybeInvalidStateChange = getInvalidStateChangeResult(object.getCurrentState().getState(), newState, false);
if (maybeInvalidStateChange.isPresent()) {
return maybeInvalidStateChange.get();
}
clearExpiringStateChangeIfInvalid(object, newState);
SingularityMachineStateHistoryUpdate newStateUpdate = new SingularityMachineStateHistoryUpdate(object.getId(), newState, System.currentTimeMillis(), user, message);
LOG.debug("{} changing state from {} to {} by {}", object.getId(), object.getCurrentState().getState(), newState, user);
saveObject(object.changeState(newStateUpdate));
return StateChangeResult.SUCCESS;
}
private void clearExpiringStateChangeIfInvalid(T object, MachineState newState) {
Optional maybeExpiring = getExpiringObject(object.getId());
if (!maybeExpiring.isPresent()) {
return;
}
MachineState targetExpiringState = maybeExpiring.get().getRevertToState();
Optional maybeInvalidStateChange = getInvalidStateChangeResult(newState, targetExpiringState, true);
if (maybeInvalidStateChange.isPresent()) {
LOG.info("Cannot complete expiring state transition from {} to {}, removing expiring action for {}", newState, targetExpiringState, object.getId());
deleteExpiringObject(object.getId());
}
}
private Optional getInvalidStateChangeResult(MachineState currentState, MachineState newState, boolean expiringAction) {
if (currentState == newState) {
return Optional.of(StateChangeResult.FAILURE_ALREADY_AT_STATE);
}
if (newState == MachineState.STARTING_DECOMMISSION && currentState.isDecommissioning()) {
return Optional.of(StateChangeResult.FAILURE_ILLEGAL_TRANSITION);
}
// can't jump from FROZEN or ACTIVE to DECOMMISSIONING or DECOMMISSIONED
if (((newState == MachineState.DECOMMISSIONING) || (newState == MachineState.DECOMMISSIONED)) && (currentState == MachineState.FROZEN || currentState == MachineState.ACTIVE)) {
return Optional.of(StateChangeResult.FAILURE_ILLEGAL_TRANSITION);
}
// can't jump from a decommissioning state to FROZEN
if ((newState == MachineState.FROZEN) && currentState.isDecommissioning()) {
return Optional.of(StateChangeResult.FAILURE_ILLEGAL_TRANSITION);
}
// User/Expiring can't jump from inactive to active state
if (currentState.isInactive() && expiringAction) {
return Optional.of(StateChangeResult.FAILURE_ILLEGAL_TRANSITION);
}
return Optional.absent();
}
private String getHistoryUpdatePath(SingularityMachineStateHistoryUpdate historyUpdate) {
final String historyChildPath = String.format("%s-%s", historyUpdate.getState().name(), historyUpdate.getTimestamp());
return ZKPaths.makePath(getHistoryPath(historyUpdate.getObjectId()), historyChildPath);
}
private SingularityCreateResult saveHistoryUpdate(SingularityMachineStateHistoryUpdate historyUpdate) {
return create(getHistoryUpdatePath(historyUpdate), historyUpdate, historyTranscoder);
}
public SingularityDeleteResult deleteObject(String objectId) {
return delete(getObjectPath(objectId));
}
public SingularityCreateResult saveObject(T object) {
saveHistoryUpdate(object.getCurrentState());
return save(getObjectPath(object.getId()), object, transcoder);
}
private String getExpiringPath(String machineId) {
return ZKPaths.makePath(getRoot(), EXPIRING_PATH, machineId);
}
public List getExpiringObjects() {
return getAsyncChildren(ZKPaths.makePath(getRoot(), EXPIRING_PATH), expiringMachineStateTranscoder);
}
public Optional getExpiringObject(String machineId) {
return getData(getExpiringPath(machineId), expiringMachineStateTranscoder);
}
public SingularityCreateResult saveExpiringObject(SingularityExpiringMachineState expiringMachineState, String machineId) {
return save(getExpiringPath(machineId), expiringMachineState, expiringMachineStateTranscoder);
}
public SingularityDeleteResult deleteExpiringObject(String machineId) {
return delete(getExpiringPath(machineId));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy