Merge "Allow to run Gerrit in replica mode"
diff --git a/operator/README.md b/operator/README.md
index 6ea2313..beeb79a 100644
--- a/operator/README.md
+++ b/operator/README.md
@@ -331,6 +331,9 @@
     ## Port used for SSH requests (optional; if unset, SSH access is disabled)
     sshPort: null
 
+  ## Whether to run Gerrit in replica mode
+  isReplica: true
+
   ## Configuration concerning the Gerrit site
   site:
     ## Size of the volume used to persist not otherwise persisted site components
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritConfigMapDependentResource.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritConfigMapDependentResource.java
index 26bbde2..829eab8 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritConfigMapDependentResource.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritConfigMapDependentResource.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.k8s.operator.cluster.GerritCluster;
 import com.google.gerrit.k8s.operator.cluster.GerritIngress;
+import com.google.gerrit.k8s.operator.gerrit.GerritSpec.GerritMode;
 import com.google.gerrit.k8s.operator.gerrit.config.GerritConfigBuilder;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
@@ -58,6 +59,8 @@
     GerritConfigBuilder gerritConfigBuilder =
         new GerritConfigBuilder().withConfig(configFiles.get("gerrit.config"));
 
+    gerritConfigBuilder.useReplicaMode(gerrit.getSpec().getMode().equals(GerritMode.REPLICA));
+
     if (gerritCluster.getSpec().getIngress().isEnabled()) {
       gerritConfigBuilder.withUrl(
           GerritIngress.getFullHostname(ServiceDependentResource.getName(gerrit), gerritCluster));
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritSpec.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritSpec.java
index dbda4af..56643b8 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritSpec.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritSpec.java
@@ -48,6 +48,7 @@
   private List<GerritPlugin> plugins = List.of();
   private Map<String, String> configFiles = Map.of();
   private Set<String> secrets = Set.of();
+  private GerritMode mode = GerritMode.PRIMARY;
 
   public String getCluster() {
     return cluster;
@@ -185,4 +186,17 @@
   public void setSecrets(Set<String> secrets) {
     this.secrets = secrets;
   }
+
+  public GerritMode getMode() {
+    return mode;
+  }
+
+  public void setMode(GerritMode mode) {
+    this.mode = mode;
+  }
+
+  public enum GerritMode {
+    PRIMARY,
+    REPLICA
+  }
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/GerritConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/GerritConfigBuilder.java
index ceb8f00..a35fe83 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/GerritConfigBuilder.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/GerritConfigBuilder.java
@@ -62,6 +62,11 @@
     return this;
   }
 
+  public GerritConfigBuilder useReplicaMode(boolean isReplica) {
+    this.requiredOptions.add(new RequiredOption<Boolean>("container", "replica", isReplica));
+    return this;
+  }
+
   public Config build() {
     GerritConfigValidator configValidator = new GerritConfigValidator(requiredOptions);
     configValidator.check(cfg);
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/GerritE2E.java b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/GerritE2E.java
index 2e0b2f9..670368a 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/GerritE2E.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/GerritE2E.java
@@ -30,6 +30,7 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.k8s.operator.gerrit.GerritSpec.GerritMode;
 import com.google.gerrit.k8s.operator.test.AbstractGerritOperatorE2ETest;
 import com.urswolfer.gerrit.client.rest.GerritAuthData;
 import com.urswolfer.gerrit.client.rest.GerritRestApiFactory;
@@ -84,78 +85,16 @@
           + "  javaOptions = -Xmx4g";
 
   @Test
-  void testGerritStatefulSetCreated() throws Exception {
+  void testPrimaryGerritIsCreated() throws Exception {
     gerritCluster.setIngressEnabled(true);
 
     Secret secureConfig = createSecureConfig();
     client.resource(secureConfig).createOrReplace();
 
-    Gerrit gerrit = createGerritCR();
+    Gerrit gerrit = createGerritCR(GerritMode.PRIMARY);
     client.resource(gerrit).createOrReplace();
 
-    logger.atInfo().log("Waiting max 1 minutes for the configmaps to be created.");
-    await()
-        .atMost(1, MINUTES)
-        .untilAsserted(
-            () -> {
-              assertThat(
-                  client
-                      .configMaps()
-                      .inNamespace(operator.getNamespace())
-                      .withName(GerritConfigMapDependentResource.getName(gerrit))
-                      .get(),
-                  is(notNullValue()));
-              assertThat(
-                  client
-                      .configMaps()
-                      .inNamespace(operator.getNamespace())
-                      .withName(GerritInitConfigMapDependentResource.getName(gerrit))
-                      .get(),
-                  is(notNullValue()));
-            });
-
-    logger.atInfo().log("Waiting max 1 minutes for the Gerrit StatefulSet to be created.");
-    await()
-        .atMost(1, MINUTES)
-        .untilAsserted(
-            () -> {
-              assertThat(
-                  client
-                      .apps()
-                      .statefulSets()
-                      .inNamespace(operator.getNamespace())
-                      .withName(gerrit.getMetadata().getName())
-                      .get(),
-                  is(notNullValue()));
-            });
-
-    logger.atInfo().log("Waiting max 1 minutes for the Gerrit Service to be created.");
-    await()
-        .atMost(1, MINUTES)
-        .untilAsserted(
-            () -> {
-              assertThat(
-                  client
-                      .services()
-                      .inNamespace(operator.getNamespace())
-                      .withName(ServiceDependentResource.getName(gerrit))
-                      .get(),
-                  is(notNullValue()));
-            });
-
-    logger.atInfo().log("Waiting max 2 minutes for the Gerrit StatefulSet to be ready.");
-    await()
-        .atMost(2, MINUTES)
-        .untilAsserted(
-            () -> {
-              assertTrue(
-                  client
-                      .apps()
-                      .statefulSets()
-                      .inNamespace(operator.getNamespace())
-                      .withName(gerrit.getMetadata().getName())
-                      .isReady());
-            });
+    waitForGerritReadiness(gerrit);
 
     logger.atInfo().log("Waiting max 2 minutes for the Ingress to have an external IP.");
     await()
@@ -197,11 +136,31 @@
   }
 
   @Test
+  void testGerritReplicaIsCreated() throws Exception {
+    Secret secureConfig = createSecureConfig();
+    client.resource(secureConfig).createOrReplace();
+
+    Gerrit gerrit = createGerritCR(GerritMode.REPLICA);
+    client.resource(gerrit).createOrReplace();
+
+    waitForGerritReadiness(gerrit);
+
+    assertTrue(
+        client
+            .pods()
+            .inNamespace(operator.getNamespace())
+            .withName(gerrit.getMetadata().getName() + "-0")
+            .inContainer("gerrit")
+            .getLog()
+            .contains("Gerrit Code Review [replica]"));
+  }
+
+  @Test
   void testRestartHandlingOnConfigChange() {
     Secret secureConfig = createSecureConfig();
     client.resource(secureConfig).createOrReplace();
 
-    Gerrit gerrit = createGerritCR();
+    Gerrit gerrit = createGerritCR(GerritMode.PRIMARY);
     client.resource(gerrit).createOrReplace();
 
     logger.atInfo().log("Waiting max 2 minutes for the Gerrit StatefulSet to be ready.");
@@ -270,7 +229,7 @@
             });
   }
 
-  private Gerrit createGerritCR() {
+  private Gerrit createGerritCR(GerritMode mode) {
     Gerrit gerrit = new Gerrit();
     ObjectMeta gerritMeta =
         new ObjectMetaBuilder().withName("gerrit").withNamespace(operator.getNamespace()).build();
@@ -279,6 +238,7 @@
     gerritSpec.setCluster(CLUSTER_NAME);
     GerritSite site = new GerritSite();
     site.setSize(new Quantity("1Gi"));
+    gerritSpec.setMode(mode);
     gerritSpec.setSite(site);
     gerritSpec.setResources(
         new ResourceRequirementsBuilder()
@@ -306,4 +266,70 @@
                             .getBytes())))
         .build();
   }
