All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gerrit.acceptance.GerritServer Maven / Gradle / Ivy

There is a newer version: 3.11.0
Show newest version
// Copyright (C) 2013 The Android Open Source Project
//
// 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 com.google.gerrit.acceptance;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.Objects.requireNonNull;

import com.google.auto.value.AutoValue;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.base.Ticker;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest.TestTicker;
import com.google.gerrit.acceptance.FakeGroupAuditService.FakeGroupAuditServiceModule;
import com.google.gerrit.acceptance.GrantDirectPushPermissionsOnStartup.GrantDirectPushPermissionsOnStartupModule;
import com.google.gerrit.acceptance.ReindexGroupsAtStartup.ReindexGroupsAtStartupModule;
import com.google.gerrit.acceptance.ReindexProjectsAtStartup.ReindexProjectsAtStartupModule;
import com.google.gerrit.acceptance.config.ConfigAnnotationParser;
import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.acceptance.config.GerritConfigs;
import com.google.gerrit.acceptance.config.GerritSystemProperties;
import com.google.gerrit.acceptance.config.GerritSystemProperty;
import com.google.gerrit.acceptance.config.GlobalPluginConfig;
import com.google.gerrit.acceptance.config.GlobalPluginConfigs;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.AccountOperationsImpl;
import com.google.gerrit.acceptance.testsuite.change.ChangeOperations;
import com.google.gerrit.acceptance.testsuite.change.ChangeOperationsImpl;
import com.google.gerrit.acceptance.testsuite.change.PerCommentOperationsImpl;
import com.google.gerrit.acceptance.testsuite.change.PerDraftCommentOperationsImpl;
import com.google.gerrit.acceptance.testsuite.change.PerPatchsetOperationsImpl;
import com.google.gerrit.acceptance.testsuite.change.PerRobotCommentOperationsImpl;
import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.acceptance.testsuite.group.GroupOperationsImpl;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperationsImpl;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperationsImpl;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.index.IndexType;
import com.google.gerrit.index.testing.FakeIndexModule;
import com.google.gerrit.lucene.LuceneIndexModule;
import com.google.gerrit.pgm.Daemon;
import com.google.gerrit.pgm.Init;
import com.google.gerrit.server.config.GerritRuntime;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.experiments.ConfigExperimentFeatures.ConfigExperimentFeaturesModule;
import com.google.gerrit.server.git.receive.AsyncReceiveCommits.AsyncReceiveCommitsModule;
import com.google.gerrit.server.git.validators.CommitValidationListener;
import com.google.gerrit.server.index.AbstractIndexModule;
import com.google.gerrit.server.index.options.AutoFlush;
import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore;
import com.google.gerrit.server.ssh.NoSshModule;
import com.google.gerrit.server.util.ReplicaUtil;
import com.google.gerrit.server.util.SocketUtil;
import com.google.gerrit.server.util.SystemLog;
import com.google.gerrit.testing.FakeAccountPatchReviewStore.FakeAccountPatchReviewStoreModule;
import com.google.gerrit.testing.FakeEmailSender.FakeEmailSenderModule;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.SshMode;
import com.google.gerrit.testing.TestLoggingActivator;
import com.google.inject.AbstractModule;
import com.google.inject.BindingAnnotation;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.multibindings.OptionalBinder;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.util.FS;
import org.junit.rules.TemporaryFolder;

public class GerritServer implements AutoCloseable {
  public static class StartupException extends Exception {
    private static final long serialVersionUID = 1L;

    StartupException(String msg, Throwable cause) {
      super(msg, cause);
    }
  }

  /** Marker on {@link InetSocketAddress} for test SSH server. */
  @Retention(RUNTIME)
  @BindingAnnotation
  public @interface TestSshServerAddress {}

