@Override
  public void handleStart(Operation start) {
    ServiceUtils.logInfo(this, "Starting service %s", getSelfLink());

    try {
      // Initialize the task state.
      State s = start.getBody(State.class);

      if (s.taskInfo == null || s.taskInfo.stage == TaskState.TaskStage.CREATED) {
        s.taskInfo = new TaskState();
        s.taskInfo.stage = TaskState.TaskStage.STARTED;
        s.taskInfo.subStage = TaskState.SubStage.UPDATE_DATASTORE_COUNTS;
      }

      if (s.documentExpirationTimeMicros <= 0) {
        s.documentExpirationTimeMicros =
            ServiceUtils.computeExpirationTime(ServiceUtils.DEFAULT_DOC_EXPIRATION_TIME);
      }

      if (s.queryPollDelay == null) {
        s.queryPollDelay = DEFAULT_QUERY_POLL_DELAY;
      }

      validateState(s);
      start.setBody(s).complete();

      sendStageProgressPatch(s, s.taskInfo.stage, s.taskInfo.subStage);
    } catch (Throwable e) {
      ServiceUtils.logSevere(this, e);
      if (!OperationUtils.isCompleted(start)) {
        start.fail(e);
      }
    }
  }
  /**
   * Patch operation handler. Implements all logic to drive our state machine.
   *
   * @param patch
   */
  @Override
  public void handlePatch(Operation patch) {
    State currentState = getState(patch);
    State patchState = patch.getBody(State.class);

    try {
      validatePatch(currentState, patchState);
      applyPatch(currentState, patchState);

      validateState(currentState);
      patch.complete();

      switch (currentState.taskInfo.stage) {
        case STARTED:
          handleStartedStage(currentState, patchState);
          break;
        case FAILED:
        case FINISHED:
        case CANCELLED:
          break;
        default:
          throw new IllegalStateException(
              String.format("Invalid stage %s", currentState.taskInfo.stage));
      }
    } catch (Throwable e) {
      ServiceUtils.logSevere(this, e);
      if (!OperationUtils.isCompleted(patch)) {
        patch.fail(e);
      }
    }
  }
  private void failTaskAndPatchDocument(
      final KubernetesClusterCreateTask currentState,
      final NodeType nodeType,
      final Throwable throwable) {
    ServiceUtils.logSevere(this, throwable);
    KubernetesClusterCreateTask patchState =
        buildPatch(
            TaskState.TaskStage.FAILED,
            null,
            new IllegalStateException(
                String.format(
                    "Failed to rollout %s. Error: %s", nodeType.toString(), throwable.toString())));

    ClusterService.State document = new ClusterService.State();
    document.clusterState = ClusterState.ERROR;
    updateStates(currentState, patchState, document);
  }
 private void failTask(Throwable e) {
   ServiceUtils.logSevere(this, e);
   TaskUtils.sendSelfPatch(this, buildPatch(TaskState.TaskStage.FAILED, null, e));
 }
 private void failTask(Map<Long, Throwable> exs) {
   exs.values().forEach(e -> ServiceUtils.logSevere(this, e));
   TaskUtils.sendSelfPatch(
       this, buildPatch(TaskState.TaskStage.FAILED, exs.values().iterator().next()));
 }
 /**
  * Log failed query.
  *
  * @param e
  */
 private void logFailure(Throwable e) {
   ServiceUtils.logSevere(this, e);
 }