Merge changes I107b5a5c,I34eee609,Ie17ad65b,I90430ed9,I2077f961, ...

* changes:
  [Operator] Update Maven JIB plugin to 3.3.1
  [Operator] Update Java Operator SDK to 4.2.4
  Allow online reindexing
  Improved method name
  Spelling: unready -> not ready
  Check whether reindexing is required independently of init
  [Operator] Improve check whether SSH is enabled
  Update Istio to 1.16.0
  [Operator] Handle sshd.*Address options automatically
  [Operator] Handle cache.directory option automatically
  [Operator] Handle httpd.listenUrl automatically
  [Operator] Update README to mention enforced Gerrit configuration
diff --git a/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/init.py b/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/init.py
index df6878a..9d5ef5a 100755
--- a/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/init.py
+++ b/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/init.py
@@ -36,6 +36,10 @@
         self.gerrit_config = git.GitConfigParser(
             os.path.join(MNT_PATH, "etc/config/gerrit.config")
         )
+        self.is_online_reindex = self.gerrit_config.get_boolean(
+            "index.onlineUpgrade", True
+        )
+        self.force_offline_reindex = False
         self.installed_plugins = self._get_installed_plugins()
 
         self.is_replica = self.gerrit_config.get_boolean("container.replica")
@@ -68,13 +72,17 @@
             installed_version,
             provided_version,
         )
+        installed_minor_version = installed_version.split(".")[0:2]
+        provided_minor_version = provided_version.split(".")[0:2]
+
+        if (
+            not self.is_online_reindex
+            and installed_minor_version != provided_minor_version
+        ):
+            self.force_offline_reindex = True
         return installed_version != provided_version
 
     def _needs_init(self):
-        if self.plugin_installer.plugins_changed:
-            LOG.info("Plugins were installed or updated. Initializing.")
-            return True
-
         installed_war_path = os.path.join(self.site, "bin", "gerrit.war")
         if not os.path.exists(installed_war_path):
             LOG.info("Gerrit is not yet installed. Initializing new site.")
@@ -84,6 +92,10 @@
             LOG.info("Reinitializing site to perform update.")
             return True
 
+        if self.plugin_installer.plugins_changed:
+            LOG.info("Plugins were installed or updated. Initializing.")
+            return True
+
         if self.config.packaged_plugins.difference(self.installed_plugins):
             LOG.info("Reininitializing site to install additional plugins.")
             return True
@@ -164,39 +176,38 @@
 
         self.plugin_installer.execute()
 
-        if not self._needs_init():
-            return
+        if self._needs_init():
+            if self.gerrit_config:
+                LOG.info("Existing gerrit.config found.")
+                dev_option = (
+                    "--dev"
+                    if self.gerrit_config.get("auth.type").lower()
+                    == "development_become_any_account"
+                    else ""
+                )
+            else:
+                LOG.info("No gerrit.config found. Initializing default site.")
+                dev_option = "--dev"
 
