@Test
  public void testAssignmentClearedOnError() throws Exception {
    expect(offerManager.getOffers(GROUP_KEY)).andReturn(ImmutableSet.of(OFFER));
    offerManager.launchTask(MESOS_OFFER.getId(), TASK_INFO);
    expectLastCall().andThrow(new OfferManager.LaunchException("expected"));
    expect(tierManager.getTier(TASK.getAssignedTask().getTask())).andReturn(DEFAULT);
    expect(filter.filter(UNUSED, RESOURCE_REQUEST)).andReturn(ImmutableSet.of());
    expect(
            stateManager.assignTask(
                storeProvider,
                Tasks.id(TASK),
                MESOS_OFFER.getHostname(),
                MESOS_OFFER.getSlaveId(),
                ImmutableMap.of(PORT_NAME, PORT)))
        .andReturn(TASK.getAssignedTask());
    expect(
            stateManager.changeState(
                storeProvider, Tasks.id(TASK), Optional.of(PENDING), LOST, LAUNCH_FAILED_MSG))
        .andReturn(StateChangeResult.SUCCESS);
    expect(taskFactory.createFrom(TASK.getAssignedTask(), MESOS_OFFER.getSlaveId()))
        .andReturn(TASK_INFO);

    control.replay();

    assertFalse(
        assigner.maybeAssign(
            storeProvider,
            new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY),
            TaskGroupKey.from(TASK.getAssignedTask().getTask()),
            Tasks.id(TASK),
            NO_RESERVATION));
  }
  @Test
  public void testAssignNoVetoes() throws Exception {
    expect(offerManager.getOffers(GROUP_KEY)).andReturn(ImmutableSet.of(OFFER));
    offerManager.launchTask(MESOS_OFFER.getId(), TASK_INFO);
    expect(tierManager.getTier(TASK.getAssignedTask().getTask())).andReturn(DEFAULT);
    expect(filter.filter(UNUSED, RESOURCE_REQUEST)).andReturn(ImmutableSet.of());
    expect(
            stateManager.assignTask(
                storeProvider,
                Tasks.id(TASK),
                MESOS_OFFER.getHostname(),
                MESOS_OFFER.getSlaveId(),
                ImmutableMap.of(PORT_NAME, PORT)))
        .andReturn(TASK.getAssignedTask());
    expect(taskFactory.createFrom(TASK.getAssignedTask(), MESOS_OFFER.getSlaveId()))
        .andReturn(TASK_INFO);

    control.replay();

    assertTrue(
        assigner.maybeAssign(
            storeProvider,
            new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY),
            TaskGroupKey.from(TASK.getAssignedTask().getTask()),
            Tasks.id(TASK),
            ImmutableMap.of(SLAVE_ID, GROUP_KEY)));
  }
  @Test
  public void testAssignerDoesNotReturnOnFirstMismatch() throws Exception {
    // Ensures scheduling loop does not terminate prematurely when the first mismatch is identified.
    HostOffer mismatched =
        new HostOffer(
            Offer.newBuilder()
                .setId(OfferID.newBuilder().setValue("offerId0"))
                .setFrameworkId(FrameworkID.newBuilder().setValue("frameworkId"))
                .setSlaveId(SlaveID.newBuilder().setValue("slaveId0"))
                .setHostname("hostName0")
                .addResources(
                    Resource.newBuilder()
                        .setName("ports")
                        .setType(Type.RANGES)
                        .setRanges(
                            Ranges.newBuilder()
                                .addRange(Range.newBuilder().setBegin(PORT).setEnd(PORT))))
                .build(),
            IHostAttributes.build(new HostAttributes()));

    expect(offerManager.getOffers(GROUP_KEY)).andReturn(ImmutableSet.of(mismatched, OFFER));
    expect(tierManager.getTier(TASK.getAssignedTask().getTask())).andReturn(DEFAULT).times(2);
    expect(
            filter.filter(
                new UnusedResource(
                    Resources.from(mismatched.getOffer()).slot(), mismatched.getAttributes()),
                new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY)))
        .andReturn(ImmutableSet.of(Veto.constraintMismatch("constraint mismatch")));
    offerManager.banOffer(mismatched.getOffer().getId(), GROUP_KEY);
    expect(
            filter.filter(
                new UnusedResource(Resources.from(OFFER.getOffer()).slot(), OFFER.getAttributes()),
                new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY)))
        .andReturn(ImmutableSet.of());

    expect(
            stateManager.assignTask(
                storeProvider,
                Tasks.id(TASK),
                OFFER.getOffer().getHostname(),
                OFFER.getOffer().getSlaveId(),
                ImmutableMap.of(PORT_NAME, PORT)))
        .andReturn(TASK.getAssignedTask());
    expect(taskFactory.createFrom(TASK.getAssignedTask(), OFFER.getOffer().getSlaveId()))
        .andReturn(TASK_INFO);
    offerManager.launchTask(OFFER.getOffer().getId(), TASK_INFO);

    control.replay();

    assertTrue(
        assigner.maybeAssign(
            storeProvider,
            new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY),
            TaskGroupKey.from(TASK.getAssignedTask().getTask()),
            Tasks.id(TASK),
            ImmutableMap.of(SLAVE_ID, GROUP_KEY)));
  }
 @Override
 public ResourceSlot apply(PreemptionVictim victim) {
   ResourceSlot slot = victim.getResourceSlot();
   if (tierManager.getTier(victim.getConfig()).isRevocable()) {
     // Revocable task CPU cannot be used for preemption purposes as it's a compressible
     // resource. We can still use RAM, DISK and PORTS as they are not compressible.
     slot = new ResourceSlot(0.0, slot.getRam(), slot.getDisk(), slot.getNumPorts());
   }
   return slot.add(executorSettings.getExecutorOverhead());
 }
  @Test
  public void testTaskWithReservedSlaveLandsElsewhere() throws Exception {
    // Ensures slave/task reservation relationship is only enforced in slave->task direction
    // and permissive in task->slave direction. In other words, a task with a slave reservation
    // should still be tried against other unreserved slaves.
    HostOffer offer =
        new HostOffer(
            Offer.newBuilder()
                .setId(OfferID.newBuilder().setValue("offerId0"))
                .setFrameworkId(FrameworkID.newBuilder().setValue("frameworkId"))
                .setSlaveId(SlaveID.newBuilder().setValue("slaveId0"))
                .setHostname("hostName0")
                .addResources(
                    Resource.newBuilder()
                        .setName("ports")
                        .setType(Type.RANGES)
                        .setRanges(
                            Ranges.newBuilder()
                                .addRange(Range.newBuilder().setBegin(PORT).setEnd(PORT))))
                .build(),
            IHostAttributes.build(new HostAttributes()));

    expect(offerManager.getOffers(GROUP_KEY)).andReturn(ImmutableSet.of(offer, OFFER));
    expect(tierManager.getTier(TASK.getAssignedTask().getTask())).andReturn(DEFAULT);
    expect(filter.filter(UNUSED, RESOURCE_REQUEST)).andReturn(ImmutableSet.of());
    expect(
            stateManager.assignTask(
                storeProvider,
                Tasks.id(TASK),
                offer.getOffer().getHostname(),
                offer.getOffer().getSlaveId(),
                ImmutableMap.of(PORT_NAME, PORT)))
        .andReturn(TASK.getAssignedTask());
    expect(taskFactory.createFrom(TASK.getAssignedTask(), offer.getOffer().getSlaveId()))
        .andReturn(TASK_INFO);
    offerManager.launchTask(offer.getOffer().getId(), TASK_INFO);

    control.replay();

    assertTrue(
        assigner.maybeAssign(
            storeProvider,
            new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY),
            TaskGroupKey.from(TASK.getAssignedTask().getTask()),
            Tasks.id(TASK),
            ImmutableMap.of(SLAVE_ID, GROUP_KEY)));
  }
  @Test
  public void testAssignVetoesWithNoStaticBan() throws Exception {
    expect(offerManager.getOffers(GROUP_KEY)).andReturn(ImmutableSet.of(OFFER));
    expect(tierManager.getTier(TASK.getAssignedTask().getTask())).andReturn(DEFAULT);
    expect(filter.filter(UNUSED, RESOURCE_REQUEST))
        .andReturn(ImmutableSet.of(Veto.unsatisfiedLimit("limit")));

    control.replay();

    assertFalse(
        assigner.maybeAssign(
            storeProvider,
            new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY),
            TaskGroupKey.from(TASK.getAssignedTask().getTask()),
            Tasks.id(TASK),
            NO_RESERVATION));
  }
  /**
   * Check validity of and populates defaults in a task configuration. This will return a deep copy
   * of the provided task configuration with default configuration values applied, and configuration
   * map values sanitized and applied to their respective struct fields.
   *
   * @param config Task config to validate and populate.
   * @return A reference to the modified {@code config} (for chaining).
   * @throws TaskDescriptionException If the task is invalid.
   */
  public ITaskConfig validateAndPopulate(ITaskConfig config) throws TaskDescriptionException {
    TaskConfig builder = config.newBuilder();

    if (!builder.isSetRequestedPorts()) {
      builder.setRequestedPorts(ImmutableSet.of());
    }

    if (config.isSetTier() && !UserProvidedStrings.isGoodIdentifier(config.getTier())) {
      throw new TaskDescriptionException("Tier contains illegal characters: " + config.getTier());
    }

    try {
      tierManager.getTier(config);
    } catch (IllegalArgumentException e) {
      throw new TaskDescriptionException(e.getMessage(), e);
    }

    if (!JobKeys.isValid(config.getJob())) {
      // Job key is set but invalid
      throw new TaskDescriptionException("Job key " + config.getJob() + " is invalid.");
    }

    // A task must either have an executor configuration or specify a Docker container.
    if (!builder.isSetExecutorConfig()
        && !(builder.isSetContainer() && builder.getContainer().isSetDocker())) {

      throw new TaskDescriptionException(NO_EXECUTOR_OR_CONTAINER);
    }

    // Docker containers don't require executors, validate the rest
    if (builder.isSetExecutorConfig()) {

      if (!builder.getExecutorConfig().isSetName()) {
        throw new TaskDescriptionException(INVALID_EXECUTOR_CONFIG);
      }

      executorSettings
          .getExecutorConfig(builder.getExecutorConfig().getName())
          .orElseThrow(
              () ->
                  new TaskDescriptionException(
                      "Configuration for executor '"
                          + builder.getExecutorConfig().getName()
                          + "' doesn't exist."));
    }

    // Maximize the usefulness of any thrown error message by checking required fields first.
    for (RequiredFieldValidator<?> validator : REQUIRED_FIELDS_VALIDATORS) {
      validator.validate(builder);
    }

    IConstraint constraint = getDedicatedConstraint(config);
    if (constraint != null) {
      if (!isValueConstraint(constraint.getConstraint())) {
        throw new TaskDescriptionException("A dedicated constraint must be of value type.");
      }

      IValueConstraint valueConstraint = constraint.getConstraint().getValue();

      if (valueConstraint.getValues().size() != 1) {
        throw new TaskDescriptionException("A dedicated constraint must have exactly one value");
      }

      String dedicatedRole = getRole(valueConstraint);
      if (!("*".equals(dedicatedRole) || config.getJob().getRole().equals(dedicatedRole))) {
        throw new TaskDescriptionException(
            "Only " + dedicatedRole + " may use hosts dedicated for that role.");
      }
    }

    Optional<Container._Fields> containerType;
    if (config.isSetContainer()) {
      IContainer containerConfig = config.getContainer();
      containerType = Optional.of(containerConfig.getSetField());
      if (containerConfig.isSetDocker()) {
        if (!containerConfig.getDocker().isSetImage()) {
          throw new TaskDescriptionException("A container must specify an image.");
        }
        if (containerConfig.getDocker().getParameters().isEmpty()) {
          for (Map.Entry<String, String> e : settings.defaultDockerParameters.entries()) {
            builder
                .getContainer()
                .getDocker()
                .addToParameters(new DockerParameter(e.getKey(), e.getValue()));
          }
        } else {
          if (!settings.allowDockerParameters) {
            throw new TaskDescriptionException(NO_DOCKER_PARAMETERS);
          }
        }

        if (settings.requireDockerUseExecutor && !config.isSetExecutorConfig()) {
          throw new TaskDescriptionException(EXECUTOR_REQUIRED_WITH_DOCKER);
        }
      }
    } else {
      // Default to mesos container type if unset.
      containerType = Optional.of(Container._Fields.MESOS);
    }

    if (!containerType.isPresent()) {
      throw new TaskDescriptionException("A job must have a container type.");
    }
    if (!settings.allowedContainerTypes.contains(containerType.get())) {
      throw new TaskDescriptionException(
          "This scheduler is not configured to allow the container type "
              + containerType.get().toString());
    }

    thriftBackfill.backfillTask(builder);

    String types =
        config
            .getResources()
            .stream()
            .collect(Collectors.groupingBy(e -> ResourceType.fromResource(e)))
            .entrySet()
            .stream()
            .filter(e -> !e.getKey().isMultipleAllowed() && e.getValue().size() > 1)
            .map(r -> r.getKey().getAuroraName())
            .sorted()
            .collect(Collectors.joining(", "));

    if (!Strings.isNullOrEmpty(types)) {
      throw new TaskDescriptionException("Multiple resource values are not supported for " + types);
    }

    if (!settings.allowGpuResource
        && config
            .getResources()
            .stream()
            .filter(r -> ResourceType.fromResource(r).equals(GPUS))
            .findAny()
            .isPresent()) {

      throw new TaskDescriptionException("GPU resource support is disabled in this cluster.");
    }

    if (!settings.enableMesosFetcher && !config.getMesosFetcherUris().isEmpty()) {
      throw new TaskDescriptionException(MESOS_FETCHER_DISABLED);
    }

    if (config.getContainer().isSetMesos()) {
      IMesosContainer container = config.getContainer().getMesos();
      if (!settings.allowContainerVolumes && !container.getVolumes().isEmpty()) {
        throw new TaskDescriptionException(NO_CONTAINER_VOLUMES);
      }
    }

    maybeFillLinks(builder);

    return ITaskConfig.build(builder);
  }