aurora-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wfar...@apache.org
Subject incubator-aurora git commit: Fix impedance mismatch between offer matching and task launching.
Date Mon, 26 Jan 2015 20:49:39 GMT
Repository: incubator-aurora
Updated Branches:
  refs/heads/master 7ba6226fa -> b39f6ee14


Fix impedance mismatch between offer matching and task launching.

Bugs closed: AURORA-1050

Reviewed at https://reviews.apache.org/r/30203/


Project: http://git-wip-us.apache.org/repos/asf/incubator-aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-aurora/commit/b39f6ee1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-aurora/tree/b39f6ee1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-aurora/diff/b39f6ee1

Branch: refs/heads/master
Commit: b39f6ee144d008e31baf79f19b88cbedead86c10
Parents: 7ba6226
Author: Bill Farner <wfarner@apache.org>
Authored: Mon Jan 26 12:46:58 2015 -0800
Committer: Bill Farner <wfarner@apache.org>
Committed: Mon Jan 26 12:46:58 2015 -0800

----------------------------------------------------------------------
 .../apache/aurora/scheduler/ResourceSlot.java   |  92 ++++++----
 .../aurora/scheduler/app/SchedulerMain.java     |  20 ++-
 .../async/preemptor/LiveClusterState.java       |   1 +
 .../async/preemptor/PreemptionVictim.java       |  10 +-
 .../async/preemptor/PreemptorImpl.java          |  18 +-
 .../scheduler/configuration/Resources.java      |  19 +-
 .../scheduler/filter/SchedulingFilter.java      |   4 +-
 .../scheduler/filter/SchedulingFilterImpl.java  |  16 +-
 .../scheduler/mesos/ExecutorSettings.java       |  67 +++++++
 .../scheduler/mesos/MesosTaskFactory.java       | 123 ++-----------
 .../aurora/scheduler/ResourceSlotTest.java      |  37 ++++
 .../aurora/scheduler/UserTaskLauncherTest.java  |  65 +------
 .../aurora/scheduler/app/SchedulerIT.java       |   8 +-
 .../async/preemptor/PreemptorImplTest.java      |  19 +-
 .../events/NotifyingSchedulingFilterTest.java   |   3 +-
 .../filter/SchedulingFilterImplTest.java        |  17 +-
 .../mesos/MesosTaskFactoryImplTest.java         | 176 ++++++-------------
 .../apache/aurora/scheduler/mesos/Offers.java   |  65 +++++++
 .../aurora/scheduler/mesos/TaskExecutors.java   |  47 +++++
 .../aurora/e2e/http/http_example_updated.aurora |   2 +-
 .../org/apache/aurora/e2e/validate_serverset.py |   6 +-
 21 files changed, 441 insertions(+), 374 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/ResourceSlot.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/ResourceSlot.java b/src/main/java/org/apache/aurora/scheduler/ResourceSlot.java
index 0b15834..1a158b4 100644
--- a/src/main/java/org/apache/aurora/scheduler/ResourceSlot.java
+++ b/src/main/java/org/apache/aurora/scheduler/ResourceSlot.java
@@ -14,18 +14,21 @@
 package org.apache.aurora.scheduler;
 
 import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Ordering;
-import com.twitter.common.args.Arg;
-import com.twitter.common.args.CmdLine;
 import com.twitter.common.quantity.Amount;
 import com.twitter.common.quantity.Data;
 
+import org.apache.aurora.scheduler.async.preemptor.PreemptionVictim;
 import org.apache.aurora.scheduler.configuration.Resources;
+import org.apache.aurora.scheduler.mesos.ExecutorSettings;
 import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
+import org.apache.mesos.Protos;
 
 import static org.apache.mesos.Protos.Offer;
 
