Merge changes I8dc66d3e,I71ae592c

* changes:
  Update operator docs
  [Operator] As part of build copy updated CRDs to helm-chart
diff --git a/Documentation/operator-api-reference.md b/Documentation/operator-api-reference.md
index 1dbb153..5b05bf4 100644
--- a/Documentation/operator-api-reference.md
+++ b/Documentation/operator-api-reference.md
@@ -711,7 +711,7 @@
 | `storage` | [`GerritStorageConfig`](#gerritstorageconfig) | Storage used by Gerrit instances |
 | `containerImages` | [`ContainerImageConfig`](#containerimageconfig) | Container images used inside GerritCluster |
 | `ingress` | [`GerritClusterIngressConfig`](#gerritclusteringressconfig) | Ingress traffic handling in GerritCluster |
-| `gerrits` | [`GerritTemplate`](#gerrittemplate)-Array | A list of Gerrit instances to be installed in the GerritCluster. Only a single primary Gerrit is permitted. |
+| `gerrits` | [`GerritTemplate`](#gerrittemplate)-Array | A list of Gerrit instances to be installed in the GerritCluster. Only a single primary Gerrit and a single Gerrit Replica is permitted. |
 | `receiver` | [`ReceiverTemplate`](#receivertemplate) | A Receiver instance to be installed in the GerritCluster. |
 
 ## GerritClusterStatus
@@ -800,7 +800,7 @@
 | Value | Description|
 |---|---|
 | `NONE` | No ingress provider will be configured |
-| `INGRESS` | An [`Ingress`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#ingress-v1-networking-k8s-io) will be provisioned |
+| `INGRESS` | An [`Ingress`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#ingress-v1-networking-k8s-io) will be provisioned. Only the [Nginx-Ingress-Controller](https://docs.nginx.com/nginx-ingress-controller/) is supported. |
 | `ISTIO` | [ISTIO](https://istio.io/latest/) will be configured to add the GerritCluster to the ServiceMesh |
 
 ## GerritIngressTlsConfig
diff --git a/Documentation/operator.md b/Documentation/operator.md
index dab36d3..61b2cf7 100644
--- a/Documentation/operator.md
+++ b/Documentation/operator.md
@@ -5,13 +5,20 @@
    2. [Versioning](#versioning)
    3. [Publish](#publish)
    4. [Tests](#tests)
-   5. [Deploy](#deploy)
-   6. [CustomResources](#customresources)
+   5. [Prerequisites](#prerequisites)
+      1. [Shared Storage (ReadWriteMany)](#shared-storage-readwritemany)
+      2. [Ingress provider](#ingress-provider)
+   6. [Deploy](#deploy)
+      1. [Using helm charts](#using-helm-charts)
+         1. [gerrit-operator-crds](#gerrit-operator-crds)
+         2. [gerrit-operator](#gerrit-operator-1)
+      2. [Without the helm charts](#without-the-helm-charts)
+   7. [CustomResources](#customresources)
       1. [GerritCluster](#gerritcluster)
       2. [Gerrit](#gerrit)
       3. [GitGarbageCollection](#gitgarbagecollection)
       4. [Receiver](#receiver)
-   7. [Configuration of Gerrit](#configuration-of-gerrit)
+   8. [Configuration of Gerrit](#configuration-of-gerrit)
 
 ## Build
 
@@ -124,6 +131,47 @@
 Note, that running the E2E tests will also involve pushing the container image
 to the repository configured in the properties file.
 
+## Prerequisites
+
+Deploying Gerrit using the operator requires some additional prerequisites to be
+fulfilled:
+
+### Shared Storage (ReadWriteMany)
+
+Gerrit instances share the repositories and other data using shared volumes. Thus,
+a StorageClass and a suitable provisioner have to be available in the cluster.
+An example for such a provisioner would be the
+[NFS-subdir-external-provisioner](https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner).
+
+### Ingress provider
+
+The Gerrit Operator will also set up network routing rules and an ingress point
+for the Gerrit instances it manages. The network routing rules ensure that requests
+will be routed to the intended GerritCluster component, e.g. in case a primary
+Gerrit and a Gerrit Replica exist in the cluster, git fetch/clone requests will
+be sent to the Gerrit Replica and all other requests to the primary Gerrit.
+The Gerrit Operator currently supports the following Ingress providers, which
+can be configured for each
+[GerritCluster](operator-api-reference.md#gerritclusteringressconfig):
+
+- **NONE**
+
+  The operator will install no Ingress components. Services will still be available.
+  No prerequisites are required for this case.
+
+- **INGRESS**
+
+  The operator will install an Ingress. Currently only the
+  [Nginx-Ingress-Controller](https://docs.nginx.com/nginx-ingress-controller/) is
+  supported, which will have to be installed in the cluster and has to be configured
+  to [allow snippet configurations](https://docs.nginx.com/nginx-ingress-controller/configuration/ingress-resources/advanced-configuration-with-snippets/).
+  An example of a working deployment can be found [here](../supplements/test-cluster/ingress/).
+
+- **ISTIO**
+
+  The operator supports the use of [Istio](https://istio.io/) as a service mesh.
+  An example on how to set up Istio can be found [here](../istio/gerrit.profile.yaml).
+
 ## Deploy
 You will need to have admin privileges for your k8s cluster in order to be able
 to deploy the following resources.
@@ -241,6 +289,12 @@
 The same holds true for the [Receiver](#receiver) CustomResource, which without
 a Gerrit instance using the same site provides little value.
 
+For now, only a single Gerrit CustomResource using each [mode](./operator-api-reference.md#gerritmode)
+can be deployed in a GerritCluster, e.g. one primary Gerrit and one Gerrit Replica.
+The reason for that is, that there is currently no sharding implemented and thus
+multiple deployments don't bring any more value than just scaling the existing
+deployment. Instead of a primary Gerrit also a Receiver can be installed.
+
 ### Gerrit
 
 The Gerrit CustomResource deploys a Gerrit, which can run in multiple modes.
@@ -277,7 +331,7 @@
 
 The Receiver-CustomResource installs a Deployment running Apache with a git-http-
 backend that is meant to receive pushes performed by Gerrit's replication plugin.
-It is meant to be installed into a GerritCluster that does not include a primary
+It can only be installed into a GerritCluster that does not include a primary
 Gerrit, but only Gerrit Replicas.
 
 The Receiver-CustomResource is mainly meant to be used by the GerritCluster-reconciler
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/GerritClusterIngress.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/GerritClusterIngress.java
index 5cf4e31..4940021 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/GerritClusterIngress.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/GerritClusterIngress.java
@@ -14,11 +14,14 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritService;
 import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
+import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplate;
+import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplateSpec.GerritMode;
 import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverService;
-import com.google.gerrit.k8s.operator.receiver.model.Receiver;
 import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath;
 import io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPathBuilder;
 import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
@@ -34,11 +37,15 @@
 import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
 @KubernetesDependent
 public class GerritClusterIngress extends CRUDKubernetesDependentResource<Ingress, GerritCluster> {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+  private static final String UPLOAD_PACK_URL_PATTERN = "/.*/git-upload-pack";
   public static final String INGRESS_NAME = "gerrit-ingress";
 
   public GerritClusterIngress() {
@@ -47,109 +54,131 @@
 
   @Override
   protected Ingress desired(GerritCluster gerritCluster, Context<GerritCluster> context) {
-    List<Gerrit> gerrits =
-        gerritCluster.getSpec().getGerrits().stream()
-            .map(g -> g.toGerrit(gerritCluster))
-            .collect(Collectors.toList());
-
-    List<String> hosts = new ArrayList<>();
-    List<IngressRule> ingressRules = new ArrayList<>();
-
-    if (gerritCluster.getSpec().getReceiver() != null) {
-      Receiver receiver = gerritCluster.getSpec().getReceiver().toReceiver(gerritCluster);
-      ingressRules.add(getReceiverIngressRule(gerritCluster, receiver));
-      hosts.add(
-          gerritCluster
-              .getSpec()
-              .getIngress()
-              .getFullHostnameForService(receiver.getMetadata().getName()));
-    }
-
-    ingressRules.addAll(getGerritIngressRules(gerritCluster, gerrits));
-    for (Gerrit gerrit : gerrits) {
-      hosts.add(
-          gerritCluster
-              .getSpec()
-              .getIngress()
-              .getFullHostnameForService(gerrit.getMetadata().getName()));
-    }
-
     Ingress gerritIngress =
         new IngressBuilder()
             .withNewMetadata()
             .withName("gerrit-ingress")
             .withNamespace(gerritCluster.getMetadata().getNamespace())
             .withLabels(gerritCluster.getLabels("gerrit-ingress", this.getClass().getSimpleName()))
-            .withAnnotations(gerritCluster.getSpec().getIngress().getAnnotations())
+            .withAnnotations(getAnnotations(gerritCluster))
             .endMetadata()
             .withNewSpec()
-            .withTls(getIngressTLS(gerritCluster, hosts))
-            .withRules(ingressRules)
+            .withTls(getIngressTLS(gerritCluster))
+            .withRules(getIngressRule(gerritCluster))
             .endSpec()
             .build();
 
     return gerritIngress;
   }
 
-  private IngressTLS getIngressTLS(GerritCluster gerritCluster, List<String> hosts) {
+  private Map<String, String> getAnnotations(GerritCluster gerritCluster) {
+    Map<String, String> annotations = gerritCluster.getSpec().getIngress().getAnnotations();
+    annotations.put("nginx.ingress.kubernetes.io/use-regex", "true");
+    annotations.put("kubernetes.io/ingress.class", "nginx");
+
+    Optional<GerritTemplate> gerritReplica =
+        gerritCluster.getSpec().getGerrits().stream()
+            .filter(g -> g.getSpec().getMode().equals(GerritMode.REPLICA))
+            .findFirst();
+    if (gerritReplica.isPresent()) {
+      String svcName = GerritService.getName(gerritReplica.get());
+      StringBuilder configSnippet = new StringBuilder();
+      configSnippet.append("if ($args ~ service=git-upload-pack){");
+      configSnippet.append("\n");
+      configSnippet.append("  set $proxy_upstream_name \"");
+      configSnippet.append(gerritCluster.getMetadata().getNamespace());
+      configSnippet.append("-");
+      configSnippet.append(svcName);
+      configSnippet.append("-");
+      configSnippet.append(GerritService.HTTP_PORT_NAME);
+      configSnippet.append("\";\n");
+      configSnippet.append("  set $proxy_host $proxy_upstream_name;");
+      configSnippet.append("\n");
+      configSnippet.append("  set $service_name \"");
+      configSnippet.append(svcName);
+      configSnippet.append("\";\n}");
+      annotations.put(
+          "nginx.ingress.kubernetes.io/configuration-snippet", configSnippet.toString());
+    }
+    return annotations;
+  }
+
+  private IngressTLS getIngressTLS(GerritCluster gerritCluster) {
     if (gerritCluster.getSpec().getIngress().getTls().isEnabled()) {
       return new IngressTLSBuilder()
-          .withHosts(hosts)
+          .withHosts(gerritCluster.getSpec().getIngress().getHost())
           .withSecretName(gerritCluster.getSpec().getIngress().getTls().getSecret())
           .build();
     }
     return new IngressTLS();
   }
 
-  private List<IngressRule> getGerritIngressRules(
-      GerritCluster gerritCluster, List<Gerrit> gerrits) {
-    List<IngressRule> ingressRules = new ArrayList<>();
+  private IngressRule getIngressRule(GerritCluster gerritCluster) {
+    List<HTTPIngressPath> ingressPaths = getGerritHTTPIngressPaths(gerritCluster);
+    ingressPaths.addAll(getReceiverIngressPaths(gerritCluster));
 
-    for (Gerrit gerrit : gerrits) {
-      String gerritSvcName = GerritService.getName(gerrit);
-      ingressRules.add(
-          new IngressRuleBuilder()
-              .withHost(
-                  gerritCluster.getSpec().getIngress().getFullHostnameForService(gerritSvcName))
-              .withNewHttp()
-              .withPaths(getGerritHTTPIngressPath(gerritSvcName))
-              .endHttp()
-              .build());
-    }
-
-    return ingressRules;
-  }
-
-  private IngressRule getReceiverIngressRule(GerritCluster gerritCluster, Receiver receiver) {
     return new IngressRuleBuilder()
-        .withHost(
-            gerritCluster
-                .getSpec()
-                .getIngress()
-                .getFullHostnameForService(receiver.getMetadata().getName()))
+        .withHost(gerritCluster.getSpec().getIngress().getHost())
         .withNewHttp()
-        .withPaths(getReceiverIngressPaths(ReceiverService.getName(receiver)))
+        .withPaths(ingressPaths)
         .endHttp()
         .build();
   }
 
-  public HTTPIngressPath getGerritHTTPIngressPath(String svcName) {
+  private List<HTTPIngressPath> getGerritHTTPIngressPaths(GerritCluster gerritCluster) {
     ServiceBackendPort port =
         new ServiceBackendPortBuilder().withName(GerritService.HTTP_PORT_NAME).build();
 
-    return new HTTPIngressPathBuilder()
-        .withPathType("Prefix")
-        .withPath("/")
-        .withNewBackend()
-        .withNewService()
-        .withName(svcName)
-        .withPort(port)
-        .endService()
-        .endBackend()
-        .build();
+    ArrayListMultimap<GerritMode, HTTPIngressPath> pathsByMode = ArrayListMultimap.create();
+    List<Gerrit> gerrits =
+        gerritCluster.getSpec().getGerrits().stream()
+            .map(g -> g.toGerrit(gerritCluster))
+            .collect(Collectors.toList());
+    for (Gerrit gerrit : gerrits) {
+      switch (gerrit.getSpec().getMode()) {
+        case REPLICA:
+          pathsByMode.put(
+              GerritMode.REPLICA,
+              new HTTPIngressPathBuilder()
+                  .withPathType("Prefix")
+                  .withPath(UPLOAD_PACK_URL_PATTERN)
+                  .withNewBackend()
+                  .withNewService()
+                  .withName(GerritService.getName(gerrit))
+                  .withPort(port)
+                  .endService()
+                  .endBackend()
+                  .build());
+          break;
+        case PRIMARY:
+          pathsByMode.put(
+              GerritMode.PRIMARY,
+              new HTTPIngressPathBuilder()
+                  .withPathType("Prefix")
+                  .withPath("/")
+                  .withNewBackend()
+                  .withNewService()
+                  .withName(GerritService.getName(gerrit))
+                  .withPort(port)
+                  .endService()
+                  .endBackend()
+                  .build());
+          break;
+        default:
+          logger.atFine().log(
+              "Encountered unknown Gerrit mode when reconciling Ingress: %s",
+              gerrit.getSpec().getMode());
+      }
+    }
+
+    List<HTTPIngressPath> paths = new ArrayList<>();
+    paths.addAll(pathsByMode.get(GerritMode.REPLICA));
+    paths.addAll(pathsByMode.get(GerritMode.PRIMARY));
+    return paths;
   }
 
-  public List<HTTPIngressPath> getReceiverIngressPaths(String svcName) {
+  private List<HTTPIngressPath> getReceiverIngressPaths(GerritCluster gerritCluster) {
+    String svcName = ReceiverService.getName(gerritCluster.getSpec().getReceiver());
     List<HTTPIngressPath> paths = new ArrayList<>();
     ServiceBackendPort port =
         new ServiceBackendPortBuilder().withName(ReceiverService.HTTP_PORT_NAME).build();
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterIngressConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterIngressConfig.java
index cb82733..5da60bb 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterIngressConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterIngressConfig.java
@@ -79,20 +79,4 @@
   public static String getFullHostnameForService(String svcName, String ingressHost) {
     return String.format("%s.%s", svcName, ingressHost);
   }
-
-  @JsonIgnore
-  public String getUrl(String svcName) {
-    return getUrl(svcName, getTls().isEnabled(), getHost(), getType());
-  }
-
-  @JsonIgnore
-  public static String getUrl(
-      String svcName, boolean tlsEnabled, String ingressHost, IngressType ingressType) {
-    String protocol = tlsEnabled ? "https" : "http";
-    String hostname =
-        ingressType == IngressType.ISTIO
-            ? ingressHost
-            : getFullHostnameForService(svcName, ingressHost);
-    return String.format("%s://%s", protocol, hostname);
-  }
 }
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 849a995..d6f5ca9 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
@@ -60,7 +60,7 @@
     boolean ingressEnabled = gerrit.getSpec().getIngress().getType() != IngressType.NONE;
 
     if (ingressEnabled) {
-      withUrl(gerrit.getSpec().getIngress().getUrl(GerritService.getName(gerrit)));
+      withUrl(gerrit.getSpec().getIngress().getUrl());
     } else {
       withUrl(GerritService.getUrl(gerrit));
     }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritService.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritService.java
index 53c2adf..65ea5e4 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritService.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritService.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
 import com.google.gerrit.k8s.operator.gerrit.GerritReconciler;
 import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
+import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplate;
 import io.fabric8.kubernetes.api.model.Service;
 import io.fabric8.kubernetes.api.model.ServiceBuilder;
 import io.fabric8.kubernetes.api.model.ServicePort;
@@ -60,6 +61,10 @@
     return gerrit.getMetadata().getName();
   }
 
+  public static String getName(GerritTemplate gerrit) {
+    return gerrit.getMetadata().getName();
+  }
+
   public static String getHostname(Gerrit gerrit) {
     return getHostname(gerrit.getMetadata().getName(), gerrit.getMetadata().getNamespace());
   }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverService.java b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverService.java
index 7ff3b3c..e5d0cb3 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverService.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverService.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
 import com.google.gerrit.k8s.operator.receiver.ReceiverReconciler;
 import com.google.gerrit.k8s.operator.receiver.model.Receiver;
+import com.google.gerrit.k8s.operator.receiver.model.ReceiverTemplate;
 import io.fabric8.kubernetes.api.model.Service;
 import io.fabric8.kubernetes.api.model.ServiceBuilder;
 import io.fabric8.kubernetes.api.model.ServicePort;
@@ -59,6 +60,10 @@
     return receiver.getMetadata().getName();
   }
 
+  public static String getName(ReceiverTemplate receiver) {
+    return receiver.getMetadata().getName();
+  }
+
   public static Map<String, String> getLabels(Receiver receiver) {
     return GerritCluster.getLabels(
         receiver.getMetadata().getName(),
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhook.java b/operator/src/main/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhook.java
index da4d5a7..a2229ae 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhook.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhook.java
@@ -45,6 +45,20 @@
           .build();
     }
 
+    if (primaryGerritAndReceiverInCluster(gerritCluster)) {
+      return new StatusBuilder()
+          .withCode(HttpServletResponse.SC_CONFLICT)
+          .withMessage("A primary Gerrit cannot be in the same Gerrit Cluster as a Receiver.")
+          .build();
+    }
+
+    if (multipleGerritReplicaInCluster(gerritCluster)) {
+      return new StatusBuilder()
+          .withCode(HttpServletResponse.SC_CONFLICT)
+          .withMessage("Only a single Gerrit Replica is allowed per Gerrit Cluster.")
+          .build();
+    }
+
     GerritAdmissionWebhook gerritAdmission = new GerritAdmissionWebhook();
     for (GerritTemplate gerrit : gerritCluster.getSpec().getGerrits()) {
       Status status = gerritAdmission.validate(gerrit.toGerrit(gerritCluster));
@@ -63,6 +77,19 @@
         > 1;
   }
 
+  private boolean primaryGerritAndReceiverInCluster(GerritCluster gerritCluster) {
+    return gerritCluster.getSpec().getGerrits().stream()
+            .anyMatch(g -> g.getSpec().getMode() == GerritMode.PRIMARY)
+        && gerritCluster.getSpec().getReceiver() != null;
+  }
+
+  private boolean multipleGerritReplicaInCluster(GerritCluster gerritCluster) {
+    return gerritCluster.getSpec().getGerrits().stream()
+            .filter(g -> g.getSpec().getMode() == GerritMode.REPLICA)
+            .count()
+        > 1;
+  }
+
   @Override
   public String getName() {
     return "gerritcluster";
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/IngressConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/IngressConfig.java
index 6275d39..7b6b0c3 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/IngressConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/IngressConfig.java
@@ -52,10 +52,9 @@
   }
 
   @JsonIgnore
-  public String getUrl(String svcName) {
+  public String getUrl() {
     String protocol = isTlsEnabled() ? "https" : "http";
-    String hostname =
-        getType() == IngressType.ISTIO ? getHost() : getFullHostnameForService(svcName);
+    String hostname = getHost();
     return String.format("%s://%s", protocol, hostname);
   }
 }
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritE2E.java b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritE2E.java
index 3188bd9..8e92152 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritE2E.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritE2E.java
@@ -123,37 +123,6 @@
   }
 
   @Test
-  void testMultipleGerritReplicaAreCreated() throws Exception {
-    String gerritName = "gerrit-replica-1";
-    TestGerrit gerrit =
-        new TestGerrit(client, testProps, GerritMode.REPLICA, gerritName, operator.getNamespace());
-    gerritCluster.addGerrit(gerrit.createGerritTemplate());
-    String gerritName2 = "gerrit-replica-2";
-    TestGerrit gerrit2 =
-        new TestGerrit(client, testProps, GerritMode.REPLICA, gerritName2, operator.getNamespace());
-    gerritCluster.addGerrit(gerrit2.createGerritTemplate());
-    gerritCluster.deploy();
-
-    assertTrue(
-        client
-            .pods()
-            .inNamespace(operator.getNamespace())
-            .withName(gerritName + "-0")
-            .inContainer("gerrit")
-            .getLog()
-            .contains("Gerrit Code Review [replica]"));
-
-    assertTrue(
-        client
-            .pods()
-            .inNamespace(operator.getNamespace())
-            .withName(gerritName2 + "-0")
-            .inContainer("gerrit")
-            .getLog()
-            .contains("Gerrit Code Review [replica]"));
-  }
-
-  @Test
   void testGerritReplicaAndPrimaryGerritAreCreated() throws Exception {
     String primaryGerritName = "gerrit";
     TestGerrit primaryGerrit =
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhookTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhookTest.java
index b59540f..c97ab7a 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhookTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhookTest.java
@@ -24,9 +24,14 @@
 import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplate;
 import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplateSpec.GerritMode;
 import com.google.gerrit.k8s.operator.receiver.model.Receiver;
+import com.google.gerrit.k8s.operator.receiver.model.ReceiverTemplate;
+import com.google.gerrit.k8s.operator.receiver.model.ReceiverTemplateSpec;
+import com.google.gerrit.k8s.operator.test.ReceiverUtil;
 import com.google.gerrit.k8s.operator.test.TestAdmissionWebhookServer;
 import com.google.gerrit.k8s.operator.test.TestGerrit;
 import com.google.gerrit.k8s.operator.test.TestGerritCluster;
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
 import io.fabric8.kubernetes.api.model.admission.v1.AdmissionRequest;
 import io.fabric8.kubernetes.api.model.admission.v1.AdmissionReview;
 import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
@@ -103,6 +108,36 @@
   }
 
   @Test
+  public void testPrimaryGerritAndReceiverAreNotAcceptedInSameGerritCluster() throws Exception {
+    Config cfg = new Config();
+    cfg.fromText(TestGerrit.DEFAULT_GERRIT_CONFIG);
+    GerritTemplate gerrit = TestGerrit.createGerritTemplate("gerrit1", GerritMode.PRIMARY, cfg);
+    TestGerritCluster gerritCluster =
+        new TestGerritCluster(kubernetesServer.getClient(), NAMESPACE);
+    gerritCluster.addGerrit(gerrit);
+
+    ReceiverTemplate receiver = new ReceiverTemplate();
+    ObjectMeta receiverMeta = new ObjectMetaBuilder().withName("receiver").build();
+    receiver.setMetadata(receiverMeta);
+    ReceiverTemplateSpec receiverTemplateSpec = new ReceiverTemplateSpec();
+    receiverTemplateSpec.setReplicas(2);
+    receiverTemplateSpec.setCredentialSecretRef(ReceiverUtil.CREDENTIALS_SECRET_NAME);
+    receiver.setSpec(receiverTemplateSpec);
+
+    gerritCluster.setReceiver(receiver);
+    HttpURLConnection http2 = sendAdmissionRequest(gerritCluster.build());
+
+    AdmissionReview response2 =
+        new ObjectMapper().readValue(http2.getInputStream(), AdmissionReview.class);
+
+    assertThat(http2.getResponseCode(), is(equalTo(HttpServletResponse.SC_OK)));
+    assertThat(response2.getResponse().getAllowed(), is(false));
+    assertThat(
+        response2.getResponse().getStatus().getCode(),
+        is(equalTo(HttpServletResponse.SC_CONFLICT)));
+  }
+
+  @Test
   public void testPrimaryAndReplicaAreAcceptedInSameGerritCluster() throws Exception {
     Config cfg = new Config();
     cfg.fromText(TestGerrit.DEFAULT_GERRIT_CONFIG);