  @AutoValue
  public abstract static class Description {
    public static Description forTestClass(
        org.junit.runner.Description testDesc, String configName) {
      VerifyNoPiiInChangeNotes verifyNoPiiInChangeNotes =
          get(VerifyNoPiiInChangeNotes.class, testDesc.getTestClass());
      return new AutoValue_GerritServer_Description(
          testDesc,
          configName,
          !has(UseLocalDisk.class, testDesc.getTestClass()) && !forceLocalDisk(),
          !has(NoHttpd.class, testDesc.getTestClass()),
          has(Sandboxed.class, testDesc.getTestClass()),
          has(SkipProjectClone.class, testDesc.getTestClass()),
          has(UseSsh.class, testDesc.getTestClass()),
          verifyNoPiiInChangeNotes != null && verifyNoPiiInChangeNotes.value(),
          false, // @UseSystemTime is only valid on methods.
          get(UseClockStep.class, testDesc.getTestClass()),
          get(UseTimezone.class, testDesc.getTestClass()),
          null, // @GerritSystemProperty is only valid on methods.
          null, // @GerritSystemProperties is only valid on methods.
          null, // @GerritConfig is only valid on methods.
          null, // @GerritConfigs is only valid on methods.
          null, // @GlobalPluginConfig is only valid on methods.
          null); // @GlobalPluginConfigs is only valid on methods.
    }

    public static Description forTestMethod(
        org.junit.runner.Description testDesc, String configName) {
      UseClockStep useClockStep = testDesc.getAnnotation(UseClockStep.class);
      if (testDesc.getAnnotation(UseSystemTime.class) == null && useClockStep == null) {
        // Only read the UseClockStep from the class if on method level neither @UseSystemTime nor
        // @UseClockStep have been used.
        // If the method defines @UseSystemTime or @UseClockStep it should overwrite @UseClockStep
        // on class level.
        useClockStep = get(UseClockStep.class, testDesc.getTestClass());
      }
      VerifyNoPiiInChangeNotes verifyNoPiiInChangeNotes =
          testDesc.getAnnotation(VerifyNoPiiInChangeNotes.class);
      if (verifyNoPiiInChangeNotes == null) {
        verifyNoPiiInChangeNotes = get(VerifyNoPiiInChangeNotes.class, testDesc.getTestClass());
      }

      return new AutoValue_GerritServer_Description(
          testDesc,
          configName,
          (testDesc.getAnnotation(UseLocalDisk.class) == null
                  && !has(UseLocalDisk.class, testDesc.getTestClass()))
              && !forceLocalDisk(),
          testDesc.getAnnotation(NoHttpd.class) == null
              && !has(NoHttpd.class, testDesc.getTestClass()),
          testDesc.getAnnotation(Sandboxed.class) != null
              || has(Sandboxed.class, testDesc.getTestClass()),
          testDesc.getAnnotation(SkipProjectClone.class) != null
              || has(SkipProjectClone.class, testDesc.getTestClass()),
          testDesc.getAnnotation(UseSsh.class) != null
              || has(UseSsh.class, testDesc.getTestClass()),
          verifyNoPiiInChangeNotes != null && verifyNoPiiInChangeNotes.value(),
          testDesc.getAnnotation(UseSystemTime.class) != null,
          useClockStep,
          testDesc.getAnnotation(UseTimezone.class) != null
              ? testDesc.getAnnotation(UseTimezone.class)
              : get(UseTimezone.class, testDesc.getTestClass()),
          testDesc.getAnnotation(GerritSystemProperty.class),
          testDesc.getAnnotation(GerritSystemProperties.class),
          testDesc.getAnnotation(GerritConfig.class),
          testDesc.getAnnotation(GerritConfigs.class),
          testDesc.getAnnotation(GlobalPluginConfig.class),
          testDesc.getAnnotation(GlobalPluginConfigs.class));
    }

    private static boolean has(Class annotation, Class clazz) {
      for (; clazz != null; clazz = clazz.getSuperclass()) {
        if (clazz.getAnnotation(annotation) != null) {
          return true;
        }
      }
      return false;
    }

