/**
   * This method queries for the document link of the cluster configuration for the Kubernetes
   * Cluster.
   *
   * @param currentState
   */
  private void queryClusterConfiguration(final KubernetesClusterCreateTask currentState) {
    QueryTask.Query kindClause =
        new QueryTask.Query()
            .setTermPropertyName(ServiceDocument.FIELD_NAME_KIND)
            .setTermMatchValue(Utils.buildKind(ClusterConfigurationService.State.class));

    QueryTask.Query idClause =
        new QueryTask.Query()
            .setTermPropertyName(ClusterConfigurationService.State.FIELD_NAME_SELF_LINK)
            .setTermMatchValue(
                ClusterConfigurationServiceFactory.SELF_LINK
                    + "/"
                    + ClusterType.KUBERNETES.toString().toLowerCase());

    QueryTask.QuerySpecification querySpecification = new QueryTask.QuerySpecification();
    querySpecification.query.addBooleanClause(kindClause);
    querySpecification.query.addBooleanClause(idClause);
    QueryTask queryTask = QueryTask.create(querySpecification).setDirect(true);

    sendRequest(
        HostUtils.getCloudStoreHelper(this)
            .createBroadcastPost(
                ServiceUriPaths.CORE_LOCAL_QUERY_TASKS, ServiceUriPaths.DEFAULT_NODE_SELECTOR)
            .setBody(queryTask)
            .setCompletion(
                (Operation operation, Throwable throwable) -> {
                  if (null != throwable) {
                    failTask(throwable);
                    return;
                  }

                  NodeGroupBroadcastResponse queryResponse =
                      operation.getBody(NodeGroupBroadcastResponse.class);
                  Set<String> documentLinks =
                      QueryTaskUtils.getBroadcastQueryResults(queryResponse);
                  if (documentLinks.isEmpty()) {
                    failTask(
                        new IllegalStateException(
                            String.format(
                                "Cannot find cluster configuration for %s",
                                ClusterType.KUBERNETES.toString())));
                    return;
                  }

                  retrieveClusterConfiguration(currentState, documentLinks.iterator().next());
                }));
  }
  /**
   * 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);
                }));
  }
  /**
   * This method retrieves the cluster configuration entity for the Kubernetes Cluster.
   *
   * @param currentState
   * @param clusterConfigurationLink
   */
  private void retrieveClusterConfiguration(
      final KubernetesClusterCreateTask currentState, String clusterConfigurationLink) {
    sendRequest(
        HostUtils.getCloudStoreHelper(this)
            .createGet(clusterConfigurationLink)
            .setCompletion(
                (Operation operation, Throwable throwable) -> {
                  if (null != throwable) {
                    failTask(throwable);
                    return;
                  }

                  ClusterConfigurationService.State clusterConfiguration =
                      operation.getBody(ClusterConfigurationService.State.class);
                  createClusterService(currentState, clusterConfiguration.imageId);
                }));
  }
  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);
                }));
  }