You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by ma...@apache.org on 2014/03/31 19:34:59 UTC

git commit: Revert "Preserving sandbox deleted task history (part 2: state machine)."

Repository: incubator-aurora
Updated Branches:
  refs/heads/master dacefbb69 -> 20a14b90a


Revert "Preserving sandbox deleted task history (part 2: state machine)."

Reason: Needs to support rollback scenario.

This reverts commit 359d0fb4885d100b44f5e5fa68cf69eb562577af.


Project: http://git-wip-us.apache.org/repos/asf/incubator-aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-aurora/commit/20a14b90
Tree: http://git-wip-us.apache.org/repos/asf/incubator-aurora/tree/20a14b90
Diff: http://git-wip-us.apache.org/repos/asf/incubator-aurora/diff/20a14b90

Branch: refs/heads/master
Commit: 20a14b90a989070ebb908adf7106cf05837f672e
Parents: dacefbb
Author: Maxim Khutornenko <ma...@apache.org>
Authored: Mon Mar 31 10:31:58 2014 -0700
Committer: Maxim Khutornenko <ma...@apache.org>
Committed: Mon Mar 31 10:31:58 2014 -0700

----------------------------------------------------------------------
 .../aurora/scheduler/MesosSchedulerImpl.java    |  23 +-
 .../org/apache/aurora/scheduler/base/Jobs.java  |  66 +++---
 .../org/apache/aurora/scheduler/base/Tasks.java |   4 -
 .../aurora/scheduler/http/SchedulerzJob.java    |  19 +-
 .../aurora/scheduler/state/SchedulerCore.java   |   7 +
 .../scheduler/state/SchedulerCoreImpl.java      |   7 +
 .../aurora/scheduler/state/StateManager.java    |   7 +-
 .../scheduler/state/StateManagerImpl.java       |  24 +-
 .../scheduler/state/TaskStateMachine.java       |  72 +++---
 .../apache/aurora/executor/gc_executor.py       |   7 +-
 .../aurora/scheduler/http/schedulerzjob.st      |  11 +-
 .../thrift/org/apache/aurora/gen/api.thrift     |   9 +-
 .../scheduler/MesosSchedulerImplTest.java       |  48 +---
 .../aurora/scheduler/app/SchedulerIT.java       |   2 +-
 .../scheduler/async/HistoryPrunerTest.java      |  11 +-
 .../apache/aurora/scheduler/base/JobsTest.java  |   9 +-
 .../aurora/scheduler/base/TaskTestUtil.java     |  14 +-
 .../apache/aurora/scheduler/base/TasksTest.java |   5 +-
 .../state/BaseSchedulerCoreImplTest.java        |  31 +++
 .../scheduler/state/StateManagerImplTest.java   |  14 +-
 .../scheduler/state/TaskStateMachineTest.java   | 223 +++++--------------
 .../org/apache/aurora/gen/api.thrift.md5        |   2 +-
 22 files changed, 224 insertions(+), 391 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/java/org/apache/aurora/scheduler/MesosSchedulerImpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/MesosSchedulerImpl.java b/src/main/java/org/apache/aurora/scheduler/MesosSchedulerImpl.java
index 1157163..772511f 100644
--- a/src/main/java/org/apache/aurora/scheduler/MesosSchedulerImpl.java
+++ b/src/main/java/org/apache/aurora/scheduler/MesosSchedulerImpl.java
@@ -22,7 +22,6 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.twitter.common.application.Lifecycle;
 import com.twitter.common.inject.TimedInterceptor.Timed;
@@ -30,14 +29,13 @@ import com.twitter.common.stats.Stats;
 
 import org.apache.aurora.GuiceUtils.AllowUnchecked;
 import org.apache.aurora.codec.ThriftBinaryCodec;
-import org.apache.aurora.gen.ScheduleStatus;
 import org.apache.aurora.gen.comm.SchedulerMessage;
 import org.apache.aurora.scheduler.base.Conversions;
 import org.apache.aurora.scheduler.base.SchedulerException;
 import org.apache.aurora.scheduler.events.EventSink;
 import org.apache.aurora.scheduler.events.PubsubEvent.DriverDisconnected;
 import org.apache.aurora.scheduler.events.PubsubEvent.DriverRegistered;
-import org.apache.aurora.scheduler.state.StateManager;
+import org.apache.aurora.scheduler.state.SchedulerCore;
 import org.apache.aurora.scheduler.storage.Storage;
 import org.apache.aurora.scheduler.storage.Storage.MutableStoreProvider;
 import org.apache.aurora.scheduler.storage.Storage.MutateWork;
