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)); } }