    @Nullable
    private static  T get(Class annotation, Class clazz) {
      for (; clazz != null; clazz = clazz.getSuperclass()) {
        if (clazz.getAnnotation(annotation) != null) {
          return clazz.getAnnotation(annotation);
        }
      }
      return null;
    }

    abstract org.junit.runner.Description testDescription();

    @Nullable
    abstract String configName();

    abstract boolean memory();

    abstract boolean httpd();

    abstract boolean sandboxed();

    public abstract boolean skipProjectClone();

    abstract boolean useSshAnnotation();

    abstract boolean verifyNoPiiInChangeNotes();

    boolean useSsh() {
      return useSshAnnotation() && SshMode.useSsh();
    }

    abstract boolean useSystemTime();

    @Nullable
    abstract UseClockStep useClockStep();

    @Nullable
    abstract UseTimezone useTimezone();

    @Nullable
    abstract GerritSystemProperty systemProperty();

    @Nullable
    abstract GerritSystemProperties systemProperties();

    @Nullable
    abstract GerritConfig config();

    @Nullable
    abstract GerritConfigs configs();

    @Nullable
    abstract GlobalPluginConfig pluginConfig();

    @Nullable
    abstract GlobalPluginConfigs pluginConfigs();

    private void checkValidAnnotations() {
      if (useClockStep() != null && useSystemTime()) {
        throw new IllegalStateException("Use either @UseClockStep or @UseSystemTime, not both");
      }
      if (systemProperties() != null && systemProperty() != null) {
        throw new IllegalStateException(
            "Use either @GerritSystemProperties or @GerritSystemProperty, not both");
      }
      if (configs() != null && config() != null) {
        throw new IllegalStateException("Use either @GerritConfigs or @GerritConfig, not both");
      }
      if (pluginConfigs() != null && pluginConfig() != null) {
        throw new IllegalStateException(
            "Use either @GlobalPluginConfig or @GlobalPluginConfigs, not both");
      }
      if ((pluginConfigs() != null || pluginConfig() != null) && memory()) {
        throw new IllegalStateException("Must use @UseLocalDisk with @GlobalPluginConfig(s)");
      }
    }

    private Config buildConfig(Config baseConfig) {
      if (configs() != null) {
        return ConfigAnnotationParser.parse(baseConfig, configs());
      } else if (config() != null) {
        return ConfigAnnotationParser.parse(baseConfig, config());
      } else {
        return baseConfig;
      }
    }

    private Map buildPluginConfigs() {
      if (pluginConfigs() != null) {
        return ConfigAnnotationParser.parse(pluginConfigs());
      } else if (pluginConfig() != null) {
        return ConfigAnnotationParser.parse(pluginConfig());
      }
      return new HashMap<>();
    }
  }

  private static boolean forceLocalDisk() {
    String value = Strings.nullToEmpty(System.getenv("GERRIT_FORCE_LOCAL_DISK"));
    if (value.isEmpty()) {
      value = Strings.nullToEmpty(System.getProperty("gerrit.forceLocalDisk"));
    }
    switch (value.trim().toLowerCase(Locale.US)) {
      case "1":
      case "yes":
      case "true":
        return true;
      default:
        return false;
    }
  }