@@ -38,32 +41,58 @@ public final class ResourceSlot {
   private final Resources resources;
 
   /**
-   * Extra CPU allocated for each executor.
+   * Minimum resources required to run Thermos. In the wild Thermos needs about 0.01 CPU and
+   * about 170MB (peak usage) of RAM. The RAM requirement has been rounded up to a power of 2.
    */
   @VisibleForTesting
-  @CmdLine(name = "thermos_executor_cpu",
-      help = "The number of CPU cores to allocate for each instance of the executor.")
-  public static final Arg<Double> EXECUTOR_OVERHEAD_CPUS = Arg.create(0.25);
-
-  /**
-   * Extra RAM allocated for the executor.
-   */
-  @VisibleForTesting
-  @CmdLine(name = "thermos_executor_ram",
-      help = "The amount of RAM to allocate for each instance of the executor.")
-  public static final Arg<Amount<Long, Data>> EXECUTOR_OVERHEAD_RAM =
-      Arg.create(Amount.of(128L, Data.MB));
+  public static final Resources MIN_THERMOS_RESOURCES = new Resources(
+      0.01,
+      Amount.of(256L, Data.MB),
+      Amount.of(1L, Data.MB),
+      0);
 
   private ResourceSlot(Resources r) {
     this.resources = r;
   }
 
-  public static ResourceSlot from(ITaskConfig task) {
-    return from(
-        task.getNumCpus(),
-        Amount.of(task.getRamMb(), Data.MB),
-        Amount.of(task.getDiskMb(), Data.MB),
-        task.getRequestedPorts().size());
+  public static ResourceSlot from(ITaskConfig task, ExecutorSettings executorSettings) {
+    return from(Resources.from(task), executorSettings);
+  }
+
+  public static ResourceSlot from(PreemptionVictim victim, ExecutorSettings executorSettings) {
+    return from(victim.getResources(), executorSettings);
+  }
+
+  private static ResourceSlot from(Resources resources, ExecutorSettings executorSettings) {
+    // Apply a flat 'tax' of executor overhead resources to the task.
+    Resources requiredTaskResources = Resources.sum(
+        resources,
+        executorSettings.getExecutorOverhead());
+
+    // Upsize tasks smaller than the minimum resources required to run the executor.
+    return new ResourceSlot(maxElements(requiredTaskResources, MIN_THERMOS_RESOURCES));
+  }
+
+  /**
+   * Generates a Resource where each resource component is a max out of the two components.
+   *
+   * @param a A resource to compare.
+   * @param b A resource to compare.
+   *
+   * @return Returns a Resources instance where each component is a max of the two components.
+   */
+  @VisibleForTesting
+  static Resources maxElements(Resources a, Resources b) {
+    double maxCPU = Math.max(a.getNumCpus(), b.getNumCpus());
+    Amount<Long, Data> maxRAM = Amount.of(
+        Math.max(a.getRam().as(Data.MB), b.getRam().as(Data.MB)),
+        Data.MB);
+    Amount<Long, Data> maxDisk = Amount.of(
+        Math.max(a.getDisk().as(Data.MB), b.getDisk().as(Data.MB)),
+        Data.MB);
+    int maxPorts = Math.max(a.getNumPorts(), b.getNumPorts());
+
+    return new Resources(maxCPU, maxRAM, maxDisk, maxPorts);
   }
 
   public static ResourceSlot from(Offer offer) {
@@ -86,19 +115,6 @@ public final class ResourceSlot {
     return resources.getNumPorts();
   }
 
-  @VisibleForTesting
-  public static ResourceSlot from(
-      double cpu,
-      Amount<Long, Data> ram,
-      Amount<Long, Data> disk,
-      int ports) {
-    double totalCPU = cpu + EXECUTOR_OVERHEAD_CPUS.get();
-    Amount<Long, Data> totalRAM =
-        Amount.of(ram.as(Data.MB) + EXECUTOR_OVERHEAD_RAM.get().as(Data.MB), Data.MB);
-
-    return new ResourceSlot(new Resources(totalCPU, totalRAM, disk, ports));
-  }
-
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof ResourceSlot)) {
@@ -129,6 +145,14 @@ public final class ResourceSlot {
     return new ResourceSlot(r);
   }
 
+  public static ResourceSlot subtract(ResourceSlot a, Resources b) {
+    return new ResourceSlot(Resources.subtract(a.resources, b));
+  }
+
+  public List<Protos.Resource> toResourceList(Set<Integer> selectedPorts) {
+    return resources.toResourceList(selectedPorts);
+  }
+
   public static final Ordering<ResourceSlot> ORDER = new Ordering<ResourceSlot>() {
     @Override
     public int compare(ResourceSlot left, ResourceSlot right) {

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java b/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
index dd64a22..24b61c1 100644
--- a/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
+++ b/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
@@ -51,8 +51,8 @@ import org.apache.aurora.scheduler.configuration.Resources;
 import org.apache.aurora.scheduler.cron.quartz.CronModule;
 import org.apache.aurora.scheduler.log.mesos.MesosLogStreamModule;
 import org.apache.aurora.scheduler.mesos.CommandLineDriverSettingsModule;
+import org.apache.aurora.scheduler.mesos.ExecutorSettings;
 import org.apache.aurora.scheduler.mesos.LibMesosLoadingModule;
-import org.apache.aurora.scheduler.mesos.MesosTaskFactory.ExecutorSettings;
 import org.apache.aurora.scheduler.storage.backup.BackupModule;
 import org.apache.aurora.scheduler.storage.db.DbModule;
 import org.apache.aurora.scheduler.storage.db.MigrationModule;
@@ -66,9 +66,6 @@ import org.apache.aurora.scheduler.thrift.auth.ThriftAuthModule;
 
 import static com.twitter.common.logging.RootLogConfig.Configuration;
 
-import static org.apache.aurora.scheduler.ResourceSlot.EXECUTOR_OVERHEAD_CPUS;
-import static org.apache.aurora.scheduler.ResourceSlot.EXECUTOR_OVERHEAD_RAM;
-
 /**
  * Launcher for the aurora scheduler.
  */
@@ -106,6 +103,21 @@ public class SchedulerMain extends AbstractApplication {
   private static final Arg<? extends Class<? extends Module>> AUTH_MODULE =
       Arg.create(UnsecureAuthModule.class);
 
+  /**
+   * Extra CPU allocated for each executor.
+   */
+  @CmdLine(name = "thermos_executor_cpu",
+      help = "The number of CPU cores to allocate for each instance of the executor.")
+  private static final Arg<Double> EXECUTOR_OVERHEAD_CPUS = Arg.create(0.25);
+
+  /**
+   * Extra RAM allocated for the executor.
+   */
+  @CmdLine(name = "thermos_executor_ram",
+      help = "The amount of RAM to allocate for each instance of the executor.")
+  private static final Arg<Amount<Long, Data>> EXECUTOR_OVERHEAD_RAM =
+      Arg.create(Amount.of(128L, Data.MB));
+
   private static final Iterable<Class<?>> AUTH_MODULE_CLASSES = ImmutableList.<Class<?>>builder()
       .add(SessionValidator.class)
       .add(CapabilityValidator.class)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/async/preemptor/LiveClusterState.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/async/preemptor/LiveClusterState.java b/src/main/java/org/apache/aurora/scheduler/async/preemptor/LiveClusterState.java
index e6bd1b5..38cb173 100644
--- a/src/main/java/org/apache/aurora/scheduler/async/preemptor/LiveClusterState.java
+++ b/src/main/java/org/apache/aurora/scheduler/async/preemptor/LiveClusterState.java
@@ -33,6 +33,7 @@ import static java.util.Objects.requireNonNull;
 import static org.apache.aurora.gen.ScheduleStatus.PREEMPTING;
 import static org.apache.aurora.scheduler.base.Tasks.SCHEDULED_TO_ASSIGNED;
 
+// TODO(wfarner): Remove this in favor of CachedClusterState.
 class LiveClusterState implements ClusterState {
 
   @VisibleForTesting

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptionVictim.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptionVictim.java b/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptionVictim.java
index 024a689..80c2023 100644
--- a/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptionVictim.java
+++ b/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptionVictim.java
@@ -15,7 +15,7 @@ package org.apache.aurora.scheduler.async.preemptor;
 
 import java.util.Objects;
 
-import org.apache.aurora.scheduler.ResourceSlot;
+import org.apache.aurora.scheduler.configuration.Resources;
 import org.apache.aurora.scheduler.storage.entities.IAssignedTask;
 import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
 
@@ -27,7 +27,7 @@ public final class PreemptionVictim {
   private final boolean production;
   private final String role;
   private final int priority;
-  private final ResourceSlot resources;
+  private final Resources resources;
   private final String taskId;
 
   private PreemptionVictim(
@@ -35,7 +35,7 @@ public final class PreemptionVictim {
       boolean production,
       String role,
       int priority,
-      ResourceSlot resources,
+      Resources resources,
       String taskId) {
 
     this.slaveHost = slaveHost;
@@ -53,7 +53,7 @@ public final class PreemptionVictim {
         config.isProduction(),
         config.getOwner().getRole(),
         config.getPriority(),
-        ResourceSlot.from(config),
+        Resources.from(task.getTask()),
         task.getTaskId());
   }
 
@@ -73,7 +73,7 @@ public final class PreemptionVictim {
     return priority;
   }
 
-  public ResourceSlot getResources() {
+  public Resources getResources() {
     return resources;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImpl.java b/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImpl.java
index 0204d14..10c4f06 100644
--- a/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImpl.java
+++ b/src/main/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImpl.java
@@ -48,6 +48,7 @@ import org.apache.aurora.scheduler.filter.AttributeAggregate;
 import org.apache.aurora.scheduler.filter.SchedulingFilter;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.ResourceRequest;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.UnusedResource;
+import org.apache.aurora.scheduler.mesos.ExecutorSettings;
 import org.apache.aurora.scheduler.state.StateManager;
 import org.apache.aurora.scheduler.storage.Storage;
 import org.apache.aurora.scheduler.storage.entities.IAssignedTask;
@@ -104,6 +105,7 @@ class PreemptorImpl implements Preemptor {
   private final Clock clock;
   private final AtomicLong missingAttributes;
   private final ClusterState clusterState;
+  private final ExecutorSettings executorSettings;
 
   /**
    * Creates a new preemptor.
@@ -125,7 +127,8 @@ class PreemptorImpl implements Preemptor {
       @PreemptionDelay Amount<Long, Time> preemptionCandidacyDelay,
       Clock clock,
       StatsProvider statsProvider,
-      ClusterState clusterState) {
+      ClusterState clusterState,
+      ExecutorSettings executorSettings) {
 
     this.storage = requireNonNull(storage);
     this.stateManager = requireNonNull(stateManager);
@@ -135,6 +138,7 @@ class PreemptorImpl implements Preemptor {
     this.clock = requireNonNull(clock);
     missingAttributes = statsProvider.makeCounter("preemptor_missing_attributes");
     this.clusterState = requireNonNull(clusterState);
+    this.executorSettings = requireNonNull(executorSettings);
   }
 
   private static final Function<HostOffer, ResourceSlot> OFFER_TO_RESOURCE_SLOT =
@@ -161,11 +165,11 @@ class PreemptorImpl implements Preemptor {
         }
       };
 
-  private static final Function<PreemptionVictim, ResourceSlot> VICTIM_TO_RESOURCES =
+  private final Function<PreemptionVictim, ResourceSlot> victimToResources =
       new Function<PreemptionVictim, ResourceSlot>() {
         @Override
         public ResourceSlot apply(PreemptionVictim victim) {
-          return victim.getResources();
+          return ResourceSlot.from(victim, executorSettings);
         }
       };
 
@@ -179,8 +183,8 @@ class PreemptorImpl implements Preemptor {
 
   // TODO(zmanji) Consider using Dominant Resource Fairness for ordering instead of the vector
   // ordering
-  private static final Ordering<PreemptionVictim> RESOURCE_ORDER =
-      ResourceSlot.ORDER.onResultOf(VICTIM_TO_RESOURCES).reverse();
+  private final Ordering<PreemptionVictim> resourceOrder =
+      ResourceSlot.ORDER.onResultOf(victimToResources).reverse();
 
   private static final Function<PreemptionVictim, String> VICTIM_TO_HOST =
       new Function<PreemptionVictim, String>() {
@@ -240,13 +244,13 @@ class PreemptorImpl implements Preemptor {
 
     List<PreemptionVictim> toPreemptTasks = Lists.newArrayList();
 
-    Iterable<PreemptionVictim> sortedVictims = RESOURCE_ORDER.immutableSortedCopy(preemptableTasks);
+    Iterable<PreemptionVictim> sortedVictims = resourceOrder.immutableSortedCopy(preemptableTasks);
 
     for (PreemptionVictim victim : sortedVictims) {
       toPreemptTasks.add(victim);
 
       ResourceSlot totalResource = ResourceSlot.sum(
-          ResourceSlot.sum(Iterables.transform(toPreemptTasks, VICTIM_TO_RESOURCES)),
+          ResourceSlot.sum(Iterables.transform(toPreemptTasks, victimToResources)),
           slackResources);
 
       Optional<IHostAttributes> attributes = getHostAttributes(host);

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/configuration/Resources.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/configuration/Resources.java b/src/main/java/org/apache/aurora/scheduler/configuration/Resources.java
index 65c4b52..b5a3140 100644
--- a/src/main/java/org/apache/aurora/scheduler/configuration/Resources.java
+++ b/src/main/java/org/apache/aurora/scheduler/configuration/Resources.java
@@ -147,6 +147,16 @@ public class Resources {
     return Objects.hash(numCpus, ram, disk, numPorts);
   }
 
+  @Override
+  public String toString() {
+    return com.google.common.base.Objects.toStringHelper(this)
+        .add("numCpus", numCpus)
+        .add("ram", ram)
+        .add("disk", disk)
+        .add("numPorts", numPorts)
+        .toString();
+  }
+
   /**
    * Extracts the resources required from a task.
    *
@@ -193,13 +203,10 @@ public class Resources {
         getNumAvailablePorts(offer.getResourcesList()));
   }
 
-  private static final Resources NO_RESOURCES =
+  @VisibleForTesting
+  public static final Resources NONE =
       new Resources(0, Amount.of(0L, Data.BITS), Amount.of(0L, Data.BITS), 0);
 
-  private static Resources none() {
-    return NO_RESOURCES;
-  }
-
   /**
    * a - b.
    */
@@ -222,7 +229,7 @@ public class Resources {
    * sum(rs).
    */
   public static Resources sum(Iterable<Resources> rs) {
-    Resources sum = none();
+    Resources sum = NONE;
 
     for (Resources r : rs) {
       double numCpus = sum.getNumCpus() + r.getNumCpus();

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilter.java b/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilter.java
index c2a342c..6a43bcd 100644
--- a/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilter.java
+++ b/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilter.java
@@ -221,8 +221,8 @@ public interface SchedulingFilter {
       return task.getConstraints();
     }
 
-    public ResourceSlot getResourceSlot() {
-      return ResourceSlot.from(task);
+    public ITaskConfig getTask() {
+      return task;
     }
 
     public AttributeAggregate getJobState() {

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilterImpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilterImpl.java b/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilterImpl.java
index 1cb56f1..f06fdae 100644
--- a/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilterImpl.java
+++ b/src/main/java/org/apache/aurora/scheduler/filter/SchedulingFilterImpl.java
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Ordering;
+import com.google.inject.Inject;
 import com.twitter.common.quantity.Amount;
 import com.twitter.common.quantity.Data;
 
@@ -30,10 +31,13 @@ import org.apache.aurora.gen.MaintenanceMode;
 import org.apache.aurora.gen.TaskConstraint;
 import org.apache.aurora.scheduler.ResourceSlot;
 import org.apache.aurora.scheduler.configuration.ConfigurationManager;
+import org.apache.aurora.scheduler.mesos.ExecutorSettings;
 import org.apache.aurora.scheduler.storage.entities.IAttribute;
 import org.apache.aurora.scheduler.storage.entities.IConstraint;
 import org.apache.aurora.scheduler.storage.entities.IHostAttributes;
 
+import static java.util.Objects.requireNonNull;
+
 import static org.apache.aurora.gen.MaintenanceMode.DRAINED;
 import static org.apache.aurora.gen.MaintenanceMode.DRAINING;
 import static org.apache.aurora.scheduler.configuration.ConfigurationManager.DEDICATED_ATTRIBUTE;
@@ -132,6 +136,14 @@ public class SchedulingFilterImpl implements SchedulingFilter {
         }
       });
 
+  private final ExecutorSettings executorSettings;
+
+  @Inject
+  @VisibleForTesting
+  public SchedulingFilterImpl(ExecutorSettings executorSettings) {
+    this.executorSettings = requireNonNull(executorSettings);
+  }
+
   private Iterable<Veto> getConstraintVetoes(
       Iterable<IConstraint> taskConstraints,
       AttributeAggregate jobState,
@@ -183,7 +195,9 @@ public class SchedulingFilterImpl implements SchedulingFilter {
             request.getConstraints(),
             request.getJobState(),
             resource.getAttributes().getAttributes()))
-        .addAll(getResourceVetoes(resource.getResourceSlot(), request.getResourceSlot()))
+        .addAll(getResourceVetoes(
+            resource.getResourceSlot(),
+            ResourceSlot.from(request.getTask(), executorSettings)))
         .build();
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/mesos/ExecutorSettings.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/mesos/ExecutorSettings.java b/src/main/java/org/apache/aurora/scheduler/mesos/ExecutorSettings.java
new file mode 100644
index 0000000..baacb71
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/mesos/ExecutorSettings.java
@@ -0,0 +1,67 @@
+/**
+ * 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.mesos;
+
+import java.util.List;
+
+import com.google.common.base.Optional;
+
+import org.apache.aurora.scheduler.configuration.Resources;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Configuration for the executor to run, and resource overhead required for it.
+ */
+public class ExecutorSettings {
+  private final String executorPath;
+  private final List<String> executorResources;
+  private final String thermosObserverRoot;
+  private final Optional<String> executorFlags;
+  private final Resources executorOverhead;
+
+  public ExecutorSettings(
+      String executorPath,
+      List<String> executorResources,
+      String thermosObserverRoot,
+      Optional<String> executorFlags,
+      Resources executorOverhead) {
+
+    this.executorPath = requireNonNull(executorPath);
+    this.executorResources = requireNonNull(executorResources);
+    this.thermosObserverRoot = requireNonNull(thermosObserverRoot);
+    this.executorFlags = requireNonNull(executorFlags);
+    this.executorOverhead = requireNonNull(executorOverhead);
+  }
+
+  public String getExecutorPath() {
+    return executorPath;
+  }
+
+  public List<String> getExecutorResources() {
+    return executorResources;
+  }
+
+  public String getThermosObserverRoot() {
+    return thermosObserverRoot;
+  }
+
+  public Optional<String> getExecutorFlags() {
+    return executorFlags;
+  }
+
+  public Resources getExecutorOverhead() {
+    return executorOverhead;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/main/java/org/apache/aurora/scheduler/mesos/MesosTaskFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/mesos/MesosTaskFactory.java b/src/main/java/org/apache/aurora/scheduler/mesos/MesosTaskFactory.java
index 846914e..5340d65 100644
--- a/src/main/java/org/apache/aurora/scheduler/mesos/MesosTaskFactory.java
+++ b/src/main/java/org/apache/aurora/scheduler/mesos/MesosTaskFactory.java
@@ -20,7 +20,6 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.protobuf.ByteString;
@@ -29,12 +28,12 @@ import com.twitter.common.quantity.Data;
 
 import org.apache.aurora.Protobufs;
 import org.apache.aurora.codec.ThriftBinaryCodec;
+import org.apache.aurora.scheduler.ResourceSlot;
 import org.apache.aurora.scheduler.base.CommandUtil;
 import org.apache.aurora.scheduler.base.JobKeys;
 import org.apache.aurora.scheduler.base.SchedulerException;
 import org.apache.aurora.scheduler.base.Tasks;
 import org.apache.aurora.scheduler.configuration.Resources;
-
 import org.apache.aurora.scheduler.storage.entities.IAssignedTask;
 import org.apache.aurora.scheduler.storage.entities.IDockerContainer;
 import org.apache.aurora.scheduler.storage.entities.IJobKey;
@@ -66,77 +65,12 @@ public interface MesosTaskFactory {
    */
   TaskInfo createFrom(IAssignedTask task, SlaveID slaveId) throws SchedulerException;
 
-  class ExecutorSettings {
-
-    private final String executorPath;
-    private final List<String> executorResources;
-    private final String thermosObserverRoot;
-    private final Optional<String> executorFlags;
-    private final Resources executorOverhead;
-
-    public ExecutorSettings(
-        String executorPath,
-        List<String> executorResources,
-        String thermosObserverRoot,
-        Optional<String> executorFlags,
-        Resources executorOverhead) {
-
-      this.executorPath = requireNonNull(executorPath);
-      this.executorResources = requireNonNull(executorResources);
-      this.thermosObserverRoot = requireNonNull(thermosObserverRoot);
-      this.executorFlags = requireNonNull(executorFlags);
-      this.executorOverhead = requireNonNull(executorOverhead);
-    }
-
-    String getExecutorPath() {
-      return executorPath;
-    }
-
-    List<String> getExecutorResources() {
-      return executorResources;
-    }
-
-    String getThermosObserverRoot() {
-      return thermosObserverRoot;
-    }
-
-    Optional<String> getExecutorFlags() {
-      return executorFlags;
-    }
-
-    Resources getExecutorOverhead() {
-      return executorOverhead;
-    }
-  }
-
   // TODO(wfarner): Move this class to its own file to reduce visibility to package private.
   class MesosTaskFactoryImpl implements MesosTaskFactory {
     private static final Logger LOG = Logger.getLogger(MesosTaskFactoryImpl.class.getName());
     private static final String EXECUTOR_PREFIX = "thermos-";
 
     /**
-     * Minimum resources required to run Thermos. In the wild Thermos needs about 0.01 CPU and
-     * about 170MB (peak usage) of RAM. The RAM requirement has been rounded up to a power of 2.
-     */
-    @VisibleForTesting
-    static final Resources MIN_THERMOS_RESOURCES = new Resources(
-        0.01,
-        Amount.of(256L, Data.MB),
-        Amount.of(1L, Data.MB),
-        0);
-
-    /**
-     * Minimum resources to allocate for a task. Mesos rejects tasks that have no CPU, no RAM, or
-     * no Disk.
-     */
-    @VisibleForTesting
-    static final Resources MIN_TASK_RESOURCES = new Resources(
-        0.01,
-        Amount.of(1L, Data.MB),
-        Amount.of(1L, Data.MB),
-        0);
-
-    /**
      * Name to associate with task executors.
      */
     @VisibleForTesting
@@ -168,26 +102,16 @@ public interface MesosTaskFactory {
     }
 
     /**
-     * Generates a Resource where each resource component is a max out of the two components.
-     *
-     * @param a A resource to compare.
-     * @param b A resource to compare.
-     *
-     * @return Returns a Resources instance where each component is a max of the two components.
+     * Resources to 'allocate' to the executor in the ExecutorInfo.  We do this since mesos
+     * disallows an executor with zero resources, but the tasks end up in the same container
+     * anyway.
      */
     @VisibleForTesting
-    static Resources maxElements(Resources a, Resources b) {
-      double maxCPU = Math.max(a.getNumCpus(), b.getNumCpus());
-      Amount<Long, Data> maxRAM = Amount.of(
-          Math.max(a.getRam().as(Data.MB), b.getRam().as(Data.MB)),
-          Data.MB);
-      Amount<Long, Data> maxDisk = Amount.of(
-          Math.max(a.getDisk().as(Data.MB), b.getDisk().as(Data.MB)),
-          Data.MB);
-      int maxPorts = Math.max(a.getNumPorts(), b.getNumPorts());
-
-      return new Resources(maxCPU, maxRAM, maxDisk, maxPorts);
-    }
+    static final Resources RESOURCES_EPSILON = new Resources(
+        0.01,
+        Amount.of(1L, Data.MB),
+        Amount.of(1L, Data.MB),
+        0);
 
     @Override
     public TaskInfo createFrom(IAssignedTask task, SlaveID slaveId) throws SchedulerException {
@@ -202,33 +126,20 @@ public interface MesosTaskFactory {
         throw new SchedulerException("Internal error.", e);
       }
 
-      // The objective of the below code is to allocate a task and executor that is in a container
-      // of task + executor overhead size. Mesos stipulates that we cannot allocate 0 sized tasks or
-      // executors and we should always ensure the ExecutorInfo has enough resources to launch or
-      // run an executor. Therefore the total desired container size (task + executor overhead) is
-      // partitioned to a small portion that is always allocated to the executor and the rest to the
-      // task. If the remaining resources are not enough for the task a small epsilon is allocated
-      // to the task.
-
       ITaskConfig config = task.getTask();
-      Resources taskResources = Resources.from(config);
-      Resources containerResources =
-          Resources.sum(taskResources, executorSettings.getExecutorOverhead());
-
-      taskResources = Resources.subtract(containerResources, MIN_THERMOS_RESOURCES);
-      // It is possible that the final task resources will be negative.
-      // This ensures the task resources are positive.
-      Resources finalTaskResources = maxElements(taskResources, MIN_TASK_RESOURCES);
+      ResourceSlot resourceSlot =
+          ResourceSlot.subtract(ResourceSlot.from(config, executorSettings), RESOURCES_EPSILON);
 
       // TODO(wfarner): Re-evaluate if/why we need to continue handling unset assignedPorts field.
-      List<Resource> resources = finalTaskResources
+      List<Resource> resources = resourceSlot
           .toResourceList(task.isSetAssignedPorts()
               ? ImmutableSet.copyOf(task.getAssignedPorts().values())
               : ImmutableSet.<Integer>of());
 
-      LOG.fine("Setting task resources to "
-          + Iterables.transform(resources, Protobufs.SHORT_TOSTRING));
-
+      if (LOG.isLoggable(Level.FINE)) {
+        LOG.fine("Setting task resources to "
+            + Iterables.transform(resources, Protobufs.SHORT_TOSTRING));
+      }
       TaskInfo.Builder taskBuilder =
           TaskInfo.newBuilder()
               .setName(JobKeys.canonicalString(Tasks.ASSIGNED_TO_JOB_KEY.apply(task)))
@@ -302,7 +213,7 @@ public interface MesosTaskFactory {
           .setExecutorId(getExecutorId(task.getTaskId()))
           .setName(EXECUTOR_NAME)
           .setSource(getInstanceSourceName(config, task.getInstanceId()))
-          .addAllResources(MIN_THERMOS_RESOURCES.toResourceList());
+          .addAllResources(RESOURCES_EPSILON.toResourceList());
     }
 
     private void configureContainerVolumes(ContainerInfo.Builder containerBuilder) {

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/java/org/apache/aurora/scheduler/ResourceSlotTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/ResourceSlotTest.java b/src/test/java/org/apache/aurora/scheduler/ResourceSlotTest.java
new file mode 100644
index 0000000..b4dbab7
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/ResourceSlotTest.java
@@ -0,0 +1,37 @@
+/**
+ * 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;
+
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Data;
+
+import org.apache.aurora.scheduler.configuration.Resources;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class ResourceSlotTest {
+
+  @Test
+  public void testMaxElements() {
+    Resources highRAM = new Resources(1, Amount.of(8L, Data.GB), Amount.of(10L, Data.MB), 0);
+    Resources rest = new Resources(10, Amount.of(1L, Data.MB), Amount.of(10L, Data.GB), 1);
+
+    Resources result = ResourceSlot.maxElements(highRAM, rest);
+    assertEquals(result.getNumCpus(), 10, 0.001);
+    assertEquals(result.getRam(), Amount.of(8L, Data.GB));
+    assertEquals(result.getDisk(), Amount.of(10L, Data.GB));
+    assertEquals(result.getNumPorts(), 1);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/java/org/apache/aurora/scheduler/UserTaskLauncherTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/UserTaskLauncherTest.java b/src/test/java/org/apache/aurora/scheduler/UserTaskLauncherTest.java
index 7ba9464..f96110c 100644
--- a/src/test/java/org/apache/aurora/scheduler/UserTaskLauncherTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/UserTaskLauncherTest.java
@@ -13,57 +13,38 @@
  */
 package org.apache.aurora.scheduler;
 
-import java.util.Set;
-
-import com.google.common.base.Function;
 import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 import com.twitter.common.collections.Pair;
 import com.twitter.common.testing.easymock.EasyMockTest;
 
 import org.apache.aurora.gen.HostAttributes;
 import org.apache.aurora.gen.ScheduleStatus;
 import org.apache.aurora.scheduler.async.OfferQueue;
-import org.apache.aurora.scheduler.configuration.Resources;
+import org.apache.aurora.scheduler.mesos.Offers;
 import org.apache.aurora.scheduler.state.StateManager;
 import org.apache.aurora.scheduler.storage.Storage.StorageException;
 import org.apache.aurora.scheduler.storage.entities.IHostAttributes;
 import org.apache.aurora.scheduler.storage.testing.StorageTestUtil;
-import org.apache.mesos.Protos.Attribute;
-import org.apache.mesos.Protos.FrameworkID;
 import org.apache.mesos.Protos.OfferID;
-import org.apache.mesos.Protos.Resource;
-import org.apache.mesos.Protos.SlaveID;
 import org.apache.mesos.Protos.TaskID;
 import org.apache.mesos.Protos.TaskState;
 import org.apache.mesos.Protos.TaskStatus;
-import org.apache.mesos.Protos.Value.Range;
-import org.apache.mesos.Protos.Value.Ranges;
-import org.apache.mesos.Protos.Value.Scalar;
-import org.apache.mesos.Protos.Value.Text;
-import org.apache.mesos.Protos.Value.Type;
 import org.junit.Before;
 import org.junit.Test;
 
 import static org.apache.aurora.gen.ScheduleStatus.FAILED;
 import static org.apache.aurora.gen.ScheduleStatus.RUNNING;
-import static org.apache.aurora.scheduler.configuration.ConfigurationManager.HOST_CONSTRAINT;
-import static org.apache.mesos.Protos.Offer;
 import static org.easymock.EasyMock.expect;
 import static org.junit.Assert.assertTrue;
 
 public class UserTaskLauncherTest extends EasyMockTest {
 
-  private static final String FRAMEWORK_ID = "FrameworkId";
-
-  private static final SlaveID SLAVE_ID = SlaveID.newBuilder().setValue("SlaveId").build();
-  private static final String SLAVE_HOST_1 = "SlaveHost1";
-
   private static final String TASK_ID_A = "task_id_a";
 
   private static final OfferID OFFER_ID = OfferID.newBuilder().setValue("OfferId").build();
-  private static final HostOffer OFFER = createOffer(SLAVE_ID, SLAVE_HOST_1, 4, 1024, 1024);
+  private static final HostOffer OFFER = new HostOffer(
+      Offers.createOffer(4, 1024, 1024, Pair.of(80, 80)),
+      IHostAttributes.build(new HostAttributes()));
 
   private OfferQueue offerQueue;
   private StateManager stateManager;
@@ -179,42 +160,4 @@ public class UserTaskLauncherTest extends EasyMockTest {
         .build();
     launcher.statusUpdate(status);
   }
-
-  private static HostOffer createOffer(SlaveID slave, String slaveHost, double cpu,
-      double ramMb, double diskMb) {
-    return createOffer(slave, slaveHost, cpu, ramMb, diskMb,
-        ImmutableSet.<Pair<Integer, Integer>>of());
-  }
-
-  private static HostOffer createOffer(SlaveID slave, String slaveHost, double cpu,
-      double ramMb, double diskMb, Set<Pair<Integer, Integer>> ports) {
-
-    Ranges portRanges = Ranges.newBuilder()
-        .addAllRange(Iterables.transform(ports, new Function<Pair<Integer, Integer>, Range>() {
-          @Override
-          public Range apply(Pair<Integer, Integer> range) {
-            return Range.newBuilder().setBegin(range.getFirst()).setEnd(range.getSecond()).build();
-          }
-        }))
-        .build();
-
-    Offer mesosOffer = Offer.newBuilder()
-        .addResources(Resource.newBuilder().setType(Type.SCALAR).setName(Resources.CPUS)
-            .setScalar(Scalar.newBuilder().setValue(cpu)))
-        .addResources(Resource.newBuilder().setType(Type.SCALAR).setName(Resources.RAM_MB)
-            .setScalar(Scalar.newBuilder().setValue(ramMb)))
-        .addResources(Resource.newBuilder().setType(Type.SCALAR).setName(Resources.DISK_MB)
-            .setScalar(Scalar.newBuilder().setValue(diskMb)))
-        .addResources(Resource.newBuilder().setType(Type.RANGES).setName(Resources.PORTS)
-            .setRanges(portRanges))
-        .addAttributes(Attribute.newBuilder().setType(Type.TEXT)
-            .setName(HOST_CONSTRAINT)
-            .setText(Text.newBuilder().setValue(slaveHost)))
-        .setSlaveId(slave)
-        .setHostname(slaveHost)
-        .setFrameworkId(FrameworkID.newBuilder().setValue(FRAMEWORK_ID).build())
-        .setId(OFFER_ID)
-        .build();
-    return new HostOffer(mesosOffer, IHostAttributes.build(new HostAttributes()));
-  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java b/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java
index 65b4836..6575b7d 100644
--- a/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java
+++ b/src/test/java/org/apache/aurora/scheduler/app/SchedulerIT.java
@@ -84,7 +84,7 @@ import org.apache.aurora.scheduler.log.Log.Position;
 import org.apache.aurora.scheduler.log.Log.Stream;
 import org.apache.aurora.scheduler.mesos.DriverFactory;
 import org.apache.aurora.scheduler.mesos.DriverSettings;
-import org.apache.aurora.scheduler.mesos.MesosTaskFactory.ExecutorSettings;
+import org.apache.aurora.scheduler.mesos.ExecutorSettings;
 import org.apache.aurora.scheduler.storage.backup.BackupModule;
 import org.apache.aurora.scheduler.storage.log.EntrySerializer;
 import org.apache.aurora.scheduler.storage.log.LogStorageModule;
@@ -106,8 +106,6 @@ import org.junit.Test;
 
 import static com.twitter.common.testing.easymock.EasyMockTest.createCapture;
 
-import static org.apache.aurora.scheduler.ResourceSlot.EXECUTOR_OVERHEAD_CPUS;
-import static org.apache.aurora.scheduler.ResourceSlot.EXECUTOR_OVERHEAD_RAM;
 import static org.apache.mesos.Protos.FrameworkInfo;
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createControl;
@@ -199,8 +197,8 @@ public class SchedulerIT extends BaseZooKeeperTest {
         bind(DriverSettings.class).toInstance(SETTINGS);
         bind(Log.class).toInstance(log);
         Resources executorOverhead = new Resources(
-            EXECUTOR_OVERHEAD_CPUS.get(),
-            EXECUTOR_OVERHEAD_RAM.get(),
+            0.1,
+            Amount.of(1L, Data.MB),
             Amount.of(0L, Data.MB),
             0);
         bind(ExecutorSettings.class)

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImplTest.java b/src/test/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImplTest.java
index 36dbcf7..4c2448f 100644
--- a/src/test/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImplTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/async/preemptor/PreemptorImplTest.java
@@ -53,6 +53,7 @@ import org.apache.aurora.scheduler.filter.SchedulingFilter;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.ResourceRequest;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.UnusedResource;
 import org.apache.aurora.scheduler.filter.SchedulingFilterImpl;
+import org.apache.aurora.scheduler.mesos.TaskExecutors;
 import org.apache.aurora.scheduler.state.StateManager;
 import org.apache.aurora.scheduler.storage.AttributeStore;
 import org.apache.aurora.scheduler.storage.Storage;
@@ -130,7 +131,8 @@ public class PreemptorImplTest extends EasyMockTest {
         PREEMPTION_DELAY,
         clock,
         statsProvider,
-        new LiveClusterState(storageUtil.storage));
+        new LiveClusterState(storageUtil.storage),
+        TaskExecutors.NO_OVERHEAD_EXECUTOR);
 
     preemptor.findPreemptionSlotFor(pendingTask.getAssignedTask().getTaskId(), emptyJob);
   }
@@ -317,7 +319,7 @@ public class PreemptorImplTest extends EasyMockTest {
   // Ensures a production task can preempt 2 tasks on the same host.
   @Test
   public void testProductionPreemptingManyNonProduction() throws Exception {
-    schedulingFilter = new SchedulingFilterImpl();
+    schedulingFilter = new SchedulingFilterImpl(TaskExecutors.NO_OVERHEAD_EXECUTOR);
     ScheduledTask a1 = makeTask(USER_A, JOB_A, TASK_ID_A + "_a1");
     a1.getAssignedTask().getTask().setNumCpus(1).setRamMb(512);
 
@@ -349,7 +351,7 @@ public class PreemptorImplTest extends EasyMockTest {
   // Ensures we select the minimal number of tasks to preempt
   @Test
   public void testMinimalSetPreempted() throws Exception {
-    schedulingFilter = new SchedulingFilterImpl();
+    schedulingFilter = new SchedulingFilterImpl(TaskExecutors.NO_OVERHEAD_EXECUTOR);
     ScheduledTask a1 = makeTask(USER_A, JOB_A, TASK_ID_A + "_a1");
     a1.getAssignedTask().getTask().setNumCpus(4).setRamMb(4096);
 
@@ -384,7 +386,7 @@ public class PreemptorImplTest extends EasyMockTest {
   // Ensures a production task *never* preempts a production task from another job.
   @Test
   public void testProductionJobNeverPreemptsProductionJob() throws Exception {
-    schedulingFilter = new SchedulingFilterImpl();
+    schedulingFilter = new SchedulingFilterImpl(TaskExecutors.NO_OVERHEAD_EXECUTOR);
     ScheduledTask p1 = makeProductionTask(USER_A, JOB_A, TASK_ID_A + "_p1");
     p1.getAssignedTask().getTask().setNumCpus(2).setRamMb(1024);
 
@@ -409,7 +411,7 @@ public class PreemptorImplTest extends EasyMockTest {
   // Ensures that we can preempt if a task + offer can satisfy a pending task.
   @Test
   public void testPreemptWithOfferAndTask() throws Exception {
-    schedulingFilter = new SchedulingFilterImpl();
+    schedulingFilter = new SchedulingFilterImpl(TaskExecutors.NO_OVERHEAD_EXECUTOR);
 
     setUpHost(HOST_A, RACK_A);
 
@@ -437,7 +439,7 @@ public class PreemptorImplTest extends EasyMockTest {
   // Ensures we can preempt if two tasks and an offer can satisfy a pending task.
   @Test
   public void testPreemptWithOfferAndMultipleTasks() throws Exception {
-    schedulingFilter = new SchedulingFilterImpl();
+    schedulingFilter = new SchedulingFilterImpl(TaskExecutors.NO_OVERHEAD_EXECUTOR);
 
     setUpHost(HOST_A, RACK_A);
 
@@ -470,7 +472,7 @@ public class PreemptorImplTest extends EasyMockTest {
   // Ensures we don't preempt if a host has enough slack to satisfy a pending task.
   @Test
   public void testPreemptWithLargeOffer() throws Exception {
-    schedulingFilter = new SchedulingFilterImpl();
+    schedulingFilter = new SchedulingFilterImpl(TaskExecutors.NO_OVERHEAD_EXECUTOR);
 
     setUpHost(HOST_A, RACK_A);
 
@@ -527,7 +529,8 @@ public class PreemptorImplTest extends EasyMockTest {
         PREEMPTION_DELAY,
         clock,
         statsProvider,
-        new LiveClusterState(storage));
+        new LiveClusterState(storage),
+        TaskExecutors.NO_OVERHEAD_EXECUTOR);
 
     assertEquals(
         Optional.<String>absent(),

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/java/org/apache/aurora/scheduler/events/NotifyingSchedulingFilterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/events/NotifyingSchedulingFilterTest.java b/src/test/java/org/apache/aurora/scheduler/events/NotifyingSchedulingFilterTest.java
index 0b41156..ab7817f 100644
--- a/src/test/java/org/apache/aurora/scheduler/events/NotifyingSchedulingFilterTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/events/NotifyingSchedulingFilterTest.java
@@ -29,6 +29,7 @@ import org.apache.aurora.scheduler.filter.SchedulingFilter;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.ResourceRequest;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.UnusedResource;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.Veto;
+import org.apache.aurora.scheduler.mesos.TaskExecutors;
 import org.apache.aurora.scheduler.storage.AttributeStore;
 import org.apache.aurora.scheduler.storage.entities.IHostAttributes;
 import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
@@ -47,7 +48,7 @@ public class NotifyingSchedulingFilterTest extends EasyMockTest {
       .setDiskMb(1024));
   private static final String TASK_ID = "taskId";
   private static final UnusedResource RESOURCE = new UnusedResource(
-      ResourceSlot.from(TASK),
+      ResourceSlot.from(TASK, TaskExecutors.NO_OVERHEAD_EXECUTOR),
       IHostAttributes.build(new HostAttributes().setHost("host").setMode(MaintenanceMode.NONE)));
   private ResourceRequest request;
 

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/java/org/apache/aurora/scheduler/filter/SchedulingFilterImplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/filter/SchedulingFilterImplTest.java b/src/test/java/org/apache/aurora/scheduler/filter/SchedulingFilterImplTest.java
index 265c38d..52ee7c1 100644
--- a/src/test/java/org/apache/aurora/scheduler/filter/SchedulingFilterImplTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/filter/SchedulingFilterImplTest.java
@@ -21,8 +21,7 @@ import com.google.common.base.Optional;
 import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
-import com.twitter.common.quantity.Amount;
-import com.twitter.common.quantity.Data;
+import com.twitter.common.collections.Pair;
 import com.twitter.common.testing.easymock.EasyMockTest;
 
 import org.apache.aurora.gen.AssignedTask;
@@ -42,6 +41,8 @@ import org.apache.aurora.scheduler.configuration.ConfigurationManager;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.ResourceRequest;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.UnusedResource;
 import org.apache.aurora.scheduler.filter.SchedulingFilter.Veto;
+import org.apache.aurora.scheduler.mesos.Offers;
+import org.apache.aurora.scheduler.mesos.TaskExecutors;
 import org.apache.aurora.scheduler.storage.AttributeStore;
 import org.apache.aurora.scheduler.storage.entities.IHostAttributes;
 import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
@@ -88,10 +89,7 @@ public class SchedulingFilterImplTest extends EasyMockTest {
   private static final long DEFAULT_RAM = 1000;
   private static final long DEFAULT_DISK = 2000;
   private static final ResourceSlot DEFAULT_OFFER = ResourceSlot.from(
-      DEFAULT_CPUS,
-      Amount.of(DEFAULT_RAM, Data.MB),
-      Amount.of(DEFAULT_DISK, Data.MB),
-      0);
+      Offers.createOffer(DEFAULT_CPUS, DEFAULT_RAM, DEFAULT_DISK, Pair.of(80, 80)));
 
   private AttributeAggregate emptyJob;
 
@@ -102,7 +100,7 @@ public class SchedulingFilterImplTest extends EasyMockTest {
 
   @Before
   public void setUp() {
-    defaultFilter = new SchedulingFilterImpl();
+    defaultFilter = new SchedulingFilterImpl(TaskExecutors.NO_OVERHEAD_EXECUTOR);
     attributeStore = createMock(AttributeStore.Mutable.class);
     emptyJob = new AttributeAggregate(
         Suppliers.ofInstance(ImmutableSet.<IScheduledTask>of()),
@@ -125,10 +123,7 @@ public class SchedulingFilterImplTest extends EasyMockTest {
     control.replay();
 
     ResourceSlot twoPorts = ResourceSlot.from(
-        DEFAULT_CPUS,
-        Amount.of(DEFAULT_RAM, Data.MB),
-        Amount.of(DEFAULT_DISK, Data.MB),
-        2);
+        Offers.createOffer(DEFAULT_CPUS, DEFAULT_RAM, DEFAULT_DISK, Pair.of(80, 81)));
 
     ITaskConfig noPortTask = ITaskConfig.build(makeTask(DEFAULT_CPUS, DEFAULT_RAM, DEFAULT_DISK)
         .newBuilder()

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/java/org/apache/aurora/scheduler/mesos/MesosTaskFactoryImplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/mesos/MesosTaskFactoryImplTest.java b/src/test/java/org/apache/aurora/scheduler/mesos/MesosTaskFactoryImplTest.java
index 22faccf..ce3bea5 100644
--- a/src/test/java/org/apache/aurora/scheduler/mesos/MesosTaskFactoryImplTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/mesos/MesosTaskFactoryImplTest.java
@@ -17,7 +17,6 @@ import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.twitter.common.quantity.Amount;
 import com.twitter.common.quantity.Data;
 
 import org.apache.aurora.gen.AssignedTask;
@@ -27,10 +26,11 @@ import org.apache.aurora.gen.Identity;
 import org.apache.aurora.gen.JobKey;
 import org.apache.aurora.gen.MesosContainer;
 import org.apache.aurora.gen.TaskConfig;
+import org.apache.aurora.scheduler.ResourceSlot;
 import org.apache.aurora.scheduler.configuration.Resources;
-import org.apache.aurora.scheduler.mesos.MesosTaskFactory.ExecutorSettings;
 import org.apache.aurora.scheduler.mesos.MesosTaskFactory.MesosTaskFactoryImpl;
 import org.apache.aurora.scheduler.storage.entities.IAssignedTask;
+import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
 import org.apache.mesos.Protos.CommandInfo;
 import org.apache.mesos.Protos.CommandInfo.URI;
 import org.apache.mesos.Protos.ExecutorInfo;
@@ -39,15 +39,14 @@ import org.apache.mesos.Protos.TaskInfo;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.apache.aurora.scheduler.mesos.MesosTaskFactory.MesosTaskFactoryImpl.MIN_TASK_RESOURCES;
-import static org.apache.aurora.scheduler.mesos.MesosTaskFactory.MesosTaskFactoryImpl.MIN_THERMOS_RESOURCES;
+import static org.apache.aurora.scheduler.ResourceSlot.MIN_THERMOS_RESOURCES;
+import static org.apache.aurora.scheduler.mesos.TaskExecutors.NO_OVERHEAD_EXECUTOR;
+import static org.apache.aurora.scheduler.mesos.TaskExecutors.SOME_OVERHEAD_EXECUTOR;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 public class MesosTaskFactoryImplTest {
 
-  private static final String EXECUTOR_PATH = "/twitter/fake/executor.pex";
-  private static final String EXECUTOR_WRAPPER_PATH = "/twitter/fake/executor.sh";
+  private static final String EXECUTOR_WRAPPER_PATH = "/fake/executor_wrapper.sh";
   private static final IAssignedTask TASK = IAssignedTask.build(new AssignedTask()
     .setInstanceId(2)
     .setTaskId("task-id")
@@ -63,22 +62,11 @@ public class MesosTaskFactoryImplTest {
         .setContainer(Container.mesos(new MesosContainer()))
         .setRequestedPorts(ImmutableSet.of("http"))));
   private static final IAssignedTask TASK_WITH_DOCKER = IAssignedTask.build(TASK.newBuilder()
-    .setTask(
-        new TaskConfig(TASK.getTask().newBuilder())
-            .setContainer(Container.docker(new DockerContainer("testimage")))));
+      .setTask(
+          new TaskConfig(TASK.getTask().newBuilder())
+              .setContainer(Container.docker(new DockerContainer("testimage")))));
 
   private static final SlaveID SLAVE = SlaveID.newBuilder().setValue("slave-id").build();
-  private static final Resources SOME_EXECUTOR_OVERHEAD = new Resources(
-      0.01,
-      Amount.of(256L, Data.MB),
-      Amount.of(0L, Data.MB),
-      0);
-
-  private static final Resources NO_EXECUTOR_OVERHEAD = new Resources(
-      0,
-      Amount.of(0L, Data.MB),
-      Amount.of(0L, Data.MB),
-      0);
 
   private MesosTaskFactory taskFactory;
   private ExecutorSettings config;
@@ -87,30 +75,27 @@ public class MesosTaskFactoryImplTest {
       .setExecutorId(MesosTaskFactoryImpl.getExecutorId(TASK.getTaskId()))
       .setName(MesosTaskFactoryImpl.EXECUTOR_NAME)
       .setSource(MesosTaskFactoryImpl.getInstanceSourceName(TASK.getTask(), TASK.getInstanceId()))
-      .addAllResources(MIN_THERMOS_RESOURCES.toResourceList())
+      .addAllResources(MesosTaskFactoryImpl.RESOURCES_EPSILON.toResourceList())
       .setCommand(CommandInfo.newBuilder()
           .setValue("./executor.pex")
           .setShell(true)
-          .addUris(URI.newBuilder().setValue(EXECUTOR_PATH).setExecutable(true)))
+          .addUris(URI.newBuilder().setValue(NO_OVERHEAD_EXECUTOR.getExecutorPath())
+              .setExecutable(true)))
       .build();
 
   private static final ExecutorInfo EXECUTOR_WITH_WRAPPER =
       ExecutorInfo.newBuilder(DEFAULT_EXECUTOR)
           .setCommand(CommandInfo.newBuilder()
-              .setValue("./executor.sh")
+              .setValue("./executor_wrapper.sh")
               .setShell(true)
-              .addUris(URI.newBuilder().setValue(EXECUTOR_PATH).setExecutable(true))
+              .addUris(URI.newBuilder().setValue(NO_OVERHEAD_EXECUTOR.getExecutorPath())
+                  .setExecutable(true))
               .addUris(URI.newBuilder().setValue(EXECUTOR_WRAPPER_PATH).setExecutable(true)))
           .build();
 
   @Before
   public void setUp() {
-    config = new ExecutorSettings(
-        EXECUTOR_PATH,
-        ImmutableList.<String>of(),
-        "/var/run/thermos",
-        Optional.<String>absent(),
-        SOME_EXECUTOR_OVERHEAD);
+    config = TaskExecutors.SOME_OVERHEAD_EXECUTOR;
   }
 
   @Test
@@ -118,117 +103,64 @@ public class MesosTaskFactoryImplTest {
     taskFactory = new MesosTaskFactoryImpl(config);
     TaskInfo task = taskFactory.createFrom(TASK, SLAVE);
     assertEquals(DEFAULT_EXECUTOR, task.getExecutor());
-
-    double taskCPU = config.getExecutorOverhead().getNumCpus()
-        + TASK.getTask().getNumCpus()
-        - MIN_THERMOS_RESOURCES.getNumCpus();
-    long taskRamMB = config.getExecutorOverhead().getRam().as(Data.MB)
-        + TASK.getTask().getRamMb()
-        - MIN_THERMOS_RESOURCES.getRam().as(Data.MB);
-    long taskDiskMB = config.getExecutorOverhead().getDisk().as(Data.MB)
-        + TASK.getTask().getDiskMb()
-        - MIN_THERMOS_RESOURCES.getDisk().as(Data.MB);
-
-    assertTrue(taskCPU > 0.0);
-    assertTrue(taskRamMB > 0);
-    assertTrue(taskDiskMB > 0);
-
-    assertEquals(ImmutableSet.of(
-            Resources.makeMesosResource(Resources.CPUS, taskCPU),
-            Resources.makeMesosResource(Resources.DISK_MB, taskDiskMB),
-            Resources.makeMesosResource(Resources.RAM_MB, taskRamMB),
-            Resources.makeMesosRangeResource(
-                Resources.PORTS,
-                ImmutableSet.copyOf(TASK.getAssignedPorts().values()))
-        ),
-        ImmutableSet.copyOf(task.getResourcesList()));
+    checkTaskResources(TASK.getTask(), task);
   }
 
   @Test
   public void testCreateFromPortsUnset() {
     taskFactory = new MesosTaskFactoryImpl(config);
     AssignedTask assignedTask = TASK.newBuilder();
+    assignedTask.getTask().unsetRequestedPorts();
     assignedTask.unsetAssignedPorts();
     TaskInfo task = taskFactory.createFrom(IAssignedTask.build(assignedTask), SLAVE);
-
-    double taskCPU = config.getExecutorOverhead().getNumCpus()
-        + TASK.getTask().getNumCpus()
-        - MIN_THERMOS_RESOURCES.getNumCpus();
-    long taskRamMB = config.getExecutorOverhead().getRam().as(Data.MB)
-        + TASK.getTask().getRamMb()
-        - MIN_THERMOS_RESOURCES.getRam().as(Data.MB);
-    long taskDiskMB = config.getExecutorOverhead().getDisk().as(Data.MB)
-        + TASK.getTask().getDiskMb()
-        - MIN_THERMOS_RESOURCES.getDisk().as(Data.MB);
-
-    assertTrue(taskCPU > 0.0);
-    assertTrue(taskRamMB > 0);
-    assertTrue(taskDiskMB > 0);
-
-    assertEquals(DEFAULT_EXECUTOR, task.getExecutor());
-    assertEquals(ImmutableSet.of(
-            Resources.makeMesosResource(Resources.CPUS, taskCPU),
-            Resources.makeMesosResource(Resources.DISK_MB, taskDiskMB),
-            Resources.makeMesosResource(Resources.RAM_MB, taskRamMB)
-        ),
-        ImmutableSet.copyOf(task.getResourcesList()));
+    checkTaskResources(ITaskConfig.build(assignedTask.getTask()), task);
   }
 
   @Test
   public void testExecutorInfoNoOverhead() {
     // Here the ram required for the executor is greater than the sum of task resources
     // + executor overhead. We need to ensure we allocate a non-zero amount of ram in this case.
-    config = new ExecutorSettings(
-        EXECUTOR_PATH,
-        ImmutableList.<String>of(),
-        "/var/run/thermos",
-        Optional.<String>absent(),
-        NO_EXECUTOR_OVERHEAD);
+    config = NO_OVERHEAD_EXECUTOR;
     taskFactory = new MesosTaskFactoryImpl(config);
     TaskInfo task = taskFactory.createFrom(TASK, SLAVE);
     assertEquals(DEFAULT_EXECUTOR, task.getExecutor());
 
-    double taskCPU = config.getExecutorOverhead().getNumCpus()
-        + TASK.getTask().getNumCpus()
-        - MIN_THERMOS_RESOURCES.getNumCpus();
+    // Simulate the upsizing needed for the task to meet the minimum thermos requirements.
+    TaskConfig dummyTask = TASK.getTask().newBuilder()
+        .setRamMb(ResourceSlot.MIN_THERMOS_RESOURCES.getRam().as(Data.MB));
+    checkTaskResources(ITaskConfig.build(dummyTask), task);
+  }
 
-    long taskDiskMB = config.getExecutorOverhead().getDisk().as(Data.MB)
-        + TASK.getTask().getDiskMb()
-        - MIN_THERMOS_RESOURCES.getDisk().as(Data.MB);
+  @Test
+  public void testSmallTaskUpsizing() {
+    // A very small task should be upsized to support the minimum resources required by the
+    // executor.
 
-    assertTrue(taskCPU > 0.0);
-    assertTrue(taskDiskMB > 0);
+    config = NO_OVERHEAD_EXECUTOR;
+    taskFactory = new MesosTaskFactoryImpl(config);
 
-    assertEquals(ImmutableSet.of(
-      Resources.makeMesosResource(Resources.CPUS, taskCPU),
-      Resources.makeMesosResource(Resources.RAM_MB, MIN_TASK_RESOURCES.getRam().as(Data.MB)),
-      Resources.makeMesosResource(Resources.DISK_MB, taskDiskMB),
-      Resources.makeMesosRangeResource(
-          Resources.PORTS,
-          ImmutableSet.copyOf(TASK.getAssignedPorts().values()))
-    ),
-    ImmutableSet.copyOf(task.getResourcesList()));
+    AssignedTask builder = TASK.newBuilder();
+    builder.getTask()
+        .setNumCpus(0.001)
+        .setRamMb(1)
+        .setDiskMb(0)
+        .setRequestedPorts(ImmutableSet.<String>of());
+    IAssignedTask assignedTask =
+        IAssignedTask.build(builder.setAssignedPorts(ImmutableMap.<String, Integer>of()));
+
+    assertEquals(
+        MIN_THERMOS_RESOURCES,
+        getTotalTaskResources(taskFactory.createFrom(assignedTask, SLAVE)));
   }
 
-  @Test
-  public void testMaxElements() {
-    Resources highRAM = new Resources(1, Amount.of(8L, Data.GB), Amount.of(10L, Data.MB), 0);
-    Resources rest = new Resources(10, Amount.of(1L, Data.MB), Amount.of(10L, Data.GB), 1);
-
-    Resources result = MesosTaskFactoryImpl.maxElements(highRAM, rest);
-    assertEquals(result.getNumCpus(), 10, 0.001);
-    assertEquals(result.getRam(), Amount.of(8L, Data.GB));
-    assertEquals(result.getDisk(), Amount.of(10L, Data.GB));
-    assertEquals(result.getNumPorts(), 1);
+  private void checkTaskResources(ITaskConfig task, TaskInfo taskInfo) {
+    assertEquals(
+        Resources.sum(Resources.from(task), config.getExecutorOverhead()),
+        getTotalTaskResources(taskInfo));
   }
 
   private TaskInfo getDockerTaskInfo() {
-    config = new ExecutorSettings(
-        EXECUTOR_PATH,
-        ImmutableList.<String>of(),
-        "/var/run/thermos",
-        Optional.<String>absent(),
-        SOME_EXECUTOR_OVERHEAD);
+    config = TaskExecutors.SOME_OVERHEAD_EXECUTOR;
     taskFactory = new MesosTaskFactoryImpl(config);
     return taskFactory.createFrom(TASK_WITH_DOCKER, SLAVE);
   }
@@ -246,19 +178,25 @@ public class MesosTaskFactoryImplTest {
         ImmutableList.<String>of(),
         "",
         Optional.<String>absent(),
-        SOME_EXECUTOR_OVERHEAD);
+        Resources.NONE);
   }
 
   @Test
   public void testExecutorAndWrapper() {
     config = new ExecutorSettings(
         EXECUTOR_WRAPPER_PATH,
-        ImmutableList.of(EXECUTOR_PATH),
+        ImmutableList.of(SOME_OVERHEAD_EXECUTOR.getExecutorPath()),
         "/var/run/thermos",
         Optional.<String>absent(),
-        SOME_EXECUTOR_OVERHEAD);
+        SOME_OVERHEAD_EXECUTOR.getExecutorOverhead());
     taskFactory = new MesosTaskFactoryImpl(config);
     TaskInfo taskInfo = taskFactory.createFrom(TASK, SLAVE);
     assertEquals(EXECUTOR_WITH_WRAPPER, taskInfo.getExecutor());
   }
+
+  private static Resources getTotalTaskResources(TaskInfo task) {
+    Resources taskResources = Resources.from(task.getResourcesList());
+    Resources executorResources = Resources.from(task.getExecutor().getResourcesList());
+    return Resources.sum(taskResources, executorResources);
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/java/org/apache/aurora/scheduler/mesos/Offers.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/mesos/Offers.java b/src/test/java/org/apache/aurora/scheduler/mesos/Offers.java
new file mode 100644
index 0000000..83eec5d
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/mesos/Offers.java
@@ -0,0 +1,65 @@
+/**
+ * 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.mesos;
+
+import com.twitter.common.collections.Pair;
+
+import org.apache.aurora.scheduler.configuration.Resources;
+import org.apache.mesos.Protos;
+import org.apache.mesos.Protos.Offer;
+import org.apache.mesos.Protos.OfferID;
+import org.apache.mesos.Protos.Resource;
+import org.apache.mesos.Protos.SlaveID;
+import org.apache.mesos.Protos.Value.Range;
+import org.apache.mesos.Protos.Value.Ranges;
+import org.apache.mesos.Protos.Value.Scalar;
+import org.apache.mesos.Protos.Value.Type;
+
+import static org.apache.aurora.scheduler.configuration.ConfigurationManager.HOST_CONSTRAINT;
+
+public final class Offers {
+  private Offers() {
+    // Utility class.
+  }
+
+  public static Offer createOffer(
+      double cpu,
+      double ramMb,
+      double diskMb,
+      Pair<Integer, Integer> portRange) {
+
+    Ranges portRanges = Ranges.newBuilder()
+        .addRange(Range
+            .newBuilder().setBegin(portRange.getFirst()).setEnd(portRange.getSecond()).build())
+        .build();
+
+    return Offer.newBuilder()
+        .addResources(Resource.newBuilder().setType(Type.SCALAR).setName(Resources.CPUS)
+            .setScalar(Scalar.newBuilder().setValue(cpu)))
+        .addResources(Resource.newBuilder().setType(Type.SCALAR).setName(Resources.RAM_MB)
+            .setScalar(Scalar.newBuilder().setValue(ramMb)))
+        .addResources(Resource.newBuilder().setType(Type.SCALAR).setName(Resources.DISK_MB)
+            .setScalar(Scalar.newBuilder().setValue(diskMb)))
+        .addResources(Resource.newBuilder().setType(Type.RANGES).setName(Resources.PORTS)
+            .setRanges(portRanges))
+        .addAttributes(Protos.Attribute.newBuilder().setType(Type.TEXT)
+            .setName(HOST_CONSTRAINT)
+            .setText(Protos.Value.Text.newBuilder().setValue("slavehost")))
+        .setSlaveId(SlaveID.newBuilder().setValue("SlaveId").build())
+        .setHostname("slavehost")
+        .setFrameworkId(Protos.FrameworkID.newBuilder().setValue("framework-id").build())
+        .setId(OfferID.newBuilder().setValue("OfferId").build())
+        .build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/java/org/apache/aurora/scheduler/mesos/TaskExecutors.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/mesos/TaskExecutors.java b/src/test/java/org/apache/aurora/scheduler/mesos/TaskExecutors.java
new file mode 100644
index 0000000..efe62ce
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/mesos/TaskExecutors.java
@@ -0,0 +1,47 @@
+/**
+ * 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.mesos;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Data;
+
+import org.apache.aurora.scheduler.configuration.Resources;
+
+/**
+ * Utility class to contain constants related to setting up executor settings.
+ */
+public final class TaskExecutors {
+
+  private TaskExecutors() {
+    // Utility class.
+  }
+
+  private static final String EXECUTOR_PATH = "/fake/executor.pex";
+
+  public static final ExecutorSettings NO_OVERHEAD_EXECUTOR = new ExecutorSettings(
+      EXECUTOR_PATH,
+      ImmutableList.<String>of(),
+      "/var/run/thermos",
+      Optional.<String>absent(),
+      Resources.NONE);
+
+  public static final ExecutorSettings SOME_OVERHEAD_EXECUTOR = new ExecutorSettings(
+      EXECUTOR_PATH,
+      ImmutableList.<String>of(),
+      "/var/run/thermos",
+      Optional.<String>absent(),
+      new Resources(0.01, Amount.of(256L, Data.MB), Amount.of(0L, Data.MB), 0));
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/sh/org/apache/aurora/e2e/http/http_example_updated.aurora
----------------------------------------------------------------------
diff --git a/src/test/sh/org/apache/aurora/e2e/http/http_example_updated.aurora b/src/test/sh/org/apache/aurora/e2e/http/http_example_updated.aurora
index 67d3dbb..98f5c47 100644
--- a/src/test/sh/org/apache/aurora/e2e/http/http_example_updated.aurora
+++ b/src/test/sh/org/apache/aurora/e2e/http/http_example_updated.aurora
@@ -35,7 +35,7 @@ health_check_config = HealthCheckConfig(initial_interval_secs=5, interval_secs=1
 job = Job(
   name = 'http_example',
   cluster = 'devcluster',
-  instances = 4,
+  instances = 3,
   update_config = update_config,
   health_check_config = health_check_config,
   task = test_task,

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/b39f6ee1/src/test/sh/org/apache/aurora/e2e/validate_serverset.py
----------------------------------------------------------------------
diff --git a/src/test/sh/org/apache/aurora/e2e/validate_serverset.py b/src/test/sh/org/apache/aurora/e2e/validate_serverset.py
index 66fa965..352cef8 100644
--- a/src/test/sh/org/apache/aurora/e2e/validate_serverset.py
+++ b/src/test/sh/org/apache/aurora/e2e/validate_serverset.py
@@ -32,14 +32,14 @@ def wait_until_znodes(count, timeout=30):
   return []
 
 
-# job is created with 4 znodes.
-znodes = wait_until_znodes(4, timeout=10)
+# job is created with 3 znodes.
+znodes = wait_until_znodes(3, timeout=10)
 if not znodes:
   sys.exit(DID_NOT_REGISTER)
 
 client.delete(znodes[0])
 
-znodes = wait_until_znodes(4, timeout=10)
+znodes = wait_until_znodes(3, timeout=10)
 if not znodes:
   sys.exit(DID_NOT_RECOVER_FROM_EXPIRY)
 


Mime
View raw message