+
+  private void waitForGerritReadiness(Gerrit gerrit) {
+    logger.atInfo().log("Waiting max 1 minutes for the configmaps to be created.");
+    await()
+        .atMost(1, MINUTES)
+        .untilAsserted(
+            () -> {
+              assertThat(
+                  client
+                      .configMaps()
+                      .inNamespace(operator.getNamespace())
+                      .withName(GerritConfigMapDependentResource.getName(gerrit))
+                      .get(),
+                  is(notNullValue()));
+              assertThat(
+                  client
+                      .configMaps()
+                      .inNamespace(operator.getNamespace())
+                      .withName(GerritInitConfigMapDependentResource.getName(gerrit))
+                      .get(),
+                  is(notNullValue()));
+            });
+
+    logger.atInfo().log("Waiting max 1 minutes for the Gerrit StatefulSet to be created.");
+    await()
+        .atMost(1, MINUTES)
+        .untilAsserted(
+            () -> {
+              assertThat(
+                  client
+                      .apps()
+                      .statefulSets()
+                      .inNamespace(operator.getNamespace())
+                      .withName(gerrit.getMetadata().getName())
+                      .get(),
+                  is(notNullValue()));
+            });
+
+    logger.atInfo().log("Waiting max 1 minutes for the Gerrit Service to be created.");
+    await()
+        .atMost(1, MINUTES)
+        .untilAsserted(
+            () -> {
+              assertThat(
+                  client
+                      .services()
+                      .inNamespace(operator.getNamespace())
+                      .withName(ServiceDependentResource.getName(gerrit))
+                      .get(),
+                  is(notNullValue()));
+            });
+
+    logger.atInfo().log("Waiting max 2 minutes for the Gerrit StatefulSet to be ready.");
+    await()
+        .atMost(2, MINUTES)
+        .untilAsserted(
+            () -> {
+              assertTrue(
+                  client
+                      .apps()
+                      .statefulSets()
+                      .inNamespace(operator.getNamespace())
+                      .withName(gerrit.getMetadata().getName())
+                      .isReady());
+            });
+  }
 }