@Override
  public void handleStart(Operation start) {
    ServiceUtils.logInfo(this, "Starting service %s", getSelfLink());
    State startState = start.getBody(State.class);
    InitializationUtils.initialize(startState);

    if (null == startState.queryCreateVmTaskInterval) {
      startState.queryCreateVmTaskInterval = HostUtils.getDeployerContext(this).getTaskPollDelay();
    }

    validateState(startState);

    if (TaskState.TaskStage.CREATED == startState.taskState.stage) {
      startState.taskState.stage = TaskState.TaskStage.STARTED;
    }

    start.setBody(startState).complete();

    try {
      if (ControlFlags.isOperationProcessingDisabled(startState.controlFlags)) {
        ServiceUtils.logInfo(this, "Skipping patch handling (disabled)");
      } else if (TaskState.TaskStage.STARTED == startState.taskState.stage) {
        TaskUtils.sendSelfPatch(this, buildPatch(startState.taskState.stage, (Throwable) null));
      }
    } catch (Throwable t) {
      failTask(t);
    }
  }
  /**
   * This method performs document state updates in response to an operation which sets the state to
   * STARTED.
   *
   * @param currentState Supplies the current state object.
   * @param vmState Supplies the state object of the VmService entity.
   * @param hostState Supplies the state object of the HostService entity.
   * @param projectState Supplies the state object of the ProjectService entity.
   * @param imageState Supplies the state object of the Image Service entity.
   * @param projectState Supplies the state object of the Project Service entity.
   * @param vmFlavorState Supplies the state object of the FlavorService entity.
   * @param diskFlavorState Supplies the state object of the FlavorService entity.
   */
  private void processStartedStage(
      final State currentState,
      final VmService.State vmState,
      final HostService.State hostState,
      final ImageService.State imageState,
      final ProjectService.State projectState,
      final FlavorService.State vmFlavorState,
      final FlavorService.State diskFlavorState)
      throws IOException {

    final Service service = this;

    FutureCallback<CreateVmTaskService.State> callback =
        new FutureCallback<CreateVmTaskService.State>() {
          @Override
          public void onSuccess(@Nullable CreateVmTaskService.State result) {
            switch (result.taskState.stage) {
              case FINISHED:
                updateVmId(currentState, result.vmId);
                break;
              case CANCELLED:
                TaskUtils.sendSelfPatch(
                    service,
                    buildPatch(TaskState.TaskStage.CANCELLED, (ServiceErrorResponse) null));
                break;
              case FAILED:
                ServiceErrorResponse failed = new ServiceErrorResponse();
                failed.message =
                    String.format(
                        "CreateVmTaskService failed to create the vm. documentSelfLink: %s. failureReason: %s",
                        result.documentSelfLink, result.taskState.failure.message);
                TaskUtils.sendSelfPatch(service, buildPatch(TaskState.TaskStage.FAILED, failed));
                break;
            }
          }

          @Override
          public void onFailure(Throwable t) {
            failTask(t);
          }
        };

    CreateVmTaskService.State startState = new CreateVmTaskService.State();
    startState.projectId = ServiceUtils.getIDFromDocumentSelfLink(projectState.documentSelfLink);
    startState.vmCreateSpec =
        composeVmCreateSpec(vmState, hostState, imageState, vmFlavorState, diskFlavorState);
    startState.queryCreateVmTaskInterval = currentState.queryCreateVmTaskInterval;

    TaskUtils.startTaskAsync(
        this,
        CreateVmTaskFactoryService.SELF_LINK,
        startState,
        (state) -> TaskUtils.finalTaskStages.contains(state.taskState.stage),
        CreateVmTaskService.State.class,
        HostUtils.getDeployerContext(this).getTaskPollDelay(),
        callback);
  }
 /**
  * This method is the completion handler for the setMetadataTask.
  *
  * @param currentState Supplies the current state object.
  * @param task Supplies the task object.
  */
 private void processSetMetadataTask(final State currentState, final Task task) {
   switch (task.getState().toUpperCase()) {
     case "QUEUED":
     case "STARTED":
       scheduleSetMetadataTaskCall(this, currentState, task.getId());
       break;
     case "ERROR":
       throw new RuntimeException(ApiUtils.getErrors(task));
     case "COMPLETED":
       TaskUtils.sendSelfPatch(this, buildPatch(TaskState.TaskStage.FINISHED, (Throwable) null));
       break;
     default:
       throw new RuntimeException("Unknown task state: " + task.getState());
   }
 }
  /**
   * This method creates a Kubernetes Cluster Service instance. On successful creation, the method
   * moves the sub-stage to SETUP_MASTER.
   *
   * @param currentState
   * @param imageId
   */
  private void createClusterService(
      final KubernetesClusterCreateTask currentState, final String imageId) {

    ClusterService.State cluster = new ClusterService.State();
    cluster.clusterState = ClusterState.CREATING;
    cluster.clusterName = currentState.clusterName;
    cluster.clusterType = ClusterType.KUBERNETES;
    cluster.imageId = imageId;
    cluster.projectId = currentState.projectId;
    cluster.diskFlavorName = currentState.diskFlavorName;
    cluster.masterVmFlavorName = currentState.masterVmFlavorName;
    cluster.otherVmFlavorName = currentState.otherVmFlavorName;
    cluster.vmNetworkId = currentState.vmNetworkId;
    cluster.slaveCount = currentState.slaveCount;
    cluster.extendedProperties = new HashMap<>();
    cluster.extendedProperties.put(ClusterManagerConstants.EXTENDED_PROPERTY_DNS, currentState.dns);
    cluster.extendedProperties.put(
        ClusterManagerConstants.EXTENDED_PROPERTY_GATEWAY, currentState.gateway);
    cluster.extendedProperties.put(
        ClusterManagerConstants.EXTENDED_PROPERTY_NETMASK, currentState.netmask);
    cluster.extendedProperties.put(
        ClusterManagerConstants.EXTENDED_PROPERTY_CONTAINER_NETWORK, currentState.containerNetwork);
    cluster.extendedProperties.put(
        ClusterManagerConstants.EXTENDED_PROPERTY_ETCD_IPS,
        NodeTemplateUtils.serializeAddressList(currentState.etcdIps));
    cluster.extendedProperties.put(
        ClusterManagerConstants.EXTENDED_PROPERTY_MASTER_IP, currentState.masterIp);
    cluster.documentSelfLink = currentState.clusterId;

    sendRequest(
        HostUtils.getCloudStoreHelper(this)
            .createPost(ClusterServiceFactory.SELF_LINK)
            .setBody(cluster)
            .setCompletion(
                (operation, throwable) -> {
                  if (null != throwable) {
                    failTask(throwable);
                    return;
                  }

                  KubernetesClusterCreateTask patchState =
                      buildPatch(TaskState.TaskStage.STARTED, TaskState.SubStage.SETUP_ETCD);
                  patchState.imageId = imageId;
                  TaskUtils.sendSelfPatch(this, patchState);
                }));
  }
  private void updateStates(
      final KubernetesClusterCreateTask currentState,
      final KubernetesClusterCreateTask patchState,
      final ClusterService.State clusterPatchState) {

    sendRequest(
        HostUtils.getCloudStoreHelper(this)
            .createPatch(ClusterServiceFactory.SELF_LINK + "/" + currentState.clusterId)
            .setBody(clusterPatchState)
            .setCompletion(
                (Operation operation, Throwable throwable) -> {
                  if (null != throwable) {
                    failTask(throwable);
                    return;
                  }

                  TaskUtils.sendSelfPatch(KubernetesClusterCreateTaskService.this, patchState);
                }));
  }
  private void startMaintenance(final KubernetesClusterCreateTask currentState) {
    ClusterMaintenanceTaskService.State patchState = new ClusterMaintenanceTaskService.State();
    patchState.taskState = new TaskState();
    patchState.taskState.stage = TaskState.TaskStage.STARTED;

    // Start the maintenance task async without waiting for its completion so that the creation task
    // can finish immediately.
    Operation patchOperation =
        Operation.createPatch(
                UriUtils.buildUri(
                    getHost(),
                    ClusterMaintenanceTaskFactoryService.SELF_LINK + "/" + currentState.clusterId))
            .setBody(patchState)
            .setCompletion(
                (Operation operation, Throwable throwable) -> {
                  // We ignore the failure here since maintenance task will kick in eventually.
                  TaskUtils.sendSelfPatch(this, buildPatch(TaskState.TaskStage.FINISHED, null));
                });
    sendRequest(patchOperation);
  }
  @Override
  public void handleStart(Operation start) {
    ServiceUtils.logInfo(this, "Starting service %s", getSelfLink());
    KubernetesClusterCreateTask startState = start.getBody(KubernetesClusterCreateTask.class);
    InitializationUtils.initialize(startState);
    validateStartState(startState);

    if (startState.taskState.stage == TaskState.TaskStage.CREATED) {
      startState.taskState.stage = TaskState.TaskStage.STARTED;
      startState.taskState.subStage = TaskState.SubStage.ALLOCATE_RESOURCES;
    }
    start.setBody(startState).complete();

    try {
      if (ControlFlags.isOperationProcessingDisabled(startState.controlFlags)) {
        ServiceUtils.logInfo(this, "Skipping start operation processing (disabled)");
      } else if (TaskState.TaskStage.STARTED == startState.taskState.stage) {
        TaskUtils.sendSelfPatch(
            this, buildPatch(startState.taskState.stage, TaskState.SubStage.ALLOCATE_RESOURCES));
      }
    } catch (Throwable t) {
      failTask(t);
    }
  }
 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()));
 }
 private void sendStageProgressPatch(final TaskState.TaskStage stage) {
   ServiceUtils.logInfo(this, "Sending stage progress patch %s", stage);
   TaskUtils.sendSelfPatch(this, buildPatch(stage, null));
 }