aurora-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wfar...@apache.org
Subject [5/8] aurora git commit: Use a simpler command line argument system
Date Wed, 11 Oct 2017 00:29:14 GMT
http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/ArgScannerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/ArgScannerTest.java b/commons/src/test/java/org/apache/aurora/common/args/ArgScannerTest.java
deleted file mode 100644
index 06ce914..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/ArgScannerTest.java
+++ /dev/null
@@ -1,632 +0,0 @@
-/**
- * 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.common.args;
-
-import java.io.PrintStream;
-import java.io.Serializable;
-import java.lang.annotation.Annotation;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.io.ByteStreams;
-
-import org.apache.aurora.common.args.ArgScannerTest.StandardArgs.Optimizations;
-import org.apache.aurora.common.args.constraints.NotEmpty;
-import org.apache.aurora.common.args.constraints.NotNegative;
-import org.apache.aurora.common.args.constraints.NotNull;
-import org.apache.aurora.common.args.constraints.Positive;
-import org.apache.aurora.common.args.constraints.Range;
-import org.apache.aurora.common.args.parsers.NonParameterizedTypeParser;
-import org.apache.aurora.common.base.Command;
-import org.apache.aurora.common.base.MorePreconditions;
-import org.apache.aurora.common.collections.Pair;
-import org.apache.aurora.common.quantity.Amount;
-import org.apache.aurora.common.quantity.Data;
-import org.apache.aurora.common.quantity.Time;
-import org.hamcrest.CoreMatchers;
-import org.junit.Before;
-import org.junit.Test;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-/**
- * @author William Farner
- */
-public class ArgScannerTest {
-
-  private static final Function<Class<?>, Predicate<Field>> TO_SCOPE_PREDICATE =
-      cls -> new Predicate<Field>() {
-        @Override public boolean apply(Field field) {
-          return field.getDeclaringClass() == cls;
-        }
-      };
-
-  @Before
-  public void setUp() {
-    // Reset args in all classes before each test.
-    for (Class<?> cls : this.getClass().getDeclaredClasses()) {
-      resetArgs(cls);
-    }
-  }
-
-  public static class StandardArgs {
-    enum Optimizations { NONE, MINIMAL, ALL }
-    @CmdLine(name = "enum", help = "help")
-    static final Arg<Optimizations> ENUM_VAL = Arg.create(Optimizations.MINIMAL);
-    @CmdLine(name = "string", help = "help")
-    static final Arg<String> STRING_VAL = Arg.create("string");
-    @CmdLine(name = "char", help = "help")
-    static final Arg<Character> CHAR_VAL = Arg.create('c');
-    @CmdLine(name = "byte", help = "help")
-    static final Arg<Byte> BYTE_VAL = Arg.create((byte) 0);
-    @CmdLine(name = "short", help = "help")
-    static final Arg<Short> SHORT_VAL = Arg.create((short) 0);
-    @CmdLine(name = "int", help = "help")
-    static final Arg<Integer> INT_VAL = Arg.create(0);
-    @CmdLine(name = "long", help = "help")
-    static final Arg<Long> LONG_VAL = Arg.create(0L);
-    @CmdLine(name = "float", help = "help")
-    static final Arg<Float> FLOAT_VAL = Arg.create(0F);
-    @CmdLine(name = "double", help = "help")
-    static final Arg<Double> DOUBLE_VAL = Arg.create(0D);
-    @CmdLine(name = "bool", help = "help")
-    static final Arg<Boolean> BOOL = Arg.create(false);
-    @CmdLine(name = "regex", help = "help")
-    static final Arg<Pattern> REGEX = Arg.create(null);
-    @CmdLine(name = "time_amount", help = "help")
-    static final Arg<Amount<Long, Time>> TIME_AMOUNT = Arg.create(Amount.of(1L, Time.SECONDS));
-    @CmdLine(name = "data_amount", help = "help")
-    static final Arg<Amount<Long, Data>> DATA_AMOUNT = Arg.create(Amount.of(1L, Data.MB));
-    @CmdLine(name = "range", help = "help")
-    static final Arg<com.google.common.collect.Range<Integer>> RANGE =
-        Arg.create(com.google.common.collect.Range.closed(1, 5));
-  }
-
-  @Test
-  public void testStandardArgs() {
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.ENUM_VAL.get(), CoreMatchers.is(Optimizations.ALL)), "enum", "ALL");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.STRING_VAL.get(), is("newstring")),
-        "string", "newstring");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.CHAR_VAL.get(), is('x')),
-        "char", "x");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.BYTE_VAL.get(), is((byte) 10)),
-        "byte", "10");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.SHORT_VAL.get(), is((short) 10)),
-        "short", "10");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.INT_VAL.get(), is(10)),
-        "int", "10");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.LONG_VAL.get(), is(10L)),
-        "long", "10");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.FLOAT_VAL.get(), is(10f)),
-        "float", "10.0");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.DOUBLE_VAL.get(), is(10d)),
-        "double", "10.0");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.BOOL.get(), is(true)),
-        "bool", "true");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.BOOL.get(), is(true)),
-        "bool", "");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.REGEX.get().matcher("jack").matches(), is(true)),
-        "regex", ".*ack$");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.BOOL.get(), is(false)),
-        "no_bool", "");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.BOOL.get(), is(true)),
-        "no_bool", "false");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.TIME_AMOUNT.get(), is(Amount.of(100L, Time.SECONDS))),
-        "time_amount", "100secs");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.DATA_AMOUNT.get(), is(Amount.of(1L, Data.Gb))),
-        "data_amount", "1Gb");
-    test(StandardArgs.class,
-        () -> assertThat(StandardArgs.RANGE.get(), is(com.google.common.collect.Range.closed(1, 5))),
-        "range", "1-5");
-  }
-
-  public static class Name {
-    private final String name;
-
-    public Name(String name) {
-      this.name = MorePreconditions.checkNotBlank(name);
-    }
-
-    public String getName() {
-      return name;
-    }
-
-    @Override
-    public int hashCode() {
-      return this.name.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      return (obj instanceof Name) && name.equals(((Name) obj).name);
-    }
-  }
-
-  @ArgParser
-  public static class NameParser extends NonParameterizedTypeParser<Name> {
-    @Override public Name doParse(String raw) {
-      return new Name(raw);
-    }
-  }
-
-  public static class MeaningOfLife {
-    private final Long answer;
-
-    public MeaningOfLife(Long answer) {
-      this.answer = Preconditions.checkNotNull(answer);
-    }
-
-    @Override
-    public int hashCode() {
-      return this.answer.hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      return (obj instanceof MeaningOfLife) && answer.equals(((MeaningOfLife) obj).answer);
-    }
-  }
-
-  public static class Monty extends NonParameterizedTypeParser<MeaningOfLife> {
-    @Override public MeaningOfLife doParse(String raw) {
-      return new MeaningOfLife(42L);
-    }
-  }
-
-  public static class CustomArgs {
-    @CmdLine(name = "custom1", help = "help")
-    static final Arg<Name> NAME_VAL = Arg.create(new Name("jim"));
-
-    @CmdLine(name = "custom2", help = "help", parser = Monty.class)
-    static final Arg<MeaningOfLife> MEANING_VAL = Arg.create(new MeaningOfLife(13L));
-  }
-
-  @Test
-  public void testCustomArgs() {
-    test(CustomArgs.class,
-        () -> assertThat(CustomArgs.NAME_VAL.get(), is(new Name("jane"))), "custom1", "jane");
-    test(CustomArgs.class,
-        () -> assertThat(CustomArgs.MEANING_VAL.get(), is(new MeaningOfLife(42L))), "custom2", "jim");
-  }
-
-  @Test
-  public void testHelp() {
-    assertFalse(parse(StandardArgs.class, "-h"));
-    assertFalse(parse(StandardArgs.class, "-help"));
-  }
-
-  @Test
-  public void testAllowsEmptyString() {
-    parse(StandardArgs.class, "-string=");
-    assertThat(StandardArgs.STRING_VAL.get(), is(""));
-
-    resetArgs(StandardArgs.class);
-
-    parse(StandardArgs.class, "-string=''");
-    assertThat(StandardArgs.STRING_VAL.get(), is(""));
-
-    resetArgs(StandardArgs.class);
-
-    parse(StandardArgs.class, "-string=\"\"");
-    assertThat(StandardArgs.STRING_VAL.get(), is(""));
-  }
-
-  public static class CollectionArgs {
-    @CmdLine(name = "stringList", help = "help")
-    static final Arg<List<String>> STRING_LIST = Arg.create(null);
-    @CmdLine(name = "intList", help = "help")
-    static final Arg<List<Integer>> INT_LIST = Arg.create(null);
-    @CmdLine(name = "stringSet", help = "help")
-    static final Arg<Set<String>> STRING_SET = Arg.create(null);
-    @CmdLine(name = "intSet", help = "help")
-    static final Arg<Set<Integer>> INT_SET = Arg.create(null);
-    @CmdLine(name = "stringStringMap", help = "help")
-    static final Arg<Map<String, String>> STRING_STRING_MAP = Arg.create(null);
-    @CmdLine(name = "intIntMap", help = "help")
-    static final Arg<Map<Integer, Integer>> INT_INT_MAP = Arg.create(null);
-    @CmdLine(name = "stringIntMap", help = "help")
-    static final Arg<Map<String, Integer>> STRING_INT_MAP = Arg.create(null);
-    @CmdLine(name = "intStringMap", help = "help")
-    static final Arg<Map<Integer, String>> INT_STRING_MAP = Arg.create(null);
-    @CmdLine(name = "stringStringPair", help = "help")
-    static final Arg<Pair<String, String>> STRING_STRING_PAIR = Arg.create(null);
-    @CmdLine(name = "intIntPair", help = "help")
-    static final Arg<Pair<Integer, Integer>> INT_INT_PAIR = Arg.create(null);
-    @CmdLine(name = "stringTimeAmountPair", help = "help")
-    static final Arg<Pair<String, Amount<Long, Time>>> STRING_TIME_AMOUNT_PAIR = Arg.create(null);
-  }
-
-  @Test
-  public void testCollectionArgs() {
-    test(CollectionArgs.class,
-        () -> assertThat(CollectionArgs.STRING_LIST.get(), is(Arrays.asList("a", "b", "c", "d"))),
-        "stringList", "a,b,c,d");
-    test(CollectionArgs.class,
-        () -> assertThat(CollectionArgs.INT_LIST.get(), is(Arrays.asList(1, 2, 3, 4))),
-        "intList", "1, 2, 3, 4");
-    test(CollectionArgs.class,
-        () -> {
-          Set<String> expected = ImmutableSet.of("a", "b", "c", "d");
-          assertThat(CollectionArgs.STRING_SET.get(), is(expected));
-        },
-        "stringSet", "a,b,c,d");
-    test(CollectionArgs.class,
-        () -> {
-          Set<Integer> expected = ImmutableSet.of(1, 2, 3, 4);
-          assertThat(CollectionArgs.INT_SET.get(), is(expected));
-        },
-        "intSet", "1, 2, 3, 4");
-    test(CollectionArgs.class,
-        () -> {
-          Map<String, String> expected = ImmutableMap.of("a", "b", "c", "d", "e", "f", "g", "h");
-          assertThat(CollectionArgs.STRING_STRING_MAP.get(), is(expected));
-        },
-        "stringStringMap", "a=b, c=d, e=f, g=h");
-    test(CollectionArgs.class,
-        () -> {
-          Map<Integer, Integer> expected = ImmutableMap.of(1, 2, 3, 4, 5, 6, 7, 8);
-          assertThat(CollectionArgs.INT_INT_MAP.get(), is(expected));
-        },
-        "intIntMap", "1 = 2,3=4, 5=6 ,7=8");
-    test(CollectionArgs.class,
-        () -> {
-          Map<String, Integer> expected = ImmutableMap.of("a", 1, "b", 2, "c", 3, "d", 4);
-          assertThat(CollectionArgs.STRING_INT_MAP.get(), is(expected));
-        },
-        "stringIntMap", "a=1  , b=2, c=3   ,d=4");
-    test(CollectionArgs.class,
-        () -> {
-          Map<Integer, String> expected = ImmutableMap.of(1, "1", 2, "2", 3, "3", 4, "4");
-          assertThat(CollectionArgs.INT_STRING_MAP.get(), is(expected));
-        },
-        "intStringMap", "  1=1  , 2=2, 3=3,4=4");
-    test(CollectionArgs.class,
-        () -> assertThat(CollectionArgs.STRING_STRING_PAIR.get(), is(Pair.of("foo", "bar"))),
-        "stringStringPair", "foo , bar");
-    test(CollectionArgs.class,
-        () -> assertThat(CollectionArgs.INT_INT_PAIR.get(), is(Pair.of(10, 20))),
-        "intIntPair", "10    ,20");
-    test(CollectionArgs.class,
-        () -> assertThat(CollectionArgs.STRING_TIME_AMOUNT_PAIR.get(),
-                   is(Pair.of("fred", Amount.of(42L, Time.MINUTES)))),
-        "stringTimeAmountPair", "fred    ,42mins");
-    test(CollectionArgs.class,
-        CollectionArgs.STRING_TIME_AMOUNT_PAIR::get,
-        true, "stringTimeAmountPair", "george,1MB");
-
-  }
-
-  static class Serializable1 implements Serializable { }
-  static class Serializable2 implements Serializable { }
-
-  public static class WildcardArgs {
-    @CmdLine(name = "class", help = "help")
-    static final Arg<? extends Class<? extends Serializable>> CLAZZ =
-        Arg.create(Serializable1.class);
-    @CmdLine(name = "classList1", help = "help")
-    static final Arg<List<Class<? extends Serializable>>> CLASS_LIST_1 = Arg.create(null);
-    @CmdLine(name = "classList2", help = "help")
-    static final Arg<List<? extends Class<? extends Serializable>>> CLASS_LIST_2 = Arg.create(null);
-  }
-
-  @Test
-  public void testWildcardArgs() {
-    test(WildcardArgs.class,
-        () -> assertSame(Serializable2.class, WildcardArgs.CLAZZ.get()),
-        "class", Serializable2.class.getName());
-
-    test(WildcardArgs.class,
-        WildcardArgs.CLAZZ::get,
-        true, "class", Runnable.class.getName());
-
-    test(WildcardArgs.class,
-        () -> assertEquals(ImmutableList.of(Serializable1.class, Serializable2.class),
-                     WildcardArgs.CLASS_LIST_1.get()),
-        "classList1", Serializable1.class.getName() + "," + Serializable2.class.getName());
-
-    test(WildcardArgs.class,
-        () -> assertEquals(ImmutableList.of(Serializable2.class), WildcardArgs.CLASS_LIST_2.get()),
-        "classList2", Serializable2.class.getName());
-
-    test(WildcardArgs.class,
-        WildcardArgs.CLASS_LIST_2::get,
-        true, "classList2", Serializable1.class.getName() + "," + Runnable.class.getName());
-  }
-
-  @Target(FIELD)
-  @Retention(RUNTIME)
-  public static @interface Equals {
-    String value();
-  }
-
-  @VerifierFor(Equals.class)
-  public static class SameName implements Verifier<Name> {
-    @Override
-    public void verify(Name value, Annotation annotation) {
-      Preconditions.checkArgument(getValue(annotation).equals(value.getName()));
-    }
-
-    @Override
-    public String toString(Class<? extends Name> argType, Annotation annotation) {
-      return "name = " + getValue(annotation);
-    }
-
-    private String getValue(Annotation annotation) {
-      return ((Equals) annotation).value();
-    }
-  }
-
-  public static class VerifyArgs {
-    @Equals("jake") @CmdLine(name = "custom", help = "help")
-    static final Arg<Name> CUSTOM_VAL = Arg.create(new Name("jake"));
-    @NotEmpty @CmdLine(name = "string", help = "help")
-    static final Arg<String> STRING_VAL = Arg.create("string");
-    @NotEmpty @CmdLine(name = "optional_string", help = "help")
-    static final Arg<String> OPTIONAL_STRING_VAL = Arg.create(null);
-    @Positive
-    @CmdLine(name = "int", help = "help")
-    static final Arg<Integer> INT_VAL = Arg.create(1);
-    @NotNegative
-    @CmdLine(name = "long", help = "help")
-    static final Arg<Long> LONG_VAL = Arg.create(0L);
-    @Range(lower = 10, upper = 20) @CmdLine(name = "float", help = "help")
-    static final Arg<Float> FLOAT_VAL = Arg.create(10F);
-    @CmdLine(name = "double", help = "help")
-    static final Arg<Double> DOUBLE_VAL = Arg.create(0D);
-    @CmdLine(name = "bool", help = "help")
-    static final Arg<Boolean> BOOL = Arg.create(false);
-    @CmdLine(name = "arg_without_default", help = "help")
-    static final Arg<Boolean> ARG_WITHOUT_DEFAULT = Arg.create();
-  }
-
-  @Test
-  public void testEnforcesConstraints() {
-    test(VerifyArgs.class,
-        () -> {
-          assertThat(VerifyArgs.STRING_VAL.get(), is("newstring"));
-          assertThat(VerifyArgs.OPTIONAL_STRING_VAL.get(), nullValue(String.class));
-        },
-        "string", "newstring");
-
-    testFails(VerifyArgs.class, "custom", "jane");
-    testFails(VerifyArgs.class, "string", "");
-    testFails(VerifyArgs.class, "optional_string", "");
-    testFails(VerifyArgs.class, "int", "0");
-    testFails(VerifyArgs.class, "long", "-1");
-
-    test(VerifyArgs.class,
-        () -> assertThat(VerifyArgs.FLOAT_VAL.get(), is(10.5f)),
-        "float", "10.5");
-    testFails(VerifyArgs.class, "float", "9");
-  }
-
-  @Test
-  public void testJoinKeysToValues() {
-    assertThat(ArgScanner.joinKeysToValues(Arrays.asList("")), is(Arrays.asList("")));
-    assertThat(ArgScanner.joinKeysToValues(Arrays.asList("-a", "b", "-c", "-d")),
-        is(Arrays.asList("-a=b", "-c", "-d")));
-    assertThat(ArgScanner.joinKeysToValues(Arrays.asList("-a='b'", "-c", "-d", "'e'")),
-        is(Arrays.asList("-a='b'", "-c", "-d='e'")));
-    assertThat(ArgScanner.joinKeysToValues(Arrays.asList("-a=-b", "c", "-d", "\"e\"")),
-        is(Arrays.asList("-a=-b", "c", "-d=\"e\"")));
-  }
-
-  public static class ShortHelpArg {
-    @CmdLine(name = "h", help = "help")
-    static final Arg<String> SHORT_HELP = Arg.create("string");
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testShortHelpReserved() {
-    parse(ShortHelpArg.class);
-  }
-
-  public static class LongHelpArg {
-    @CmdLine(name = "help", help = "help")
-    static final Arg<String> LONG_HELP = Arg.create("string");
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testLongHelpReserved() {
-    parse(LongHelpArg.class);
-  }
-
-  public static class DuplicateNames {
-    @CmdLine(name = "string", help = "help") static final Arg<String> STRING_1 = Arg.create();
-    @CmdLine(name = "string", help = "help") static final Arg<String> STRING_2 = Arg.create();
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testRejectsDuplicates() {
-    parse(DuplicateNames.class, "-string-str");
-  }
-
-  public static class OneRequired {
-    @CmdLine(name = "string1", help = "help")
-    static final Arg<String> STRING_1 = Arg.create(null);
-    @NotNull
-    @CmdLine(name = "string2", help = "help")
-    static final Arg<String> STRING_2 = Arg.create(null);
-  }
-
-  @Test
-  public void testRequiredProvided() {
-    parse(OneRequired.class, "-string2=blah");
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testMissingRequired() {
-    parse(OneRequired.class, "-string1=blah");
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testUnrecognizedArg() {
-    parse(OneRequired.class, "-string2=blah", "-string3=blah");
-  }
-
-  public static class NameClashA {
-    @CmdLine(name = "string", help = "help")
-    static final Arg<String> STRING = Arg.create(null);
-    @CmdLine(name = "boolean", help = "help")
-    static final Arg<Boolean> BOOLEAN = Arg.create(true);
-  }
-
-  public static class NameClashB {
-    @CmdLine(name = "string", help = "help")
-    static final Arg<String> STRING_1 = Arg.create(null);
-    @CmdLine(name = "boolean", help = "help")
-    static final Arg<Boolean> BOOLEAN_1 = Arg.create(true);
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testDisallowsShortNameOnArgCollision() {
-    parse(ImmutableList.of(NameClashA.class, NameClashB.class), "-string=blah");
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testDisallowsShortNegNameOnArgCollision() {
-    parse(ImmutableList.of(NameClashA.class, NameClashB.class), "-no_boolean");
-  }
-
-  public static class AmountContainer {
-    @CmdLine(name = "time_amount", help = "help")
-    static final Arg<Amount<Integer, Time>> TIME_AMOUNT = Arg.create(null);
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testBadUnitType() {
-    parse(ImmutableList.of(AmountContainer.class), "-time_amount=1Mb");
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testUnrecognizedUnitType() {
-    parse(ImmutableList.of(AmountContainer.class), "-time_amount=1abcd");
-  }
-
-  // TODO(William Farner): Do we want to support nested parameterized args?  If so, need to define a
-  // syntax for that and build it in.
-  //    e.g. List<List<Integer>>, List<Pair<String, String>>
-
-  private static void testFails(Class<?> scope, String arg, String value) {
-    test(scope, null, true, arg, value);
-  }
-
-  private static void test(Class<?> scope, Command validate, String arg, String value) {
-    test(scope, validate, false, arg, value);
-  }
-
-  private static void test(Class<?> scope, Command validate, boolean expectFails, String arg,
-      String value) {
-
-    if (value.isEmpty()) {
-      testValidate(scope, validate, expectFails, String.format("-%s", arg));
-    } else {
-      testValidate(scope, validate, expectFails, String.format("-%s=%s", arg, value));
-      testValidate(scope, validate, expectFails, String.format("-%s='%s'", arg, value));
-      testValidate(scope, validate, expectFails, String.format("-%s=\"%s\"", arg, value));
-      testValidate(scope, validate, expectFails, String.format("-%s", arg), value);
-      testValidate(scope, validate, expectFails,
-          String.format("-%s", arg), String.format("'%s'", value));
-      testValidate(scope, validate, expectFails, String.format("-%s \"%s\"", arg, value));
-      testValidate(scope, validate, expectFails,
-          String.format("-%s", arg), String.format("%s", value));
-    }
-  }
-
-  private static void testValidate(Class<?> scope, Command validate, boolean expectFails,
-      String... args) {
-    resetArgs(scope);
-    IllegalArgumentException exception = null;
-    try {
-      assertTrue(parse(scope, args));
-    } catch (IllegalArgumentException e) {
-      exception = e;
-    }
-
-    if (!expectFails && exception != null) {
-      throw exception;
-    }
-    if (expectFails && exception == null) {
-      fail("Expected exception.");
-    }
-
-    if (validate != null) {
-      validate.execute();
-    }
-    resetArgs(scope);
-  }
-
-  private static void resetArgs(Class<?> scope) {
-    for (Field field : scope.getDeclaredFields()) {
-      if (Arg.class.isAssignableFrom(field.getType()) && Modifier.isStatic(field.getModifiers())) {
-        try {
-          ((Arg) field.get(null)).reset();
-        } catch (IllegalAccessException e) {
-          fail(e.getMessage());
-        }
-      }
-    }
-  }
-
-  private static boolean parse(final Class<?> scope, String... args) {
-    return parse(ImmutableList.of(scope), args);
-  }
-
-  private static boolean parse(Iterable<? extends Class<?>> scopes, String... args) {
-    Predicate<Field> filter = Predicates.or(Iterables.transform(scopes, TO_SCOPE_PREDICATE));
-    PrintStream devNull = new PrintStream(ByteStreams.nullOutputStream());
-    return new ArgScanner(devNull).parse(filter, Arrays.asList(args));
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/ArgTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/ArgTest.java b/commons/src/test/java/org/apache/aurora/common/args/ArgTest.java
deleted file mode 100644
index cbcc575..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/ArgTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * 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.common.args;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-
-public class ArgTest {
-
-  @Test
-  public void testSetAfterGet() {
-    Arg<Boolean> arg = new Arg<Boolean>(false);
-    arg.get();
-    try {
-      arg.set(true);
-      fail("Expected set after get to throw");
-    } catch (IllegalStateException e) {
-      assertFalse(arg.get());
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/ArgsTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/ArgsTest.java b/commons/src/test/java/org/apache/aurora/common/args/ArgsTest.java
deleted file mode 100644
index 7bccaba..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/ArgsTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * 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.common.args;
-
-import java.io.File;
-import java.io.IOException;
-
-import com.google.common.collect.ImmutableList;
-
-import org.apache.aurora.common.args.constraints.NotEmpty;
-import org.apache.aurora.common.args.constraints.Range;
-import org.junit.Test;
-
-import static junit.framework.Assert.assertEquals;
-
-public class ArgsTest {
-  private static class App {
-    @CmdLine(name = "db", help = "help")
-    private static final Arg<File> DB = Arg.create();
-
-    @NotEmpty
-    @CmdLine(name = "name", help = "help")
-    private final Arg<String> name = Arg.create();
-  }
-
-  @Test
-  public void testMixed() throws IOException {
-    App app = new App();
-
-    new ArgScanner().parse(Args.from(ArgFilters.selectClass(App.class), app),
-        ImmutableList.of("-name=bob", "-db=fred", "1", "137"));
-
-    assertEquals(new File("fred"), App.DB.get());
-    assertEquals("bob", app.name.get());
-  }
-
-  @Test
-  public void testReentrance() throws IOException {
-    class InnerApp {
-      @Range(lower = 0.0, upper = 1.0)
-      @CmdLine(name = "level", help = "help")
-      private final Arg<Double> level = Arg.create();
-    }
-
-    InnerApp app1 = new InnerApp();
-    InnerApp app2 = new InnerApp();
-
-    new ArgScanner().parse(Args.from(ArgFilters.selectClass(InnerApp.class), app1),
-        ImmutableList.of("-level=0.5"));
-    new ArgScanner().parse(Args.from(ArgFilters.selectClass(InnerApp.class), app2),
-        ImmutableList.of("-level=0.00729"));
-
-    assertEquals(0.5, app1.level.get(), 0.00001);
-    assertEquals(0.00729, app2.level.get(), 0.00001);
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/OptionInfoTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/OptionInfoTest.java b/commons/src/test/java/org/apache/aurora/common/args/OptionInfoTest.java
deleted file mode 100644
index 7573430..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/OptionInfoTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * 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.common.args;
-
-import java.io.File;
-import java.util.List;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import static junit.framework.Assert.assertEquals;
-
-public class OptionInfoTest {
-  private static class App {
-    @CmdLine(name = "files", help = "help.", argFile = true)
-    private final Arg<List<File>> files = Arg.<List<File>>create(ImmutableList.<File>of());
-
-    @CmdLine(name = "flag", help = "help.")
-    private final Arg<Boolean> flag = Arg.create();
-  }
-
-  @Rule
-  public TemporaryFolder tmpDir = new TemporaryFolder();
-  private App app;
-
-  @Before
-  public void setUp() throws Exception {
-    app = new App();
-  }
-
-  @Test
-  public void testArgumentFilesRegularFormat() throws Exception {
-    new ArgScanner().parse(Args.from(ArgFilters.selectClass(App.class), app),
-        ImmutableList.of("-files=1.txt,2.txt"));
-    assertEquals(
-        ImmutableList.of(new File("1.txt"), new File("2.txt")),
-        app.files.get());
-  }
-
-  @Test
-  public void testArgumentFlagCreateFromField() throws Exception {
-    OptionInfo optionInfo = OptionInfo.createFromField(App.class.getDeclaredField("flag"), app);
-    assertEquals("flag", optionInfo.getName());
-    assertEquals("help.", optionInfo.getHelp());
-    assertEquals("no_flag", optionInfo.getNegatedName());
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/ParsersTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/ParsersTest.java b/commons/src/test/java/org/apache/aurora/common/args/ParsersTest.java
deleted file mode 100644
index 991291d..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/ParsersTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * 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.common.args;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.reflect.TypeToken;
-
-import org.apache.aurora.common.args.apt.Configuration.ParserInfo;
-import org.apache.aurora.common.args.parsers.NonParameterizedTypeParser;
-import org.apache.aurora.common.args.parsers.PairParser;
-import org.apache.aurora.common.collections.Pair;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-
-public class ParsersTest {
-
-  private Parsers defaultParsers;
-
-  @Before
-  public void setUp() {
-    defaultParsers =
-        new Parsers(ImmutableMap.<Class<?>, Parser<?>>of(
-            String.class, new StringParser(),
-            Pair.class, new PairParser()));
-  }
-
-  @Test
-  public void testParseTypeFamily() {
-    assertNotNull(defaultParsers.get(TypeToken.of(String.class)));
-
-    class Credentials extends Pair<String, String> {
-      public Credentials(String first, String second) {
-        super(first, second);
-      }
-    }
-    Parser parser = defaultParsers.get(TypeToken.of(Credentials.class));
-    assertNotNull(parser);
-    assertSame(parser, defaultParsers.get(TypeToken.of(Pair.class)));
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testNoParser() {
-    class NoParserForMe { }
-    assertNull(defaultParsers.get(TypeToken.of(NoParserForMe.class)));
-  }
-
-  static class StringParser extends NonParameterizedTypeParser<Integer> {
-    @Override public Integer doParse(String raw) throws IllegalArgumentException {
-      return raw.length();
-    }
-  }
-
-  @Test
-  public void testNonPublicParsers() {
-    @SuppressWarnings("unchecked")
-    Parser<Integer> parser = (Parser<Integer>)
-        Parsers.INFO_TO_PARSER.apply(
-            new ParserInfo(Integer.class.getName(), StringParser.class.getName()));
-
-    assertEquals(
-        Integer.valueOf(42),
-        parser.parse(null, null, "themeaningoflifeisfortytwointhebookbyadams"));
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/ArgsRoot.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/ArgsRoot.java b/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/ArgsRoot.java
deleted file mode 100644
index fefff0c..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/ArgsRoot.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * 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.common.args.argfilterstest;
-
-import org.apache.aurora.common.args.Arg;
-import org.apache.aurora.common.args.CmdLine;
-
-/**
- * @author John Sirois
- */
-public final class ArgsRoot {
-  @CmdLine(name = "args_root", help = "")
-  static final Arg<String> ARGS_ROOT = Arg.create();
-
-  private ArgsRoot() {
-    // Test class.
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageA/ArgsA.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageA/ArgsA.java b/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageA/ArgsA.java
deleted file mode 100644
index 7777875..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageA/ArgsA.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * 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.common.args.argfilterstest.subpackageA;
-
-import org.apache.aurora.common.args.Arg;
-import org.apache.aurora.common.args.CmdLine;
-
-/**
- * @author John Sirois
- */
-public final class ArgsA {
-  @CmdLine(name = "args_a", help = "")
-  static final Arg<String> ARGS_A = Arg.create();
-
-  private ArgsA() {
-    // Test class.
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageA/subsubpackage1/ArgsA1.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageA/subsubpackage1/ArgsA1.java b/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageA/subsubpackage1/ArgsA1.java
deleted file mode 100644
index 43af4f2..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageA/subsubpackage1/ArgsA1.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * 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.common.args.argfilterstest.subpackageA.subsubpackage1;
-
-import org.apache.aurora.common.args.Arg;
-import org.apache.aurora.common.args.CmdLine;
-
-/**
- * @author John Sirois
- */
-public final class ArgsA1 {
-  @CmdLine(name = "args_a1", help = "")
-  static final Arg<String> ARGS_A1 = Arg.create();
-
-  private ArgsA1() {
-    // Test class.
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageB/ArgsB.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageB/ArgsB.java b/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageB/ArgsB.java
deleted file mode 100644
index 11ed7e3..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageB/ArgsB.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * 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.common.args.argfilterstest.subpackageB;
-
-import org.apache.aurora.common.args.Arg;
-import org.apache.aurora.common.args.CmdLine;
-
-/**
- * @author John Sirois
- */
-public final class ArgsB {
-  @CmdLine(name = "args_b", help = "")
-  static final Arg<String> ARGS_B = Arg.create();
-
-  private ArgsB() {
-    // Test class.
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageBwithSuffix/ArgsBWithSuffix.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageBwithSuffix/ArgsBWithSuffix.java b/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageBwithSuffix/ArgsBWithSuffix.java
deleted file mode 100644
index 0ef8d5f..0000000
--- a/commons/src/test/java/org/apache/aurora/common/args/argfilterstest/subpackageBwithSuffix/ArgsBWithSuffix.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * 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.common.args.argfilterstest.subpackageBwithSuffix;
-
-import org.apache.aurora.common.args.Arg;
-import org.apache.aurora.common.args.CmdLine;
-
-/**
- * @author John Sirois
- */
-public final class ArgsBWithSuffix {
-  @CmdLine(name = "args_b_with_suffix", help = "")
-  static final Arg<String> ARGS_B_WITH_SUFFIX = Arg.create();
-
-  private ArgsBWithSuffix() {
-    // Test class.
-  }
-}

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/config/checkstyle/checkstyle.xml
----------------------------------------------------------------------
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index abc0760..d1f8b0f 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -249,11 +249,6 @@ limitations under the License.
     <module name="FinalClass"/>
     <module name="HideUtilityClassConstructor"/>
     <module name="InterfaceIsType"/>
-    <module name="VisibilityModifier">
-      <property name="protectedAllowed" value="true"/>
-      <!-- Allow public members at the coder's discretion, for struct-like things. -->
-      <property name="publicMemberPattern" value="^.*$" />
-    </module>
     <module name="MutableException"/>
 
     <!-- Miscellaneous other checks.                   -->

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/config/checkstyle/suppressions.xml
----------------------------------------------------------------------
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
index cd7fd0a..c4081b9 100644
--- a/config/checkstyle/suppressions.xml
+++ b/config/checkstyle/suppressions.xml
@@ -19,7 +19,7 @@ limitations under the License.
 
 <suppressions>
   <!-- Allow use of System.exit() in main. -->
-  <suppress files="org/apache/aurora/scheduler/app/SchedulerMain.java"
+  <suppress files="org/apache/aurora/scheduler/config/CommandLine.java"
             checks="RegexpSinglelineJava"/>
   <suppress files="org/apache/aurora/scheduler/storage/db/migration/.*" checks="TypeName" />
   <suppress files="org/apache/aurora/scheduler/storage/db/testmigration/.*" checks="TypeName" />

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/config/findbugs/excludeFilter.xml
----------------------------------------------------------------------
diff --git a/config/findbugs/excludeFilter.xml b/config/findbugs/excludeFilter.xml
index f7d5ae0..4d5b36f 100644
--- a/config/findbugs/excludeFilter.xml
+++ b/config/findbugs/excludeFilter.xml
@@ -120,4 +120,10 @@ limitations under the License.
     </Or>
     <Bug pattern="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT" />
   </Match>
+
+  <Match>
+    <!-- Options fields may be flagged as always null, since they are set reflectively. -->
+    <Class name="~org.apache.aurora.scheduler.*Options" />
+    <Bug pattern="UWF_NULL_FIELD" />
+  </Match>
 </FindBugsFilter>

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java
----------------------------------------------------------------------
diff --git a/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java b/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java
index cb783ce..7d37668 100644
--- a/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java
+++ b/src/jmh/java/org/apache/aurora/benchmark/SchedulingBenchmarks.java
@@ -47,6 +47,8 @@ import org.apache.aurora.scheduler.TierModule;
 import org.apache.aurora.scheduler.async.AsyncModule;
 import org.apache.aurora.scheduler.async.DelayExecutor;
 import org.apache.aurora.scheduler.base.TaskTestUtil;
+import org.apache.aurora.scheduler.config.CliOptions;
+import org.apache.aurora.scheduler.config.types.TimeAmount;
 import org.apache.aurora.scheduler.configuration.executor.ExecutorSettings;
 import org.apache.aurora.scheduler.events.EventSink;
 import org.apache.aurora.scheduler.filter.SchedulingFilter;
@@ -99,7 +101,7 @@ public class SchedulingBenchmarks {
   @Fork(1)
   @State(Scope.Thread)
   public abstract static class AbstractBase {
-    private static final Amount<Long, Time> NO_DELAY = Amount.of(1L, Time.MILLISECONDS);
+    private static final TimeAmount NO_DELAY = new TimeAmount(1L, Time.MILLISECONDS);
     private static final Amount<Long, Time> DELAY_FOREVER = Amount.of(30L, Time.DAYS);
     private static final Integer BATCH_SIZE = 5;
     protected Storage storage;
@@ -118,10 +120,16 @@ public class SchedulingBenchmarks {
       final FakeClock clock = new FakeClock();
       clock.setNowMillis(System.currentTimeMillis());
 
+      CliOptions options = new CliOptions();
+      options.preemptor.enablePreemptor = true;
+      options.preemptor.preemptionDelay = NO_DELAY;
+      options.preemptor.preemptionSlotSearchInterval = NO_DELAY;
+      options.preemptor.reservationMaxBatchSize = BATCH_SIZE;
+
       // TODO(maxim): Find a way to DRY it and reuse existing modules instead.
       Injector injector = Guice.createInjector(
-          new StateModule(),
-          new PreemptorModule(true, NO_DELAY, NO_DELAY, BATCH_SIZE),
+          new StateModule(new CliOptions()),
+          new PreemptorModule(options),
           new TierModule(TaskTestUtil.TIER_CONFIG),
           new PrivateModule() {
             @Override

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/jmh/java/org/apache/aurora/benchmark/StateManagerBenchmarks.java
----------------------------------------------------------------------
diff --git a/src/jmh/java/org/apache/aurora/benchmark/StateManagerBenchmarks.java b/src/jmh/java/org/apache/aurora/benchmark/StateManagerBenchmarks.java
index b4f14f1..c293a9f 100644
--- a/src/jmh/java/org/apache/aurora/benchmark/StateManagerBenchmarks.java
+++ b/src/jmh/java/org/apache/aurora/benchmark/StateManagerBenchmarks.java
@@ -185,9 +185,9 @@ public class StateManagerBenchmarks {
             bind(StatsProvider.class).toInstance(new FakeStatsProvider());
           }
         },
-        DbModule.productionModule(Bindings.KeyFactory.PLAIN),
+        DbModule.productionModule(Bindings.KeyFactory.PLAIN, new DbModule.Options()),
         // This is needed for storage
-        new AsyncModule()
+        new AsyncModule(new AsyncModule.Options())
     );
   }
 }

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/jmh/java/org/apache/aurora/benchmark/StatusUpdateBenchmark.java
----------------------------------------------------------------------
diff --git a/src/jmh/java/org/apache/aurora/benchmark/StatusUpdateBenchmark.java b/src/jmh/java/org/apache/aurora/benchmark/StatusUpdateBenchmark.java
index c81387f..45c2ab9 100644
--- a/src/jmh/java/org/apache/aurora/benchmark/StatusUpdateBenchmark.java
+++ b/src/jmh/java/org/apache/aurora/benchmark/StatusUpdateBenchmark.java
@@ -53,6 +53,7 @@ import org.apache.aurora.scheduler.TaskStatusHandlerImpl;
 import org.apache.aurora.scheduler.TierModule;
 import org.apache.aurora.scheduler.base.AsyncUtil;
 import org.apache.aurora.scheduler.base.TaskTestUtil;
+import org.apache.aurora.scheduler.config.CliOptions;
 import org.apache.aurora.scheduler.configuration.executor.ExecutorSettings;
 import org.apache.aurora.scheduler.events.EventSink;
 import org.apache.aurora.scheduler.events.PubsubEvent;
@@ -179,7 +180,7 @@ public class StatusUpdateBenchmark {
     storage = new SlowStorageWrapper(DbUtil.createStorage());
 
     Injector injector = Guice.createInjector(
-        new StateModule(),
+        new StateModule(new CliOptions()),
         new TierModule(TaskTestUtil.TIER_CONFIG),
         new AbstractModule() {
           @Override

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/jmh/java/org/apache/aurora/benchmark/TaskStoreBenchmarks.java
----------------------------------------------------------------------
diff --git a/src/jmh/java/org/apache/aurora/benchmark/TaskStoreBenchmarks.java b/src/jmh/java/org/apache/aurora/benchmark/TaskStoreBenchmarks.java
index 5b4d2a2..f5e44df 100644
--- a/src/jmh/java/org/apache/aurora/benchmark/TaskStoreBenchmarks.java
+++ b/src/jmh/java/org/apache/aurora/benchmark/TaskStoreBenchmarks.java
@@ -85,7 +85,9 @@ public class TaskStoreBenchmarks {
     public void setUp() {
       storage = Guice.createInjector(
           Modules.combine(
-              DbModule.testModuleWithWorkQueue(PLAIN, Optional.of(new InMemStoresModule(PLAIN))),
+              DbModule.testModuleWithWorkQueue(
+                  PLAIN,
+                  Optional.of(new InMemStoresModule(new DbModule.Options(), PLAIN))),
               new AbstractModule() {
                 @Override
                 protected void configure() {

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/jmh/java/org/apache/aurora/benchmark/ThriftApiBenchmarks.java
----------------------------------------------------------------------
diff --git a/src/jmh/java/org/apache/aurora/benchmark/ThriftApiBenchmarks.java b/src/jmh/java/org/apache/aurora/benchmark/ThriftApiBenchmarks.java
index 440c4fc..7b40506 100644
--- a/src/jmh/java/org/apache/aurora/benchmark/ThriftApiBenchmarks.java
+++ b/src/jmh/java/org/apache/aurora/benchmark/ThriftApiBenchmarks.java
@@ -154,8 +154,8 @@ public class ThriftApiBenchmarks {
             bind(ConfigurationManager.class).toInstance(TaskTestUtil.CONFIGURATION_MANAGER);
           }
         },
-        new AsyncModule(),
-        DbModule.productionModule(Bindings.KeyFactory.PLAIN),
+        new AsyncModule(new AsyncModule.Options()),
+        DbModule.productionModule(Bindings.KeyFactory.PLAIN, new DbModule.Options()),
         new ThriftModule.ReadOnly());
   }
 

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/SchedulerModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/SchedulerModule.java b/src/main/java/org/apache/aurora/scheduler/SchedulerModule.java
index a62bb06..3821819 100644
--- a/src/main/java/org/apache/aurora/scheduler/SchedulerModule.java
+++ b/src/main/java/org/apache/aurora/scheduler/SchedulerModule.java
@@ -20,20 +20,21 @@ import java.util.concurrent.ScheduledExecutorService;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
 import com.google.inject.AbstractModule;
 import com.google.inject.PrivateModule;
 import com.google.inject.TypeLiteral;
 
-import org.apache.aurora.common.args.Arg;
-import org.apache.aurora.common.args.CmdLine;
-import org.apache.aurora.common.args.constraints.Positive;
-import org.apache.aurora.common.quantity.Amount;
 import org.apache.aurora.common.quantity.Time;
 import org.apache.aurora.common.stats.StatsProvider;
 import org.apache.aurora.scheduler.BatchWorker.NoResult;
 import org.apache.aurora.scheduler.SchedulerLifecycle.LeadingOptions;
 import org.apache.aurora.scheduler.TaskIdGenerator.TaskIdGeneratorImpl;
 import org.apache.aurora.scheduler.base.AsyncUtil;
+import org.apache.aurora.scheduler.config.CliOptions;
+import org.apache.aurora.scheduler.config.types.TimeAmount;
+import org.apache.aurora.scheduler.config.validators.PositiveNumber;
 import org.apache.aurora.scheduler.events.PubsubEventModule;
 import org.apache.aurora.scheduler.storage.Storage;
 import org.apache.mesos.v1.Protos;
@@ -49,25 +50,33 @@ public class SchedulerModule extends AbstractModule {
 
   private static final Logger LOG = LoggerFactory.getLogger(SchedulerModule.class);
 
-  @CmdLine(name = "max_registration_delay",
-      help = "Max allowable delay to allow the driver to register before aborting")
-  private static final Arg<Amount<Long, Time>> MAX_REGISTRATION_DELAY =
-      Arg.create(Amount.of(1L, Time.MINUTES));
-
-  @CmdLine(name = "max_leading_duration",
-      help = "After leading for this duration, the scheduler should commit suicide.")
-  private static final Arg<Amount<Long, Time>> MAX_LEADING_DURATION =
-      Arg.create(Amount.of(1L, Time.DAYS));
+  @Parameters(separators = "=")
+  public static class Options {
+    @Parameter(names = "-max_registration_delay",
+        description = "Max allowable delay to allow the driver to register before aborting")
+    public TimeAmount maxRegistrationDelay = new TimeAmount(1, Time.MINUTES);
+
+    @Parameter(names = "-max_leading_duration",
+        description = "After leading for this duration, the scheduler should commit suicide.")
+    public TimeAmount maxLeadingDuration = new TimeAmount(1, Time.DAYS);
+
+    @Parameter(names = "-max_status_update_batch_size",
+        validateValueWith = PositiveNumber.class,
+        description = "The maximum number of status updates that can be processed in a batch.")
+    public int maxStatusUpdateBatchSize = 1000;
+
+    @Parameter(names = "-max_task_event_batch_size",
+        validateValueWith = PositiveNumber.class,
+        description =
+            "The maximum number of task state change events that can be processed in a batch.")
+    public int maxTaskEventBatchSize = 300;
+  }
 
-  @Positive
-  @CmdLine(name = "max_status_update_batch_size",
-      help = "The maximum number of status updates that can be processed in a batch.")
-  private static final Arg<Integer> MAX_STATUS_UPDATE_BATCH_SIZE = Arg.create(1000);
+  private final Options options;
 
-  @Positive
-  @CmdLine(name = "max_task_event_batch_size",
-      help = "The maximum number of task state change events that can be processed in a batch.")
-  private static final Arg<Integer> MAX_TASK_EVENT_BATCH_SIZE = Arg.create(300);
+  public SchedulerModule(Options options) {
+    this.options = options;
+  }
 
   @Override
   protected void configure() {
@@ -77,7 +86,7 @@ public class SchedulerModule extends AbstractModule {
       @Override
       protected void configure() {
         bind(LeadingOptions.class).toInstance(
-            new LeadingOptions(MAX_REGISTRATION_DELAY.get(), MAX_LEADING_DURATION.get()));
+            new LeadingOptions(options.maxRegistrationDelay, options.maxLeadingDuration));
 
         final ScheduledExecutorService executor =
             AsyncUtil.singleThreadLoggingScheduledExecutor("Lifecycle-%d", LOG);
@@ -98,7 +107,7 @@ public class SchedulerModule extends AbstractModule {
         .toInstance(new LinkedBlockingQueue<>());
     bind(new TypeLiteral<Integer>() { })
         .annotatedWith(TaskStatusHandlerImpl.MaxBatchSize.class)
-        .toInstance(MAX_STATUS_UPDATE_BATCH_SIZE.get());
+        .toInstance(options.maxStatusUpdateBatchSize);
 
     bind(TaskStatusHandler.class).to(TaskStatusHandlerImpl.class);
     bind(TaskStatusHandlerImpl.class).in(Singleton.class);
@@ -110,9 +119,8 @@ public class SchedulerModule extends AbstractModule {
 
   public static class TaskEventBatchWorker extends BatchWorker<NoResult> {
     @Inject
-    TaskEventBatchWorker(Storage storage, StatsProvider statsProvider) {
-
-      super(storage, statsProvider, MAX_TASK_EVENT_BATCH_SIZE.get());
+    TaskEventBatchWorker(CliOptions options, Storage storage, StatsProvider statsProvider) {
+      super(storage, statsProvider, options.scheduler.maxTaskEventBatchSize);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/TierModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/TierModule.java b/src/main/java/org/apache/aurora/scheduler/TierModule.java
index 61afa31..4244103 100644
--- a/src/main/java/org/apache/aurora/scheduler/TierModule.java
+++ b/src/main/java/org/apache/aurora/scheduler/TierModule.java
@@ -17,17 +17,17 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
 import com.google.common.io.Files;
 import com.google.common.io.Resources;
 import com.google.inject.AbstractModule;
 
-import org.apache.aurora.common.args.Arg;
-import org.apache.aurora.common.args.CmdLine;
-import org.apache.aurora.common.args.constraints.CanRead;
 import org.apache.aurora.scheduler.TierManager.TierManagerImpl;
 import org.apache.aurora.scheduler.TierManager.TierManagerImpl.TierConfig;
+import org.apache.aurora.scheduler.config.validators.ReadableFile;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -45,15 +45,19 @@ public class TierModule extends AbstractModule {
   @VisibleForTesting
   static final String TIER_CONFIG_PATH = "org/apache/aurora/scheduler/tiers.json";
 
-  @CanRead
-  @CmdLine(name = "tier_config",
-      help = "Configuration file defining supported task tiers, task traits and behaviors.")
-  private static final Arg<File> TIER_CONFIG_FILE = Arg.create();
+  @Parameters(separators = "=")
+  public static class Options {
+    @Parameter(names = "-tier_config",
+        validateValueWith = ReadableFile.class,
+        description =
+            "Configuration file defining supported task tiers, task traits and behaviors.")
+    public File tierConfigFile;
+  }
 
   private final TierConfig tierConfig;
 
-  public TierModule() {
-    this(parseTierConfig(readTierFile()));
+  public TierModule(Options options) {
+    this(parseTierConfig(readTierFile(options)));
   }
 
   @VisibleForTesting
@@ -66,13 +70,14 @@ public class TierModule extends AbstractModule {
     bind(TierManager.class).toInstance(new TierManagerImpl(tierConfig));
   }
 
-  static String readTierFile() {
+  static String readTierFile(Options options) {
     try {
-      return TIER_CONFIG_FILE.hasAppliedValue()
-          ? Files.toString(TIER_CONFIG_FILE.get(), StandardCharsets.UTF_8)
-          : Resources.toString(
+      File tierConfig = options.tierConfigFile;
+      return tierConfig == null
+          ? Resources.toString(
               TierModule.class.getClassLoader().getResource(TIER_CONFIG_PATH),
-              StandardCharsets.UTF_8);
+              StandardCharsets.UTF_8)
+          : Files.toString(tierConfig, StandardCharsets.UTF_8);
     } catch (IOException e) {
       LOG.error("Error loading tier configuration file.");
       throw new RuntimeException(e);

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/app/AppModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/app/AppModule.java b/src/main/java/org/apache/aurora/scheduler/app/AppModule.java
index 081dff2..54d7d4c 100644
--- a/src/main/java/org/apache/aurora/scheduler/app/AppModule.java
+++ b/src/main/java/org/apache/aurora/scheduler/app/AppModule.java
@@ -13,30 +13,31 @@
  */
 package org.apache.aurora.scheduler.app;
 
-import java.util.Set;
+import java.util.List;
 
 import javax.inject.Singleton;
 
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
 import com.google.inject.AbstractModule;
 
 import org.apache.aurora.GuiceUtils;
-import org.apache.aurora.common.args.Arg;
-import org.apache.aurora.common.args.CmdLine;
-import org.apache.aurora.common.args.constraints.Positive;
 import org.apache.aurora.common.inject.TimedInterceptor;
 import org.apache.aurora.common.stats.Stats;
 import org.apache.aurora.common.stats.StatsProvider;
 import org.apache.aurora.common.util.Clock;
 import org.apache.aurora.gen.Container;
 import org.apache.aurora.gen.Container._Fields;
+import org.apache.aurora.gen.DockerParameter;
 import org.apache.aurora.scheduler.SchedulerModule;
 import org.apache.aurora.scheduler.SchedulerServicesModule;
-import org.apache.aurora.scheduler.app.SchedulerMain.DriverKind;
+import org.apache.aurora.scheduler.app.SchedulerMain.Options.DriverKind;
 import org.apache.aurora.scheduler.async.AsyncModule;
+import org.apache.aurora.scheduler.config.CliOptions;
+import org.apache.aurora.scheduler.config.validators.PositiveNumber;
 import org.apache.aurora.scheduler.configuration.ConfigurationManager.ConfigurationManagerSettings;
 import org.apache.aurora.scheduler.events.PubsubEventModule;
 import org.apache.aurora.scheduler.filter.SchedulingFilterImpl;
@@ -62,75 +63,90 @@ import static java.util.Objects.requireNonNull;
  * Binding module for the aurora scheduler application.
  */
 public class AppModule extends AbstractModule {
-  private static final int DEFAULT_MAX_TASKS_PER_JOB = 4000;
 
-  @Positive
-  @CmdLine(name = "max_tasks_per_job", help = "Maximum number of allowed tasks in a single job.")
-  public static final Arg<Integer> MAX_TASKS_PER_JOB = Arg.create(DEFAULT_MAX_TASKS_PER_JOB);
-
-  private static final int DEFAULT_MAX_UPDATE_INSTANCE_FAILURES = DEFAULT_MAX_TASKS_PER_JOB * 5;
-
-  @Positive
-  @CmdLine(name = "max_update_instance_failures", help = "Upper limit on the number of "
-      + "failures allowed during a job update. This helps cap potentially unbounded entries into "
-      + "storage.")
-  public static final Arg<Integer> MAX_UPDATE_INSTANCE_FAILURES = Arg.create(
-      DEFAULT_MAX_UPDATE_INSTANCE_FAILURES);
-
-  @CmdLine(name = "allowed_container_types",
-      help = "Container types that are allowed to be used by jobs.")
-  private static final Arg<Set<_Fields>> ALLOWED_CONTAINER_TYPES =
-      Arg.create(ImmutableSet.of(Container._Fields.MESOS));
-
-  @CmdLine(name = "allow_docker_parameters",
-      help = "Allow to pass docker container parameters in the job.")
-  private static final Arg<Boolean> ENABLE_DOCKER_PARAMETERS = Arg.create(false);
-
-  @CmdLine(name = "default_docker_parameters",
-      help = "Default docker parameters for any job that does not explicitly declare parameters.")
-  private static final Arg<Multimap<String, String>> DEFAULT_DOCKER_PARAMETERS =
-      Arg.create(ImmutableMultimap.of());
-
-  @CmdLine(name = "require_docker_use_executor",
-      help = "If false, Docker tasks may run without an executor (EXPERIMENTAL)")
-  private static final Arg<Boolean> REQUIRE_DOCKER_USE_EXECUTOR = Arg.create(true);
-
-  @CmdLine(name = "enable_mesos_fetcher", help = "Allow jobs to pass URIs "
-      + "to the Mesos Fetcher. Note that enabling this feature could pose "
-      + "a privilege escalation threat.")
-  private static final Arg<Boolean> ENABLE_MESOS_FETCHER = Arg.create(false);
-
-  @CmdLine(name = "allow_container_volumes",
-      help = "Allow passing in volumes in the job. Enabling this could pose a privilege "
-          + "escalation threat.")
-  private static final Arg<Boolean> ALLOW_CONTAINER_VOLUMES = Arg.create(false);
+  @Parameters(separators = "=")
+  public static class Options {
+    @Parameter(names = "-max_tasks_per_job",
+        validateValueWith = PositiveNumber.class,
+        description = "Maximum number of allowed tasks in a single job.")
+    public int maxTasksPerJob = 4000;
+
+    @Parameter(names = "-max_update_instance_failures",
+        validateValueWith = PositiveNumber.class,
+        description = "Upper limit on the number of "
+            + "failures allowed during a job update. This helps cap potentially unbounded entries"
+            + " into storage.")
+    public int maxUpdateInstanceFailures = maxTasksPerJob * 5;
+
+    // TODO(wfarner): From jcommander docs - "Also, note that only List<String> is allowed for
+    // parameters that define an arity. You will have to convert these values yourself..."
+
+    @Parameter(names = "-allowed_container_types",
+        description = "Container types that are allowed to be used by jobs.")
+    public List<_Fields> allowedContainerTypes = ImmutableList.of(Container._Fields.MESOS);
+
+    @Parameter(names = "-allow_docker_parameters",
+        description = "Allow to pass docker container parameters in the job.",
+        arity = 1)
+    public boolean enableDockerParameters = false;
+
+    @Parameter(names = "-default_docker_parameters",
+        description =
+            "Default docker parameters for any job that does not explicitly declare parameters.")
+    public List<DockerParameter> defaultDockerParameters = ImmutableList.of();
+
+    @Parameter(names = "-require_docker_use_executor",
+        description = "If false, Docker tasks may run without an executor (EXPERIMENTAL)",
+        arity = 1)
+    public boolean requireDockerUseExecutor = true;
+
+    @Parameter(names = "-enable_mesos_fetcher", description = "Allow jobs to pass URIs "
+        + "to the Mesos Fetcher. Note that enabling this feature could pose "
+        + "a privilege escalation threat.",
+        arity = 1)
+    public boolean enableMesosFetcher = false;
+
+    @Parameter(names = "-allow_container_volumes",
+        description = "Allow passing in volumes in the job. Enabling this could pose a privilege "
+            + "escalation threat.",
+        arity = 1)
+    public boolean allowContainerVolumes = false;
+  }
 
   private final ConfigurationManagerSettings configurationManagerSettings;
   private final DriverKind kind;
+  private final CliOptions options;
 
   @VisibleForTesting
-  public AppModule(ConfigurationManagerSettings configurationManagerSettings, DriverKind kind) {
+  public AppModule(
+      ConfigurationManagerSettings configurationManagerSettings,
+      DriverKind kind,
+      CliOptions options) {
     this.configurationManagerSettings = requireNonNull(configurationManagerSettings);
     this.kind = kind;
+    this.options = options;
   }
 
-  public AppModule(boolean allowGpuResource, DriverKind kind) {
+  public AppModule(CliOptions opts) {
     this(new ConfigurationManagerSettings(
-        ImmutableSet.copyOf(ALLOWED_CONTAINER_TYPES.get()),
-        ENABLE_DOCKER_PARAMETERS.get(),
-        DEFAULT_DOCKER_PARAMETERS.get(),
-        REQUIRE_DOCKER_USE_EXECUTOR.get(),
-        allowGpuResource,
-        ENABLE_MESOS_FETCHER.get(),
-        ALLOW_CONTAINER_VOLUMES.get()),
-        kind);
+        ImmutableSet.copyOf(opts.app.allowedContainerTypes),
+            opts.app.enableDockerParameters,
+            opts.app.defaultDockerParameters,
+            opts.app.requireDockerUseExecutor,
+            opts.main.allowGpuResource,
+            opts.app.enableMesosFetcher,
+            opts.app.allowContainerVolumes),
+        opts.main.driverImpl,
+        opts);
   }
 
   @Override
   protected void configure() {
     bind(ConfigurationManagerSettings.class).toInstance(configurationManagerSettings);
     bind(Thresholds.class)
-        .toInstance(new Thresholds(MAX_TASKS_PER_JOB.get(), MAX_UPDATE_INSTANCE_FAILURES.get()));
+        .toInstance(
+            new Thresholds(options.app.maxTasksPerJob,
+            options.app.maxUpdateInstanceFailures));
 
     // Enable intercepted method timings and context classloader repair.
     TimedInterceptor.bind(binder());
@@ -143,22 +159,22 @@ public class AppModule extends AbstractModule {
     PubsubEventModule.bindSchedulingFilterDelegate(binder()).to(SchedulingFilterImpl.class);
     bind(SchedulingFilterImpl.class).in(Singleton.class);
 
-    install(new AsyncModule());
-    install(new OffersModule());
-    install(new PruningModule());
-    install(new ReconciliationModule());
-    install(new SchedulingModule());
-    install(new AsyncStatsModule());
+    install(new AsyncModule(options.async));
+    install(new OffersModule(options));
+    install(new PruningModule(options.pruning));
+    install(new ReconciliationModule(options.reconciliation));
+    install(new SchedulingModule(options.scheduling));
+    install(new AsyncStatsModule(options.asyncStats));
     install(new MetadataModule());
     install(new QuotaModule());
-    install(new JettyServerModule());
-    install(new PreemptorModule());
+    install(new JettyServerModule(options));
+    install(new PreemptorModule(options));
     install(new SchedulerDriverModule(kind));
     install(new SchedulerServicesModule());
-    install(new SchedulerModule());
-    install(new StateModule());
-    install(new SlaModule());
-    install(new UpdaterModule());
+    install(new SchedulerModule(options.scheduler));
+    install(new StateModule(options));
+    install(new SlaModule(options.sla));
+    install(new UpdaterModule(options.updater));
     bind(StatsProvider.class).toInstance(Stats.STATS_PROVIDER);
   }
 }

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/app/MoreModules.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/app/MoreModules.java b/src/main/java/org/apache/aurora/scheduler/app/MoreModules.java
index 074f220..bf0ed92 100644
--- a/src/main/java/org/apache/aurora/scheduler/app/MoreModules.java
+++ b/src/main/java/org/apache/aurora/scheduler/app/MoreModules.java
@@ -13,9 +13,16 @@
  */
 package org.apache.aurora.scheduler.app;
 
-import com.google.inject.AbstractModule;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.FluentIterable;
 import com.google.inject.Module;
 
+import org.apache.aurora.scheduler.config.CliOptions;
+
 /**
  * A utility class for managing guice modules.
  */
@@ -24,13 +31,36 @@ public final class MoreModules {
     // Utility class
   }
 
-  private static Module instantiateModule(final Class<? extends Module> moduleClass) {
+  /**
+   * Instantiate a module, supplying command line options if accepted by the module class.
+   * <p>
+   * Reflectively instantiates a module, first invoking a constructor that accepts
+   * {@link CliOptions} as the only parameter, falling back to a default constructor if options are
+   * not accepted by the class.
+   *
+   * @param moduleClass Module to instantiate
+   * @param options Options to provide the module.
+   * @return An instance of the module class.
+   */
+  public static Module instantiate(Class<?> moduleClass, CliOptions options) {
     try {
-      return moduleClass.newInstance();
+      // If it exists, use the constructor accepting CliOptions.
+      try {
+        Constructor<?> constructor = moduleClass.getConstructor(CliOptions.class);
+        return (Module) constructor.newInstance(options);
+      } catch (NoSuchMethodException e) {
+        // Fall back to default constructor.
+        return (Module) moduleClass.newInstance();
+      } catch (InvocationTargetException e) {
+        throw new IllegalArgumentException(
+            String.format("Failed to invoke %s(CliOption)", moduleClass.getName()),
+            e);
+      }
     } catch (InstantiationException e) {
       throw new IllegalArgumentException(
           String.format(
-              "Failed to instantiate module %s. Are you sure it has a no-arg constructor?",
+              "Failed to instantiate module %s."
+                  + "Dynamic modules must have a default constructor or accept CliOptions",
               moduleClass.getName()),
           e);
     } catch (IllegalAccessException e) {
@@ -42,32 +72,11 @@ public final class MoreModules {
     }
   }
 
-  static Module getModule(Class<? extends Module> moduleClass) {
-    return instantiateModule(moduleClass);
-  }
-
   /**
-   * Creates a module that will lazily instantiate and install another module.
-   * <p/>
-   * This serves as an indirection between module procurement and installation, which is necessary
-   * in cases where a module is referenced within a static initializer.  In this scenario, a module
-   * must not be instantiated if it reads command line arguments, as the args system has not yet
-   * had a chance to populate them.
-   *
-   * @param moduleClass Module to install.
-   * @return An installer that will install {@code moduleClass}.
+   * Identical to {@link #instantiate(Class, CliOptions)} for multiple module classes.
    */
-  public static Module lazilyInstantiated(final Class<? extends Module> moduleClass) {
-    return new AbstractModule() {
-      @Override
-      protected void configure() {
-        install(getModule(moduleClass));
-      }
-
-      @Override
-      public String toString() {
-        return moduleClass.toString();
-      }
-    };
+  @SuppressWarnings("rawtypes")
+  public static Set<Module> instantiateAll(List<Class> moduleClasses, CliOptions options) {
+    return FluentIterable.from(moduleClasses).transform(c -> instantiate(c, options)).toSet();
   }
 }

http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/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 bb7055e..dd0e480 100644
--- a/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
+++ b/src/main/java/org/apache/aurora/scheduler/app/SchedulerMain.java
@@ -14,7 +14,6 @@
 package org.apache.aurora.scheduler.app;
 
 import java.net.InetSocketAddress;
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -22,6 +21,8 @@ import java.util.concurrent.atomic.AtomicLong;
 
 import javax.inject.Inject;
 
+import com.beust.jcommander.Parameter;
+import com.beust.jcommander.Parameters;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -31,17 +32,12 @@ import com.google.inject.CreationException;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Module;
+import com.google.inject.ProvisionException;
 import com.google.inject.spi.Message;
 import com.google.inject.util.Modules;
 
 import org.apache.aurora.GuavaUtils.ServiceManagerIface;
 import org.apache.aurora.common.application.Lifecycle;
-import org.apache.aurora.common.args.Arg;
-import org.apache.aurora.common.args.ArgScanner;
-import org.apache.aurora.common.args.ArgScanner.ArgScanException;
-import org.apache.aurora.common.args.CmdLine;
-import org.apache.aurora.common.args.constraints.NotEmpty;
-import org.apache.aurora.common.args.constraints.NotNull;
 import org.apache.aurora.common.inject.Bindings;
 import org.apache.aurora.common.stats.Stats;
 import org.apache.aurora.common.zookeeper.SingletonService;
@@ -50,6 +46,9 @@ import org.apache.aurora.gen.ServerInfo;
 import org.apache.aurora.scheduler.AppStartup;
 import org.apache.aurora.scheduler.SchedulerLifecycle;
 import org.apache.aurora.scheduler.TierModule;
+import org.apache.aurora.scheduler.config.CliOptions;
+import org.apache.aurora.scheduler.config.CommandLine;
+import org.apache.aurora.scheduler.config.validators.NotEmptyString;
 import org.apache.aurora.scheduler.configuration.executor.ExecutorModule;
 import org.apache.aurora.scheduler.cron.quartz.CronModule;
 import org.apache.aurora.scheduler.discovery.FlaggedZooKeeperConfig;
@@ -76,51 +75,65 @@ import org.slf4j.LoggerFactory;
 public class SchedulerMain {
   private static final Logger LOG = LoggerFactory.getLogger(SchedulerMain.class);
 
-  @NotNull
-  @CmdLine(name = "cluster_name", help = "Name to identify the cluster being served.")
-  private static final Arg<String> CLUSTER_NAME = Arg.create();
+  @Parameters(separators = "=")
+  public static class Options {
+    @Parameter(names = "-cluster_name",
+        required = true,
+        description = "Name to identify the cluster being served.")
+    public String clusterName;
 
-  @NotNull
-  @NotEmpty
-  @CmdLine(name = "serverset_path", help = "ZooKeeper ServerSet path to register at.")
-  private static final Arg<String> SERVERSET_PATH = Arg.create();
+    @Parameter(
+        names = "-serverset_path",
+        required = true,
+        validateValueWith = NotEmptyString.class,
+        description = "ZooKeeper ServerSet path to register at.")
+    public String serversetPath;
 
-  // TODO(zmanji): Consider making this an enum of HTTP or HTTPS.
-  @CmdLine(name = "serverset_endpoint_name",
-      help = "Name of the scheduler endpoint published in ZooKeeper.")
-  private static final Arg<String> SERVERSET_ENDPOINT_NAME = Arg.create("http");
+    // TODO(zmanji): Consider making this an enum of HTTP or HTTPS.
+    @Parameter(names = "-serverset_endpoint_name",
+        description = "Name of the scheduler endpoint published in ZooKeeper.")
+    public String serversetEndpointName = "http";
 
-  // TODO(Suman Karumuri): Rename viz_job_url_prefix to stats_job_url_prefix for consistency.
-  @CmdLine(name = "viz_job_url_prefix", help = "URL prefix for job container stats.")
-  private static final Arg<String> STATS_URL_PREFIX = Arg.create("");
+    // TODO(Suman Karumuri): Rename viz_job_url_prefix to stats_job_url_prefix for consistency.
+    @Parameter(names = "-viz_job_url_prefix", description = "URL prefix for job container stats.")
+    public String statsUrlPrefix = "";
 
-  @CmdLine(name = "allow_gpu_resource", help = "Allow jobs to request Mesos GPU resource.")
-  private static final Arg<Boolean> ALLOW_GPU_RESOURCE = Arg.create(false);
+    @Parameter(names = "-allow_gpu_resource",
+        description = "Allow jobs to request Mesos GPU resource.",
+        arity = 1)
+    public boolean allowGpuResource = false;
 
-  public enum DriverKind {
-    // TODO(zmanji): Remove this option once V0_DRIVER has been proven out in production.
-    // This is the original driver that libmesos shipped with. Uses unversioned protobufs, and has
-    // minimal backwards compatability guarantees.
-    SCHEDULER_DRIVER,
-    // These are the new drivers that libmesos ships with. They use versioned (V1) protobufs for
-    // the Java API.
-    // V0 Driver offers the V1 API over the old Scheduler Driver. It does not fully support
-    // the V1 API (ie mesos maintenance).
-    V0_DRIVER,
-    // V1 Driver offers the V1 API over a full HTTP API implementation. It allows for maintenance
-    // primatives and other new features.
-    V1_DRIVER,
-  }
+    public enum DriverKind {
+      // TODO(zmanji): Remove this option once V0_DRIVER has been proven out in production.
+      // This is the original driver that libmesos shipped with. Uses unversioned protobufs, and has
+      // minimal backwards compatability guarantees.
+      SCHEDULER_DRIVER,
+      // These are the new drivers that libmesos ships with. They use versioned (V1) protobufs for
+      // the Java API.
+      // V0 Driver offers the V1 API over the old Scheduler Driver. It does not fully support
+      // the V1 API (ie mesos maintenance).
+      V0_DRIVER,
+      // V1 Driver offers the V1 API over a full HTTP API implementation. It allows for maintenance
+      // primatives and other new features.
+      V1_DRIVER,
+    }
 
-  @CmdLine(name = "mesos_driver", help = "Which Mesos Driver to use")
-  private static final Arg<DriverKind> DRIVER_IMPL = Arg.create(DriverKind.SCHEDULER_DRIVER);
+    @Parameter(names = "-mesos_driver", description = "Which Mesos Driver to use")
+    public DriverKind driverImpl = DriverKind.SCHEDULER_DRIVER;
+  }
 
   public static class ProtocolModule extends AbstractModule {
+    private final Options options;
+
+    public ProtocolModule(Options options) {
+      this.options = options;
+    }
+
     @Override
     protected void configure() {
       bind(String.class)
           .annotatedWith(SchedulerProtocol.class)
-          .toInstance(SERVERSET_ENDPOINT_NAME.get());
+          .toInstance(options.serversetEndpointName);
     }
   }
 
@@ -142,7 +155,7 @@ public class SchedulerMain {
     appLifecycle.shutdown();
   }
 
-  void run() {
+  void run(Options options) {
     try {
       startupServices.startAsync();
       Runtime.getRuntime().addShutdownHook(new Thread(SchedulerMain.this::stop, "ShutdownHook"));
@@ -156,7 +169,7 @@ public class SchedulerMain {
 
       schedulerService.lead(
           httpSocketAddress,
-          ImmutableMap.of(SERVERSET_ENDPOINT_NAME.get(), httpSocketAddress),
+          ImmutableMap.of(options.serversetEndpointName, httpSocketAddress),
           leaderListener);
     } catch (SingletonService.LeadException e) {
       throw new IllegalStateException("Failed to lead service.", e);
@@ -169,16 +182,16 @@ public class SchedulerMain {
   }
 
   @VisibleForTesting
-  static Module getUniversalModule() {
+  static Module getUniversalModule(CliOptions options) {
     return Modules.combine(
-        new ProtocolModule(),
+        new ProtocolModule(options.main),
         new LifecycleModule(),
-        new StatsModule(),
-        new AppModule(ALLOW_GPU_RESOURCE.get(), DRIVER_IMPL.get()),
-        new CronModule(),
+        new StatsModule(options.stats),
+        new AppModule(options),
+        new CronModule(options.cron),
         new DbModule.MigrationManagerModule(),
-        DbModule.productionModule(Bindings.annotatedKeyFactory(Storage.Volatile.class)),
-        new DbModule.GarbageCollectorModule());
+        DbModule.productionModule(Bindings.annotatedKeyFactory(Storage.Volatile.class), options.db),
+        new DbModule.GarbageCollectorModule(options.db));
   }
 
   /**
@@ -188,7 +201,7 @@ public class SchedulerMain {
    * @param appEnvironmentModule Additional modules based on the execution environment.
    */
   @VisibleForTesting
-  public static void flagConfiguredMain(Module appEnvironmentModule) {
+  public static void flagConfiguredMain(CliOptions options, Module appEnvironmentModule) {
     AtomicLong uncaughtExceptions = Stats.exportLong("uncaught_exceptions");
     Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
       uncaughtExceptions.incrementAndGet();
@@ -207,6 +220,12 @@ public class SchedulerMain {
             LOG.error("  source: " + m.getSource());
           }
         }
+      } else if (e instanceof ProvisionException) {
+        // More special handling for guice 3 + java 8.  Remove this once using guice >=4.0.
+        ProvisionException pe = (ProvisionException) e;
+        for (Message message : pe.getErrorMessages()) {
+          LOG.error(message.getMessage());
+        }
       } else {
         LOG.error("Uncaught exception from " + t + ":" + e, e);
       }
@@ -214,18 +233,21 @@ public class SchedulerMain {
 
     Module module = Modules.combine(
         appEnvironmentModule,
-        getUniversalModule(),
-        new ServiceDiscoveryModule(FlaggedZooKeeperConfig.create(), SERVERSET_PATH.get()),
-        new BackupModule(SnapshotStoreImpl.class),
-        new ExecutorModule(),
+        getUniversalModule(options),
+        new ServiceDiscoveryModule(
+            FlaggedZooKeeperConfig.create(options.zk),
+            options.main.serversetPath),
+        new BackupModule(options.backup, SnapshotStoreImpl.class),
+        new ExecutorModule(options.executor),
         new AbstractModule() {
           @Override
           protected void configure() {
+            bind(CliOptions.class).toInstance(options);
             bind(IServerInfo.class).toInstance(
                 IServerInfo.build(
                     new ServerInfo()
-                        .setClusterName(CLUSTER_NAME.get())
-                        .setStatsUrlPrefix(STATS_URL_PREFIX.get())));
+                        .setClusterName(options.main.clusterName)
+                        .setStatsUrlPrefix(options.main.statsUrlPrefix)));
           }
         });
 
@@ -236,7 +258,7 @@ public class SchedulerMain {
       SchedulerMain scheduler = new SchedulerMain();
       injector.injectMembers(scheduler);
       try {
-        scheduler.run();
+        scheduler.run(options.main);
       } finally {
         LOG.info("Application run() exited.");
       }
@@ -248,42 +270,18 @@ public class SchedulerMain {
   }
 
   public static void main(String... args) {
-    applyStaticArgumentValues(args);
+    CliOptions options = CommandLine.parseOptions(args);
 
     List<Module> modules = ImmutableList.<Module>builder()
         .add(
-            new CommandLineDriverSettingsModule(ALLOW_GPU_RESOURCE.get()),
-            new LibMesosLoadingModule(DRIVER_IMPL.get()),
-            new MesosLogStreamModule(FlaggedZooKeeperConfig.create()),
-            new LogStorageModule(),
-            new TierModule(),
-            new WebhookModule()
+            new CommandLineDriverSettingsModule(options.driver, options.main.allowGpuResource),
+            new LibMesosLoadingModule(options.main.driverImpl),
+            new MesosLogStreamModule(options.mesosLog, FlaggedZooKeeperConfig.create(options.zk)),
+            new LogStorageModule(options.logStorage, options.db.useDbTaskStore),
+            new TierModule(options.tiers),
+            new WebhookModule(options.webhook)
         )
         .build();
-    flagConfiguredMain(Modules.combine(modules));
-  }
-
-  private static void exit(String message, Exception error) {
-    LOG.error(message + "\n" + error, error);
-    System.exit(1);
-  }
-
-  /**
-   * Applies {@link CmdLine} arg values throughout the classpath.  This must be invoked before
-   * attempting to read any argument values in the system.
-   *
-   * @param args Command line arguments.
-   */
-  @VisibleForTesting
-  public static void applyStaticArgumentValues(String... args) {
-    try {
-      if (!new ArgScanner().parse(Arrays.asList(args))) {
-        System.exit(0);
-      }
-    } catch (ArgScanException e) {
-      exit("Failed to scan arguments", e);
-    } catch (IllegalArgumentException e) {
-      exit("Failed to apply arguments", e);
-    }
+    flagConfiguredMain(options, Modules.combine(modules));
   }
 }


Mime
View raw message