-        if self.gerrit_config:
-            LOG.info("Existing gerrit.config found.")
-            dev_option = (
-                "--dev"
-                if self.gerrit_config.get("auth.type").lower()
-                == "development_become_any_account"
-                else ""
+            flags = f"--no-auto-start --batch {dev_option}"
+
+            command = f"java -jar /var/war/gerrit.war init {flags} -d {self.site}"
+
+            init_process = subprocess.run(
+                command.split(), stdout=subprocess.PIPE, check=True
             )
-        else:
-            LOG.info("No gerrit.config found. Initializing default site.")
-            dev_option = "--dev"
 
-        flags = f"--no-auto-start --batch {dev_option}"
+            if init_process.returncode > 0:
+                LOG.error(
+                    "An error occurred, when initializing Gerrit. Exit code: %d",
+                    init_process.returncode,
+                )
+                sys.exit(1)
 
-        command = f"java -jar /var/war/gerrit.war init {flags} -d {self.site}"
+            self._symlink_configuration()
 
-        init_process = subprocess.run(
-            command.split(), stdout=subprocess.PIPE, check=True
-        )
+            if self.is_replica:
+                self._symlink_mounted_site_components()
 
-        if init_process.returncode > 0:
-            LOG.error(
-                "An error occurred, when initializing Gerrit. Exit code: %d",
-                init_process.returncode,
-            )
-            sys.exit(1)
-
-        self._symlink_configuration()
-
-        if self.is_replica:
-            self._symlink_mounted_site_components()
-        else:
-            get_reindexer(self.site, self.config).start(False)
+        if not self.is_replica:
+            get_reindexer(self.site, self.config).start(self.force_offline_reindex)
diff --git a/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/reindex.py b/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/reindex.py
index 1f05b01..1cd67b1 100755
--- a/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/reindex.py
+++ b/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/reindex.py
@@ -25,6 +25,8 @@
 from ..helpers import git, log
 
 LOG = log.get_logger("reindex")
+MNT_PATH = "/var/mnt"
+INDEXES = set(["accounts", "changes", "groups", "projects"])
 
 
 class IndexType(enum.Enum):
@@ -38,6 +40,13 @@
         self.index_config_path = f"{self.gerrit_site_path}/index/gerrit_index.config"
         self.init_config = config
 
+        self.gerrit_config = git.GitConfigParser(
+            os.path.join(MNT_PATH, "etc/config/gerrit.config")
+        )
+        self.is_online_reindex = self.gerrit_config.get_boolean(
+            "index.onlineUpgrade", True
+        )
+
         self.configured_indices = self._parse_gerrit_index_config()
 
     @abc.abstractmethod
@@ -51,32 +60,42 @@
             options = config.list()
             for opt in options:
                 name, version = opt["subsection"].rsplit("_", 1)
-                # TODO (Thomas): Properly handle multiple versions of the same index,
-                # which may be present due to online-reindexing in progress.
-                indices[name] = {
-                    "version": int(version),
-                    "ready": opt["value"].lower() == "true",
-                }
+                ready = opt["value"].lower() == "true"
+                if name in indices:
+                    indices[name] = {
+                        "read": version if ready else indices[name]["read"],
+                        "latest_write": max(version, indices[name]["latest_write"]),
+                    }
+                else:
+                    indices[name] = {
+                        "read": version if ready else None,
+                        "latest_write": version,
+                    }
         return indices
 
-    def _get_unready_indices(self):
-        unready_indices = []
+    def _get_not_ready_indices(self):
+        not_ready_indices = []
         for index, index_attrs in self.configured_indices.items():
-            if not index_attrs["ready"]:
+            if not index_attrs["read"]:
                 LOG.info("Index %s not ready.", index)
-                unready_indices.append(index)
-        return unready_indices
+                not_ready_indices.append(index)
+        not_ready_indices.extend(INDEXES.difference(self.configured_indices.keys()))
+        return not_ready_indices
 
-    def _check_index_versions(self):
+    def _indexes_need_update(self):
         indices = self._get_indices()
 
         if not indices:
-            return False
+            return True
 
         for index, index_attrs in self.configured_indices.items():
-            if index not in indices or index_attrs["version"] is not indices[index]:
-                return False
-        return True
+            if (
+                index not in indices
+                or index_attrs["latest_write"] != indices[index]
+                or index_attrs["read"] != index_attrs["latest_write"]
+            ):
+                return True
+        return False
 
     def reindex(self, indices=None):
         LOG.info("Starting to reindex.")
@@ -108,11 +127,11 @@
             self.reindex()
             return
 
-        unready_indices = self._get_unready_indices()
-        if unready_indices:
-            self.reindex(unready_indices)
+        not_ready_indices = self._get_not_ready_indices()
+        if not_ready_indices:
+            self.reindex(not_ready_indices)
 
-        if not self._check_index_versions():
+        if not self.is_online_reindex and self._indexes_need_update():
             LOG.info("Not all indices are up-to-date.")
             self.reindex()
             return
@@ -128,7 +147,10 @@
         for index in file_list:
             try:
                 (name, version) = index.split("_")
-                lucene_indices[name] = int(version)
+                if name in lucene_indices:
+                    lucene_indices[name] = max(version, lucene_indices[name])
+                else:
+                    lucene_indices[name] = version
             except ValueError:
                 LOG.debug("Ignoring invalid file in index-directory: %s", index)
         return lucene_indices
@@ -161,7 +183,7 @@
             try:
                 index = index.replace(es_config["prefix"], "", 1)
                 (name, version) = index.split("_")
-                es_indices[name] = int(version)
+                es_indices[name] = version
             except ValueError:
                 LOG.debug("Found unknown index: %s", index)
 
diff --git a/helm-charts/gerrit/README.md b/helm-charts/gerrit/README.md
index c7ef97c..094f9de 100644
--- a/helm-charts/gerrit/README.md
+++ b/helm-charts/gerrit/README.md
@@ -359,13 +359,6 @@
 
     The canonical web URL has to be set to the Ingress host.
 
-- `index.onlineUpgrade`
-
-    Online reindexing is currently **NOT** supported. An offline reindexing will
-    be enforced upon Gerrit updates. Online reindexing might under some circum-
-    stances interfere with the Gerrit pod startup procedure and thus has to be
-    deactivated.
-
 - `httpd.listenURL`
 
     This has to be set to `proxy-http://*:8080/` or `proxy-https://*:8080`,
diff --git a/helm-charts/gerrit/values.yaml b/helm-charts/gerrit/values.yaml
index 24992b1..13bd9ba 100644
--- a/helm-charts/gerrit/values.yaml
+++ b/helm-charts/gerrit/values.yaml
@@ -253,7 +253,6 @@
           disableReverseDnsLookup = true
         [index]
           type = LUCENE
-          onlineUpgrade = false # FIXED
         [auth]
           type = DEVELOPMENT_BECOME_ANY_ACCOUNT
         [httpd]
diff --git a/istio/gerrit.profile.yaml b/istio/gerrit.profile.yaml
index 43f46eb..d81dea6 100644
--- a/istio/gerrit.profile.yaml
+++ b/istio/gerrit.profile.yaml
@@ -17,7 +17,9 @@
           metrics:
           - resource:
               name: cpu
-              targetAverageUtilization: 80
+              target:
+                type: Utilization
+                averageUtilization: 80
             type: Resource
           minReplicas: 1
           scaleTargetRef:
@@ -61,7 +63,9 @@
           metrics:
           - resource:
               name: cpu
-              targetAverageUtilization: 80
+              target:
+                type: Utilization
+                averageUtilization: 80
             type: Resource
           minReplicas: 5
           scaleTargetRef:
@@ -162,7 +166,8 @@
     defaultConfig:
       proxyMetadata: {}
     enablePrometheusMerge: true
-  tag: 1.11.4
+  profile: default
+  tag: 1.16.0
   values:
     base:
       enableCRDTemplates: false
@@ -180,7 +185,6 @@
           name: egressgateway-ca-certs
           secretName: istio-egressgateway-ca-certs
         type: ClusterIP
-        zvpn: {}
       istio-ingressgateway:
         autoscaleEnabled: true
         env: {}
@@ -193,7 +197,6 @@
           name: ingressgateway-ca-certs
           secretName: istio-ingressgateway-ca-certs
         type: LoadBalancer
-        zvpn: {}
     global:
       configValidation: true
       defaultNodeSelector: {}
@@ -283,6 +286,7 @@
       image: pilot
       keepaliveMaxServerConnectionAge: 24h
       nodeSelector: {}
+      podLabels: {}
       replicaCount: 1
       traceSampling: 1
     sidecarInjectorWebhook:
diff --git a/operator/README.md b/operator/README.md
index aee956a..9ce7d15 100644
--- a/operator/README.md
+++ b/operator/README.md
@@ -407,33 +407,22 @@
   configFiles:
     gerrit.config: |-
         [gerrit]
-          basePath = git
           serverId = gerrit-1
-          canonicalWebUrl = http://example.com/
           disableReverseDnsLookup = true
         [index]
           type = LUCENE
-          onlineUpgrade = false
         [auth]
           type = DEVELOPMENT_BECOME_ANY_ACCOUNT
         [httpd]
-          listenUrl = proxy-http://*:8080/
           requestLog = true
           gracefulStopTimeout = 1m
-        [sshd]
-          listenAddress = off
         [transfer]
           timeout = 120 s
         [user]
           name = Gerrit Code Review
           email = gerrit@example.com
           anonymousCoward = Unnamed User
-        [cache]
-          directory = cache
         [container]
-          user = gerrit
-          javaHome = /usr/lib/jvm/java-11-openjdk
-          javaOptions = -Djavax.net.ssl.trustStore=/var/gerrit/etc/keystore
           javaOptions = -Xms200m
           javaOptions = -Xmx4g
 
@@ -443,6 +432,58 @@
   # - gerrit-secure-config
 ```
 
+#### Prohibited options in gerrit.config
+
+Some options in the gerrit.config are not allowed to be changed. Their values
+are preset by the containers/Kubernetes. The operator will configure those options
+automatically and won't allow different values, i.e. it will fail to reconcile
+if a value is set to an illegal value. These options are:
+
+- `cache.directory`
+
+    This should stay in the volume mounted to contain the Gerrit site and will
+    thus be set to `cache`.
+
+- `container.javaHome`
+
+    This has to be set to `/usr/lib/jvm/java-11-openjdk-amd64`, since this is
+    the path of the Java installation in the container.
+
+- `container.javaOptions = -Djavax.net.ssl.trustStore`
+
+    The keystore will be mounted to `/var/gerrit/etc/keystore`.
+
+- `container.replica`
+
+    This has to be set in the Gerrit-CustomResource under `spec.isReplica`.
+
+- `container.user`
+
+    The technical user in the Gerrit container is called `gerrit`.
+
+- `gerrit.basePath`
+
+    The git repositories are mounted to `/var/gerrit/git` in the container.
+
+- `gerrit.canonicalWebUrl`
+
+    The canonical web URL has to be set to the hostname used by the Ingress/Istio.
+
+- `httpd.listenURL`
+
+    This has to be set to `proxy-http://*:8080/` or `proxy-https://*:8080`,
+    depending of TLS is enabled in the Ingress or not, otherwise the Jetty
+    servlet will run into an endless redirect loop.
+
+- `sshd.advertisedAddress`
+
+    This is only enforced, if Istio is enabled. It can be configured otherwise.
+
+- `sshd.listenAddress`
+
+    Since the container port for SSH is fixed, this will be set automatically.
+    If no SSH port is configured in the service, the SSHD is disabled.
+
 ### GitGarbageCollection
 
 ```yaml
diff --git a/operator/k8s/gerrit.sample.yaml b/operator/k8s/gerrit.sample.yaml
index 058242e..483ad52 100644
--- a/operator/k8s/gerrit.sample.yaml
+++ b/operator/k8s/gerrit.sample.yaml
@@ -11,8 +11,6 @@
     gerrit.config: |-
       [auth]
         type = DEVELOPMENT_BECOME_ANY_ACCOUNT
-      [cache]
-        directory = cache
       [container]
         javaOptions = -Xms200m
         javaOptions = -Xmx4g
diff --git a/operator/pom.xml b/operator/pom.xml
index 3a09bf8..649e89b 100644
--- a/operator/pom.xml
+++ b/operator/pom.xml
@@ -13,7 +13,7 @@
 	<properties>
 		<fabric8.version>6.2.0</fabric8.version>
 		<flogger.version>0.7.4</flogger.version>
-		<javaoperatorsdk.version>4.1.0</javaoperatorsdk.version>
+		<javaoperatorsdk.version>4.2.4</javaoperatorsdk.version>
 		<maven.compiler.source>11</maven.compiler.source>
 		<maven.compiler.target>11</maven.compiler.target>
 	</properties>
@@ -122,7 +122,7 @@
 			<plugin>
 				<groupId>com.google.cloud.tools</groupId>
 				<artifactId>jib-maven-plugin</artifactId>
-				<version>3.2.0</version>
+				<version>3.3.1</version>
 				<configuration>
 					<container>
 						<mainClass>com.google.gerrit.k8s.operator.GerritOperator</mainClass>
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritIngressConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritIngressConfig.java
index 21ab427..21e7289 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritIngressConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritIngressConfig.java
@@ -82,6 +82,12 @@
   }
 
   @JsonIgnore
+  public String getUrl(String svcName) {
+    String protocol = getTls().isEnabled() ? "https" : "http";
+    return String.format("%s://%s", protocol, getFullHostnameForService(svcName));
+  }
+
+  @JsonIgnore
   public List<String> computeHostnames(KubernetesClient client, GerritCluster gerritCluster) {
     List<String> hostnames = new ArrayList<>();
     hostnames.addAll(computeGerritHostnames(client, gerritCluster));
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritIstioGateway.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritIstioGateway.java
index 830e1d2..5566c59 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritIstioGateway.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritIstioGateway.java
@@ -83,7 +83,7 @@
 
     boolean sshEnabled =
         client.resources(Gerrit.class).list().getItems().stream()
-            .anyMatch(g -> g.getSpec().getService().getSshPort() != null);
+            .anyMatch(g -> g.getSpec().getService().isSshEnabled());
 
     if (sshEnabled) {
       servers.add(
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 b2d175d..bc9f013 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.GerritClusterMemberDependentResource;
+import com.google.gerrit.k8s.operator.cluster.GerritIngressConfig.IngressType;
 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;
@@ -53,14 +54,23 @@
 
     if (gerritCluster.getSpec().getIngress().isEnabled()) {
       gerritConfigBuilder.withUrl(
-          gerritCluster
-              .getSpec()
-              .getIngress()
-              .getFullHostnameForService(ServiceDependentResource.getName(gerrit)));
+          gerritCluster.getSpec().getIngress().getUrl(ServiceDependentResource.getName(gerrit)));
     } else {
       gerritConfigBuilder.withUrl(ServiceDependentResource.getUrl(gerrit));
     }
 
+    if (gerritCluster.getSpec().getIngress().getType() == IngressType.ISTIO) {
+      gerritConfigBuilder.withSsh(
+          gerrit.getSpec().getService().isSshEnabled(),
+          gerritCluster
+                  .getSpec()
+                  .getIngress()
+                  .getFullHostnameForService(ServiceDependentResource.getName(gerrit))
+              + ":29418");
+    } else {
+      gerritConfigBuilder.withSsh(gerrit.getSpec().getService().isSshEnabled());
+    }
+
     configFiles.put("gerrit.config", gerritConfigBuilder.build().toText());
 
     if (!configFiles.containsKey("healthcheck.config")) {
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritIstioVirtualService.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritIstioVirtualService.java
index f30f866..625e294 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritIstioVirtualService.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritIstioVirtualService.java
@@ -65,7 +65,7 @@
 
   private List<TCPRoute> getTCPRoutes(Gerrit gerrit) {
     List<TCPRoute> routes = new ArrayList<>();
-    if (gerrit.getSpec().getService().getSshPort() != null) {
+    if (gerrit.getSpec().getService().isSshEnabled()) {
       routes.add(
           new TCPRouteBuilder()
               .withMatch(List.of(new L4MatchAttributesBuilder().withPort(29418).build()))
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritServiceConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritServiceConfig.java
index 841e02d..1237585 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritServiceConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritServiceConfig.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.k8s.operator.gerrit;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.io.Serializable;
 
 public class GerritServiceConfig implements Serializable {
@@ -46,4 +47,9 @@
   public void setSshPort(int sshPort) {
     this.sshPort = sshPort;
   }
+
+  @JsonIgnore
+  public boolean isSshEnabled() {
+    return this.sshPort != null;
+  }
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/ServiceDependentResource.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/ServiceDependentResource.java
index 34c15c0..5bc8549 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/ServiceDependentResource.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/ServiceDependentResource.java
@@ -83,7 +83,7 @@
             .withPort(gerrit.getSpec().getService().getHttpPort())
             .withNewTargetPort(HTTP_PORT)
             .build());
-    if (gerrit.getSpec().getService().getSshPort() != null) {
+    if (gerrit.getSpec().getService().isSshEnabled()) {
       ports.add(
           new ServicePortBuilder()
               .withName("ssh")
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/StatefulSetDependentResource.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/StatefulSetDependentResource.java
index a02280c..dad0796 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/StatefulSetDependentResource.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/StatefulSetDependentResource.java
@@ -257,7 +257,7 @@
     List<ContainerPort> containerPorts = new ArrayList<>();
     containerPorts.add(new ContainerPort(HTTP_PORT, null, null, "http", null));
 
-    if (gerrit.getSpec().getService().getSshPort() != null) {
+    if (gerrit.getSpec().getService().isSshEnabled()) {
       containerPorts.add(new ContainerPort(SSH_PORT, null, null, "ssh", null));
     }
 
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 a35fe83..3ca77d8 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
@@ -14,15 +14,21 @@
 
 package com.google.gerrit.k8s.operator.gerrit.config;
 
+import static com.google.gerrit.k8s.operator.gerrit.StatefulSetDependentResource.HTTP_PORT;
+import static com.google.gerrit.k8s.operator.gerrit.StatefulSetDependentResource.SSH_PORT;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
 
 @SuppressWarnings("rawtypes")
 public class GerritConfigBuilder {
+  private static final Pattern PROTOCOL_PATTERN = Pattern.compile("^(https?)://.+");
   private List<RequiredOption> requiredOptions = new ArrayList<>(setupStaticRequiredOptions());
   private Config cfg;
 
@@ -37,7 +43,7 @@
             Set.of("-Djavax.net.ssl.trustStore=/var/gerrit/etc/keystore")));
     requiredOptions.add(new RequiredOption<String>("container", "user", "gerrit"));
     requiredOptions.add(new RequiredOption<String>("gerrit", "basepath", "git"));
-    requiredOptions.add(new RequiredOption<Boolean>("index", "onlineUpgrade", false));
+    requiredOptions.add(new RequiredOption<String>("cache", "directory", "cache"));
     return requiredOptions;
   }
 
@@ -59,9 +65,41 @@
 
   public GerritConfigBuilder withUrl(String url) {
     this.requiredOptions.add(new RequiredOption<String>("gerrit", "canonicalWebUrl", url));
+
+    StringBuilder listenUrlBuilder = new StringBuilder();
+    listenUrlBuilder.append("proxy-");
+    Matcher protocolMatcher = PROTOCOL_PATTERN.matcher(url);
+    if (protocolMatcher.matches()) {
+      listenUrlBuilder.append(protocolMatcher.group(1));
+    } else {
+      throw new IllegalStateException(
+          String.format("Unknown protocol used for canonicalWebUrl: %s", url));
+    }
+    listenUrlBuilder.append("://*:");
+    listenUrlBuilder.append(HTTP_PORT);
+    listenUrlBuilder.append("/");
+    this.requiredOptions.add(
+        new RequiredOption<String>("httpd", "listenUrl", listenUrlBuilder.toString()));
     return this;
   }
 
+  public GerritConfigBuilder withSsh(boolean enabled) {
+    String listenAddress;
+    if (enabled) {
+      listenAddress = "*:" + SSH_PORT;
+    } else {
+      listenAddress = "off";
+    }
+    this.requiredOptions.add(new RequiredOption<String>("sshd", "listenAddress", listenAddress));
+    return this;
+  }
+
+  public GerritConfigBuilder withSsh(boolean enabled, String advertisedAddress) {
+    this.requiredOptions.add(
+        new RequiredOption<String>("sshd", "advertisedAddress", advertisedAddress));
+    return withSsh(enabled);
+  }
+
   public GerritConfigBuilder useReplicaMode(boolean isReplica) {
     this.requiredOptions.add(new RequiredOption<Boolean>("container", "replica", isReplica));
     return this;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestGerrit.java b/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestGerrit.java
index ec5d31f..31ffd4d 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestGerrit.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestGerrit.java
@@ -62,19 +62,14 @@
           + "  accountBase = dc=example,dc=org\n"
           + "  username = cn=admin,dc=example,dc=org\n"
           + "[httpd]\n"
-          + "  listenUrl = proxy-https://*:8080/\n"
           + "  requestLog = true\n"
           + "  gracefulStopTimeout = 1m\n"
-          + "[sshd]\n"
-          + "  listenAddress = off\n"
           + "[transfer]\n"
           + "  timeout = 120 s\n"
           + "[user]\n"
           + "  name = Gerrit Code Review\n"
           + "  email = gerrit@example.com\n"
           + "  anonymousCoward = Unnamed User\n"
-          + "[cache]\n"
-          + "  directory = cache\n"
           + "[container]\n"
           + "  javaOptions = -Xmx4g";
 
diff --git a/tests/container-images/gerrit-init/test_container_integration_gerrit_init_reindexing.py b/tests/container-images/gerrit-init/test_container_integration_gerrit_init_reindexing.py
index b2b1327..c8a5b49 100644
--- a/tests/container-images/gerrit-init/test_container_integration_gerrit_init_reindexing.py
+++ b/tests/container-images/gerrit-init/test_container_integration_gerrit_init_reindexing.py
@@ -108,7 +108,7 @@
         exit_code, _ = container_run_endless.exec_run("/var/gerrit/bin/gerrit.sh start")
         assert exit_code == 0
 
-    def test_gerrit_init_fixes_unready_indices(self, container_run_endless):
+    def test_gerrit_init_fixes_not_ready_indices(self, container_run_endless):
         container_run_endless.exec_run(
             (
                 "python3 /var/tools/gerrit-initializer "