content: /**
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.aurora.scheduler.app;
  
  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.ImmutableList;
  import com.google.common.collect.ImmutableSet;
  import com.google.inject.AbstractModule;
  
  import org.apache.aurora.GuiceUtils;
  import org.apache.aurora.common.inject.TimedInterceptor;
  import org.apache.aurora.common.quantity.Time;
  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.Options.DriverKind;
  import org.apache.aurora.scheduler.async.AsyncModule;
  import org.apache.aurora.scheduler.config.CliOptions;
  import org.apache.aurora.scheduler.config.splitters.CommaSplitter;
  import org.apache.aurora.scheduler.config.validators.PositiveNumber;
  import org.apache.aurora.scheduler.configuration.ConfigurationManager;
  import org.apache.aurora.scheduler.configuration.ConfigurationManager.ConfigurationManagerSettings;
  import org.apache.aurora.scheduler.events.NotifyingSchedulingFilter;
  import org.apache.aurora.scheduler.events.PubsubEventModule;
  import org.apache.aurora.scheduler.filter.SchedulingFilter;
  import org.apache.aurora.scheduler.filter.SchedulingFilterImpl;
  import org.apache.aurora.scheduler.http.JettyServerModule;
  import org.apache.aurora.scheduler.maintenance.MaintenanceModule;
  import org.apache.aurora.scheduler.mesos.SchedulerDriverModule;
  import org.apache.aurora.scheduler.metadata.MetadataModule;
  import org.apache.aurora.scheduler.offers.OfferManagerModule;
  import org.apache.aurora.scheduler.preemptor.PreemptorModule;
  import org.apache.aurora.scheduler.pruning.PruningModule;
  import org.apache.aurora.scheduler.quota.QuotaModule;
  import org.apache.aurora.scheduler.reconciliation.ReconciliationModule;
  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.thrift.Thresholds;
  import org.apache.aurora.scheduler.updater.UpdaterModule;
  import org.apache.mesos.Scheduler;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * Binding module for the aurora scheduler application.
   */
  public class AppModule extends AbstractModule {
  
    @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.",
          splitter = CommaSplitter.class)
      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.",
          splitter = CommaSplitter.class)
      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;
  
      @Parameter(names = "-allowed_job_environments", description = "Regular expression describing "
              + "the environments that are allowed to be used by jobs.")
      public String allowedJobEnvironments = ConfigurationManager.DEFAULT_ALLOWED_JOB_ENVIRONMENTS;
    }
  
    private final ConfigurationManagerSettings configurationManagerSettings;
    private final DriverKind kind;
    private final CliOptions options;
  
    @VisibleForTesting
    public AppModule(
        ConfigurationManagerSettings configurationManagerSettings,
        DriverKind kind,
        CliOptions options) {
      this.configurationManagerSettings = requireNonNull(configurationManagerSettings);
      this.kind = kind;
      this.options = options;
    }
  
    public AppModule(CliOptions opts) {
      this(new ConfigurationManagerSettings(
          ImmutableSet.copyOf(opts.app.allowedContainerTypes),
              opts.app.enableDockerParameters,
              opts.app.defaultDockerParameters,
              opts.app.requireDockerUseExecutor,
              opts.main.allowGpuResource,
              opts.app.enableMesosFetcher,
              opts.app.allowContainerVolumes,
              opts.sla.minRequiredInstances,
              opts.sla.maxSlaDuration.as(Time.SECONDS),
              opts.app.allowedJobEnvironments,
              opts.sla.slaAwareKillNonProd),
          opts.main.driverImpl,
          opts);
    }
  
    @Override
    protected void configure() {
      bind(ConfigurationManagerSettings.class).toInstance(configurationManagerSettings);
      bind(Thresholds.class)
          .toInstance(
              new Thresholds(options.app.maxTasksPerJob,
              options.app.maxUpdateInstanceFailures));
  
      // Enable intercepted method timings and context classloader repair.
      TimedInterceptor.bind(binder());
      GuiceUtils.bindJNIContextClassLoader(binder(), Scheduler.class);
      GuiceUtils.bindExceptionTrap(binder(), Scheduler.class);
  
      bind(Clock.class).toInstance(Clock.SYSTEM_CLOCK);
      // Filter layering: notifier filter -> base impl
      bind(SchedulingFilter.class).to(NotifyingSchedulingFilter.class);
      bind(NotifyingSchedulingFilter.class).in(Singleton.class);
      bind(SchedulingFilter.class)
          .annotatedWith(NotifyingSchedulingFilter.NotifyDelegate.class)
          .to(SchedulingFilterImpl.class);
      bind(SchedulingFilterImpl.class).in(Singleton.class);
  
      install(new PubsubEventModule());
      install(new AsyncModule(options.async));
      install(new OfferManagerModule(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(options));
      install(new PreemptorModule(options));
      install(new SchedulerDriverModule(kind));
      install(new SchedulerServicesModule());
      install(new SchedulerModule(options.scheduler));
      install(new StateModule(options));
      install(new SlaModule(options.sla));
      install(new UpdaterModule(options.updater));
      install(new MaintenanceModule(options.maintenance));
      bind(StatsProvider.class).toInstance(Stats.STATS_PROVIDER);
    }
  }
  