  /**
   * Initializes on-disk site but does not start server.
   *
   * @param desc server description
   * @param baseConfig default config values; merged with config from {@code desc} and then written
   *     into {@code site/etc/gerrit.config}.
   * @param site temp directory where site will live.
   */
  public static void init(Description desc, Config baseConfig, Path site) throws Exception {
    checkArgument(!desc.memory(), "can't initialize site path for in-memory test: %s", desc);
    Config cfg = desc.buildConfig(baseConfig);
    Map pluginConfigs = desc.buildPluginConfigs();

    MergeableFileBasedConfig gerritConfig =
        new MergeableFileBasedConfig(
            site.resolve("etc").resolve("gerrit.config").toFile(), FS.DETECTED);
    gerritConfig.load();
    gerritConfig.merge(cfg);
    mergeTestConfig(gerritConfig);
    String configuredIndexBackend = cfg.getString("index", null, "type");
    if (configuredIndexBackend == null) {
      // Propagate index type to pgms that run off of the gerrit.config file on local disk.
      IndexType indexType = IndexType.fromEnvironment().orElseGet(() -> new IndexType("fake"));
      gerritConfig.setString("index", null, "type", indexType.isLucene() ? "lucene" : "fake");
    }
    gerritConfig.save();

    Init init = new Init();
    int rc =
        init.main(
            new String[] {
              "-d", site.toString(), "--batch", "--no-auto-start", "--skip-plugins",
            });
    if (rc != 0) {
      throw new RuntimeException("Couldn't initialize site");
    }

    for (String pluginName : pluginConfigs.keySet()) {
      MergeableFileBasedConfig pluginCfg =
          new MergeableFileBasedConfig(
              site.resolve("etc").resolve(pluginName + ".config").toFile(), FS.DETECTED);
      pluginCfg.load();
      pluginCfg.merge(pluginConfigs.get(pluginName));
      pluginCfg.save();
    }
  }

