package com.hubspot.singularity.data.zkmigrations; import java.util.HashMap; import java.util.Map; import org.apache.curator.framework.CuratorFramework; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Optional; import com.google.common.base.Throwables; import com.google.inject.Inject; import com.hubspot.mesos.JavaUtils; import com.hubspot.singularity.RequestState; import com.hubspot.singularity.RequestType; import com.hubspot.singularity.data.RequestManager; import com.hubspot.singularity.data.transcoders.JsonTranscoder; import com.hubspot.singularity.data.transcoders.Transcoder; public class SingularityRequestTypeMigration extends ZkDataMigration { private static final Logger LOG = LoggerFactory.getLogger(SingularityRequestTypeMigration.class); private final RequestManager requestManager; private final CuratorFramework curator; private final Transcoder<OldSingularityRequestWithState> oldSingularityRequestTranscoder; @Inject public SingularityRequestTypeMigration(ObjectMapper objectMapper, CuratorFramework curator, RequestManager requestManager) { super(9); this.curator = curator; this.requestManager = requestManager; this.oldSingularityRequestTranscoder = new JsonTranscoder<>(objectMapper, OldSingularityRequestWithState.class); } @Override public void applyMigration() { LOG.info("Starting migration to ensure all Requests have a value for requestType field"); final long start = System.currentTimeMillis(); int num = 0; for (String requestId : requestManager.getAllRequestIds()) { try { OldSingularityRequestWithState requestWithState = oldSingularityRequestTranscoder.fromBytes(curator.getData().forPath("/requests/all/" + requestId)); if (requestWithState.getRequest().getOriginalRequestType().isPresent()) { LOG.info("Skipping {}, requestType is present ({})", requestId, requestWithState.getRequest().getOriginalRequestType().get()); continue; } LOG.info("Saving request {} with requestType {}", requestId, requestWithState.getRequest().getRequestType()); curator.setData().forPath("/requests/all/" + requestId, oldSingularityRequestTranscoder.toBytes(requestWithState)); num++; } catch (Throwable t) { LOG.error("Failed to read {}", requestId, t); throw Throwables.propagate(t); } } LOG.info("Applied {} in {}", num, JavaUtils.duration(start)); } static class OldSingularityRequest { private final String id; private final Optional<RequestType> originalRequestType; private final RequestType requestType; private final Optional<String> schedule; private final Optional<Boolean> daemon; private final Optional<Boolean> loadBalanced; private final Map<String, Object> unknownFields = new HashMap<>(); @JsonCreator public OldSingularityRequest(@JsonProperty("id") String id, @JsonProperty("requestType") Optional<RequestType> originalRequestType, @JsonProperty("schedule") Optional<String> schedule, @JsonProperty("daemon") Optional<Boolean> daemon, @JsonProperty("loadBalanced") Optional<Boolean> loadBalanced) { this.id = id; this.schedule = schedule; this.daemon = daemon; this.loadBalanced = loadBalanced; this.originalRequestType = originalRequestType == null ? Optional.<RequestType>absent() : originalRequestType; this.requestType = this.originalRequestType.or(RequestType.fromDaemonAndScheduleAndLoadBalanced(schedule, daemon, loadBalanced)); } @JsonAnySetter public void setUnknownField(String name, Object value) { unknownFields.put(name, value); } @JsonAnyGetter public Map<String, Object> getUnknownFields() { return unknownFields; } public String getId() { return id; } public RequestType getRequestType() { return requestType; } public Optional<String> getSchedule() { return schedule; } public Optional<Boolean> getDaemon() { return daemon; } public Optional<Boolean> getLoadBalanced() { return loadBalanced; } @JsonIgnore public Optional<RequestType> getOriginalRequestType() { return originalRequestType; } } static class OldSingularityRequestWithState { private final OldSingularityRequest request; private final RequestState state; private final long timestamp; @JsonCreator public OldSingularityRequestWithState(@JsonProperty("request") OldSingularityRequest request, @JsonProperty("state") RequestState state, @JsonProperty("timestamp") long timestamp) { this.request = request; this.state = state; this.timestamp = timestamp; } public OldSingularityRequest getRequest() { return request; } public RequestState getState() { return state; } public long getTimestamp() { return timestamp; } } }