@@ -70,7 +68,7 @@ class MesosSchedulerImpl implements Scheduler {
   private final List<TaskLauncher> taskLaunchers;
 
   private final Storage storage;
-  private final StateManager stateManager;
+  private final SchedulerCore schedulerCore;
   private final Lifecycle lifecycle;
   private final EventSink eventSink;
   private volatile boolean registered = false;
@@ -78,7 +76,7 @@ class MesosSchedulerImpl implements Scheduler {
   /**
    * Creates a new scheduler.
    *
-   * @param stateManager Scheduler state manager.
+   * @param schedulerCore Core scheduler.
    * @param lifecycle Application lifecycle manager.
    * @param taskLaunchers Task launchers, which will be used in order.  Calls to
    *                      {@link TaskLauncher#willUse(Offer)} and
@@ -89,13 +87,13 @@ class MesosSchedulerImpl implements Scheduler {
   @Inject
   public MesosSchedulerImpl(
       Storage storage,
-      StateManager stateManager,
+      SchedulerCore schedulerCore,
       final Lifecycle lifecycle,
       List<TaskLauncher> taskLaunchers,
       EventSink eventSink) {
 
     this.storage = checkNotNull(storage);
-    this.stateManager = checkNotNull(stateManager);
+    this.schedulerCore = checkNotNull(schedulerCore);
     this.lifecycle = checkNotNull(lifecycle);
     this.taskLaunchers = checkNotNull(taskLaunchers);
     this.eventSink = checkNotNull(eventSink);
@@ -240,13 +238,10 @@ class MesosSchedulerImpl implements Scheduler {
 
       switch (schedulerMsg.getSetField()) {
         case DELETED_TASKS:
-          for (String taskId : schedulerMsg.getDeletedTasks().getTaskIds()) {
-            stateManager.changeState(
-                taskId,
-                Optional.<ScheduleStatus>absent(),
-                ScheduleStatus.SANDBOX_DELETED,
-                Optional.of("Sandbox disk space reclaimed."));
-          }
+          // TODO(William Farner): Refactor this to use a thinner interface here.  As it stands
+          // it is odd that we route the registered() call to schedulerCore via the
+          // registeredListener and call the schedulerCore directly here.
+          schedulerCore.tasksDeleted(schedulerMsg.getDeletedTasks().getTaskIds());
           break;
 
         default:

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/java/org/apache/aurora/scheduler/base/Jobs.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/base/Jobs.java b/src/main/java/org/apache/aurora/scheduler/base/Jobs.java
index 3c41db2..4ac218b 100644
--- a/src/main/java/org/apache/aurora/scheduler/base/Jobs.java
+++ b/src/main/java/org/apache/aurora/scheduler/base/Jobs.java
@@ -16,7 +16,6 @@
 package org.apache.aurora.scheduler.base;
 
 import org.apache.aurora.gen.JobStats;
-import org.apache.aurora.gen.ScheduleStatus;
 import org.apache.aurora.scheduler.storage.entities.IJobStats;
 import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
 
@@ -38,47 +37,38 @@ public final class Jobs {
   public static IJobStats getJobStats(Iterable<IScheduledTask> tasks) {
     JobStats stats = new JobStats();
     for (IScheduledTask task : tasks) {
-      ScheduleStatus status = task.getStatus();
-      if (status == ScheduleStatus.SANDBOX_DELETED) {
-        // SANDBOX_DELETED must be preceded by the real terminal state.
-        updateStats(stats, Tasks.getSecondToLatestEvent(task).getStatus());
-      } else {
-        updateStats(stats, status);
-      }
-    }
-    return IJobStats.build(stats);
-  }
+      switch (task.getStatus()) {
+        case INIT:
+        case PENDING:
+        case THROTTLED:
+          stats.pendingTaskCount++;
+          break;
 
-  private static void updateStats(JobStats stats, ScheduleStatus status) {
-    switch (status) {
-      case INIT:
-      case PENDING:
-      case THROTTLED:
-        stats.pendingTaskCount++;
-        break;
+        case ASSIGNED:
+        case STARTING:
+        case RESTARTING:
+        case RUNNING:
+        case KILLING:
+        case DRAINING:
+        case PREEMPTING:
+          stats.activeTaskCount++;
+          break;
 
-      case ASSIGNED:
-      case STARTING:
-      case RESTARTING:
-      case RUNNING:
-      case KILLING:
-      case DRAINING:
-      case PREEMPTING:
-        stats.activeTaskCount++;
-        break;
+        case KILLED:
+        case FINISHED:
+          stats.finishedTaskCount++;
+          break;
 
-      case KILLED:
-      case FINISHED:
-        stats.finishedTaskCount++;
-        break;
+        case LOST:
+        case FAILED:
+        case UNKNOWN:
+          stats.failedTaskCount++;
+          break;
 
-      case LOST:
-      case FAILED:
-        stats.failedTaskCount++;
-        break;
-
-      default:
-        throw new IllegalArgumentException("Unsupported status: " + status);
+        default:
+          throw new IllegalArgumentException("Unsupported status: " + task.getStatus());
+      }
     }
+    return IJobStats.build(stats);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/java/org/apache/aurora/scheduler/base/Tasks.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/base/Tasks.java b/src/main/java/org/apache/aurora/scheduler/base/Tasks.java
index fae2d23..003d475 100644
--- a/src/main/java/org/apache/aurora/scheduler/base/Tasks.java
+++ b/src/main/java/org/apache/aurora/scheduler/base/Tasks.java
@@ -228,10 +228,6 @@ public final class Tasks {
         .max(tasks);
   }
 
-  public static ITaskEvent getSecondToLatestEvent(IScheduledTask task) {
-    return task.getTaskEvents().get(task.getTaskEvents().size() - 2);
-  }
-
   public static ITaskEvent getLatestEvent(IScheduledTask task) {
     return Iterables.getLast(task.getTaskEvents());
   }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/java/org/apache/aurora/scheduler/http/SchedulerzJob.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/SchedulerzJob.java b/src/main/java/org/apache/aurora/scheduler/http/SchedulerzJob.java
index 1838573..2ccc6f3 100644
--- a/src/main/java/org/apache/aurora/scheduler/http/SchedulerzJob.java
+++ b/src/main/java/org/apache/aurora/scheduler/http/SchedulerzJob.java
@@ -65,7 +65,6 @@ import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
 import org.apache.aurora.scheduler.storage.entities.IServerInfo;
 import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
 import org.apache.aurora.scheduler.storage.entities.ITaskConstraint;
-import org.apache.aurora.scheduler.storage.entities.ITaskEvent;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.twitter.common.base.MorePreconditions.checkNotBlank;
@@ -78,7 +77,6 @@ import static org.apache.aurora.gen.ScheduleStatus.KILLING;
 import static org.apache.aurora.gen.ScheduleStatus.LOST;
 import static org.apache.aurora.gen.ScheduleStatus.PENDING;
 import static org.apache.aurora.gen.ScheduleStatus.RUNNING;
-import static org.apache.aurora.gen.ScheduleStatus.SANDBOX_DELETED;
 import static org.apache.aurora.gen.ScheduleStatus.STARTING;
 
 /**
@@ -100,9 +98,7 @@ public class SchedulerzJob extends JerseyTemplateServlet {
       ImmutableMap.<ScheduleStatus, Set<ScheduleStatus>>builder()
         .put(PENDING, EnumSet.of(PENDING))
         .put(RUNNING, EnumSet.of(ASSIGNED, STARTING, RUNNING, KILLING))
-        // TODO(maxim): SANDBOX_DELETED can be issued for both FINISHED and FAILED states
-        // Add a post-processing filtering to place it into the right bucket.
-        .put(FINISHED, EnumSet.of(KILLED, FINISHED, SANDBOX_DELETED))
+        .put(FINISHED, EnumSet.of(KILLED, FINISHED))
         .put(FAILED, EnumSet.of(LOST, FAILED))
       .build();
 
@@ -151,15 +147,11 @@ public class SchedulerzJob extends JerseyTemplateServlet {
             .put("taskId", task.getTaskId())
             .put("instanceId", task.getInstanceId())
             .put("slaveHost", task.isSetSlaveHost() ? task.getSlaveHost() : "")
+            .put("status", scheduledTask.getStatus())
+            .put("statusTimestamp", Tasks.getLatestEvent(scheduledTask).getTimestamp())
             .put("taskEvents", scheduledTask.getTaskEvents());
 
-          ITaskEvent statusEvent = Tasks.getLatestEvent(scheduledTask);
-
-          if (scheduledTask.getStatus() == SANDBOX_DELETED) {
-            // Avoid displaying SANDBOX_DELETED as completed status.
-            statusEvent = Tasks.getSecondToLatestEvent(scheduledTask);
-            builder.put("hideExecutorUri", true);
-          } else if (scheduledTask.getStatus() == PENDING) {
+          if (scheduledTask.getStatus() == ScheduleStatus.PENDING) {
             String pendingReason;
             Set<Veto> vetoes = nearestFit.getNearestFit(task.getTaskId());
             if (vetoes.isEmpty()) {
@@ -170,9 +162,6 @@ public class SchedulerzJob extends JerseyTemplateServlet {
             builder.put("pendingReason", pendingReason);
           }
 
-          builder.put("status", statusEvent.getStatus());
-          builder.put("statusTimestamp", statusEvent.getTimestamp());
-
           Function<String, String> expander = new Function<String, String>() {
             @Override
             public String apply(String input) {

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/java/org/apache/aurora/scheduler/state/SchedulerCore.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/state/SchedulerCore.java b/src/main/java/org/apache/aurora/scheduler/state/SchedulerCore.java
index 085a1dc..7a13a8b 100644
--- a/src/main/java/org/apache/aurora/scheduler/state/SchedulerCore.java
+++ b/src/main/java/org/apache/aurora/scheduler/state/SchedulerCore.java
@@ -99,4 +99,11 @@ public interface SchedulerCore {
    */
   void restartShards(IJobKey jobKey, Set<Integer> shards, String requestingUser)
       throws ScheduleException;
+
+  /**
+   * Indicates to the scheduler that tasks were deleted on the assigned host.
+   *
+   * @param taskIds IDs of tasks that were deleted.
+   */
+  void tasksDeleted(Set<String> taskIds);
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/java/org/apache/aurora/scheduler/state/SchedulerCoreImpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/state/SchedulerCoreImpl.java b/src/main/java/org/apache/aurora/scheduler/state/SchedulerCoreImpl.java
index 1eaf7b8..3ca4ee5 100644
--- a/src/main/java/org/apache/aurora/scheduler/state/SchedulerCoreImpl.java
+++ b/src/main/java/org/apache/aurora/scheduler/state/SchedulerCoreImpl.java
@@ -118,6 +118,13 @@ class SchedulerCoreImpl implements SchedulerCore {
   }
 
   @Override
+  public synchronized void tasksDeleted(Set<String> taskIds) {
+    for (String taskId : taskIds) {
+      setTaskStatus(taskId, ScheduleStatus.UNKNOWN, Optional.<String>absent());
+    }
+  }
+
+  @Override
   public synchronized void createJob(final SanitizedConfiguration sanitizedConfiguration)
       throws ScheduleException {
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/java/org/apache/aurora/scheduler/state/StateManager.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/state/StateManager.java b/src/main/java/org/apache/aurora/scheduler/state/StateManager.java
index 5c162b9..4249707 100644
--- a/src/main/java/org/apache/aurora/scheduler/state/StateManager.java
+++ b/src/main/java/org/apache/aurora/scheduler/state/StateManager.java
@@ -80,9 +80,10 @@ public interface StateManager {
   void insertPendingTasks(Map<Integer, ITaskConfig> tasks);
 
   /**
-   * Attempts to delete tasks from the task store.
-   * If the task is not currently in a state that is considered safe for deletion,
-   * side-effect actions will be performed to reconcile the state conflict.
+   * Deletes records of tasks from the task store.
+   * This will not perform any state checking or state transitions, but will immediately remove
+   * the tasks from the store.  It will also silently ignore attempts to delete task IDs that do
+   * not exist.
    *
    * @param taskIds IDs of tasks to delete.
    */

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/java/org/apache/aurora/scheduler/state/StateManagerImpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/state/StateManagerImpl.java b/src/main/java/org/apache/aurora/scheduler/state/StateManagerImpl.java
index 74ef64d..0161fac 100644
--- a/src/main/java/org/apache/aurora/scheduler/state/StateManagerImpl.java
+++ b/src/main/java/org/apache/aurora/scheduler/state/StateManagerImpl.java
@@ -137,7 +137,7 @@ public class StateManagerImpl implements StateManager {
           updateTaskAndExternalState(
               Tasks.id(task),
               Optional.of(task),
-              Optional.of(PENDING),
+              PENDING,
               Optional.<String>absent());
         }
       }
@@ -260,11 +260,7 @@ public class StateManagerImpl implements StateManager {
           return false;
         }
 
-        return updateTaskAndExternalState(
-            taskId,
-            task,
-            Optional.of(targetState),
-            transitionMessage);
+        return updateTaskAndExternalState(taskId, task, targetState, transitionMessage);
       }
     });
   }