  /**
   * Initializes new Gerrit site and returns started server.
   *
   * 

A new temporary directory for the site will be created with {@code temporaryFolder}, even in * the server is otherwise configured in-memory. Closing the server stops the daemon but does not * delete the temporary directory.. * * @param temporaryFolder helper rule for creating site directories. * @param desc server description. * @param baseConfig default config values; merged with config from {@code desc}. * @param testSysModule additional Guice module to use. * @param testSshModule additional Guice module to use. * @return started server. */ public static GerritServer initAndStart( TemporaryFolder temporaryFolder, Description desc, Config baseConfig, @Nullable Module testSysModule, @Nullable Module testAuditModule, @Nullable Module testSshModule) throws Exception { Path site = temporaryFolder.newFolder().toPath(); try { if (!desc.memory()) { init(desc, baseConfig, site); } return start(desc, baseConfig, site, testSysModule, testAuditModule, testSshModule, null); } catch (Exception e) { throw e; } } /** * Starts Gerrit server from existing on-disk site. * * @param desc server description. * @param baseConfig default config values; merged with config from {@code desc}. * @param site existing temporary directory for site. Required, but may be empty, for in-memory * servers. For on-disk servers, assumes that {@link #init} was previously called to * initialize this directory. Can be retrieved from the returned instance via {@link * #getSitePath()}. * @param testSysModule optional additional module to add to the system injector. * @param testSshModule optional additional module to add to the ssh injector. * @param inMemoryRepoManager {@link InMemoryRepositoryManager} that should be used if the site is * started in memory * @param additionalArgs additional command-line arguments for the daemon program; only allowed if * the test is not in-memory. * @return started server. */ public static GerritServer start( Description desc, Config baseConfig, Path site, @Nullable Module testSysModule, @Nullable Module testAuditModule, @Nullable Module testSshModule, @Nullable InMemoryRepositoryManager inMemoryRepoManager, String... additionalArgs) throws Exception { checkArgument(site != null, "site is required (even for in-memory server"); desc.checkValidAnnotations(); TestLoggingActivator.configureLogging(); CyclicBarrier serverStarted = new CyclicBarrier(2); Daemon daemon = new Daemon( () -> { try { serverStarted.await(); } catch (InterruptedException | BrokenBarrierException e) { throw new RuntimeException(e); } }, site); daemon.setAccountPatchReviewStoreModuleForTesting(new FakeAccountPatchReviewStoreModule()); daemon.setEmailModuleForTesting(new FakeEmailSenderModule()); daemon.setAuditEventModuleForTesting( MoreObjects.firstNonNull(testAuditModule, new FakeGroupAuditServiceModule())); if (testSysModule != null) { daemon.addAdditionalSysModuleForTesting(testSysModule); } if (testSshModule != null) { daemon.addAdditionalSshModuleForTesting(testSshModule); } daemon.addAdditionalSysModuleForTesting( new AbstractModule() { @Override protected void configure() { bind(CommitValidationListener.class) .annotatedWith(Exports.named("object-visibility-listener")) .to(GitObjectVisibilityChecker.class); } }); daemon.addAdditionalSysModuleForTesting( new AbstractModule() { @Override protected void configure() { super.configure(); // GerritServer isn't restarted between tests. TestTicker allows to replace actual // Ticker in tests without restarting server and transparently for other code. // Alternative option with Provider is less convinient, because it affects how // gerrit code should be written - i.e. Ticker must not be stored in fields and must // always be obtained from the provider. TestTicker testTicker = new TestTicker(); OptionalBinder.newOptionalBinder(binder(), Ticker.class) .setBinding() .toInstance(testTicker); bind(TestTicker.class).toInstance(testTicker); } }); // Assure that HTTPD is enabled if SSHD is not required. If both are disabled the Gerrit server // does not start. Alternatively we could assure that SSHD is enabled if HTTPD is not required, // but this would break the tests at Google, because they don't have support for SSHD. daemon.setEnableHttpd(desc.httpd() || !desc.useSsh()); daemon.setEnableSshd(desc.useSsh()); daemon.setReplica( ReplicaUtil.isReplica(baseConfig) || ReplicaUtil.isReplica(desc.buildConfig(baseConfig))); if (desc.memory()) { checkArgument(additionalArgs.length == 0, "cannot pass args to in-memory server"); AbstractIndexModule testIndexModule = (testSysModule instanceof AbstractIndexModule) ? (AbstractIndexModule) testSysModule : null; return startInMemory(desc, site, baseConfig, daemon, inMemoryRepoManager, testIndexModule); } return startOnDisk(desc, site, daemon, serverStarted, additionalArgs); } private static GerritServer startInMemory( Description desc, Path site, Config baseConfig, Daemon daemon, @Nullable InMemoryRepositoryManager inMemoryRepoManager, @Nullable AbstractIndexModule testIndexModule) throws Exception { Config cfg = desc.buildConfig(baseConfig); mergeTestConfig(cfg); // Set the log4j configuration to an invalid one to prevent system logs // from getting configured and creating log files. System.setProperty(SystemLog.LOG4J_CONFIGURATION, "invalidConfiguration"); cfg.setBoolean("httpd", null, "requestLog", false); cfg.setBoolean("sshd", null, "requestLog", false); cfg.setBoolean("index", "lucene", "testInmemory", true); cfg.setBoolean("index", null, "onlineUpgrade", false); cfg.setString("gitweb", null, "cgi", ""); cfg.setString( "accountPatchReviewDb", null, "url", JdbcAccountPatchReviewStore.TEST_IN_MEMORY_URL); String configuredIndexBackend = cfg.getString("index", null, "type"); IndexType indexType = (configuredIndexBackend != null) ? new IndexType(configuredIndexBackend) : IndexType.fromEnvironment().orElseGet(() -> new IndexType("fake")); daemon.setIndexModule(createIndexModule(indexType, baseConfig, testIndexModule)); daemon.setEnableHttpd(desc.httpd()); daemon.setInMemory(true); daemon.setDatabaseForTesting( ImmutableList.of( new InMemoryTestingDatabaseModule(cfg, site, inMemoryRepoManager), new AbstractModule() { @Override protected void configure() { bind(GerritRuntime.class).toInstance(GerritRuntime.DAEMON); } }, new ConfigExperimentFeaturesModule())); daemon.addAdditionalSysModuleForTesting( new GrantDirectPushPermissionsOnStartupModule(), new ReindexProjectsAtStartupModule(), new ReindexGroupsAtStartupModule()); daemon.start(); return new GerritServer(desc, null, createTestInjector(daemon), Optional.of(daemon), null); } private static AbstractIndexModule createIndexModule( IndexType indexType, Config baseConfig, @Nullable AbstractIndexModule testIndexModule) { if (testIndexModule != null) { return testIndexModule; } return indexType.isLucene() ? LuceneIndexModule.singleVersionAllLatest( 0, ReplicaUtil.isReplica(baseConfig), AutoFlush.ENABLED) : FakeIndexModule.latestVersion(false); } private static GerritServer startOnDisk( Description desc, Path site, Daemon daemon, CyclicBarrier serverStarted, String[] additionalArgs) throws Exception { requireNonNull(site); daemon.addAdditionalSysModuleForTesting( new GrantDirectPushPermissionsOnStartupModule(), new ReindexProjectsAtStartupModule(), new ReindexGroupsAtStartupModule()); ExecutorService daemonService = Executors.newSingleThreadExecutor(); String[] args = Stream.concat( Stream.of( "-d", site.toString(), "--headless", "--console-log", "--show-stack-trace"), Arrays.stream(additionalArgs)) .toArray(String[]::new); @SuppressWarnings("unused") Future possiblyIgnoredError = daemonService.submit( () -> { int rc = daemon.main(args); if (rc != 0) { System.err.println("Failed to start Gerrit daemon"); serverStarted.reset(); } return null; }); try { serverStarted.await(); } catch (BrokenBarrierException e) { daemon.stop(); throw new StartupException("Failed to start Gerrit daemon; see log", e); } System.out.println("Gerrit Server Started"); return new GerritServer( desc, site, createTestInjector(daemon), Optional.of(daemon), daemonService); } private static void mergeTestConfig(Config cfg) { String forceEphemeralPort = String.format("%s:0", getLocalHost().getHostName()); String url = "http://" + forceEphemeralPort + "/"; if (cfg.getString("gerrit", null, "canonicalWebUrl") == null) { cfg.setString("gerrit", null, "canonicalWebUrl", url); } if (cfg.getString("httpd", null, "listenUrl") == null) { cfg.setString("httpd", null, "listenUrl", url); } if (cfg.getString("sshd", null, "listenAddress") == null) { cfg.setString("sshd", null, "listenAddress", forceEphemeralPort); } cfg.setBoolean("sshd", null, "testUseInsecureRandom", true); cfg.unset("cache", null, "directory"); cfg.setString("gerrit", null, "basePath", "git"); cfg.setBoolean("sendemail", null, "enable", true); cfg.setInt("sendemail", null, "threadPoolSize", 0); cfg.setInt("execution", null, "fanOutThreadPoolSize", 0); cfg.setInt("plugins", null, "checkFrequency", 0); cfg.setInt("sshd", null, "threads", 1); cfg.setInt("sshd", null, "commandStartThreads", 1); cfg.setInt("receive", null, "threadPoolSize", 1); cfg.setInt("index", null, "threads", 1); if (cfg.getString("index", null, "mergeabilityComputationBehavior") == null) { cfg.setString("index", null, "mergeabilityComputationBehavior", "NEVER"); } } private static Injector createTestInjector(Daemon daemon) throws Exception { Injector sysInjector = getInjector(daemon, "sysInjector"); Module module = new FactoryModule() { @Override protected void configure() { bindConstant().annotatedWith(SshEnabled.class).to(daemon.getEnableSshd()); bind(AccountCreator.class); bind(AccountOperations.class).to(AccountOperationsImpl.class); bind(GroupOperations.class).to(GroupOperationsImpl.class); bind(ProjectOperations.class).to(ProjectOperationsImpl.class); bind(RequestScopeOperations.class).to(RequestScopeOperationsImpl.class); bind(ChangeOperations.class).to(ChangeOperationsImpl.class); factory(PerPatchsetOperationsImpl.Factory.class); factory(PerCommentOperationsImpl.Factory.class); factory(PerDraftCommentOperationsImpl.Factory.class); factory(PerRobotCommentOperationsImpl.Factory.class); install(new PushOneCommit.Module()); install(InProcessProtocol.module()); install(new NoSshModule()); install(new AsyncReceiveCommitsModule()); factory(ProjectResetter.Builder.Factory.class); } @Provides @Singleton @Nullable @TestSshServerAddress InetSocketAddress getSshAddress(@GerritServerConfig Config cfg) { String addr = cfg.getString("sshd", null, "listenAddress"); // We do not use InitSshd.isOff to avoid coupling GerritServer to the SSH code. return !"off".equalsIgnoreCase(addr) ? SocketUtil.resolve(cfg.getString("sshd", null, "listenAddress"), 0) : null; } }; return sysInjector.createChildInjector(module); } private static Injector getInjector(Object obj, String field) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field f = obj.getClass().getDeclaredField(field); f.setAccessible(true); Object v = f.get(obj); checkArgument(v instanceof Injector, "not an Injector: %s", v); return (Injector) f.get(obj); } private static InetAddress getLocalHost() { return InetAddress.getLoopbackAddress(); } private final Description desc; private final Path sitePath; private final Optional daemon; private final ExecutorService daemonService; protected final Injector testInjector; private final String url; private final Optional httpAddress; protected GerritServer( Description desc, @Nullable Path sitePath, Injector testInjector, Optional daemon, @Nullable ExecutorService daemonService) { this.desc = requireNonNull(desc); this.sitePath = sitePath; this.testInjector = requireNonNull(testInjector); this.daemon = requireNonNull(daemon); this.daemonService = daemonService; Config cfg = testInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)); url = cfg.getString("gerrit", null, "canonicalWebUrl"); if (daemon.isPresent()) { URI uri = URI.create(url); httpAddress = Optional.of(new InetSocketAddress(uri.getHost(), uri.getPort())); } else { httpAddress = Optional.empty(); } } public String getUrl() { return url; } Optional getHttpAddress() { return httpAddress; } public Injector getTestInjector() { return testInjector; } public Optional getHttpdInjector() { return daemon.map(Daemon::getHttpdInjector); } Description getDescription() { return desc; } public static GerritServer restartAsSlave(GerritServer server) throws Exception { checkState(server.desc.sandboxed(), "restarting as slave requires @Sandboxed"); Path site = server.testInjector.getInstance(Key.get(Path.class, SitePath.class)); Config cfg = server.testInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)); cfg.setBoolean("container", null, "replica", true); InMemoryRepositoryManager inMemoryRepoManager = null; if (hasBinding(server.testInjector, InMemoryRepositoryManager.class)) { inMemoryRepoManager = server.testInjector.getInstance(InMemoryRepositoryManager.class); } server.close(); server.daemon.ifPresent(Daemon::stop); return start(server.desc, cfg, site, null, null, null, inMemoryRepoManager); } public static GerritServer restart( GerritServer server, @Nullable Module testSysModule, @Nullable Module testSshModule) throws Exception { checkState(server.desc.sandboxed(), "restarting as slave requires @Sandboxed"); Config cfg = server.testInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)); Path site = server.testInjector.getInstance(Key.get(Path.class, SitePath.class)); InMemoryRepositoryManager inMemoryRepoManager = null; if (hasBinding(server.testInjector, InMemoryRepositoryManager.class)) { inMemoryRepoManager = server.testInjector.getInstance(InMemoryRepositoryManager.class); } server.close(); server.daemon.ifPresent(Daemon::stop); return start(server.desc, cfg, site, testSysModule, null, testSshModule, inMemoryRepoManager); } private static boolean hasBinding(Injector injector, Class clazz) { return injector.getExistingBinding(Key.get(clazz)) != null; } @Override public void close() throws Exception { daemon.ifPresent(d -> d.getLifecycleManager().stop()); if (daemonService != null) { System.out.println("Gerrit Server Shutdown"); daemonService.shutdownNow(); daemonService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); } RepositoryCache.clear(); } public Path getSitePath() { return sitePath; } @Override public String toString() { return MoreObjects.toStringHelper(this).addValue(desc).toString(); } public boolean isReplica() { return daemon.map(Daemon::isReplica).orElse(false); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy