/**
   * Deletes all the jobs from Chronos.
   *
   * @param jobgraph the job graph.
   * @param client the {@link Chronos} client.
   * @param failAtFirst if <tt>true</tt> throws an exception at the first job deletion error,
   *     otherwise it tries to delete every other job.
   * @return <tt>true</tt> if all jobs have been deleted, <tt>false</tt> otherwise.
   */
  protected boolean deleteJobsOnChronosIteratively(
      Deployment deployment,
      Map<String, IndigoJob> jobgraph,
      TemplateTopologicalOrderIterator templateTopologicalOrderIterator,
      Chronos client,
      boolean failAtFirst) {

    Resource currentNode = templateTopologicalOrderIterator.getCurrent();
    if (currentNode == null) {
      return false;
    }

    IndigoJob currentJob = jobgraph.get(currentNode.getToscaNodeName());
    boolean failed = false;

    // Delete current job (all jobs iteratively)
    try {
      try {
        updateResource(deployment, currentJob, NodeStates.DELETING);
        currentNode.setState(NodeStates.DELETING);

        String jobName = currentJob.getChronosJob().getName();

        client.deleteJob(jobName);

        LOG.debug(
            "Deleted Chronos job <{}> for deployment <{}> ({}/{})",
            jobName,
            deployment.getId(),
            templateTopologicalOrderIterator.getPosition() + 1,
            templateTopologicalOrderIterator.getNodeSize());
      } catch (ChronosException ce) {
        // Chronos API hack (to avoid error 400 if the job to delete does not exist)
        if (ce.getStatus() != 400 && ce.getStatus() != 404) {
          throw new RuntimeException(String.format("Status Code: <%s>", ce.getStatus()));
        }
      }
    } catch (Exception ex) {
      // Just log the error
      String errorMsg = String.format("Failed to delete job <%s> on Chronos: %s", ex.getMessage());
      LOG.error(errorMsg);

      failed = true;
      // Update job status
      updateResource(deployment, currentJob, NodeStates.ERROR);
      currentNode.setState(NodeStates.ERROR);

      // Only throw exception if required
      if (failAtFirst) {
        // TODO use a custom exception ?
        throw new RuntimeException(errorMsg);
      }
    }

    return !failed;
  }
  /**
   * @param deployment the deployment from which create the jobs
   * @param jobgraph the graph of the jobs
   * @param templateTopologicalOrderIterator the topological order iterator of the jobs
   * @param client the Chronos client to use
   * @return <tt>true</tt> if there are more nodes to create, <tt>false</tt> otherwise.
   */
  protected boolean createJobsOnChronosIteratively(
      Deployment deployment,
      Map<String, IndigoJob> jobgraph,
      TemplateTopologicalOrderIterator templateTopologicalOrderIterator,
      Chronos client) {

    // Create Jobs in the required order on Chronos
    Resource currentNode = templateTopologicalOrderIterator.getCurrent();
    if (currentNode == null) {
      return false;
    }

    IndigoJob currentJob = jobgraph.get(currentNode.getToscaNodeName());

    // Create jobs based on the topological order
    try {
      String nodeTypeMsg;
      if (currentJob.getParents().isEmpty()) {
        // Scheduled job (not dependent)
        nodeTypeMsg = "scheduled";
        client.createJob(currentJob.getChronosJob());
      } else {
        // Dependent job
        nodeTypeMsg = String.format("parents <%s>", currentJob.getChronosJob().getParents());
        client.createDependentJob(currentJob.getChronosJob());
      }

      LOG.debug(
          "Created job for deployment <{}> on Chronos: name <{}>, {} ({}/{})",
          deployment.getId(),
          currentJob.getChronosJob().getName(),
          nodeTypeMsg,
          templateTopologicalOrderIterator.getPosition() + 1,
          templateTopologicalOrderIterator.getNodeSize());
      // Update job status
      updateResource(deployment, currentJob, NodeStates.CREATED);
      // The node in the iterator is not actually an entity (serialization issues)
      currentNode.setState(NodeStates.CREATED);

    } catch (ChronosException exception) { // Chronos job launch error
      // Update job status
      updateResource(deployment, currentJob, NodeStates.ERROR);
      currentNode.setState(NodeStates.ERROR);
      // TODO use a custom exception ?
      throw new RuntimeException(
          String.format(
              "Failed to launch job <%s> on Chronos. Status Code: <%s>",
              currentJob.getChronosJob().getName(), exception.getStatus()));
    }

    return templateTopologicalOrderIterator.getNext() != null;
  }