@@ -305,7 +301,7 @@ public class StateManagerImpl implements StateManager {
       // highly-risky, since it doesn't necessarily represent the value in storage.
       // As a result, it would be easy to accidentally clobber mutations.
       Optional<IScheduledTask> task,
-      final Optional<ScheduleStatus> targetState,
+      final ScheduleStatus targetState,
       final Optional<String> transitionMessage) {
 
     if (task.isPresent()) {
@@ -348,10 +344,10 @@ public class StateManagerImpl implements StateManager {
                 @Override
                 public IScheduledTask apply(IScheduledTask task) {
                   ScheduledTask mutableTask = task.newBuilder();
-                  mutableTask.setStatus(targetState.get());
+                  mutableTask.setStatus(targetState);
                   mutableTask.addToTaskEvents(new TaskEvent()
                       .setTimestamp(clock.nowMillis())
-                      .setStatus(targetState.get())
+                      .setStatus(targetState)
                       .setMessage(transitionMessage.orNull())
                       .setScheduler(LOCAL_HOST_SUPPLIER.get()));
                   return IScheduledTask.build(mutableTask);
@@ -399,7 +395,7 @@ public class StateManagerImpl implements StateManager {
               updateTaskAndExternalState(
                   Tasks.id(newTask),
                   Optional.of(newTask),
-                  Optional.of(newState),
+                  newState,
                   Optional.of(auditMessage));
               break;
 
@@ -441,13 +437,7 @@ public class StateManagerImpl implements StateManager {
     storage.write(new MutateWork.NoResult.Quiet() {
       @Override
       protected void execute(final MutableStoreProvider storeProvider) {
-        for (String taskId : taskIds) {
-          updateTaskAndExternalState(
-              taskId,
-              Optional.<IScheduledTask>absent(),
-              Optional.<ScheduleStatus>absent(),
-              Optional.<String>absent());
-        }
+        eventSink.post(deleteTasks(storeProvider, taskIds));
       }
     });
   }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/java/org/apache/aurora/scheduler/state/TaskStateMachine.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/state/TaskStateMachine.java b/src/main/java/org/apache/aurora/scheduler/state/TaskStateMachine.java
index c0bd287..d2becea 100644
--- a/src/main/java/org/apache/aurora/scheduler/state/TaskStateMachine.java
+++ b/src/main/java/org/apache/aurora/scheduler/state/TaskStateMachine.java
@@ -21,9 +21,7 @@ import java.util.logging.Logger;
 
 import javax.annotation.Nullable;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
-import com.google.common.base.Functions;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -52,7 +50,6 @@ import static org.apache.aurora.scheduler.state.SideEffect.Action.RESCHEDULE;
 import static org.apache.aurora.scheduler.state.SideEffect.Action.SAVE_STATE;
 import static org.apache.aurora.scheduler.state.SideEffect.Action.STATE_CHANGE;
 import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.ASSIGNED;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.DELETED;
 import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.DRAINING;
 import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.FAILED;
 import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.FINISHED;
@@ -64,9 +61,9 @@ import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.PENDI
 import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.PREEMPTING;
 import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.RESTARTING;
 import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.RUNNING;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.SANDBOX_DELETED;
 import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.STARTING;
 import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.THROTTLED;
+import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.UNKNOWN;
 
 /**
  * State machine for a task.
@@ -90,22 +87,17 @@ class TaskStateMachine {
 
   private final Set<SideEffect> sideEffects = Sets.newHashSet();
 
-  private static final Function<ScheduleStatus, TaskState> STATUS_TO_TASK_STATE =
-      new Function<ScheduleStatus, TaskState>() {
+  private static final Function<IScheduledTask, TaskState> SCHEDULED_TO_TASK_STATE =
+      new Function<IScheduledTask, TaskState>() {
         @Override
-        public TaskState apply(ScheduleStatus input) {
-          return TaskState.valueOf(input.name());
+        public TaskState apply(IScheduledTask task) {
+          return TaskState.valueOf(task.getStatus().name());
         }
       };
 
-  private static final Function<IScheduledTask, TaskState> SCHEDULED_TO_TASK_STATE =
-      Functions.compose(STATUS_TO_TASK_STATE, Tasks.GET_STATUS);
-
   /**
    * ScheduleStatus enum extension to account for cases where no direct state mapping exists.
-   * TODO:(maxim): Consider making it private.
    */
-  @VisibleForTesting
   enum TaskState {
     INIT(Optional.of(ScheduleStatus.INIT)),
     THROTTLED(Optional.of(ScheduleStatus.THROTTLED)),
@@ -121,11 +113,7 @@ class TaskStateMachine {
     KILLED(Optional.of(ScheduleStatus.KILLED)),
     KILLING(Optional.of(ScheduleStatus.KILLING)),
     LOST(Optional.of(ScheduleStatus.LOST)),
-    SANDBOX_DELETED(Optional.of(ScheduleStatus.SANDBOX_DELETED)),
-    /**
-     * The task does not have an associated state as it has been deleted from the store.
-     */
-    DELETED(Optional.<ScheduleStatus>absent());
+    UNKNOWN(Optional.of(ScheduleStatus.UNKNOWN));
 
     private final Optional<ScheduleStatus> status;
 
@@ -162,15 +150,15 @@ class TaskStateMachine {
     MorePreconditions.checkNotBlank(name);
     checkNotNull(task);
 
-    final TaskState initialState = task.transform(SCHEDULED_TO_TASK_STATE).or(DELETED);
+    final TaskState initialState = task.transform(SCHEDULED_TO_TASK_STATE).or(UNKNOWN);
     if (task.isPresent()) {
       Preconditions.checkState(
-          initialState != DELETED,
-          "A task that exists may not be in DELETED state.");
+          initialState != UNKNOWN,
+          "A task that exists may not be in UNKNOWN state.");
     } else {
       Preconditions.checkState(
-          initialState == DELETED,
-          "A task that does not exist must start in DELETED state.");
+          initialState == UNKNOWN,
+          "A task that does not exist must start in UNKNOWN state.");
     }
 
     Closure<Transition<TaskState>> manageTerminatedTasks = Closures.combine(
@@ -178,9 +166,9 @@ class TaskStateMachine {
             // Kill a task that we believe to be terminated when an attempt is made to revive.
             .add(
                 Closures.filter(Transition.to(ASSIGNED, STARTING, RUNNING),
-                    addFollowupClosure(KILL)))
-            // Remove a terminated task that is requested to be deleted.
-            .add(Closures.filter(Transition.to(DELETED), addFollowupClosure(DELETE)))
+                addFollowupClosure(KILL)))
+            // Remove a terminated task that is remotely removed.
+            .add(Closures.filter(Transition.to(UNKNOWN), addFollowupClosure(DELETE)))
             .build());
 
     final Closure<Transition<TaskState>> manageRestartingTask =
@@ -217,7 +205,7 @@ class TaskStateMachine {
                 addFollowup(RESCHEDULE);
                 break;
 
-              case SANDBOX_DELETED:
+              case UNKNOWN:
                 addFollowupTransition(LOST);
                 break;
 
@@ -266,7 +254,7 @@ class TaskStateMachine {
         .initialState(initialState)
         .addState(
             Rule.from(INIT)
-                .to(PENDING, THROTTLED))
+                .to(PENDING, THROTTLED, UNKNOWN))
         .addState(
             Rule.from(PENDING)
                 .to(ASSIGNED, KILLING)
@@ -364,7 +352,7 @@ class TaskStateMachine {
                             addFollowup(RESCHEDULE);
                             break;
 
-                          case SANDBOX_DELETED:
+                          case UNKNOWN:
                             // The slave previously acknowledged that it had the task, and now
                             // stopped reporting it.
                             addFollowupTransition(LOST);
@@ -416,7 +404,7 @@ class TaskStateMachine {
                             addFollowup(RESCHEDULE);
                             break;
 
-                          case SANDBOX_DELETED:
+                          case UNKNOWN:
                             addFollowupTransition(LOST);
                             break;
 
@@ -428,7 +416,7 @@ class TaskStateMachine {
                 ))
         .addState(
             Rule.from(FINISHED)
-                .to(SANDBOX_DELETED, DELETED)
+                .to(UNKNOWN)
                 .withCallback(manageTerminatedTasks))
         .addState(
             Rule.from(PREEMPTING)
@@ -444,26 +432,22 @@ class TaskStateMachine {
                 .withCallback(manageRestartingTask))
         .addState(
             Rule.from(FAILED)
-                .to(SANDBOX_DELETED, DELETED)
+                .to(UNKNOWN)
                 .withCallback(manageTerminatedTasks))
         .addState(
             Rule.from(KILLED)
-                .to(SANDBOX_DELETED, DELETED)
+                .to(UNKNOWN)
                 .withCallback(manageTerminatedTasks))
         .addState(
             Rule.from(KILLING)
-                .to(FINISHED, FAILED, KILLED, LOST, SANDBOX_DELETED, DELETED)
+                .to(FINISHED, FAILED, KILLED, LOST, UNKNOWN)
                 .withCallback(manageTerminatedTasks))
         .addState(
             Rule.from(LOST)
-                .to(SANDBOX_DELETED, DELETED)
-                .withCallback(manageTerminatedTasks))
-        .addState(
-            Rule.from(SANDBOX_DELETED)
-                .to(DELETED)
+                .to(UNKNOWN)
                 .withCallback(manageTerminatedTasks))
         .addState(
-            Rule.from(DELETED)
+            Rule.from(UNKNOWN)
                 .noTransitions()
                 .withCallback(manageTerminatedTasks))
         // Since we want this action to be performed last in the transition sequence, the callback
@@ -483,7 +467,7 @@ class TaskStateMachine {
                       !(((from == PENDING) || (from == THROTTLED)) && (to == KILLING));
 
                   // Don't bother saving state of a task that is being removed.
-                  if ((to != DELETED) && pendingDeleteHack) {
+                  if ((to != UNKNOWN) && pendingDeleteHack) {
                     addFollowup(SAVE_STATE);
                   }
                   previousState = Optional.of(from);
@@ -530,10 +514,10 @@ class TaskStateMachine {
    * At the time this method returns, any work commands required to satisfy the state transition
    * will be appended to the work queue.
    *
-   * @param status Status to apply to the task or absent.
+   * @param status Status to apply to the task.
    * @return {@code true} if the state change was allowed, {@code false} otherwise.
    */
-  public synchronized TransitionResult updateState(final Optional<ScheduleStatus> status) {
+  public synchronized TransitionResult updateState(final ScheduleStatus status) {
     checkNotNull(status);
     Preconditions.checkState(sideEffects.isEmpty());
 
@@ -542,7 +526,7 @@ class TaskStateMachine {
      * state transition (e.g. storing resource consumption of a running task), we need to find
      * a different way to suppress noop transitions.
      */
-    TaskState taskState = status.transform(STATUS_TO_TASK_STATE).or(TaskState.DELETED);
+    TaskState taskState = TaskState.valueOf(status.name());
     if (stateMachine.getState() == taskState) {
       return new TransitionResult(false, ImmutableSet.<SideEffect>of());
     }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/python/apache/aurora/executor/gc_executor.py
----------------------------------------------------------------------
diff --git a/src/main/python/apache/aurora/executor/gc_executor.py b/src/main/python/apache/aurora/executor/gc_executor.py
index e860a58..4a866b2 100644
--- a/src/main/python/apache/aurora/executor/gc_executor.py
+++ b/src/main/python/apache/aurora/executor/gc_executor.py
@@ -259,8 +259,7 @@ class ThermosGCExecutor(ExecutorBase, ExceptionalThread, Observable):
 
     self.log('Told to retain the following task ids:')
     for task_id, schedule_status in retained_tasks.items():
-      self.log('  => %s as %s' %
-          (task_id, ScheduleStatus._VALUES_TO_NAMES.get(schedule_status, 'UNKNOWN')))
+      self.log('  => %s as %s' % (task_id, ScheduleStatus._VALUES_TO_NAMES[schedule_status]))
 
     self.log('Local active tasks:')
     for task_id in local_active:
@@ -286,9 +285,7 @@ class ThermosGCExecutor(ExecutorBase, ExceptionalThread, Observable):
         states = self.get_states(task_id)
         if states:
           _, last_state = states[-1]
-          updates[task_id] = THERMOS_TO_TWITTER_STATES.get(
-              last_state,
-              ScheduleStatus.SANDBOX_DELETED)
+          updates[task_id] = THERMOS_TO_TWITTER_STATES.get(last_state, ScheduleStatus.UNKNOWN)
           self.send_update(
               driver,
               task_id,

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/resources/org/apache/aurora/scheduler/http/schedulerzjob.st
----------------------------------------------------------------------
diff --git a/src/main/resources/org/apache/aurora/scheduler/http/schedulerzjob.st b/src/main/resources/org/apache/aurora/scheduler/http/schedulerzjob.st
index 185f219..28b5667 100644
--- a/src/main/resources/org/apache/aurora/scheduler/http/schedulerzjob.st
+++ b/src/main/resources/org/apache/aurora/scheduler/http/schedulerzjob.st
@@ -201,20 +201,13 @@
                   }
                 }
                 if (aliveTimestamp != 0) {
-                  document.write(printElapsed($task.statusTimestamp$, aliveTimestamp));
+                  document.write(printElapsed(events[events.length - 1][1], aliveTimestamp));
                 }
               </script>
             $if(admin)$
             <td><a href='/structdump/task/$task.taskId$'>$task.taskId$</a>
             $endif$
-            <td>
-              $if(task.slaveHost)$
-                $if(task.hideExecutorUri)$
-                  $task.slaveHost$
-                $else$
-                  <a href='$task.executorUri$'>$task.slaveHost$</a>
-                $endif$
-              $endif$
+            <td>$if(task.slaveHost)$ <a href='$task.executorUri$'>$task.slaveHost$</a> $endif$
           </tr>
           }$
           </tbody>

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/main/thrift/org/apache/aurora/gen/api.thrift
----------------------------------------------------------------------
diff --git a/src/main/thrift/org/apache/aurora/gen/api.thrift b/src/main/thrift/org/apache/aurora/gen/api.thrift
index ed8432e..c0618e4 100644
--- a/src/main/thrift/org/apache/aurora/gen/api.thrift
+++ b/src/main/thrift/org/apache/aurora/gen/api.thrift
@@ -246,7 +246,6 @@ struct AcquireLockResult {
 
 // States that a task may be in.
 enum ScheduleStatus {
-  // TODO(maxim): This state does not add much value. Consider dropping it completely.
   // Initial state for a task.  A task will remain in this state until it has been persisted.
   INIT             = 11,
   // The task will be rescheduled, but is being throttled for restarting too frequently.
@@ -276,8 +275,9 @@ enum ScheduleStatus {
   // A fault in the task environment has caused the system to believe the task no longer exists.
   // This can happen, for example, when a slave process disappears.
   LOST             = 7,
-  // The task sandbox has been deleted by the executor.
-  SANDBOX_DELETED  = 10
+  // The task is unknown to one end of the system.  This is used to reconcile state when the
+  // scheduler believes a task to exist in a location that stops reporting it, or vice versa.
+  UNKNOWN          = 10
 }
 
 // States that a task may be in while still considered active.
@@ -311,8 +311,7 @@ const set<ScheduleStatus> LIVE_STATES = [ScheduleStatus.KILLING,
 const set<ScheduleStatus> TERMINAL_STATES = [ScheduleStatus.FAILED,
                                              ScheduleStatus.FINISHED,
                                              ScheduleStatus.KILLED,
-                                             ScheduleStatus.LOST,
-                                             ScheduleStatus.SANDBOX_DELETED]
+                                             ScheduleStatus.LOST]
 
 // Environment assigned to a job when unspecified
 const string DEFAULT_ENVIRONMENT = "devel"

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/java/org/apache/aurora/scheduler/MesosSchedulerImplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/MesosSchedulerImplTest.java b/src/test/java/org/apache/aurora/scheduler/MesosSchedulerImplTest.java
index ecc6cfa..534b2b6 100644
--- a/src/test/java/org/apache/aurora/scheduler/MesosSchedulerImplTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/MesosSchedulerImplTest.java
@@ -20,9 +20,7 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.testing.TearDown;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
@@ -32,32 +30,29 @@ import com.twitter.common.application.Lifecycle;
 import com.twitter.common.base.Command;
 import com.twitter.common.testing.easymock.EasyMockTest;
 
-import org.apache.aurora.codec.ThriftBinaryCodec;
-import org.apache.aurora.gen.ScheduleStatus;
-import org.apache.aurora.gen.comm.DeletedTasks;
-import org.apache.aurora.gen.comm.SchedulerMessage;
 import org.apache.aurora.scheduler.base.Conversions;
 import org.apache.aurora.scheduler.base.SchedulerException;
 import org.apache.aurora.scheduler.events.EventSink;
 import org.apache.aurora.scheduler.events.PubsubEvent.DriverDisconnected;
 import org.apache.aurora.scheduler.events.PubsubEvent.DriverRegistered;
-import org.apache.aurora.scheduler.state.StateManager;
+import org.apache.aurora.scheduler.state.SchedulerCore;
 import org.apache.aurora.scheduler.storage.Storage;
 import org.apache.aurora.scheduler.storage.Storage.StorageException;
 import org.apache.aurora.scheduler.storage.testing.StorageTestUtil;
-import org.apache.mesos.Protos.ExecutorID;
 import org.apache.mesos.Protos.FrameworkID;
 import org.apache.mesos.Protos.MasterInfo;
 import org.apache.mesos.Protos.Offer;
 import org.apache.mesos.Protos.OfferID;
 import org.apache.mesos.Protos.SlaveID;
 import org.apache.mesos.Protos.TaskID;
+import org.apache.mesos.Protos.TaskInfo;
 import org.apache.mesos.Protos.TaskState;
 import org.apache.mesos.Protos.TaskStatus;
 import org.apache.mesos.SchedulerDriver;
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.apache.mesos.Protos.Status.DRIVER_RUNNING;
 import static org.easymock.EasyMock.expect;
 import static org.junit.Assert.assertTrue;
 
@@ -71,8 +66,6 @@ public class MesosSchedulerImplTest extends EasyMockTest {
   private static final SlaveID SLAVE_ID = SlaveID.newBuilder().setValue("slave-id").build();
   private static final String SLAVE_HOST_2 = "slave-hostname-2";
   private static final SlaveID SLAVE_ID_2 = SlaveID.newBuilder().setValue("slave-id-2").build();
-  private static final ExecutorID EXECUTOR_ID =
-      ExecutorID.newBuilder().setValue("executor-id").build();
 
   private static final OfferID OFFER_ID = OfferID.newBuilder().setValue("offer-id").build();
   private static final Offer OFFER = Offer.newBuilder()
@@ -98,7 +91,6 @@ public class MesosSchedulerImplTest extends EasyMockTest {
   private TaskLauncher userLauncher;
   private SchedulerDriver driver;
   private EventSink eventSink;
-  private StateManager stateManager;
 
   private MesosSchedulerImpl scheduler;
 
@@ -110,13 +102,12 @@ public class MesosSchedulerImplTest extends EasyMockTest {
     systemLauncher = createMock(TaskLauncher.class);
     userLauncher = createMock(TaskLauncher.class);
     eventSink = createMock(EventSink.class);
-    stateManager = createMock(StateManager.class);
 
     Injector injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
         bind(Storage.class).toInstance(storageUtil.storage);
-        bind(StateManager.class).toInstance(stateManager);
+        bind(SchedulerCore.class).toInstance(createMock(SchedulerCore.class));
         bind(Lifecycle.class).toInstance(lifecycle);
         bind(new TypeLiteral<List<TaskLauncher>>() { })
             .toInstance(Arrays.asList(systemLauncher, userLauncher));
@@ -258,32 +249,6 @@ public class MesosSchedulerImplTest extends EasyMockTest {
     }.run();
   }
 
-  @Test
-  public void testSandboxesDeleted() throws Exception {
-    String task1 = "task1";
-    String task2 = "task2";
-    String message = "Sandbox disk space reclaimed.";
-
-    expect(stateManager.changeState(
-        task1,
-        Optional.<ScheduleStatus>absent(),
-        ScheduleStatus.SANDBOX_DELETED,
-        Optional.of(message))).andReturn(true);
-
-    expect(stateManager.changeState(
-        task2,
-        Optional.<ScheduleStatus>absent(),
-        ScheduleStatus.SANDBOX_DELETED,
-        Optional.of(message))).andReturn(true);
-
-    control.replay();
-
-    SchedulerMessage schedulerMessage =
-        SchedulerMessage.deletedTasks(new DeletedTasks(ImmutableSet.of("task1", "task2")));
-    byte[] data = ThriftBinaryCodec.encode(schedulerMessage);
-    scheduler.frameworkMessage(driver, EXECUTOR_ID, SLAVE_ID, data);
-  }
-
   private void expectOfferAttributesSaved(Offer offer) {
     storageUtil.attributeStore.saveHostAttributes(Conversions.getAttributes(offer));
   }
@@ -314,6 +279,11 @@ public class MesosSchedulerImplTest extends EasyMockTest {
       test();
     }
 
+    protected void expectLaunch(TaskInfo task) {
+      expect(driver.launchTasks(ImmutableList.of(OFFER_ID), ImmutableList.of(task)))
+          .andReturn(DRIVER_RUNNING);
+    }
+
     void expectations() throws Exception {
       // Default no-op, subclasses may override.
     }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java b/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java
index db63e79..a4e9464 100644
--- a/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java
+++ b/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java
@@ -372,7 +372,7 @@ public class SchedulerIT extends BaseZooKeeperTest {
     assertEquals(0L, Stats.<Long>getVariable("task_store_PENDING").read().longValue());
     assertEquals(1L, Stats.<Long>getVariable("task_store_ASSIGNED").read().longValue());
     assertEquals(1L, Stats.<Long>getVariable("task_store_RUNNING").read().longValue());
-    assertEquals(0L, Stats.<Long>getVariable("task_store_SANDBOX_DELETED").read().longValue());
+    assertEquals(0L, Stats.<Long>getVariable("task_store_UNKNOWN").read().longValue());
 
     // TODO(William Farner): Send a thrift RPC to the scheduler.
     // TODO(William Farner): Also send an admin thrift RPC to verify capability (e.g. ROOT) mapping.

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/java/org/apache/aurora/scheduler/async/HistoryPrunerTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/async/HistoryPrunerTest.java b/src/test/java/org/apache/aurora/scheduler/async/HistoryPrunerTest.java
index 52ca8cd..378ece9 100644
--- a/src/test/java/org/apache/aurora/scheduler/async/HistoryPrunerTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/async/HistoryPrunerTest.java
@@ -58,7 +58,6 @@ import static org.apache.aurora.gen.ScheduleStatus.FINISHED;
 import static org.apache.aurora.gen.ScheduleStatus.KILLED;
 import static org.apache.aurora.gen.ScheduleStatus.LOST;
 import static org.apache.aurora.gen.ScheduleStatus.RUNNING;
-import static org.apache.aurora.gen.ScheduleStatus.SANDBOX_DELETED;
 import static org.apache.aurora.gen.ScheduleStatus.STARTING;
 import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expectLastCall;
@@ -104,7 +103,7 @@ public class HistoryPrunerTest extends EasyMockTest {
 
     clock.advance(ONE_MS);
     long taskBTimestamp = clock.nowMillis();
-    IScheduledTask b = makeTask("b", SANDBOX_DELETED);
+    IScheduledTask b = makeTask("b", LOST);
 
     expectNoImmediatePrune(ImmutableSet.of(a));
     expectOneDelayedPrune(taskATimestamp);
@@ -120,7 +119,7 @@ public class HistoryPrunerTest extends EasyMockTest {
   @Test
   public void testStorageStartedWithPruning() {
     long taskATimestamp = clock.nowMillis();
-    IScheduledTask a = makeTask("a", SANDBOX_DELETED);
+    IScheduledTask a = makeTask("a", FINISHED);
 
     clock.advance(ONE_MINUTE);
     long taskBTimestamp = clock.nowMillis();
@@ -178,7 +177,7 @@ public class HistoryPrunerTest extends EasyMockTest {
     Capture<Runnable> delayedDelete = expectDefaultDelayedPrune();
 
     // Expect task "a" to be pruned when future is activated.
-    expectDeleteTasks("a");
+    stateManager.deleteTasks(ImmutableSet.of("a"));
 
     control.replay();
 
@@ -307,10 +306,6 @@ public class HistoryPrunerTest extends EasyMockTest {
     return eventDelivered;
   }
 
-  private void expectDeleteTasks(String... tasks) {
-    stateManager.deleteTasks(ImmutableSet.copyOf(tasks));
-  }
-
   private Capture<Runnable> expectDefaultDelayedPrune() {
     return expectDelayedPrune(ONE_DAY.as(Time.MILLISECONDS), 1);
   }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/java/org/apache/aurora/scheduler/base/JobsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/base/JobsTest.java b/src/test/java/org/apache/aurora/scheduler/base/JobsTest.java
index a9d4282..effe14b 100644
--- a/src/test/java/org/apache/aurora/scheduler/base/JobsTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/base/JobsTest.java
@@ -29,7 +29,6 @@ import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
 import org.junit.Test;
 
 import static org.apache.aurora.scheduler.base.TaskTestUtil.makeTask;
-import static org.apache.aurora.scheduler.base.TaskTestUtil.makeTaskEvent;
 import static org.junit.Assert.assertEquals;
 
 public class JobsTest {
@@ -42,13 +41,7 @@ public class JobsTest {
             .transform(new Function<ScheduleStatus, IScheduledTask>() {
               @Override
               public IScheduledTask apply(ScheduleStatus status) {
-                int startTime = 100;
-                if (status == ScheduleStatus.SANDBOX_DELETED) {
-                  return makeTask(status, ImmutableList.of(
-                      makeTaskEvent(startTime + 10, ScheduleStatus.FAILED),
-                      makeTaskEvent(startTime + 20, ScheduleStatus.SANDBOX_DELETED)));
-                }
-                return makeTask(status, startTime);
+                return makeTask(status, 100);
               }
             }).toList();
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/java/org/apache/aurora/scheduler/base/TaskTestUtil.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/base/TaskTestUtil.java b/src/test/java/org/apache/aurora/scheduler/base/TaskTestUtil.java
index 7485a41..2006ddb 100644
--- a/src/test/java/org/apache/aurora/scheduler/base/TaskTestUtil.java
+++ b/src/test/java/org/apache/aurora/scheduler/base/TaskTestUtil.java
@@ -38,7 +38,7 @@ public final class TaskTestUtil {
   }
 
   public static IScheduledTask makeTask(ScheduleStatus status, long startTime) {
-    return makeTask(status, makeTaskEvents(startTime, 3, status));
+    return makeTask(status, makeTaskEvents(startTime, 3));
   }
 
   public static IScheduledTask makeTask(ScheduleStatus status, List<TaskEvent> taskEvents) {
@@ -53,16 +53,16 @@ public final class TaskTestUtil {
         .setTaskEvents(taskEvents));
   }
 
-  public static TaskEvent makeTaskEvent(long ts, ScheduleStatus status) {
-    return new TaskEvent().setTimestamp(ts).setStatus(status);
-  }
-
-  private static List<TaskEvent> makeTaskEvents(long startTs, int count, ScheduleStatus status) {
+  private static List<TaskEvent> makeTaskEvents(long startTs, int count) {
     List<TaskEvent> taskEvents = Lists.newArrayListWithCapacity(3);
     for (int i = 0; i < count; i++) {
-      taskEvents.add(makeTaskEvent(startTs - (i * 10), status));
+      taskEvents.add(makeTaskEvent(startTs - (i * 10)));
     }
     Collections.reverse(taskEvents);
     return taskEvents;
   }
+
+  private static TaskEvent makeTaskEvent(long ts) {
+    return new TaskEvent().setTimestamp(ts);
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/java/org/apache/aurora/scheduler/base/TasksTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/base/TasksTest.java b/src/test/java/org/apache/aurora/scheduler/base/TasksTest.java
index 86116df..102fe04 100644
--- a/src/test/java/org/apache/aurora/scheduler/base/TasksTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/base/TasksTest.java
@@ -33,12 +33,13 @@ public class TasksTest {
 
   @Test
   public void testOrderedStatusesForCompleteness() {
-    // OrderedTaskStatuses should contain all ScheduleStatus values except INIT.
+    // OrderedTaskStatuses should contain all ScheduleStatus values except INIT and UNKNOWN.
     assertEquals(
         ImmutableSet.copyOf(ScheduleStatus.values()),
-        ImmutableSet.<ScheduleStatus>builder()
+        ImmutableSet.builder()
             .addAll(Tasks.ORDERED_TASK_STATUSES)
             .add(ScheduleStatus.INIT)
+            .add(ScheduleStatus.UNKNOWN)
             .build());
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/java/org/apache/aurora/scheduler/state/BaseSchedulerCoreImplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/state/BaseSchedulerCoreImplTest.java b/src/test/java/org/apache/aurora/scheduler/state/BaseSchedulerCoreImplTest.java
index c0e518f..033ebd2 100644
--- a/src/test/java/org/apache/aurora/scheduler/state/BaseSchedulerCoreImplTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/state/BaseSchedulerCoreImplTest.java
@@ -987,6 +987,37 @@ public abstract class BaseSchedulerCoreImplTest extends EasyMockTest {
   }
 
   @Test
+  public void testSlaveDeletesTasks() throws Exception {
+    expectTaskNotThrottled();
+
+    control.replay();
+    buildScheduler();
+
+    scheduler.createJob(makeJob(KEY_A, 2));
+
+    Query.Builder builder = Query.unscoped().active();
+    String taskId1 = Tasks.id(getOnlyTask(builder.byInstances(KEY_A, 0)));
+    String taskId2 = Tasks.id(getOnlyTask(builder.byInstances(KEY_A, 1)));
+
+    assignTask(taskId1, SLAVE_ID, SLAVE_HOST_1);
+    assignTask(taskId2, SLAVE_ID, SLAVE_HOST_1);
+
+    changeStatus(taskId1, STARTING, RUNNING);
+    changeStatus(taskId2, STARTING, FINISHED);
+
+    scheduler.tasksDeleted(ImmutableSet.of(taskId1, taskId2));
+
+    // The expected outcome is that one task is moved into the LOST state and rescheduled.
+    // The FINISHED task's state is deleted.
+    assertTaskCount(2);
+    assertEquals(LOST, getOnlyTask(Query.taskScoped(taskId1)).getStatus());
+    assertTrue(getTasks(Query.taskScoped(taskId2)).isEmpty());
+
+    IScheduledTask rescheduled = Iterables.getOnlyElement(getTasksByStatus(PENDING));
+    assertEquals(taskId1, rescheduled.getAncestorId());
+  }
+
+  @Test
   public void testRestartShards() throws Exception {
     expectKillTask(2);
     expectTaskNotThrottled().times(2);

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/java/org/apache/aurora/scheduler/state/StateManagerImplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/state/StateManagerImplTest.java b/src/test/java/org/apache/aurora/scheduler/state/StateManagerImplTest.java
index af17c2c..24949c0 100644
--- a/src/test/java/org/apache/aurora/scheduler/state/StateManagerImplTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/state/StateManagerImplTest.java
@@ -62,8 +62,8 @@ import static org.apache.aurora.gen.ScheduleStatus.KILLING;
 import static org.apache.aurora.gen.ScheduleStatus.LOST;
 import static org.apache.aurora.gen.ScheduleStatus.PENDING;
 import static org.apache.aurora.gen.ScheduleStatus.RUNNING;
-import static org.apache.aurora.gen.ScheduleStatus.SANDBOX_DELETED;
 import static org.apache.aurora.gen.ScheduleStatus.THROTTLED;
+import static org.apache.aurora.gen.ScheduleStatus.UNKNOWN;
 import static org.apache.aurora.gen.apiConstants.DEFAULT_ENVIRONMENT;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
@@ -208,7 +208,8 @@ public class StateManagerImplTest extends EasyMockTest {
     ITaskConfig task = makeTask(JIM, MY_JOB);
     String taskId = "a";
     expect(taskIdGenerator.generate(task, 0)).andReturn(taskId);
-    expectStateTransitions(taskId, INIT, PENDING, ASSIGNED, RUNNING, KILLING, SANDBOX_DELETED);
+    expectStateTransitions(taskId, INIT, PENDING, ASSIGNED, RUNNING, KILLING);
+    eventSink.post(matchTasksDeleted(taskId));
 
     driver.killTask(EasyMock.<String>anyObject());
 
@@ -219,7 +220,7 @@ public class StateManagerImplTest extends EasyMockTest {
     assignTask(taskId, HOST_A);
     changeState(taskId, RUNNING);
     changeState(taskId, KILLING);
-    changeState(taskId, SANDBOX_DELETED);
+    changeState(taskId, UNKNOWN);
   }
 
   @Test
@@ -247,7 +248,7 @@ public class StateManagerImplTest extends EasyMockTest {
   }
 
   @Test
-  public void testDeletePendingTask() {
+  public void testDeleteTask() {
     ITaskConfig task = makeTask(JIM, MY_JOB);
     String taskId = "a";
     expect(taskIdGenerator.generate(task, 0)).andReturn(taskId);
@@ -257,7 +258,7 @@ public class StateManagerImplTest extends EasyMockTest {
     control.replay();
 
     insertTask(task, 0);
-    changeState(taskId, KILLING);
+    stateManager.deleteTasks(ImmutableSet.of(taskId));
   }
 
   @Test
@@ -317,7 +318,6 @@ public class StateManagerImplTest extends EasyMockTest {
     changeState(taskId, FAILED);
     IScheduledTask rescheduledTask = Iterables.getOnlyElement(
         Storage.Util.consistentFetchTasks(storage, Query.taskScoped(taskId2)));
-    assertEquals(taskId, rescheduledTask.getAncestorId());
     assertEquals(1, rescheduledTask.getFailureCount());
   }
 
@@ -341,7 +341,7 @@ public class StateManagerImplTest extends EasyMockTest {
 
     assignTask(taskId, HOST_A);
     changeState(taskId, RUNNING);
-    changeState(taskId, SANDBOX_DELETED);
+    changeState(taskId, UNKNOWN);
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/java/org/apache/aurora/scheduler/state/TaskStateMachineTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/state/TaskStateMachineTest.java b/src/test/java/org/apache/aurora/scheduler/state/TaskStateMachineTest.java
index 0d8dc48..e77063a 100644
--- a/src/test/java/org/apache/aurora/scheduler/state/TaskStateMachineTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/state/TaskStateMachineTest.java
@@ -34,27 +34,25 @@ import org.apache.aurora.gen.ScheduledTask;
 import org.apache.aurora.gen.TaskConfig;
 import org.apache.aurora.scheduler.base.Tasks;
 import org.apache.aurora.scheduler.state.SideEffect.Action;
-import org.apache.aurora.scheduler.state.TaskStateMachine.TaskState;
 import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.ASSIGNED;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.DELETED;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.DRAINING;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.FAILED;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.FINISHED;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.INIT;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.KILLED;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.KILLING;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.LOST;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.PENDING;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.PREEMPTING;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.RESTARTING;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.RUNNING;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.SANDBOX_DELETED;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.STARTING;
-import static org.apache.aurora.scheduler.state.TaskStateMachine.TaskState.THROTTLED;
+import static org.apache.aurora.gen.ScheduleStatus.ASSIGNED;
+import static org.apache.aurora.gen.ScheduleStatus.DRAINING;
+import static org.apache.aurora.gen.ScheduleStatus.FAILED;
+import static org.apache.aurora.gen.ScheduleStatus.FINISHED;
+import static org.apache.aurora.gen.ScheduleStatus.INIT;
+import static org.apache.aurora.gen.ScheduleStatus.KILLED;
+import static org.apache.aurora.gen.ScheduleStatus.KILLING;
+import static org.apache.aurora.gen.ScheduleStatus.LOST;
+import static org.apache.aurora.gen.ScheduleStatus.PENDING;
+import static org.apache.aurora.gen.ScheduleStatus.PREEMPTING;
+import static org.apache.aurora.gen.ScheduleStatus.RESTARTING;
+import static org.apache.aurora.gen.ScheduleStatus.RUNNING;
+import static org.apache.aurora.gen.ScheduleStatus.STARTING;
+import static org.apache.aurora.gen.ScheduleStatus.THROTTLED;
+import static org.apache.aurora.gen.ScheduleStatus.UNKNOWN;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -79,7 +77,7 @@ public class TaskStateMachineTest {
   @Test
   public void testSimpleTransition() {
     expectUpdateStateOnTransitionTo(PENDING, ASSIGNED, STARTING, RUNNING, FINISHED);
-    legalTransition(SANDBOX_DELETED, Action.SAVE_STATE);
+    legalTransition(UNKNOWN, Action.DELETE);
   }
 
   @Test
@@ -91,8 +89,7 @@ public class TaskStateMachineTest {
 
   @Test
   public void testPostTerminalTransitionDenied() {
-    Set<ScheduleStatus> terminalStates =
-        Sets.difference(Tasks.TERMINAL_STATES, ImmutableSet.of(ScheduleStatus.SANDBOX_DELETED));
+    Set<ScheduleStatus> terminalStates = Tasks.TERMINAL_STATES;
 
     for (ScheduleStatus endState : terminalStates) {
       stateMachine = makeStateMachine(makeTask(false));
@@ -120,108 +117,15 @@ public class TaskStateMachineTest {
       }
 
       expectUpdateStateOnTransitionTo(PENDING, ASSIGNED, STARTING, RUNNING);
-      legalTransition(TaskState.valueOf(endState.name()), finalActions);
+      legalTransition(endState, finalActions);
 
       for (ScheduleStatus badTransition : terminalStates) {
-        illegalTransition(TaskState.valueOf(badTransition.name()));
+        illegalTransition(badTransition);
       }
     }
   }
 
   @Test
-  public void testTerminalToSandboxDeleted() {
-    Set<ScheduleStatus> terminalStates =
-        Sets.difference(Tasks.TERMINAL_STATES, ImmutableSet.of(ScheduleStatus.SANDBOX_DELETED));
-    System.out.print(terminalStates);
-    for (ScheduleStatus endState : terminalStates) {
-      stateMachine = makeStateMachine(makeTask(false));
-      Set<SideEffect.Action> finalActions = Sets.newHashSet(Action.SAVE_STATE);
-
-      switch (endState) {
-        case FAILED:
-          finalActions.add(Action.INCREMENT_FAILURES);
-          break;
-
-        case FINISHED:
-          break;
-
-        case KILLED:
-        case LOST:
-          finalActions.add(Action.RESCHEDULE);
-          break;
-
-        case KILLING:
-          finalActions.add(Action.KILL);
-          break;
-
-        default:
-          fail("Unknown state " + endState);
-      }
-
-      expectUpdateStateOnTransitionTo(
-          PENDING,
-          ASSIGNED,
-          STARTING,
-          RUNNING);
-      legalTransition(TaskState.valueOf(endState.name()), finalActions);
-      legalTransition(SANDBOX_DELETED, Action.SAVE_STATE);
-    }
-  }
-
-  @Test
-  public void testTerminalToDeleted() {
-    Set<ScheduleStatus> terminalStates =
-        Sets.difference(Tasks.TERMINAL_STATES, ImmutableSet.of(ScheduleStatus.SANDBOX_DELETED));
-    System.out.print(terminalStates);
-    for (ScheduleStatus endState : terminalStates) {
-      stateMachine = makeStateMachine(makeTask(false));
-      Set<SideEffect.Action> finalActions = Sets.newHashSet(Action.SAVE_STATE);
-
-      switch (endState) {
-        case FAILED:
-          finalActions.add(Action.INCREMENT_FAILURES);
-          break;
-
-        case FINISHED:
-          break;
-
-        case KILLED:
-        case LOST:
-          finalActions.add(Action.RESCHEDULE);
-          break;
-
-        case KILLING:
-          finalActions.add(Action.KILL);
-          break;
-
-        default:
-          fail("Unknown state " + endState);
-      }
-
-      expectUpdateStateOnTransitionTo(
-          PENDING,
-          ASSIGNED,
-          STARTING,
-          RUNNING);
-      legalTransition(TaskState.valueOf(endState.name()), finalActions);
-      legalTransition(DELETED, Action.DELETE);
-    }
-  }
-
-  @Test
-  public void testSandboxDeletedToDeleted() {
-    stateMachine = makeStateMachine(makeTask(false));
-    expectUpdateStateOnTransitionTo(
-        PENDING,
-        ASSIGNED,
-        STARTING,
-        RUNNING,
-        FINISHED,
-        SANDBOX_DELETED);
-    legalTransition(DELETED, Action.DELETE);
-  }
-
-  @Test
   public void testUnknownTask() {
     stateMachine = new TaskStateMachine("id");
 
@@ -243,15 +147,15 @@ public class TaskStateMachineTest {
   @Test
   public void testMissingStartingRescheduledImmediately() {
     expectUpdateStateOnTransitionTo(PENDING, ASSIGNED, STARTING);
-    illegalTransition(SANDBOX_DELETED,
-        ImmutableSet.of(new SideEffect(Action.STATE_CHANGE, Optional.of(LOST.getStatus().get()))));
+    illegalTransition(UNKNOWN,
+        ImmutableSet.of(new SideEffect(Action.STATE_CHANGE, Optional.of(LOST))));
   }
 
   @Test
   public void testMissingRunningRescheduledImmediately() {
     expectUpdateStateOnTransitionTo(PENDING, ASSIGNED, STARTING, RUNNING);
-    illegalTransition(SANDBOX_DELETED,
-        ImmutableSet.of(new SideEffect(Action.STATE_CHANGE, Optional.of(LOST.getStatus().get()))));
+    illegalTransition(UNKNOWN,
+        ImmutableSet.of(new SideEffect(Action.STATE_CHANGE, Optional.of(LOST))));
   }
 
   @Test
@@ -353,13 +257,13 @@ public class TaskStateMachineTest {
         }
       };
 
-  private void legalTransition(TaskState state, SideEffect.Action... expectedActions) {
+  private void legalTransition(ScheduleStatus state, SideEffect.Action... expectedActions) {
     legalTransition(state, ImmutableSet.copyOf(expectedActions));
   }
 
-  private void legalTransition(TaskState state, Set<SideEffect.Action> expectedActions) {
+  private void legalTransition(ScheduleStatus state, Set<SideEffect.Action> expectedActions) {
     ScheduleStatus previousState = stateMachine.getPreviousState();
-    TransitionResult result = stateMachine.updateState(state.getStatus());
+    TransitionResult result = stateMachine.updateState(state);
     assertTrue("Transition to " + state + " was not successful", result.isSuccess());
     assertNotEquals(previousState, stateMachine.getPreviousState());
     assertEquals(
@@ -367,28 +271,28 @@ public class TaskStateMachineTest {
         result.getSideEffects());
   }
 
-  private void expectUpdateStateOnTransitionTo(TaskState... states) {
-    for (TaskState status : states) {
+  private void expectUpdateStateOnTransitionTo(ScheduleStatus... states) {
+    for (ScheduleStatus status : states) {
       legalTransition(status, Action.SAVE_STATE);
     }
   }
 
-  private void illegalTransition(TaskState state, SideEffect.Action... expectedActions) {
+  private void illegalTransition(ScheduleStatus state, SideEffect.Action... expectedActions) {
     illegalTransition(
         state,
         FluentIterable.from(
             ImmutableSet.copyOf(expectedActions)).transform(TO_SIDE_EFFECT).toSet());
   }
 
-  private void illegalTransition(TaskState state, Set<SideEffect> sideEffects) {
-    TransitionResult result = stateMachine.updateState(state.getStatus());
+  private void illegalTransition(ScheduleStatus state, Set<SideEffect> sideEffects) {
+    TransitionResult result = stateMachine.updateState(state);
     assertFalse(result.isSuccess());
     assertEquals(sideEffects, result.getSideEffects());
   }
 
   private static ScheduledTask makeTask(boolean service) {
     return new ScheduledTask()
-        .setStatus(INIT.getStatus().get())
+        .setStatus(INIT)
         .setAssignedTask(
             new AssignedTask()
                 .setTaskId("test")
@@ -399,6 +303,8 @@ public class TaskStateMachineTest {
                         .setIsService(service)));
   }
 
+  private static final TransitionResult LEGAL_NO_ACTION =
+      new TransitionResult(true, ImmutableSet.<SideEffect>of());
   private static final TransitionResult SAVE = new TransitionResult(
       true,
       ImmutableSet.of(new SideEffect(Action.SAVE_STATE, Optional.<ScheduleStatus>absent())));
@@ -431,14 +337,14 @@ public class TaskStateMachineTest {
       ImmutableSet.of(new SideEffect(Action.DELETE, Optional.<ScheduleStatus>absent())));
   private static final TransitionResult MARK_LOST = new TransitionResult(
       false,
-      ImmutableSet.of(new SideEffect(Action.STATE_CHANGE, Optional.of(LOST.getStatus().get()))));
+      ImmutableSet.of(new SideEffect(Action.STATE_CHANGE, Optional.of(LOST))));
 
   private static final class TestCase {
     private final boolean taskPresent;
-    private final TaskState from;
-    private final TaskState to;
+    private final ScheduleStatus from;
+    private final ScheduleStatus to;
 
-    private TestCase(boolean taskPresent, TaskState from, TaskState to) {
+    private TestCase(boolean taskPresent, ScheduleStatus from, ScheduleStatus to) {
       this.taskPresent = taskPresent;
       this.from = from;
       this.to = to;
@@ -478,6 +384,7 @@ public class TaskStateMachineTest {
           .put(new TestCase(false, INIT, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, INIT, STARTING), ILLEGAL_KILL)
           .put(new TestCase(false, INIT, RUNNING), ILLEGAL_KILL)
+          .put(new TestCase(true, INIT, UNKNOWN), LEGAL_NO_ACTION)
           .put(new TestCase(true, THROTTLED, PENDING), SAVE)
           .put(new TestCase(true, THROTTLED, KILLING), DELETE_TASK)
           .put(new TestCase(false, THROTTLED, ASSIGNED), ILLEGAL_KILL)
@@ -513,7 +420,7 @@ public class TaskStateMachineTest {
           .put(new TestCase(true, STARTING, KILLED), SAVE_AND_RESCHEDULE)
           .put(new TestCase(true, STARTING, KILLING), SAVE_AND_KILL)
           .put(new TestCase(true, STARTING, LOST), SAVE_AND_RESCHEDULE)
-          .put(new TestCase(true, STARTING, SANDBOX_DELETED), MARK_LOST)
+          .put(new TestCase(true, STARTING, UNKNOWN), MARK_LOST)
           .put(new TestCase(false, RUNNING, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, RUNNING, STARTING), ILLEGAL_KILL)
           .put(new TestCase(false, RUNNING, RUNNING), ILLEGAL_KILL)
@@ -525,15 +432,14 @@ public class TaskStateMachineTest {
           .put(new TestCase(true, RUNNING, KILLED), SAVE_AND_RESCHEDULE)
           .put(new TestCase(true, RUNNING, KILLING), SAVE_AND_KILL)
           .put(new TestCase(true, RUNNING, LOST), SAVE_AND_RESCHEDULE)
-          .put(new TestCase(true, RUNNING, SANDBOX_DELETED), MARK_LOST)
+          .put(new TestCase(true, RUNNING, UNKNOWN), MARK_LOST)
           .put(new TestCase(true, FINISHED, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, FINISHED, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(true, FINISHED, STARTING), ILLEGAL_KILL)
           .put(new TestCase(false, FINISHED, STARTING), ILLEGAL_KILL)
           .put(new TestCase(true, FINISHED, RUNNING), ILLEGAL_KILL)
           .put(new TestCase(false, FINISHED, RUNNING), ILLEGAL_KILL)
-          .put(new TestCase(true, FINISHED, SANDBOX_DELETED), SAVE)
-          .put(new TestCase(true, FINISHED, DELETED), DELETE_TASK)
+          .put(new TestCase(true, FINISHED, UNKNOWN), DELETE_TASK)
           .put(new TestCase(true, PREEMPTING, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, PREEMPTING, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(true, PREEMPTING, STARTING), ILLEGAL_KILL)
@@ -545,7 +451,7 @@ public class TaskStateMachineTest {
           .put(new TestCase(true, PREEMPTING, KILLED), SAVE_AND_RESCHEDULE)
           .put(new TestCase(true, PREEMPTING, KILLING), SAVE)
           .put(new TestCase(true, PREEMPTING, LOST), SAVE_KILL_AND_RESCHEDULE)
-          .put(new TestCase(true, PREEMPTING, SANDBOX_DELETED), MARK_LOST)
+          .put(new TestCase(true, PREEMPTING, UNKNOWN), MARK_LOST)
           .put(new TestCase(true, RESTARTING, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, RESTARTING, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(true, RESTARTING, STARTING), ILLEGAL_KILL)
@@ -557,7 +463,7 @@ public class TaskStateMachineTest {
           .put(new TestCase(true, RESTARTING, KILLED), SAVE_AND_RESCHEDULE)
           .put(new TestCase(true, RESTARTING, KILLING), SAVE)
           .put(new TestCase(true, RESTARTING, LOST), SAVE_KILL_AND_RESCHEDULE)
-          .put(new TestCase(true, RESTARTING, SANDBOX_DELETED), MARK_LOST)
+          .put(new TestCase(true, RESTARTING, UNKNOWN), MARK_LOST)
           .put(new TestCase(true, DRAINING, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, DRAINING, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(true, DRAINING, STARTING), ILLEGAL_KILL)
@@ -569,23 +475,21 @@ public class TaskStateMachineTest {
           .put(new TestCase(true, DRAINING, KILLED), SAVE_AND_RESCHEDULE)
           .put(new TestCase(true, DRAINING, KILLING), SAVE)
           .put(new TestCase(true, DRAINING, LOST), SAVE_KILL_AND_RESCHEDULE)
-          .put(new TestCase(true, DRAINING, SANDBOX_DELETED), MARK_LOST)
+          .put(new TestCase(true, DRAINING, UNKNOWN), MARK_LOST)
           .put(new TestCase(true, FAILED, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, FAILED, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(true, FAILED, STARTING), ILLEGAL_KILL)
           .put(new TestCase(false, FAILED, STARTING), ILLEGAL_KILL)
           .put(new TestCase(true, FAILED, RUNNING), ILLEGAL_KILL)
           .put(new TestCase(false, FAILED, RUNNING), ILLEGAL_KILL)
-          .put(new TestCase(true, FAILED, SANDBOX_DELETED), SAVE)
-          .put(new TestCase(true, FAILED, DELETED), DELETE_TASK)
+          .put(new TestCase(true, FAILED, UNKNOWN), DELETE_TASK)
           .put(new TestCase(true, KILLED, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, KILLED, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(true, KILLED, STARTING), ILLEGAL_KILL)
           .put(new TestCase(false, KILLED, STARTING), ILLEGAL_KILL)
           .put(new TestCase(true, KILLED, RUNNING), ILLEGAL_KILL)
           .put(new TestCase(false, KILLED, RUNNING), ILLEGAL_KILL)
-          .put(new TestCase(true, KILLED, SANDBOX_DELETED), SAVE)
-          .put(new TestCase(true, KILLED, DELETED), DELETE_TASK)
+          .put(new TestCase(true, KILLED, UNKNOWN), DELETE_TASK)
           .put(new TestCase(true, KILLING, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, KILLING, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(true, KILLING, STARTING), ILLEGAL_KILL)
@@ -596,32 +500,23 @@ public class TaskStateMachineTest {
           .put(new TestCase(true, KILLING, FAILED), SAVE)
           .put(new TestCase(true, KILLING, KILLED), SAVE)
           .put(new TestCase(true, KILLING, LOST), SAVE)
-          .put(new TestCase(true, KILLING, SANDBOX_DELETED), SAVE)
-          .put(new TestCase(true, KILLING, DELETED), DELETE_TASK)
+          .put(new TestCase(true, KILLING, UNKNOWN), DELETE_TASK)
           .put(new TestCase(true, LOST, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(false, LOST, ASSIGNED), ILLEGAL_KILL)
           .put(new TestCase(true, LOST, STARTING), ILLEGAL_KILL)
           .put(new TestCase(false, LOST, STARTING), ILLEGAL_KILL)
           .put(new TestCase(true, LOST, RUNNING), ILLEGAL_KILL)
           .put(new TestCase(false, LOST, RUNNING), ILLEGAL_KILL)
-          .put(new TestCase(true, LOST, SANDBOX_DELETED), SAVE)
-          .put(new TestCase(true, LOST, DELETED), DELETE_TASK)
-          .put(new TestCase(true, SANDBOX_DELETED, ASSIGNED), ILLEGAL_KILL)
-          .put(new TestCase(false, SANDBOX_DELETED, ASSIGNED), ILLEGAL_KILL)
-          .put(new TestCase(true, SANDBOX_DELETED, STARTING), ILLEGAL_KILL)
-          .put(new TestCase(false, SANDBOX_DELETED, STARTING), ILLEGAL_KILL)
-          .put(new TestCase(true, SANDBOX_DELETED, RUNNING), ILLEGAL_KILL)
-          .put(new TestCase(false, SANDBOX_DELETED, RUNNING), ILLEGAL_KILL)
-          .put(new TestCase(true, SANDBOX_DELETED, DELETED), DELETE_TASK)
-          .put(new TestCase(false, DELETED, ASSIGNED), ILLEGAL_KILL)
-          .put(new TestCase(false, DELETED, STARTING), ILLEGAL_KILL)
-          .put(new TestCase(false, DELETED, RUNNING), ILLEGAL_KILL)
+          .put(new TestCase(true, LOST, UNKNOWN), DELETE_TASK)
+          .put(new TestCase(false, UNKNOWN, ASSIGNED), ILLEGAL_KILL)
+          .put(new TestCase(false, UNKNOWN, STARTING), ILLEGAL_KILL)
+          .put(new TestCase(false, UNKNOWN, RUNNING), ILLEGAL_KILL)
           .build();
 
   @Test
   public void testAllTransitions() {
-    for (TaskState from : TaskState.values()) {
-      for (TaskState to : TaskState.values()) {
+    for (ScheduleStatus from : ScheduleStatus.values()) {
+      for (ScheduleStatus to : ScheduleStatus.values()) {
         for (Boolean taskPresent : ImmutableList.of(Boolean.TRUE, Boolean.FALSE)) {
           TestCase testCase = new TestCase(taskPresent, from, to);
 
@@ -632,11 +527,11 @@ public class TaskStateMachineTest {
 
           TaskStateMachine machine;
           if (taskPresent) {
-            // Cannot create a state machine for an DELETED task that is in the store.
-            boolean expectException = from == DELETED;
+            // Cannot create a state machine for an UNKNOWN task that is in the store.
+            boolean expectException = from == UNKNOWN;
             try {
-              machine = new TaskStateMachine(
-                  IScheduledTask.build(makeTask(false).setStatus(from.getStatus().get())));
+              machine =
+                  new TaskStateMachine(IScheduledTask.build(makeTask(false).setStatus(from)));
               if (expectException) {
                 fail();
               }
@@ -654,7 +549,7 @@ public class TaskStateMachineTest {
           assertEquals(
               "Unexpected behavior for " + testCase,
               expectation,
-              machine.updateState(to.getStatus()));
+              machine.updateState(to));
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/20a14b90/src/test/resources/org/apache/aurora/gen/api.thrift.md5
----------------------------------------------------------------------
diff --git a/src/test/resources/org/apache/aurora/gen/api.thrift.md5 b/src/test/resources/org/apache/aurora/gen/api.thrift.md5
index 5bd9e6b..05c6e8a 100644
--- a/src/test/resources/org/apache/aurora/gen/api.thrift.md5
+++ b/src/test/resources/org/apache/aurora/gen/api.thrift.md5
@@ -1 +1 @@
-132f8956d6153a2e20f29840f65c2dab
+f91209ac428266e87dac5430bf46ed10