http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/app/VolumeConverter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/app/VolumeConverter.java b/src/main/java/org/apache/aurora/scheduler/app/VolumeConverter.java new file mode 100644 index 0000000..d8af1f9 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/app/VolumeConverter.java @@ -0,0 +1,54 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.aurora.scheduler.app; + +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.converters.BaseConverter; +import com.google.common.base.Joiner; + +import org.apache.aurora.gen.Mode; +import org.apache.aurora.gen.Volume; + +/** + * Converter to transform a string in host:container:mode form to a VolumeConfig object. + */ +public class VolumeConverter extends BaseConverter<Volume> { + + public VolumeConverter() { + super(""); + } + + public VolumeConverter(String optionName) { + super(optionName); + } + + @Override + public Volume convert(String raw) { + String[] split = raw.split(":"); + if (split.length != 3) { + throw new ParameterException( + getErrorString(raw, "must be in the format of 'host:container:mode'")); + } + + Mode mode; + try { + mode = Mode.valueOf(split[2].toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ParameterException( + getErrorString(raw, "Read/Write spec must be in " + Joiner.on(", ").join(Mode.values())), + e); + } + return new Volume(split[1], split[0], mode); + } +}
http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/app/VolumeParser.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/app/VolumeParser.java b/src/main/java/org/apache/aurora/scheduler/app/VolumeParser.java deleted file mode 100644 index c1e99ce..0000000 --- a/src/main/java/org/apache/aurora/scheduler/app/VolumeParser.java +++ /dev/null @@ -1,46 +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.scheduler.app; - -import com.google.common.base.Joiner; - -import org.apache.aurora.common.args.ArgParser; -import org.apache.aurora.common.args.parsers.NonParameterizedTypeParser; -import org.apache.aurora.gen.Mode; -import org.apache.aurora.gen.Volume; - -/** - * Parser to transform a string in host:container:mode form to a VolumeConfig - * object. - */ -@ArgParser -public class VolumeParser extends NonParameterizedTypeParser<Volume> { - @Override - public Volume doParse(String raw) throws IllegalArgumentException { - String[] split = raw.split(":"); - if (split.length != 3) { - throw new IllegalArgumentException("Illegal mount string " + raw + ". " - + "Mounts must be in the format of 'host:container:mode'"); - } - - Mode mode; - try { - mode = Mode.valueOf(split[2].toUpperCase()); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Illegal mount string " + raw + ". " - + "Read/Write spec must be in " + Joiner.on(", ").join(Mode.values()), e); - } - return new Volume(split[1], split[0], mode); - } -} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/async/AsyncModule.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/async/AsyncModule.java b/src/main/java/org/apache/aurora/scheduler/async/AsyncModule.java index da07df6..68f7ddb 100644 --- a/src/main/java/org/apache/aurora/scheduler/async/AsyncModule.java +++ b/src/main/java/org/apache/aurora/scheduler/async/AsyncModule.java @@ -23,13 +23,13 @@ import javax.inject.Inject; import javax.inject.Qualifier; import javax.inject.Singleton; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; import com.google.common.annotations.VisibleForTesting; import com.google.common.util.concurrent.AbstractIdleService; import com.google.inject.AbstractModule; import com.google.inject.PrivateModule; -import org.apache.aurora.common.args.Arg; -import org.apache.aurora.common.args.CmdLine; import org.apache.aurora.common.stats.StatsProvider; import org.apache.aurora.scheduler.SchedulerServicesModule; import org.apache.aurora.scheduler.base.AsyncUtil; @@ -48,19 +48,26 @@ import static java.util.Objects.requireNonNull; public class AsyncModule extends AbstractModule { private static final Logger LOG = LoggerFactory.getLogger(AsyncModule.class); - @CmdLine(name = "async_worker_threads", - help = "The number of worker threads to process async task operations with.") - private static final Arg<Integer> ASYNC_WORKER_THREADS = Arg.create(8); + @Parameters(separators = "=") + public static class Options { + @Parameter(names = "-async_worker_threads", + description = "The number of worker threads to process async task operations with.") + public int asyncWorkerThreads = 8; + } + private final ScheduledThreadPoolExecutor afterTransaction; @Qualifier @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) public @interface AsyncExecutor { } - public AsyncModule() { + public AsyncModule(Options options) { // Don't worry about clean shutdown, these can be daemon and cleanup-free. // TODO(wfarner): Should we use a bounded caching thread pool executor instead? - this(AsyncUtil.loggingScheduledExecutor(ASYNC_WORKER_THREADS.get(), "AsyncProcessor-%d", LOG)); + this(AsyncUtil.loggingScheduledExecutor( + options.asyncWorkerThreads, + "AsyncProcessor-%d", + LOG)); } @VisibleForTesting http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/base/TaskTestUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/base/TaskTestUtil.java b/src/main/java/org/apache/aurora/scheduler/base/TaskTestUtil.java index 186fa1b..60bbe39 100644 --- a/src/main/java/org/apache/aurora/scheduler/base/TaskTestUtil.java +++ b/src/main/java/org/apache/aurora/scheduler/base/TaskTestUtil.java @@ -19,7 +19,6 @@ import java.util.Set; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import org.apache.aurora.gen.AssignedTask; @@ -83,7 +82,7 @@ public final class TaskTestUtil { new ConfigurationManagerSettings( ImmutableSet.of(_Fields.MESOS), false, - ImmutableMultimap.of(), + ImmutableList.of(), true, true, true, http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/CliOptions.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/CliOptions.java b/src/main/java/org/apache/aurora/scheduler/config/CliOptions.java new file mode 100644 index 0000000..64733e5 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/CliOptions.java @@ -0,0 +1,114 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config; + +import java.util.List; + +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; + +import org.apache.aurora.scheduler.SchedulerModule; +import org.apache.aurora.scheduler.TierModule; +import org.apache.aurora.scheduler.app.AppModule; +import org.apache.aurora.scheduler.app.SchedulerMain; +import org.apache.aurora.scheduler.async.AsyncModule; +import org.apache.aurora.scheduler.configuration.executor.ExecutorModule; +import org.apache.aurora.scheduler.cron.quartz.CronModule; +import org.apache.aurora.scheduler.discovery.FlaggedZooKeeperConfig; +import org.apache.aurora.scheduler.events.WebhookModule; +import org.apache.aurora.scheduler.http.H2ConsoleModule; +import org.apache.aurora.scheduler.http.JettyServerModule; +import org.apache.aurora.scheduler.http.api.ApiModule; +import org.apache.aurora.scheduler.http.api.security.HttpSecurityModule; +import org.apache.aurora.scheduler.http.api.security.IniShiroRealmModule; +import org.apache.aurora.scheduler.http.api.security.Kerberos5ShiroRealmModule; +import org.apache.aurora.scheduler.log.mesos.MesosLogStreamModule; +import org.apache.aurora.scheduler.mesos.CommandLineDriverSettingsModule; +import org.apache.aurora.scheduler.offers.OffersModule; +import org.apache.aurora.scheduler.preemptor.PreemptorModule; +import org.apache.aurora.scheduler.pruning.PruningModule; +import org.apache.aurora.scheduler.reconciliation.ReconciliationModule; +import org.apache.aurora.scheduler.resources.ResourceSettings; +import org.apache.aurora.scheduler.scheduling.SchedulingModule; +import org.apache.aurora.scheduler.sla.SlaModule; +import org.apache.aurora.scheduler.state.StateModule; +import org.apache.aurora.scheduler.stats.AsyncStatsModule; +import org.apache.aurora.scheduler.stats.StatsModule; +import org.apache.aurora.scheduler.storage.backup.BackupModule; +import org.apache.aurora.scheduler.storage.db.DbModule; +import org.apache.aurora.scheduler.storage.log.LogStorageModule; +import org.apache.aurora.scheduler.thrift.aop.AopModule; +import org.apache.aurora.scheduler.updater.UpdaterModule; + +public class CliOptions { + public final ReconciliationModule.Options reconciliation = + new ReconciliationModule.Options(); + public final OffersModule.Options offer = new OffersModule.Options(); + public final ExecutorModule.Options executor = new ExecutorModule.Options(); + public final AppModule.Options app = new AppModule.Options(); + public final SchedulerMain.Options main = new SchedulerMain.Options(); + public final SchedulingModule.Options scheduling = new SchedulingModule.Options(); + public final AsyncModule.Options async = new AsyncModule.Options(); + public final FlaggedZooKeeperConfig.Options zk = new FlaggedZooKeeperConfig.Options(); + public final UpdaterModule.Options updater = new UpdaterModule.Options(); + public final StateModule.Options state = new StateModule.Options(); + public final DbModule.Options db = new DbModule.Options(); + public final LogStorageModule.Options logStorage = new LogStorageModule.Options(); + public final BackupModule.Options backup = new BackupModule.Options(); + public final AopModule.Options aop = new AopModule.Options(); + public final PruningModule.Options pruning = new PruningModule.Options(); + public final CommandLineDriverSettingsModule.Options driver = + new CommandLineDriverSettingsModule.Options(); + public final JettyServerModule.Options jetty = new JettyServerModule.Options(); + public final HttpSecurityModule.Options httpSecurity = new HttpSecurityModule.Options(); + public final Kerberos5ShiroRealmModule.Options kerberos = new Kerberos5ShiroRealmModule.Options(); + public final IniShiroRealmModule.Options iniShiroRealm = new IniShiroRealmModule.Options(); + public final ApiModule.Options api = new ApiModule.Options(); + public final H2ConsoleModule.Options h2Console = new H2ConsoleModule.Options(); + public final PreemptorModule.Options preemptor = new PreemptorModule.Options(); + public final MesosLogStreamModule.Options mesosLog = new MesosLogStreamModule.Options(); + public final SlaModule.Options sla = new SlaModule.Options(); + public final WebhookModule.Options webhook = new WebhookModule.Options(); + public final SchedulerModule.Options scheduler = new SchedulerModule.Options(); + public final TierModule.Options tiers = new TierModule.Options(); + public final AsyncStatsModule.Options asyncStats = new AsyncStatsModule.Options(); + public final StatsModule.Options stats = new StatsModule.Options(); + public final CronModule.Options cron = new CronModule.Options(); + public final ResourceSettings resourceSettings = new ResourceSettings(); + final List<Object> custom; + + public CliOptions() { + this(ImmutableList.of()); + } + + public CliOptions(List<Object> custom) { + this.custom = custom; + } + + /** + * Gets a custom options object of a particular type. + * + * @param customOptionType Custom option type class. + * @param <T> Custom option type. + * @return The matching custom option object. + */ + @SuppressWarnings("unchecked") + public <T> T getCustom(Class<T> customOptionType) { + return (T) FluentIterable.from(custom) + .firstMatch(Predicates.instanceOf(customOptionType)) + .get(); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/CommandLine.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/CommandLine.java b/src/main/java/org/apache/aurora/scheduler/config/CommandLine.java new file mode 100644 index 0000000..2085810 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/CommandLine.java @@ -0,0 +1,198 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.Map; + +import javax.security.auth.kerberos.KerberosPrincipal; + +import com.beust.jcommander.IStringConverter; +import com.beust.jcommander.IStringConverterFactory; +import com.beust.jcommander.JCommander; +import com.beust.jcommander.ParameterException; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; + +import org.apache.aurora.gen.DockerParameter; +import org.apache.aurora.gen.Volume; +import org.apache.aurora.scheduler.app.SchedulerMain; +import org.apache.aurora.scheduler.app.VolumeConverter; +import org.apache.aurora.scheduler.config.converters.ClassConverter; +import org.apache.aurora.scheduler.config.converters.DataAmountConverter; +import org.apache.aurora.scheduler.config.converters.DockerParameterConverter; +import org.apache.aurora.scheduler.config.converters.InetSocketAddressConverter; +import org.apache.aurora.scheduler.config.converters.TimeAmountConverter; +import org.apache.aurora.scheduler.config.types.DataAmount; +import org.apache.aurora.scheduler.config.types.TimeAmount; +import org.apache.aurora.scheduler.http.api.security.KerberosPrincipalConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Parses command line options and populates {@link CliOptions}. + */ +public final class CommandLine { + + private static final Logger LOG = LoggerFactory.getLogger(CommandLine.class); + + // TODO(wfarner): This can go away if/when options are no longer accessed statically. + private static CliOptions instance = null; + + private static List<Object> customOptions = Lists.newArrayList(); + + private CommandLine() { + // Utility class. + } + + /** + * Similar to {@link #initializeForTest()}, but resets the class to an un-parsed state. + */ + @VisibleForTesting + static void clearForTest() { + instance = null; + customOptions = Lists.newArrayList(); + } + + /** + * Initializes static command line state - the static parsed instance, and custom options objects. + */ + @VisibleForTesting + public static void initializeForTest() { + instance = new CliOptions(); + customOptions = Lists.newArrayList(); + } + + private static JCommander prepareParser(CliOptions options) { + JCommander.Builder builder = JCommander.newBuilder() + .programName(SchedulerMain.class.getName()); + + builder.addConverterFactory(new IStringConverterFactory() { + private Map<Class<?>, Class<? extends IStringConverter<?>>> classConverters = + ImmutableMap.<Class<?>, Class<? extends IStringConverter<?>>>builder() + .put(Class.class, ClassConverter.class) + .put(DataAmount.class, DataAmountConverter.class) + .put(DockerParameter.class, DockerParameterConverter.class) + .put(InetSocketAddress.class, InetSocketAddressConverter.class) + .put(KerberosPrincipal.class, KerberosPrincipalConverter.class) + .put(TimeAmount.class, TimeAmountConverter.class) + .put(Volume.class, VolumeConverter.class) + .build(); + + @SuppressWarnings("unchecked") + @Override + public <T> Class<? extends IStringConverter<T>> getConverter(Class<T> forType) { + return (Class<IStringConverter<T>>) classConverters.get(forType); + } + }); + + builder.addObject(getOptionsObjects(options)); + return builder.build(); + } + + /** + * Applies arg values to the options object. + * + * @param args Command line arguments. + */ + @VisibleForTesting + public static CliOptions parseOptions(String... args) { + JCommander parser = null; + try { + parser = prepareParser(new CliOptions(ImmutableList.copyOf(customOptions))); + + // We first perform a 'dummy' parsing round. This induces classloading on any third-party + // code, where they can statically invoke registerCustomOptions(). + parser.setAcceptUnknownOptions(true); + parser.parseWithoutValidation(args); + + CliOptions options = new CliOptions(ImmutableList.copyOf(customOptions)); + parser = prepareParser(options); + parser.parse(args); + instance = options; + return options; + } catch (ParameterException e) { + if (parser != null) { + parser.usage(); + } + LOG.error(e.getMessage()); + System.exit(1); + throw new RuntimeException(e); + } catch (RuntimeException e) { + throw e; + } + } + + /** + * Gets the static and globally-accessible CLI options. This exists only to support legacy use + * cases that cannot yet support injected arguments. New callers should not be added. + * + * @return global options + */ + public static CliOptions legacyGetStaticOptions() { + if (instance == null) { + throw new IllegalStateException("Attempted to fetch command line arguments before parsing."); + } + return instance; + } + + /** + * Registers a custom options container for inclusion during command line option parsing. This + * is useful to allow third-party modules to include custom command line options. + * + * @param options Custom options object. + * See {@link com.beust.jcommander.JCommander.Builder#addObject(Object)} for + * details. + */ + public static void registerCustomOptions(Object options) { + Preconditions.checkState( + instance == null, + "Attempted to register custom options after command line parsing."); + + customOptions.add(options); + } + + @VisibleForTesting + static List<Object> getOptionsObjects(CliOptions options) { + ImmutableList.Builder<Object> objects = ImmutableList.builder(); + + // Reflect on fields defined in CliOptions to DRY and avoid mistakes of forgetting to add an + // option field here. + for (Field field : CliOptions.class.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + + try { + if (Iterable.class.isAssignableFrom(field.getType())) { + Iterable<?> iterableValue = (Iterable<?>) field.get(options); + objects.addAll(iterableValue); + } else { + objects.add(field.get(options)); + } + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + return objects.build(); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/converters/ClassConverter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/converters/ClassConverter.java b/src/main/java/org/apache/aurora/scheduler/config/converters/ClassConverter.java new file mode 100644 index 0000000..b2ecfa0 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/converters/ClassConverter.java @@ -0,0 +1,49 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.converters; + +import java.util.Map; + +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.converters.BaseConverter; +import com.google.common.collect.ImmutableMap; + +import org.apache.aurora.scheduler.http.api.security.IniShiroRealmModule; +import org.apache.aurora.scheduler.http.api.security.Kerberos5ShiroRealmModule; + +public class ClassConverter extends BaseConverter<Class<?>> { + + private static final Map<String, String> NAME_ALIASES = ImmutableMap.of( + "KERBEROS5_AUTHN", Kerberos5ShiroRealmModule.class.getCanonicalName(), + "INI_AUTHNZ", IniShiroRealmModule.class.getCanonicalName()); + + public ClassConverter(String optionName) { + super(optionName); + } + + @Override + public Class<?> convert(String value) { + if (value.isEmpty()) { + throw new ParameterException(getErrorString(value, "must not be blank")); + } + + String unaliased = NAME_ALIASES.getOrDefault(value, value); + try { + return Class.forName(unaliased); + } catch (ClassNotFoundException e) { + throw new ParameterException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/converters/DataAmountConverter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/converters/DataAmountConverter.java b/src/main/java/org/apache/aurora/scheduler/config/converters/DataAmountConverter.java new file mode 100644 index 0000000..a0d2620 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/converters/DataAmountConverter.java @@ -0,0 +1,57 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.converters; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.converters.BaseConverter; +import com.google.common.base.Functions; +import com.google.common.base.Optional; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; + +import org.apache.aurora.common.quantity.Data; +import org.apache.aurora.scheduler.config.types.DataAmount; + +public class DataAmountConverter extends BaseConverter<DataAmount> { + private static final Pattern AMOUNT_PATTERN = Pattern.compile("(\\d+)([A-Za-z]+)"); + + public DataAmountConverter(String optionName) { + super(optionName); + } + + @Override + public DataAmount convert(String raw) { + Matcher matcher = AMOUNT_PATTERN.matcher(raw); + + if (!matcher.matches()) { + throw new ParameterException(getErrorString(raw, "must be of the format 1KB, 2GB, etc.")); + } + + Optional<Data> unit = FluentIterable.from(Data.values()) + .firstMatch(Predicates.compose( + Predicates.equalTo(matcher.group(2)), + Functions.toStringFunction())); + if (unit.isPresent()) { + return new DataAmount(Integer.parseInt(matcher.group(1)), unit.get()); + } else { + throw new ParameterException( + getErrorString(raw, "one of " + ImmutableList.copyOf(Data.values()))); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/converters/DockerParameterConverter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/converters/DockerParameterConverter.java b/src/main/java/org/apache/aurora/scheduler/config/converters/DockerParameterConverter.java new file mode 100644 index 0000000..5c126e3 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/converters/DockerParameterConverter.java @@ -0,0 +1,36 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.converters; + +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.converters.BaseConverter; + +import org.apache.aurora.gen.DockerParameter; + +public class DockerParameterConverter extends BaseConverter<DockerParameter> { + public DockerParameterConverter(String optionName) { + super(optionName); + } + + @Override + public DockerParameter convert(String value) { + int pos = value.indexOf('='); + if (pos == -1 || pos == 0 || pos == value.length() - 1) { + throw new ParameterException(getErrorString(value, "formatted as name=value")); + } + + return new DockerParameter(value.substring(0, pos), value.substring(pos + 1)); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/converters/InetSocketAddressConverter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/converters/InetSocketAddressConverter.java b/src/main/java/org/apache/aurora/scheduler/config/converters/InetSocketAddressConverter.java new file mode 100644 index 0000000..1261592 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/converters/InetSocketAddressConverter.java @@ -0,0 +1,32 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.converters; + +import java.net.InetSocketAddress; + +import com.beust.jcommander.converters.BaseConverter; + +import org.apache.aurora.common.net.InetSocketAddressHelper; + +public class InetSocketAddressConverter extends BaseConverter<InetSocketAddress> { + public InetSocketAddressConverter(String optionName) { + super(optionName); + } + + @Override + public InetSocketAddress convert(String value) { + return InetSocketAddressHelper.parse(value); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/converters/TimeAmountConverter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/converters/TimeAmountConverter.java b/src/main/java/org/apache/aurora/scheduler/config/converters/TimeAmountConverter.java new file mode 100644 index 0000000..7e66cf2 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/converters/TimeAmountConverter.java @@ -0,0 +1,57 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.converters; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.converters.BaseConverter; +import com.google.common.base.Functions; +import com.google.common.base.Optional; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; + +import org.apache.aurora.common.quantity.Time; +import org.apache.aurora.scheduler.config.types.TimeAmount; + +public class TimeAmountConverter extends BaseConverter<TimeAmount> { + private static final Pattern AMOUNT_PATTERN = Pattern.compile("(\\d+)([A-Za-z]+)"); + + public TimeAmountConverter(String optionName) { + super(optionName); + } + + @Override + public TimeAmount convert(String raw) { + Matcher matcher = AMOUNT_PATTERN.matcher(raw); + + if (!matcher.matches()) { + throw new ParameterException(getErrorString(raw, "must be of the format 1ns, 2secs, etc.")); + } + + Optional<Time> unit = FluentIterable.from(Time.values()) + .firstMatch(Predicates.compose( + Predicates.equalTo(matcher.group(2)), + Functions.toStringFunction())); + if (unit.isPresent()) { + return new TimeAmount(Long.parseLong(matcher.group(1)), unit.get()); + } else { + throw new ParameterException( + getErrorString(raw, "one of " + ImmutableList.copyOf(Time.values()))); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/types/DataAmount.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/types/DataAmount.java b/src/main/java/org/apache/aurora/scheduler/config/types/DataAmount.java new file mode 100644 index 0000000..061234f --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/types/DataAmount.java @@ -0,0 +1,28 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.types; + +import org.apache.aurora.common.quantity.Amount; +import org.apache.aurora.common.quantity.Data; + +public class DataAmount extends Amount<Integer, Data> { + public DataAmount(int number, Data unit) { + super(number, unit, Integer.MAX_VALUE); + } + + @Override protected Integer scale(double multiplier) { + return (int) (getValue() * multiplier); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/types/TimeAmount.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/types/TimeAmount.java b/src/main/java/org/apache/aurora/scheduler/config/types/TimeAmount.java new file mode 100644 index 0000000..776e411 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/types/TimeAmount.java @@ -0,0 +1,28 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.types; + +import org.apache.aurora.common.quantity.Amount; +import org.apache.aurora.common.quantity.Time; + +public class TimeAmount extends Amount<Long, Time> { + public TimeAmount(long number, Time unit) { + super(number, unit, Long.MAX_VALUE); + } + + @Override protected Long scale(double multiplier) { + return (long) (getValue() * multiplier); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/validators/NotEmptyIterable.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/validators/NotEmptyIterable.java b/src/main/java/org/apache/aurora/scheduler/config/validators/NotEmptyIterable.java new file mode 100644 index 0000000..83be1dd --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/validators/NotEmptyIterable.java @@ -0,0 +1,28 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.validators; + +import com.beust.jcommander.IValueValidator; +import com.beust.jcommander.ParameterException; +import com.google.common.collect.Iterables; + +public class NotEmptyIterable implements IValueValidator<Iterable<?>> { + @Override + public void validate(String name, Iterable<?> value) throws ParameterException { + if (Iterables.isEmpty(value)) { + throw new ParameterException(String.format("%s must not be empty", name)); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/validators/NotEmptyString.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/validators/NotEmptyString.java b/src/main/java/org/apache/aurora/scheduler/config/validators/NotEmptyString.java new file mode 100644 index 0000000..4b8f3e3 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/validators/NotEmptyString.java @@ -0,0 +1,27 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.validators; + +import com.beust.jcommander.IValueValidator; +import com.beust.jcommander.ParameterException; + +public class NotEmptyString implements IValueValidator<String> { + @Override + public void validate(String name, String value) throws ParameterException { + if (value.isEmpty()) { + throw new ParameterException(String.format("%s must not be empty", name)); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/validators/NotNegativeAmount.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/validators/NotNegativeAmount.java b/src/main/java/org/apache/aurora/scheduler/config/validators/NotNegativeAmount.java new file mode 100644 index 0000000..1325e60 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/validators/NotNegativeAmount.java @@ -0,0 +1,29 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.validators; + +import com.beust.jcommander.IValueValidator; +import com.beust.jcommander.ParameterException; + +import org.apache.aurora.common.quantity.Amount; + +public class NotNegativeAmount implements IValueValidator<Amount<? extends Number, ?>> { + @Override + public void validate(String name, Amount<? extends Number, ?> value) throws ParameterException { + if (value.getValue().longValue() < 0) { + throw new ParameterException(String.format("%s must not be negative", name)); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/validators/PositiveAmount.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/validators/PositiveAmount.java b/src/main/java/org/apache/aurora/scheduler/config/validators/PositiveAmount.java new file mode 100644 index 0000000..db4476f --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/validators/PositiveAmount.java @@ -0,0 +1,29 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.validators; + +import com.beust.jcommander.IValueValidator; +import com.beust.jcommander.ParameterException; + +import org.apache.aurora.common.quantity.Amount; + +public class PositiveAmount implements IValueValidator<Amount<? extends Number, ?>> { + @Override + public void validate(String name, Amount<? extends Number, ?> value) throws ParameterException { + if (value.getValue().longValue() <= 0) { + throw new ParameterException(String.format("%s must be positive", name)); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/validators/PositiveNumber.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/validators/PositiveNumber.java b/src/main/java/org/apache/aurora/scheduler/config/validators/PositiveNumber.java new file mode 100644 index 0000000..2c477f1 --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/validators/PositiveNumber.java @@ -0,0 +1,27 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.validators; + +import com.beust.jcommander.IValueValidator; +import com.beust.jcommander.ParameterException; + +public class PositiveNumber implements IValueValidator<Number> { + @Override + public void validate(String name, Number value) throws ParameterException { + if (value.longValue() <= 0) { + throw new ParameterException(String.format("%s must be positive", name)); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/config/validators/ReadableFile.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/config/validators/ReadableFile.java b/src/main/java/org/apache/aurora/scheduler/config/validators/ReadableFile.java new file mode 100644 index 0000000..f67fb2f --- /dev/null +++ b/src/main/java/org/apache/aurora/scheduler/config/validators/ReadableFile.java @@ -0,0 +1,29 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.aurora.scheduler.config.validators; + +import java.io.File; + +import com.beust.jcommander.IValueValidator; +import com.beust.jcommander.ParameterException; + +public class ReadableFile implements IValueValidator<File> { + @Override + public void validate(String name, File value) throws ParameterException { + if (!value.canRead()) { + throw new ParameterException(String.format("File %s for %s is not readable", value, name)); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/configuration/ConfigurationManager.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/configuration/ConfigurationManager.java b/src/main/java/org/apache/aurora/scheduler/configuration/ConfigurationManager.java index 754fde0..b7f5e35 100644 --- a/src/main/java/org/apache/aurora/scheduler/configuration/ConfigurationManager.java +++ b/src/main/java/org/apache/aurora/scheduler/configuration/ConfigurationManager.java @@ -13,7 +13,7 @@ */ package org.apache.aurora.scheduler.configuration; -import java.util.Map; +import java.util.List; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -23,10 +23,10 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Strings; +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.collect.Multimap; import org.apache.aurora.gen.Container; import org.apache.aurora.gen.DockerParameter; @@ -91,7 +91,7 @@ public class ConfigurationManager { public static class ConfigurationManagerSettings { private final ImmutableSet<Container._Fields> allowedContainerTypes; private final boolean allowDockerParameters; - private final Multimap<String, String> defaultDockerParameters; + private final List<DockerParameter> defaultDockerParameters; private final boolean requireDockerUseExecutor; private final boolean allowGpuResource; private final boolean enableMesosFetcher; @@ -100,7 +100,7 @@ public class ConfigurationManager { public ConfigurationManagerSettings( ImmutableSet<Container._Fields> allowedContainerTypes, boolean allowDockerParameters, - Multimap<String, String> defaultDockerParameters, + List<DockerParameter> defaultDockerParameters, boolean requireDockerUseExecutor, boolean allowGpuResource, boolean enableMesosFetcher, @@ -294,10 +294,8 @@ public class ConfigurationManager { throw new TaskDescriptionException("A container must specify an image."); } if (containerConfig.getDocker().getParameters().isEmpty()) { - for (Map.Entry<String, String> e : settings.defaultDockerParameters.entries()) { - builder.getContainer().getDocker().addToParameters( - new DockerParameter(e.getKey(), e.getValue())); - } + builder.getContainer().getDocker() + .setParameters(ImmutableList.copyOf(settings.defaultDockerParameters)); } else { if (!settings.allowDockerParameters) { throw new TaskDescriptionException(NO_DOCKER_PARAMETERS); http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/configuration/executor/ExecutorModule.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/configuration/executor/ExecutorModule.java b/src/main/java/org/apache/aurora/scheduler/configuration/executor/ExecutorModule.java index 4dac975..6594325 100644 --- a/src/main/java/org/apache/aurora/scheduler/configuration/executor/ExecutorModule.java +++ b/src/main/java/org/apache/aurora/scheduler/configuration/executor/ExecutorModule.java @@ -21,6 +21,8 @@ import java.util.List; import java.util.Optional; import java.util.stream.Stream; +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; @@ -28,15 +30,12 @@ import com.google.common.collect.Iterables; import com.google.inject.AbstractModule; import org.apache.aurora.GuavaUtils; -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.common.args.constraints.Exists; import org.apache.aurora.common.base.MorePreconditions; -import org.apache.aurora.common.quantity.Amount; import org.apache.aurora.common.quantity.Data; import org.apache.aurora.gen.Volume; import org.apache.aurora.gen.apiConstants; +import org.apache.aurora.scheduler.config.types.DataAmount; +import org.apache.aurora.scheduler.config.validators.ReadableFile; import org.apache.aurora.scheduler.resources.ResourceType; import org.apache.mesos.v1.Protos; import org.apache.mesos.v1.Protos.CommandInfo; @@ -54,56 +53,65 @@ import static org.apache.aurora.scheduler.resources.ResourceType.RAM_MB; */ public class ExecutorModule extends AbstractModule { - @CmdLine( - name = "custom_executor_config", - help = "Path to custom executor settings configuration file.") - @Exists - @CanRead - private static final Arg<File> CUSTOM_EXECUTOR_CONFIG = Arg.create(null); - - @CmdLine(name = "thermos_executor_path", help = "Path to the thermos executor entry point.") - private static final Arg<String> THERMOS_EXECUTOR_PATH = Arg.create(); - - @CmdLine(name = "thermos_executor_resources", - help = "A comma separated list of additional resources to copy into the sandbox." - + "Note: if thermos_executor_path is not the thermos_executor.pex file itself, " - + "this must include it.") - private static final Arg<List<String>> THERMOS_EXECUTOR_RESOURCES = - Arg.create(ImmutableList.of()); - - @CmdLine(name = "thermos_executor_flags", - help = "Extra arguments to be passed to the thermos executor") - private static final Arg<String> THERMOS_EXECUTOR_FLAGS = Arg.create(null); - - @CmdLine(name = "thermos_home_in_sandbox", - help = "If true, changes HOME to the sandbox before running the executor. " - + "This primarily has the effect of causing the executor and runner " - + "to extract themselves into the sandbox.") - private static final Arg<Boolean> THERMOS_HOME_IN_SANDBOX = Arg.create(false); - - /** - * Extra CPU allocated for each executor. - */ - @CmdLine(name = "thermos_executor_cpu", - help = "The number of CPU cores to allocate for each instance of the executor.") - private static final Arg<Double> EXECUTOR_OVERHEAD_CPUS = Arg.create(0.25); - - /** - * Extra RAM allocated for the executor. - */ - @CmdLine(name = "thermos_executor_ram", - help = "The amount of RAM to allocate for each instance of the executor.") - private static final Arg<Amount<Long, Data>> EXECUTOR_OVERHEAD_RAM = - Arg.create(Amount.of(128L, Data.MB)); - - @CmdLine(name = "global_container_mounts", - help = "A comma separated list of mount points (in host:container form) to mount " - + "into all (non-mesos) containers.") - private static final Arg<List<Volume>> GLOBAL_CONTAINER_MOUNTS = Arg.create(ImmutableList.of()); - - @CmdLine(name = "populate_discovery_info", - help = "If true, Aurora populates DiscoveryInfo field of Mesos TaskInfo.") - private static final Arg<Boolean> POPULATE_DISCOVERY_INFO = Arg.create(false); + @Parameters(separators = "=") + public static class Options { + @Parameter( + names = "-custom_executor_config", + validateValueWith = ReadableFile.class, + description = "Path to custom executor settings configuration file.") + public File customExecutorConfig; + + @Parameter(names = "-thermos_executor_path", + description = "Path to the thermos executor entry point.") + public String thermosExecutorPath; + + @Parameter(names = "-thermos_executor_resources", + description = "A comma separated list of additional resources to copy into the sandbox." + + "Note: if thermos_executor_path is not the thermos_executor.pex file itself, " + + "this must include it.") + public List<String> thermosExecutorResources = ImmutableList.of(); + + @Parameter(names = "-thermos_executor_flags", + description = "Extra arguments to be passed to the thermos executor") + public String thermosExecutorFlags; + + @Parameter(names = "-thermos_home_in_sandbox", + description = "If true, changes HOME to the sandbox before running the executor. " + + "This primarily has the effect of causing the executor and runner " + + "to extract themselves into the sandbox.", + arity = 1) + public boolean thermosHomeInSandbox = false; + + /** + * Extra CPU allocated for each executor. + */ + @Parameter(names = "-thermos_executor_cpu", + description = "The number of CPU cores to allocate for each instance of the executor.") + public double executorOverheadCpus = 0.25; + + /** + * Extra RAM allocated for the executor. + */ + @Parameter(names = "-thermos_executor_ram", + description = "The amount of RAM to allocate for each instance of the executor.") + public DataAmount executorOverheadRam = new DataAmount(128, Data.MB); + + @Parameter(names = "-global_container_mounts", + description = "A comma separated list of mount points (in host:container form) to mount " + + "into all (non-mesos) containers.") + public List<Volume> globalContainerMounts = ImmutableList.of(); + + @Parameter(names = "-populate_discovery_info", + description = "If true, Aurora populates DiscoveryInfo field of Mesos TaskInfo.", + arity = 1) + public boolean populateDiscoveryInfo = false; + } + + private final Options options; + + public ExecutorModule(Options options) { + this.options = options; + } @VisibleForTesting static CommandInfo makeExecutorCommand( @@ -135,11 +143,11 @@ public class ExecutorModule extends AbstractModule { .build(); } - private static ExecutorConfig makeThermosExecutorConfig() { + private static ExecutorConfig makeThermosExecutorConfig(Options opts) { List<Protos.Volume> volumeMounts = ImmutableList.<Protos.Volume>builder() .addAll(Iterables.transform( - GLOBAL_CONTAINER_MOUNTS.get(), + opts.globalContainerMounts, v -> Protos.Volume.newBuilder() .setHostPath(v.getHostPath()) .setContainerPath(v.getContainerPath()) @@ -154,33 +162,32 @@ public class ExecutorModule extends AbstractModule { .setExecutorId(Executors.PLACEHOLDER_EXECUTOR_ID) .setCommand( makeExecutorCommand( - THERMOS_EXECUTOR_PATH.get(), - THERMOS_EXECUTOR_RESOURCES.get(), - THERMOS_HOME_IN_SANDBOX.get(), - THERMOS_EXECUTOR_FLAGS.get())) - .addResources(makeResource(CPUS, EXECUTOR_OVERHEAD_CPUS.get())) - .addResources(makeResource(RAM_MB, EXECUTOR_OVERHEAD_RAM.get().as(Data.MB))) + opts.thermosExecutorPath, + opts.thermosExecutorResources, + opts.thermosHomeInSandbox, + opts.thermosExecutorFlags)) + .addResources(makeResource(CPUS, opts.executorOverheadCpus)) + .addResources(makeResource(RAM_MB, opts.executorOverheadRam.as(Data.MB))) .build(), volumeMounts, "thermos-"); } - private static ExecutorSettings makeExecutorSettings() { + private static ExecutorSettings makeExecutorSettings(Options opts) { try { - ImmutableMap.Builder<String, ExecutorConfig> configsBuilder = ImmutableMap.builder(); - configsBuilder.put(apiConstants.AURORA_EXECUTOR_NAME, makeThermosExecutorConfig()); + configsBuilder.put(apiConstants.AURORA_EXECUTOR_NAME, makeThermosExecutorConfig(opts)); - if (CUSTOM_EXECUTOR_CONFIG.hasAppliedValue()) { + if (opts.customExecutorConfig != null) { configsBuilder.putAll( ExecutorSettingsLoader.read( Files.newBufferedReader( - CUSTOM_EXECUTOR_CONFIG.get().toPath(), + opts.customExecutorConfig.toPath(), StandardCharsets.UTF_8))); } - return new ExecutorSettings(configsBuilder.build(), POPULATE_DISCOVERY_INFO.get()); + return new ExecutorSettings(configsBuilder.build(), opts.populateDiscoveryInfo); } catch (ExecutorSettingsLoader.ExecutorConfigException | IOException e) { throw new IllegalArgumentException("Failed to read executor settings: " + e, e); @@ -189,7 +196,7 @@ public class ExecutorModule extends AbstractModule { @Override protected void configure() { - bind(ExecutorSettings.class).toInstance(makeExecutorSettings()); + bind(ExecutorSettings.class).toInstance(makeExecutorSettings(options)); } private static Resource makeResource(ResourceType type, double value) { http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/cron/quartz/CronModule.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/cron/quartz/CronModule.java b/src/main/java/org/apache/aurora/scheduler/cron/quartz/CronModule.java index 9c88a2a..33e0eaa 100644 --- a/src/main/java/org/apache/aurora/scheduler/cron/quartz/CronModule.java +++ b/src/main/java/org/apache/aurora/scheduler/cron/quartz/CronModule.java @@ -19,16 +19,16 @@ import java.util.concurrent.atomic.AtomicLong; import javax.inject.Singleton; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; import com.google.inject.AbstractModule; import com.google.inject.Provides; 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.util.BackoffHelper; +import org.apache.aurora.scheduler.config.types.TimeAmount; +import org.apache.aurora.scheduler.config.validators.PositiveNumber; import org.apache.aurora.scheduler.cron.CronJobManager; import org.apache.aurora.scheduler.cron.CronPredictor; import org.apache.aurora.scheduler.cron.CronScheduler; @@ -57,31 +57,38 @@ import static org.quartz.impl.StdSchedulerFactory.PROP_THREAD_POOL_PREFIX; public class CronModule extends AbstractModule { private static final Logger LOG = LoggerFactory.getLogger(CronModule.class); - @CmdLine(name = "cron_scheduler_num_threads", - help = "Number of threads to use for the cron scheduler thread pool.") - private static final Arg<Integer> NUM_THREADS = Arg.create(10); + @Parameters(separators = "=") + public static class Options { + @Parameter(names = "-cron_scheduler_num_threads", + description = "Number of threads to use for the cron scheduler thread pool.") + public int cronSchedulerNumThreads = 10; - @CmdLine(name = "cron_timezone", help = "TimeZone to use for cron predictions.") - private static final Arg<String> CRON_TIMEZONE = Arg.create("GMT"); + @Parameter(names = "-cron_timezone", description = "TimeZone to use for cron predictions.") + public String cronTimezone = "GMT"; - @CmdLine(name = "cron_start_initial_backoff", help = - "Initial backoff delay while waiting for a previous cron run to be killed.") - public static final Arg<Amount<Long, Time>> CRON_START_INITIAL_BACKOFF = - Arg.create(Amount.of(5L, Time.SECONDS)); + @Parameter(names = "-cron_start_initial_backoff", description = + "Initial backoff delay while waiting for a previous cron run to be killed.") + public TimeAmount cronStartInitialBackoff = new TimeAmount(5, Time.SECONDS); - @CmdLine(name = "cron_start_max_backoff", help = - "Max backoff delay while waiting for a previous cron run to be killed.") - public static final Arg<Amount<Long, Time>> CRON_START_MAX_BACKOFF = - Arg.create(Amount.of(1L, Time.MINUTES)); + @Parameter(names = "-cron_start_max_backoff", description = + "Max backoff delay while waiting for a previous cron run to be killed.") + public TimeAmount cronStartMaxBackoff = new TimeAmount(1, Time.MINUTES); - @Positive - @CmdLine(name = "cron_scheduling_max_batch_size", - help = "The maximum number of triggered cron jobs that can be processed in a batch.") - private static final Arg<Integer> CRON_MAX_BATCH_SIZE = Arg.create(10); + @Parameter(names = "-cron_scheduling_max_batch_size", + validateValueWith = PositiveNumber.class, + description = "The maximum number of triggered cron jobs that can be processed in a batch.") + public int cronMaxBatchSize = 10; + } // Global per-JVM ID number generator for the provided Quartz Scheduler. private static final AtomicLong ID_GENERATOR = new AtomicLong(); + private final Options options; + + public CronModule(Options options) { + this.options = options; + } + @Override protected void configure() { bind(CronPredictor.class).to(CronPredictorImpl.class); @@ -97,7 +104,7 @@ public class CronModule extends AbstractModule { bind(AuroraCronJob.class).in(Singleton.class); bind(AuroraCronJob.Config.class).toInstance(new AuroraCronJob.Config( - new BackoffHelper(CRON_START_INITIAL_BACKOFF.get(), CRON_START_MAX_BACKOFF.get()))); + new BackoffHelper(options.cronStartInitialBackoff, options.cronStartMaxBackoff))); PubsubEventModule.bindSubscriber(binder(), AuroraCronJob.class); @@ -106,14 +113,14 @@ public class CronModule extends AbstractModule { bind(new TypeLiteral<Integer>() { }) .annotatedWith(AuroraCronJob.CronMaxBatchSize.class) - .toInstance(CRON_MAX_BATCH_SIZE.get()); + .toInstance(options.cronMaxBatchSize); bind(CronBatchWorker.class).in(Singleton.class); addSchedulerActiveServiceBinding(binder()).to(CronBatchWorker.class); } @Provides TimeZone provideTimeZone() { - TimeZone timeZone = TimeZone.getTimeZone(CRON_TIMEZONE.get()); + TimeZone timeZone = TimeZone.getTimeZone(options.cronTimezone); TimeZone systemTimeZone = TimeZone.getDefault(); if (!timeZone.equals(systemTimeZone)) { LOG.warn("Cron schedules are configured to fire according to timezone " @@ -126,7 +133,7 @@ public class CronModule extends AbstractModule { @Provides @Singleton - static Scheduler provideScheduler(AuroraCronJobFactory jobFactory) throws SchedulerException { + Scheduler provideScheduler(AuroraCronJobFactory jobFactory) throws SchedulerException { // There are several ways to create a quartz Scheduler instance. This path was chosen as the // simplest to create a Scheduler that uses a *daemon* QuartzSchedulerThread instance. Properties props = new Properties(); @@ -134,7 +141,9 @@ public class CronModule extends AbstractModule { props.setProperty(PROP_SCHED_NAME, name); props.setProperty(PROP_SCHED_INSTANCE_ID, name); props.setProperty(PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getCanonicalName()); - props.setProperty(PROP_THREAD_POOL_PREFIX + ".threadCount", NUM_THREADS.get().toString()); + props.setProperty( + PROP_THREAD_POOL_PREFIX + ".threadCount", + String.valueOf(options.cronSchedulerNumThreads)); props.setProperty(PROP_THREAD_POOL_PREFIX + ".makeThreadsDaemons", Boolean.TRUE.toString()); props.setProperty(PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON, Boolean.TRUE.toString()); http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/discovery/FlaggedZooKeeperConfig.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/discovery/FlaggedZooKeeperConfig.java b/src/main/java/org/apache/aurora/scheduler/discovery/FlaggedZooKeeperConfig.java index e8aafe4..c2e8ce2 100644 --- a/src/main/java/org/apache/aurora/scheduler/discovery/FlaggedZooKeeperConfig.java +++ b/src/main/java/org/apache/aurora/scheduler/discovery/FlaggedZooKeeperConfig.java @@ -18,51 +18,59 @@ import java.util.List; import javax.annotation.Nullable; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; import com.google.common.base.Optional; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; -import org.apache.aurora.common.args.Arg; -import org.apache.aurora.common.args.CmdLine; -import org.apache.aurora.common.args.constraints.NotEmpty; import org.apache.aurora.common.quantity.Amount; -import org.apache.aurora.common.quantity.Time; import org.apache.aurora.common.zookeeper.Credentials; -import org.apache.aurora.common.zookeeper.ZooKeeperUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.aurora.scheduler.config.types.TimeAmount; +import org.apache.aurora.scheduler.config.validators.NotEmptyIterable; + +import static org.apache.aurora.scheduler.discovery.ZooKeeperConfig.DEFAULT_SESSION_TIMEOUT; /** * A factory that creates a {@link ZooKeeperConfig} instance based on command line argument * values. */ public final class FlaggedZooKeeperConfig { - private static final Logger LOG = LoggerFactory.getLogger(FlaggedZooKeeperConfig.class); - @CmdLine(name = "zk_use_curator", - help = "DEPRECATED: Uses Apache Curator as the zookeeper client; otherwise a copy of Twitter " - + "commons/zookeeper (the legacy library) is used.") - private static final Arg<Boolean> USE_CURATOR = Arg.create(true); + @Parameters(separators = "=") + public static class Options { + @Parameter(names = "-zk_use_curator", + description = + "DEPRECATED: Uses Apache Curator as the zookeeper client; otherwise a copy of Twitter " + + "commons/zookeeper (the legacy library) is used.", + arity = 1) + public boolean useCurator = true; - @CmdLine(name = "zk_in_proc", - help = "Launches an embedded zookeeper server for local testing causing -zk_endpoints " - + "to be ignored if specified.") - private static final Arg<Boolean> IN_PROCESS = Arg.create(false); + @Parameter(names = "-zk_in_proc", + description = + "Launches an embedded zookeeper server for local testing causing -zk_endpoints " + + "to be ignored if specified.", + arity = 1) + public boolean inProcess = false; - @NotEmpty - @CmdLine(name = "zk_endpoints", help = "Endpoint specification for the ZooKeeper servers.") - private static final Arg<List<InetSocketAddress>> ZK_ENDPOINTS = Arg.create(); + @Parameter( + names = "-zk_endpoints", + required = true, + validateValueWith = NotEmptyIterable.class, + description = "Endpoint specification for the ZooKeeper servers.") + public List<InetSocketAddress> zkEndpoints; - @CmdLine(name = "zk_chroot_path", help = "chroot path to use for the ZooKeeper connections") - private static final Arg<String> CHROOT_PATH = Arg.create(null); + @Parameter(names = "-zk_chroot_path", + description = "chroot path to use for the ZooKeeper connections") + public String chrootPath; - @CmdLine(name = "zk_session_timeout", help = "The ZooKeeper session timeout.") - private static final Arg<Amount<Integer, Time>> SESSION_TIMEOUT = - Arg.create(ZooKeeperUtils.DEFAULT_ZK_SESSION_TIMEOUT); + @Parameter(names = "-zk_session_timeout", description = "The ZooKeeper session timeout.") + public TimeAmount sessionTimeout = DEFAULT_SESSION_TIMEOUT; - @CmdLine(name = "zk_digest_credentials", - help = "user:password to use when authenticating with ZooKeeper.") - private static final Arg<String> DIGEST_CREDENTIALS = Arg.create(null); + @Parameter(names = "-zk_digest_credentials", + description = "user:password to use when authenticating with ZooKeeper.") + public String digestCredentials; + } private FlaggedZooKeeperConfig() { // Utility class. @@ -73,17 +81,14 @@ public final class FlaggedZooKeeperConfig { * * @return Configuration instance. */ - public static ZooKeeperConfig create() { - if (USE_CURATOR.hasAppliedValue()) { - LOG.warn("The -zk_use_curator flag is deprecated and will be removed in a future release."); - } + public static ZooKeeperConfig create(Options opts) { return new ZooKeeperConfig( - USE_CURATOR.get(), - ZK_ENDPOINTS.get(), - Optional.fromNullable(CHROOT_PATH.get()), - IN_PROCESS.get(), - SESSION_TIMEOUT.get(), - getCredentials(DIGEST_CREDENTIALS.get())); + opts.useCurator, + opts.zkEndpoints, + Optional.fromNullable(opts.chrootPath), + opts.inProcess, + Amount.of(opts.sessionTimeout.getValue().intValue(), opts.sessionTimeout.getUnit()), + getCredentials(opts.digestCredentials)); } private static Optional<Credentials> getCredentials(@Nullable String userAndPass) { http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/discovery/ZooKeeperConfig.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/discovery/ZooKeeperConfig.java b/src/main/java/org/apache/aurora/scheduler/discovery/ZooKeeperConfig.java index 4014a91..f6faca5 100644 --- a/src/main/java/org/apache/aurora/scheduler/discovery/ZooKeeperConfig.java +++ b/src/main/java/org/apache/aurora/scheduler/discovery/ZooKeeperConfig.java @@ -22,6 +22,7 @@ import org.apache.aurora.common.quantity.Amount; import org.apache.aurora.common.quantity.Time; import org.apache.aurora.common.zookeeper.Credentials; import org.apache.aurora.common.zookeeper.ZooKeeperUtils; +import org.apache.aurora.scheduler.config.types.TimeAmount; import static java.util.Objects.requireNonNull; @@ -32,6 +33,10 @@ import static java.util.Objects.requireNonNull; */ public class ZooKeeperConfig { + public static final TimeAmount DEFAULT_SESSION_TIMEOUT = new TimeAmount( + ZooKeeperUtils.DEFAULT_ZK_SESSION_TIMEOUT.getValue(), + ZooKeeperUtils.DEFAULT_ZK_SESSION_TIMEOUT.getUnit()); + /** * Creates a new client configuration with defaults for the session timeout and credentials. * http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/events/WebhookModule.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/events/WebhookModule.java b/src/main/java/org/apache/aurora/scheduler/events/WebhookModule.java index 8c9ea05..8e13341 100644 --- a/src/main/java/org/apache/aurora/scheduler/events/WebhookModule.java +++ b/src/main/java/org/apache/aurora/scheduler/events/WebhookModule.java @@ -17,19 +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.Optional; import com.google.common.base.Strings; import com.google.common.io.Files; -import com.google.common.io.Resources; import com.google.inject.AbstractModule; - import com.google.inject.Singleton; -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.common.args.constraints.Exists; import org.apache.aurora.scheduler.SchedulerServicesModule; +import org.apache.aurora.scheduler.config.validators.ReadableFile; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.DefaultAsyncHttpClientConfig; import org.asynchttpclient.channel.DefaultKeepAliveStrategy; @@ -51,26 +49,36 @@ public class WebhookModule extends AbstractModule { @VisibleForTesting static final String WEBHOOK_CONFIG_PATH = "org/apache/aurora/scheduler/webhook.json"; - @CmdLine(name = "webhook_config", help = "Path to webhook configuration file.") - @Exists - @CanRead - private static final Arg<File> WEBHOOK_CONFIG_FILE = Arg.create(); - - private final boolean enableWebhook; + @Parameters(separators = "=") + public static class Options { + @Parameter(names = "-webhook_config", + validateValueWith = ReadableFile.class, + description = "Path to webhook configuration file.") + public File webhookConfigFile = null; + } - public WebhookModule() { - this(WEBHOOK_CONFIG_FILE.hasAppliedValue()); + private final Optional<String> webhookConfig; + + public WebhookModule(Options options) { + this.webhookConfig = Optional.fromNullable(options.webhookConfigFile) + .transform(f -> { + try { + return Files.toString(options.webhookConfigFile, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); } @VisibleForTesting - private WebhookModule(boolean enableWebhook) { - this.enableWebhook = enableWebhook; + WebhookModule(Optional<String> webhookConfig) { + this.webhookConfig = webhookConfig; } @Override protected void configure() { - if (enableWebhook) { - WebhookInfo webhookInfo = parseWebhookConfig(readWebhookFile()); + if (webhookConfig.isPresent()) { + WebhookInfo webhookInfo = parseWebhookConfig(webhookConfig.get()); DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder() .setConnectTimeout(webhookInfo.getConnectonTimeoutMsec()) .setHandshakeTimeout(webhookInfo.getConnectonTimeoutMsec()) @@ -92,20 +100,6 @@ public class WebhookModule extends AbstractModule { } @VisibleForTesting - static String readWebhookFile() { - try { - return WEBHOOK_CONFIG_FILE.hasAppliedValue() - ? Files.toString(WEBHOOK_CONFIG_FILE.get(), StandardCharsets.UTF_8) - : Resources.toString( - Webhook.class.getClassLoader().getResource(WEBHOOK_CONFIG_PATH), - StandardCharsets.UTF_8); - } catch (IOException e) { - LOG.error("Error loading webhook configuration file."); - throw new RuntimeException(e); - } - } - - @VisibleForTesting static WebhookInfo parseWebhookConfig(String config) { checkArgument(!Strings.isNullOrEmpty(config), "Webhook configuration cannot be empty"); try { http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/http/H2ConsoleModule.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/http/H2ConsoleModule.java b/src/main/java/org/apache/aurora/scheduler/http/H2ConsoleModule.java index 01d6b5d..ee10f47 100644 --- a/src/main/java/org/apache/aurora/scheduler/http/H2ConsoleModule.java +++ b/src/main/java/org/apache/aurora/scheduler/http/H2ConsoleModule.java @@ -13,12 +13,12 @@ */ package org.apache.aurora.scheduler.http; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.inject.servlet.ServletModule; -import org.apache.aurora.common.args.Arg; -import org.apache.aurora.common.args.CmdLine; import org.h2.server.web.WebServlet; /** @@ -30,13 +30,19 @@ public class H2ConsoleModule extends ServletModule { public static final String H2_PATH = "/h2console"; public static final String H2_PERM = "h2_management_console"; - @CmdLine(name = "enable_h2_console", help = "Enable H2 DB management console.") - private static final Arg<Boolean> ENABLE_H2_CONSOLE = Arg.create(false); + @Parameters(separators = "=") + public static class Options { + @Parameter( + names = "-enable_h2_console", + description = "Enable H2 DB management console.", + arity = 1) + public boolean enableH2Console = false; + } private final boolean enabled; - public H2ConsoleModule() { - this(ENABLE_H2_CONSOLE.get()); + public H2ConsoleModule(Options options) { + this(options.enableH2Console); } @VisibleForTesting http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java b/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java index e29bb41..93cd20a 100644 --- a/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java +++ b/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java @@ -20,13 +20,14 @@ import java.util.EnumSet; import java.util.Map; import java.util.Set; -import javax.annotation.Nonnegative; import javax.inject.Inject; import javax.inject.Singleton; import javax.servlet.DispatcherType; import javax.servlet.ServletContextListener; import javax.ws.rs.HttpMethod; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Optional; @@ -53,8 +54,6 @@ import com.google.inject.util.Modules; import com.sun.jersey.guice.JerseyServletModule; import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; -import org.apache.aurora.common.args.Arg; -import org.apache.aurora.common.args.CmdLine; import org.apache.aurora.common.net.http.handlers.AbortHandler; import org.apache.aurora.common.net.http.handlers.ContentionPrinter; import org.apache.aurora.common.net.http.handlers.HealthHandler; @@ -65,9 +64,11 @@ import org.apache.aurora.common.net.http.handlers.VarsHandler; import org.apache.aurora.common.net.http.handlers.VarsJsonHandler; import org.apache.aurora.scheduler.SchedulerServicesModule; import org.apache.aurora.scheduler.app.ServiceGroupMonitor.MonitorException; +import org.apache.aurora.scheduler.config.CliOptions; import org.apache.aurora.scheduler.http.api.ApiModule; import org.apache.aurora.scheduler.http.api.security.HttpSecurityModule; import org.apache.aurora.scheduler.thrift.ThriftModule; +import org.apache.aurora.scheduler.thrift.aop.AopModule; import org.eclipse.jetty.rewrite.handler.RewriteHandler; import org.eclipse.jetty.rewrite.handler.RewriteRegexRule; import org.eclipse.jetty.server.Handler; @@ -102,18 +103,23 @@ public class JettyServerModule extends AbstractModule { // rewritten is stored. static final String ORIGINAL_PATH_ATTRIBUTE_NAME = "originalPath"; - @CmdLine(name = "hostname", - help = "The hostname to advertise in ZooKeeper instead of the locally-resolved hostname.") - private static final Arg<String> HOSTNAME_OVERRIDE = Arg.create(null); - - @Nonnegative - @CmdLine(name = "http_port", - help = "The port to start an HTTP server on. Default value will choose a random port.") - protected static final Arg<Integer> HTTP_PORT = Arg.create(0); - - @CmdLine(name = "ip", - help = "The ip address to listen. If not set, the scheduler will listen on all interfaces.") - protected static final Arg<String> LISTEN_IP = Arg.create(); + @Parameters(separators = "=") + public static class Options { + @Parameter(names = "-hostname", + description = + "The hostname to advertise in ZooKeeper instead of the locally-resolved hostname.") + public String hostnameOverride; + + @Parameter(names = "-http_port", + description = + "The port to start an HTTP server on. Default value will choose a random port.") + public int httpPort = 0; + + @Parameter(names = "-ip", + description = + "The ip address to listen. If not set, the scheduler will listen on all interfaces.") + public String listenIp; + } public static final Map<String, String> GUICE_CONTAINER_PARAMS = ImmutableMap.of( FEATURE_POJO_MAPPING, Boolean.TRUE.toString()); @@ -123,14 +129,16 @@ public class JettyServerModule extends AbstractModule { .toString() .replace("assets/index.html", ""); + private final CliOptions options; private final boolean production; - public JettyServerModule() { - this(true); + public JettyServerModule(CliOptions options) { + this(options, true); } @VisibleForTesting - JettyServerModule(boolean production) { + JettyServerModule(CliOptions options, boolean production) { + this.options = options; this.production = production; } @@ -147,7 +155,8 @@ public class JettyServerModule extends AbstractModule { .annotatedWith(Names.named(HealthHandler.HEALTH_CHECKER_KEY)) .toInstance(Suppliers.ofInstance(true)); - final Optional<String> hostnameOverride = Optional.fromNullable(HOSTNAME_OVERRIDE.get()); + final Optional<String> hostnameOverride = + Optional.fromNullable(options.jetty.hostnameOverride); if (hostnameOverride.isPresent()) { try { InetAddress.getByName(hostnameOverride.get()); @@ -173,28 +182,27 @@ public class JettyServerModule extends AbstractModule { SchedulerServicesModule.addAppStartupServiceBinding(binder()).to(RedirectMonitor.class); if (production) { - install(PRODUCTION_SERVLET_CONTEXT_LISTENER); - } - } - - private static final Module PRODUCTION_SERVLET_CONTEXT_LISTENER = new AbstractModule() { - @Override - protected void configure() { - // Provider binding only. - } + install(new AbstractModule() { + @Override + protected void configure() { + // Provider binding only. + } - @Provides - @Singleton - ServletContextListener provideServletContextListener(Injector parentInjector) { - return makeServletContextListener( - parentInjector, - Modules.combine( - new ApiModule(), - new H2ConsoleModule(), - new HttpSecurityModule(), - new ThriftModule())); + @Provides + @Singleton + ServletContextListener provideServletContextListener(Injector parentInjector) { + return makeServletContextListener( + parentInjector, + Modules.combine( + new ApiModule(options.api), + new H2ConsoleModule(options.h2Console), + new HttpSecurityModule(options), + new ThriftModule(), + new AopModule(options))); + } + }); } - }; + } private static final Set<String> LEADER_ENDPOINTS = ImmutableSet.of( "agents", @@ -304,6 +312,7 @@ public class JettyServerModule extends AbstractModule { } public static final class HttpServerLauncher extends AbstractIdleService implements HttpService { + private final CliOptions options; private final ServletContextListener servletContextListener; private final Optional<String> advertisedHostOverride; private volatile Server server; @@ -311,9 +320,11 @@ public class JettyServerModule extends AbstractModule { @Inject HttpServerLauncher( + CliOptions options, ServletContextListener servletContextListener, Optional<String> advertisedHostOverride) { + this.options = requireNonNull(options); this.servletContextListener = requireNonNull(servletContextListener); this.advertisedHostOverride = requireNonNull(advertisedHostOverride); } @@ -380,9 +391,9 @@ public class JettyServerModule extends AbstractModule { rootHandler.addHandler(servletHandler); ServerConnector connector = new ServerConnector(server); - connector.setPort(HTTP_PORT.get()); - if (LISTEN_IP.hasAppliedValue()) { - connector.setHost(LISTEN_IP.get()); + connector.setPort(options.jetty.httpPort); + if (options.jetty.listenIp != null) { + connector.setHost(options.jetty.listenIp); } server.addConnector(connector); http://git-wip-us.apache.org/repos/asf/aurora/blob/519e3df7/src/main/java/org/apache/aurora/scheduler/http/api/ApiModule.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/http/api/ApiModule.java b/src/main/java/org/apache/aurora/scheduler/http/api/ApiModule.java index e468209..a19663a 100644 --- a/src/main/java/org/apache/aurora/scheduler/http/api/ApiModule.java +++ b/src/main/java/org/apache/aurora/scheduler/http/api/ApiModule.java @@ -16,13 +16,13 @@ package org.apache.aurora.scheduler.http.api; import javax.inject.Singleton; import javax.ws.rs.core.MediaType; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; import com.google.common.collect.ImmutableMap; import com.google.inject.Provides; import com.google.inject.servlet.ServletModule; import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; -import org.apache.aurora.common.args.Arg; -import org.apache.aurora.common.args.CmdLine; import org.apache.aurora.gen.AuroraAdmin; import org.apache.aurora.scheduler.http.CorsFilter; import org.apache.aurora.scheduler.http.JettyServerModule; @@ -46,13 +46,22 @@ public class ApiModule extends ServletModule { private static final MediaType THRIFT_BINARY = new MediaType("application", "vnd.apache.thrift.binary"); - /** - * Set the {@code Access-Control-Allow-Origin} header for API requests. See - * http://www.w3.org/TR/cors/ - */ - @CmdLine(name = "enable_cors_for", - help = "List of domains for which CORS support should be enabled.") - private static final Arg<String> ENABLE_CORS_FOR = Arg.create(null); + @Parameters(separators = "=") + public static class Options { + /** + * Set the {@code Access-Control-Allow-Origin} header for API requests. See + * http://www.w3.org/TR/cors/ + */ + @Parameter(names = "-enable_cors_for", + description = "List of domains for which CORS support should be enabled.") + public String enableCorsFor; + } + + private final Options options; + + public ApiModule(Options options) { + this.options = options; + } private static final String API_CLIENT_ROOT = Resource .newClassPathResource("org/apache/aurora/scheduler/gen/client") @@ -60,8 +69,8 @@ public class ApiModule extends ServletModule { @Override protected void configureServlets() { - if (ENABLE_CORS_FOR.get() != null) { - filter(API_PATH).through(new CorsFilter(ENABLE_CORS_FOR.get())); + if (options.enableCorsFor != null) { + filter(API_PATH).through(new CorsFilter(options.enableCorsFor)); } serve(API_PATH).with(TContentAwareServlet.class);
