package com.hubspot.singularity.data.zkmigrations;
import java.util.Collections;
import java.util.List;
import javax.inject.Singleton;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.utils.ZKPaths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.mesos.Resources;
import com.hubspot.singularity.InvalidSingularityTaskIdException;
import com.hubspot.singularity.SingularityPendingRequest.PendingType;
import com.hubspot.singularity.SingularityPendingTask;
import com.hubspot.singularity.SingularityPendingTaskId;
import com.hubspot.singularity.data.TaskManager;
import com.hubspot.singularity.data.transcoders.StringTranscoder;
@Singleton
public class SingularityPendingTaskIdMigration extends ZkDataMigration {
private static final Logger LOG = LoggerFactory.getLogger(SingularityPendingTaskIdMigration.class);
private final CuratorFramework curator;
private final TaskManager taskManager;
private final String PENDING_TASKS_ROOT = "/tasks/scheduled";
@Inject
public SingularityPendingTaskIdMigration(CuratorFramework curator, TaskManager taskManager) {
super(2);
this.curator = curator;
this.taskManager = taskManager;
}
@Override
public void applyMigration() {
final long start = System.currentTimeMillis();
try {
if (curator.checkExists().forPath(PENDING_TASKS_ROOT) == null) {
return;
}
} catch (Exception e) {
throw Throwables.propagate(e);
}
try {
for (String pendingTaskId : curator.getChildren().forPath(PENDING_TASKS_ROOT)) {
SingularityPendingTaskId newPendingTaskId = createFrom(pendingTaskId, start);
if (!newPendingTaskId.toString().equals(pendingTaskId)) {
LOG.info("Migrating {} to {}", pendingTaskId, newPendingTaskId);
Optional<String> cmdLineArgs = getCmdLineArgs(pendingTaskId);
taskManager.savePendingTask(new SingularityPendingTask(newPendingTaskId, cmdLineArgs.isPresent() ? Optional.of(Collections.singletonList(cmdLineArgs.get())) :
Optional.<List<String>> absent(), Optional.<String> absent(), Optional.<String> absent(), Optional.<Boolean> absent(), Optional.<String> absent(), Optional.<Resources>absent(), Optional.<String>absent()));
curator.delete().forPath(ZKPaths.makePath(PENDING_TASKS_ROOT, pendingTaskId));
}
}
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
private Optional<String> getCmdLineArgs(String pendingTaskId) throws Exception {
byte[] data = curator.getData().forPath(ZKPaths.makePath(PENDING_TASKS_ROOT, pendingTaskId));
if (data != null && data.length > 0) {
return Optional.of(StringTranscoder.INSTANCE.fromBytes(data));
}
return Optional.absent();
}
public SingularityPendingTaskId createFrom(String string, long createdAt) {
if (Character.isDigit(string.charAt(string.length() - 1))) {
LOG.warn("Not migrating {} - it appears to be migrated already", string);
return SingularityPendingTaskId.valueOf(string);
}
String[] splits = null;
try {
splits = JavaUtils.reverseSplit(string, 5, "-");
} catch (IllegalStateException ise) {
throw new InvalidSingularityTaskIdException(String.format("PendingTaskId %s was invalid (%s)", string, ise.getMessage()));
}
try {
final String requestId = splits[0];
final String deployId = splits[1];
final long nextRunAt = Long.parseLong(splits[2]);
final int instanceNo = Integer.parseInt(splits[3]);
final PendingType pendingType = PendingType.valueOf(splits[4]);
return new SingularityPendingTaskId(requestId, deployId, nextRunAt, instanceNo, pendingType, createdAt);
} catch (IllegalArgumentException e) {
throw new InvalidSingularityTaskIdException(String.format("PendingTaskId %s had an invalid parameter (%s)", string, e.getMessage()));
}
}
}