content: /**
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.aurora.scheduler.configuration;
  
  import java.util.List;
  
  import com.google.common.collect.ImmutableList;
  import com.google.common.collect.ImmutableMap;
  import com.google.common.collect.ImmutableSet;
  
  import org.apache.aurora.gen.AppcImage;
  import org.apache.aurora.gen.Constraint;
  import org.apache.aurora.gen.Container;
  import org.apache.aurora.gen.CoordinatorSlaPolicy;
  import org.apache.aurora.gen.CountSlaPolicy;
  import org.apache.aurora.gen.CronCollisionPolicy;
  import org.apache.aurora.gen.DockerParameter;
  import org.apache.aurora.gen.ExecutorConfig;
  import org.apache.aurora.gen.Identity;
  import org.apache.aurora.gen.Image;
  import org.apache.aurora.gen.JobConfiguration;
  import org.apache.aurora.gen.JobKey;
  import org.apache.aurora.gen.LimitConstraint;
  import org.apache.aurora.gen.MesosContainer;
  import org.apache.aurora.gen.MesosFetcherURI;
  import org.apache.aurora.gen.Mode;
  import org.apache.aurora.gen.PercentageSlaPolicy;
  import org.apache.aurora.gen.SlaPolicy;
  import org.apache.aurora.gen.TaskConfig;
  import org.apache.aurora.gen.TaskConstraint;
  import org.apache.aurora.gen.ValueConstraint;
  import org.apache.aurora.gen.Volume;
  import org.apache.aurora.gen.apiConstants;
  import org.apache.aurora.scheduler.base.JobKeys;
  import org.apache.aurora.scheduler.base.TaskTestUtil;
  import org.apache.aurora.scheduler.configuration.ConfigurationManager.ConfigurationManagerSettings;
  import org.apache.aurora.scheduler.configuration.ConfigurationManager.TaskDescriptionException;
  import org.apache.aurora.scheduler.mesos.TestExecutorSettings;
  import org.apache.aurora.scheduler.storage.entities.IDockerParameter;
  import org.apache.aurora.scheduler.storage.entities.IJobConfiguration;
  import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
  import org.junit.Rule;
  import org.junit.Test;
  import org.junit.rules.ExpectedException;
  
  import static org.apache.aurora.gen.Resource.diskMb;
  import static org.apache.aurora.gen.Resource.namedPort;
  import static org.apache.aurora.gen.Resource.numCpus;
  import static org.apache.aurora.gen.Resource.numGpus;
  import static org.apache.aurora.gen.Resource.ramMb;
  import static org.apache.aurora.gen.test.testConstants.INVALID_IDENTIFIERS;
  import static org.apache.aurora.gen.test.testConstants.VALID_IDENTIFIERS;
  import static org.apache.aurora.scheduler.base.UserProvidedStrings.isGoodIdentifier;
  import static org.apache.aurora.scheduler.configuration.ConfigurationManager.DEDICATED_ATTRIBUTE;
  import static org.apache.aurora.scheduler.configuration.ConfigurationManager.NO_CONTAINER_VOLUMES;
  import static org.hamcrest.CoreMatchers.is;
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertThat;
  import static org.junit.Assert.assertTrue;
  
  // TODO(kevints): Improve test coverage for this class.
  public class ConfigurationManagerTest {
  
    @Rule
    public ExpectedException expectedException = ExpectedException.none();
  
    private static final ImmutableSet<Container._Fields> ALL_CONTAINER_TYPES =
        ImmutableSet.copyOf(Container._Fields.values());
    private static final int MIN_REQUIRED_INSTANCES = 20;
    private static final long MAX_SLA_DURATION_SECS = 7200;
    private static final long DEFAULT_SLA_PERCENTAGE = 95;
  
    private static final JobKey JOB_KEY = new JobKey("owner-role", "devel", "email_stats");
    private static final JobConfiguration UNSANITIZED_JOB_CONFIGURATION = new JobConfiguration()
        .setKey(JOB_KEY)
        .setCronSchedule("0 2 * * *")
        .setCronCollisionPolicy(CronCollisionPolicy.KILL_EXISTING)
        .setInstanceCount(1)
        .setTaskConfig(
            new TaskConfig()
                .setJob(JOB_KEY)
                .setIsService(false)
                .setTaskLinks(ImmutableMap.of())
                .setExecutorConfig(new ExecutorConfig(apiConstants.AURORA_EXECUTOR_NAME, "config"))
                .setPriority(0)
                .setOwner(null)
                .setContactEmail("foo@twitter.com")
                .setProduction(false)
                .setMetadata(null)
                .setMaxTaskFailures(0)
                .setConstraints(
                    ImmutableSet.of(
                        new Constraint()
                            .setName("executor")
                            .setConstraint(TaskConstraint
                                .value(new ValueConstraint()
                                    .setNegated(false)
                                    .setValues(ImmutableSet.of("legacy")))),
                        new Constraint()
                            .setName("host")
                            .setConstraint(TaskConstraint.limit(new LimitConstraint()
                                .setLimit(1))),
                        new Constraint()
                            .setName(DEDICATED_ATTRIBUTE)
                            .setConstraint(TaskConstraint.value(new ValueConstraint(
                                false, ImmutableSet.of("foo"))))))
                .setOwner(new Identity().setUser("owner-user"))
                .setResources(ImmutableSet.of(
                    numCpus(1.0),
                    ramMb(1),
                    diskMb(1))));
    private static final ITaskConfig CONFIG_WITH_CONTAINER =
        TaskTestUtil.makeConfig(JobKeys.from("role", "env", "job"));
  
    private static final ConfigurationManager CONFIGURATION_MANAGER = new ConfigurationManager(
        new ConfigurationManagerSettings(
            ALL_CONTAINER_TYPES,
            false,
            ImmutableList.of(),
            true,
            false,
            true,
            false,
            MIN_REQUIRED_INSTANCES,
            MAX_SLA_DURATION_SECS,
            ConfigurationManager.DEFAULT_ALLOWED_JOB_ENVIRONMENTS,
            false),
        TaskTestUtil.TIER_MANAGER,
        TaskTestUtil.THRIFT_BACKFILL,
        TestExecutorSettings.THERMOS_EXECUTOR);
    private static final ConfigurationManager DOCKER_CONFIGURATION_MANAGER = new ConfigurationManager(
        new ConfigurationManagerSettings(
            ALL_CONTAINER_TYPES,
            true,
            ImmutableList.of(new DockerParameter("foo", "bar")),
            false,
            true,
            true,
            true,
            MIN_REQUIRED_INSTANCES,
            MAX_SLA_DURATION_SECS,
            ConfigurationManager.DEFAULT_ALLOWED_JOB_ENVIRONMENTS,
            false),
        TaskTestUtil.TIER_MANAGER,
        TaskTestUtil.THRIFT_BACKFILL,
        TestExecutorSettings.THERMOS_EXECUTOR);
  
    @Test
    public void testIsGoodIdentifier() {
      for (String identifier : VALID_IDENTIFIERS) {
        assertTrue(isGoodIdentifier(identifier));
      }
      for (String identifier : INVALID_IDENTIFIERS) {
        assertFalse(isGoodIdentifier(identifier));
      }
    }
  
    @Test
    public void testBadContainerConfig() throws TaskDescriptionException {
      TaskConfig taskConfig = CONFIG_WITH_CONTAINER.newBuilder();
      taskConfig.getContainer().getDocker().setImage(null);
  
      expectTaskDescriptionException("A container must specify an image");
      CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(taskConfig), 100);
    }
  
    @Test
    public void testDisallowedDockerParameters() throws TaskDescriptionException {
      TaskConfig taskConfig = CONFIG_WITH_CONTAINER.newBuilder();
      taskConfig.getContainer().getDocker().addToParameters(new DockerParameter("foo", "bar"));
  
      expectTaskDescriptionException(ConfigurationManager.NO_DOCKER_PARAMETERS);
      CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(taskConfig), 100);
    }
  
    @Test
    public void testDisallowNoExecutorDockerTask() throws TaskDescriptionException {
      TaskConfig builder = CONFIG_WITH_CONTAINER.newBuilder();
      builder.getContainer().getDocker().unsetParameters();
      builder.unsetExecutorConfig();
  
      expectTaskDescriptionException(ConfigurationManager.EXECUTOR_REQUIRED_WITH_DOCKER);
      CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 1);
    }
  
    @Test
    public void testAllowNoExecutorDockerTask() throws TaskDescriptionException {
      TaskConfig builder = CONFIG_WITH_CONTAINER.newBuilder();
      builder.unsetExecutorConfig();
  
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 100);
    }
  
    @Test
    public void testInvalidTier() throws TaskDescriptionException {
      ITaskConfig config = ITaskConfig.build(UNSANITIZED_JOB_CONFIGURATION.deepCopy().getTaskConfig()
          .setTier("pr/d"));
  
      expectTaskDescriptionException("Tier contains illegal characters");
      CONFIGURATION_MANAGER.validateAndPopulate(config, 100);
    }
  
    @Test
    public void testDefaultDockerParameters() throws TaskDescriptionException {
      TaskConfig builder = CONFIG_WITH_CONTAINER.newBuilder();
      builder.getContainer().getDocker().setParameters(ImmutableList.of());
  
      ITaskConfig result =
          DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 100);
  
      // The resulting task config should contain parameters supplied to the ConfigurationManager.
      List<IDockerParameter> params = result.getContainer().getDocker().getParameters();
      assertThat(
          params, is(ImmutableList.of(IDockerParameter.build(new DockerParameter("foo", "bar")))));
    }
  
    @Test
    public void testPassthroughDockerParameters() throws TaskDescriptionException {
      TaskConfig taskConfig = CONFIG_WITH_CONTAINER.newBuilder();
      DockerParameter userParameter = new DockerParameter("bar", "baz");
      taskConfig.getContainer().getDocker().getParameters().clear();
      taskConfig.getContainer().getDocker().addToParameters(userParameter);
  
      ITaskConfig result = DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          ITaskConfig.build(taskConfig), 100);
  
      // The resulting task config should contain parameters supplied from user config.
      List<IDockerParameter> params = result.getContainer().getDocker().getParameters();
      assertThat(params, is(ImmutableList.of(IDockerParameter.build(userParameter))));
    }
  
    @Test
    public void testExclusiveDedicatedRoleAllowed() throws Exception {
      TaskConfig builder = UNSANITIZED_JOB_CONFIGURATION.deepCopy().getTaskConfig();
      builder.setConstraints(ImmutableSet.of(new Constraint()
          .setName(DEDICATED_ATTRIBUTE)
          .setConstraint(TaskConstraint.value(
              new ValueConstraint(false, ImmutableSet.of(JOB_KEY.getRole() + "/f"))))));
  
      CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 100);
    }
  
    @Test
    public void testNonExclusiveDedicatedAllowed() throws Exception {
      TaskConfig builder = UNSANITIZED_JOB_CONFIGURATION.deepCopy().getTaskConfig();
      builder.setConstraints(ImmutableSet.of(new Constraint()
          .setName(DEDICATED_ATTRIBUTE)
          .setConstraint(TaskConstraint.value(new ValueConstraint(false, ImmutableSet.of("*/f"))))));
  
      CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 100);
    }
  
    @Test
    public void testExclusiveDedicatedRoleMismatch() throws Exception {
      TaskConfig builder = UNSANITIZED_JOB_CONFIGURATION.deepCopy().getTaskConfig();
      builder.setConstraints(ImmutableSet.of(new Constraint()
          .setName(DEDICATED_ATTRIBUTE)
          .setConstraint(TaskConstraint.value(new ValueConstraint(false, ImmutableSet.of("r/f"))))));
  
      expectTaskDescriptionException("Only r may use hosts dedicated for that role.");
      CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 100);
    }
  
    @Test
    public void testMultipleResourceValuesBlocked() throws Exception {
      TaskConfig builder = CONFIG_WITH_CONTAINER.newBuilder();
      builder.addToResources(numCpus(3.0));
      builder.addToResources(ramMb(72));
  
      expectTaskDescriptionException("Multiple resource values are not supported for CPU, RAM");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 100);
    }
  
    @Test
    public void testMultipleResourceValuesAllowed() throws Exception {
      TaskConfig builder = CONFIG_WITH_CONTAINER.newBuilder();
      builder.addToResources(namedPort("thrift"));
  
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 100);
    }
  
    @Test
    public void testGpuResourcesNotAllowed() throws Exception {
      TaskConfig builder = CONFIG_WITH_CONTAINER.newBuilder();
      builder.addToResources(numGpus(2));
  
      expectTaskDescriptionException("GPU resource support is disabled in this cluster.");
      new ConfigurationManager(
          new ConfigurationManagerSettings(
              ALL_CONTAINER_TYPES,
              true,
              ImmutableList.of(new DockerParameter("foo", "bar")),
              false,
              false,
              false,
              false,
              MIN_REQUIRED_INSTANCES,
              MAX_SLA_DURATION_SECS,
              ConfigurationManager.DEFAULT_ALLOWED_JOB_ENVIRONMENTS,
              false),
          TaskTestUtil.TIER_MANAGER,
          TaskTestUtil.THRIFT_BACKFILL,
          TestExecutorSettings.THERMOS_EXECUTOR).validateAndPopulate(ITaskConfig.build(builder), 100);
    }
  
    @Test
    public void testMesosFetcherDisabled() throws Exception {
      TaskConfig builder = CONFIG_WITH_CONTAINER.newBuilder();
      builder.setMesosFetcherUris(
          ImmutableSet.of(
              new MesosFetcherURI("pathA").setExtract(true).setCache(true),
              new MesosFetcherURI("pathB").setExtract(true).setCache(true)));
  
      expectTaskDescriptionException(ConfigurationManager.MESOS_FETCHER_DISABLED);
      new ConfigurationManager(
              new ConfigurationManagerSettings(
                      ALL_CONTAINER_TYPES,
                      true,
                      ImmutableList.of(new DockerParameter("foo", "bar")),
                      false,
                      false,
                      false,
                      false,
                      MIN_REQUIRED_INSTANCES,
                      MAX_SLA_DURATION_SECS,
                      ".+",
                      false),
              TaskTestUtil.TIER_MANAGER,
              TaskTestUtil.THRIFT_BACKFILL,
              TestExecutorSettings.THERMOS_EXECUTOR)
          .validateAndPopulate(ITaskConfig.build(builder), 100);
    }
  
    @Test
    public void testContainerVolumesDisabled() throws Exception {
      TaskConfig builder = CONFIG_WITH_CONTAINER.newBuilder();
      MesosContainer container = new MesosContainer()
          .setImage(Image.appc(new AppcImage("name", "id")))
          .setVolumes(ImmutableList.of(new Volume("container", "host", Mode.RO)));
      builder.setContainer(Container.mesos(container));
  
      expectTaskDescriptionException(NO_CONTAINER_VOLUMES);
  
      CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 100);
    }
  
    @Test
    public void testTaskLinks() throws Exception {
      TaskConfig builder = CONFIG_WITH_CONTAINER.newBuilder();
      builder.addToResources(namedPort("health"));
      builder.unsetTaskLinks();
  
      ITaskConfig populated =
          DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(ITaskConfig.build(builder), 100);
      assertEquals(ImmutableSet.of("health", "http"), populated.getTaskLinks().keySet());
    }
  
    @Test
    public void testJobEnvironmentValidation() throws Exception {
      JobConfiguration jobConfiguration = UNSANITIZED_JOB_CONFIGURATION.deepCopy();
      jobConfiguration.getKey().setEnvironment("foo");
      expectTaskDescriptionException("Job environment foo doesn't match: b.r");
      new ConfigurationManager(
        new ConfigurationManagerSettings(
            ALL_CONTAINER_TYPES,
            true,
            ImmutableList.of(new DockerParameter("foo", "bar")),
            false,
            true,
            true,
            true,
            MIN_REQUIRED_INSTANCES,
            MAX_SLA_DURATION_SECS,
            "b.r",
            false),
        TaskTestUtil.TIER_MANAGER,
        TaskTestUtil.THRIFT_BACKFILL,
        TestExecutorSettings.THERMOS_EXECUTOR)
              .validateAndPopulate(IJobConfiguration.build(jobConfiguration));
    }
  
    @Test
    public void testSlaPolicyNonProdTiersEnabled() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setTier(TaskTestUtil.DEV_TIER_NAME)
              .setSlaPolicy(SlaPolicy.countSlaPolicy(
                  new CountSlaPolicy()
                      .setCount(MIN_REQUIRED_INSTANCES - 1))));
  
      new ConfigurationManager(
          new ConfigurationManagerSettings(
              ALL_CONTAINER_TYPES,
              true,
              ImmutableList.of(new DockerParameter("foo", "bar")),
              false,
              true,
              true,
              true,
              MIN_REQUIRED_INSTANCES,
              MAX_SLA_DURATION_SECS,
              ConfigurationManager.DEFAULT_ALLOWED_JOB_ENVIRONMENTS,
              true),
          TaskTestUtil.TIER_MANAGER,
          TaskTestUtil.THRIFT_BACKFILL,
          TestExecutorSettings.THERMOS_EXECUTOR).validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES);
    }
  
    @Test
    public void testCountSlaPolicyUnsupportedTier() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setTier(TaskTestUtil.DEV_TIER_NAME)
              .setSlaPolicy(SlaPolicy.countSlaPolicy(
                  new CountSlaPolicy()
                      .setCount(MIN_REQUIRED_INSTANCES))));
  
      expectTaskDescriptionException("Tier 'tier-dev' does not support SlaPolicy.");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES);
    }
  
    @Test
    public void testCountSlaPolicyTooFewInstances() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setSlaPolicy(SlaPolicy.countSlaPolicy(
                  new CountSlaPolicy()
                      .setCount(MIN_REQUIRED_INSTANCES))));
  
      expectTaskDescriptionException(
          "Job with fewer than 20 instances cannot have Percentage/Count SlaPolicy.");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES - 1);
    }
  
    @Test
    public void testCountSlaPolicyCountTooHigh() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setSlaPolicy(SlaPolicy.countSlaPolicy(
                  new CountSlaPolicy()
                    .setCount(MIN_REQUIRED_INSTANCES))));
  
      expectTaskDescriptionException(
          "Current CountSlaPolicy: count=20 will not allow any instances to be killed. "
              + "Must be less than instanceCount=20.");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES);
    }
  
    @Test
    public void testCountSlaPolicyDurationSecsTooHigh() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setSlaPolicy(SlaPolicy.countSlaPolicy(
                  new CountSlaPolicy()
                      .setDurationSecs(MAX_SLA_DURATION_SECS + 1))));
  
      expectTaskDescriptionException("CountSlaPolicy: durationSecs=7201 must be less than "
          + "cluster-wide maximum of 7200 secs.");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES);
    }
  
    @Test
    public void testPercentageSlaPolicyUnsupportedTier() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setTier(TaskTestUtil.DEV_TIER_NAME)
              .setSlaPolicy(SlaPolicy.percentageSlaPolicy(
                  new PercentageSlaPolicy()
                      .setPercentage(DEFAULT_SLA_PERCENTAGE))));
  
      expectTaskDescriptionException("Tier 'tier-dev' does not support SlaPolicy.");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES);
    }
  
    @Test
    public void testPercentageSlaPolicyTooFewInstances() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setSlaPolicy(SlaPolicy.percentageSlaPolicy(
                  new PercentageSlaPolicy()
                      .setPercentage(DEFAULT_SLA_PERCENTAGE))));
  
      expectTaskDescriptionException(
          "Job with fewer than 20 instances cannot have Percentage/Count SlaPolicy.");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES - 1);
    }
  
    @Test
    public void testPercentageSlaPolicyPercentageTooHigh() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setSlaPolicy(SlaPolicy.percentageSlaPolicy(
                  new PercentageSlaPolicy()
                      .setPercentage(100))));
  
      expectTaskDescriptionException(
          "Current PercentageSlaPolicy: percentage=100.000000 will not allow any instances "
              + "to be killed. Must be less than 95.000000.");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES);
    }
  
    @Test
    public void testPercentageSlaPolicyDurationSecsTooHigh() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setSlaPolicy(SlaPolicy.percentageSlaPolicy(
                  new PercentageSlaPolicy()
                      .setDurationSecs(MAX_SLA_DURATION_SECS + 1))));
  
      expectTaskDescriptionException("PercentageSlaPolicy: durationSecs=7201 must be less than "
          + "cluster-wide maximum of 7200 secs.");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES);
    }
  
    @Test
    public void testCoordinatorSlaPolicyUnsupportedTier() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setTier(TaskTestUtil.DEV_TIER_NAME)
              .setSlaPolicy(SlaPolicy.coordinatorSlaPolicy(
                  new CoordinatorSlaPolicy()
                      .setCoordinatorUrl("http://localhost"))));
  
      expectTaskDescriptionException("Tier 'tier-dev' does not support SlaPolicy.");
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES);
    }
  
    @Test
    public void testCoordinatorSlaPolicyTooFewInstances() throws Exception {
      ITaskConfig config = ITaskConfig.build(
          CONFIG_WITH_CONTAINER
              .newBuilder()
              .setSlaPolicy(SlaPolicy.coordinatorSlaPolicy(
                  new CoordinatorSlaPolicy()
                      .setCoordinatorUrl("http://localhost"))));
  
      DOCKER_CONFIGURATION_MANAGER.validateAndPopulate(
          config,
          MIN_REQUIRED_INSTANCES - 1);
    }
  
    private void expectTaskDescriptionException(String message) {
      expectedException.expect(TaskDescriptionException.class);
      expectedException.expectMessage(message);
    }
  }
  