Merge changes I712e8448,Ia18b5da3,Ib7df35ab,I226f0f23,Ia5e49da3, ...

* changes:
  Refactor ConfigBuilderImpls to be version specific
  Use a factory to create ValidationWebhookConfigurations
  Move admission webhook to version specific package
  Move POJOs representing the CRDs under its own package
  Remove unused @Buildable annotation
  Remove GerritClusterMember* classes
  Fix healthcheck plugin URL
diff --git a/container-images/gerrit-base/Dockerfile b/container-images/gerrit-base/Dockerfile
index f705808..1f08ac6 100644
--- a/container-images/gerrit-base/Dockerfile
+++ b/container-images/gerrit-base/Dockerfile
@@ -21,7 +21,7 @@
     ln -s /var/war/gerrit.war /var/gerrit/bin/gerrit.war
 
 # Download healthcheck plugin
-ARG HEALTHCHECK_JAR_URL=https://gerrit-ci.gerritforge.com/view/Plugins-stable-3.8/job/plugin-healthcheck-bazel-master-stable-3.8/lastSuccessfulBuild/artifact/bazel-bin/plugins/healthcheck/healthcheck.jar
+ARG HEALTHCHECK_JAR_URL=https://gerrit-ci.gerritforge.com/view/Plugins-stable-3.8/job/plugin-healthcheck-bazel-stable-3.8/lastSuccessfulBuild/artifact/bazel-bin/plugins/healthcheck/healthcheck.jar
 RUN curl -k -o /var/plugins/healthcheck.jar ${HEALTHCHECK_JAR_URL} && \
     ln -s /var/plugins/healthcheck.jar /var/gerrit/plugins/healthcheck.jar
 
diff --git a/operator/pom.xml b/operator/pom.xml
index 52c065a..890c400 100644
--- a/operator/pom.xml
+++ b/operator/pom.xml
@@ -17,6 +17,7 @@
 
 		<fabric8.version>6.6.2</fabric8.version>
 		<flogger.version>0.7.4</flogger.version>
+		<guice.version>5.1.0</guice.version>
 		<javaoperatorsdk.version>4.3.3</javaoperatorsdk.version>
 		<jetty.version>11.0.15</jetty.version>
 		<lombok.version>1.18.28</lombok.version>
@@ -206,7 +207,12 @@
 		<dependency>
 			<groupId>com.google.inject</groupId>
 			<artifactId>guice</artifactId>
-			<version>5.1.0</version>
+			<version>${guice.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.inject.extensions</groupId>
+			<artifactId>guice-assistedinject</artifactId>
+			<version>${guice.version}</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.logging.log4j</groupId>
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressSshConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/Constants.java
similarity index 63%
copy from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressSshConfig.java
copy to operator/src/main/java/com/google/gerrit/k8s/operator/Constants.java
index 62ac188..d2fb405 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressSshConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/Constants.java
@@ -12,16 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator;
 
-public class GerritIngressSshConfig {
-  private boolean enabled = false;
+import com.google.inject.AbstractModule;
 
-  public boolean isEnabled() {
-    return enabled;
-  }
-
-  public void setEnabled(boolean enabled) {
-    this.enabled = enabled;
-  }
+public class Constants extends AbstractModule {
+  public static final String[] VERSIONS = new String[] {"v1alpha"};
+  public static final String[] CUSTOM_RESOURCES =
+      new String[] {"GerritCluster", "Gerrit", "Receiver", "GerritNetwork", "GitGarbageCollection"};
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/OperatorModule.java b/operator/src/main/java/com/google/gerrit/k8s/operator/OperatorModule.java
index 2a1b4b9..3e61528 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/OperatorModule.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/OperatorModule.java
@@ -35,11 +35,13 @@
   protected void configure() {
     install(new EnvModule());
     install(new ServerModule());
-    install(new AdmissionWebhookModule());
 
     bind(KubernetesClient.class).toInstance(getKubernetesClient());
     bind(LifecycleManager.class);
     bind(GerritOperator.class);
+
+    install(new AdmissionWebhookModule());
+
     Multibinder<Reconciler> reconcilers = Multibinder.newSetBinder(binder(), Reconciler.class);
     reconcilers.addBinding().to(GerritClusterReconciler.class);
     reconcilers.addBinding().to(GerritReconciler.class);
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/AbstractValidationWebhookConfigApplier.java b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/AbstractValidationWebhookConfigApplier.java
deleted file mode 100644
index 37d7572..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/AbstractValidationWebhookConfigApplier.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.admission;
-
-import static com.google.gerrit.k8s.operator.GerritOperator.SERVICE_NAME;
-import static com.google.gerrit.k8s.operator.GerritOperator.SERVICE_PORT;
-
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.k8s.operator.server.KeyStoreProvider;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.RuleWithOperations;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.ValidatingWebhookBuilder;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.ValidatingWebhookConfiguration;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.ValidatingWebhookConfigurationBuilder;
-import io.fabric8.kubernetes.client.KubernetesClient;
-import java.io.IOException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.util.Base64;
-import java.util.List;
-
-public abstract class AbstractValidationWebhookConfigApplier
-    implements ValidationWebhookConfigApplier {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  private final KubernetesClient client;
-  private final String namespace;
-  private final KeyStoreProvider keyStoreProvider;
-  private final ValidatingWebhookConfiguration cfg;
-
-  public AbstractValidationWebhookConfigApplier(
-      KubernetesClient client, String namespace, KeyStoreProvider keyStoreProvider) {
-    this.client = client;
-    this.namespace = namespace;
-    this.keyStoreProvider = keyStoreProvider;
-
-    this.cfg = build();
-  }
-
-  abstract String name();
-
-  abstract String webhookPath();
-
-  abstract List<RuleWithOperations> rules();
-
-  private String caBundle()
-      throws CertificateEncodingException, KeyStoreException, NoSuchAlgorithmException,
-          CertificateException, IOException {
-    return Base64.getEncoder().encodeToString(keyStoreProvider.getCertificate().getBytes());
-  }
-
-  @Override
-  public ValidatingWebhookConfiguration build() {
-    try {
-      return new ValidatingWebhookConfigurationBuilder()
-          .withNewMetadata()
-          .withName(name())
-          .endMetadata()
-          .withWebhooks(
-              new ValidatingWebhookBuilder()
-                  .withName(name() + ".validator.google.com")
-                  .withAdmissionReviewVersions("v1", "v1beta1")
-                  .withNewClientConfig()
-                  .withCaBundle(caBundle())
-                  .withNewService()
-                  .withName(SERVICE_NAME)
-                  .withNamespace(namespace)
-                  .withPath(webhookPath())
-                  .withPort(SERVICE_PORT)
-                  .endService()
-                  .endClientConfig()
-                  .withFailurePolicy("Fail")
-                  .withMatchPolicy("Equivalent")
-                  .withRules(rules())
-                  .withTimeoutSeconds(10)
-                  .withSideEffects("None")
-                  .build())
-          .build();
-    } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
-      throw new RuntimeException("Failed to deploy ValidationWebhookConfiguration " + name(), e);
-    }
-  }
-
-  @Override
-  public void apply()
-      throws KeyStoreException, NoSuchProviderException, IOException, NoSuchAlgorithmException,
-          CertificateException {
-    logger.atInfo().log("Applying webhook config %s", cfg);
-    client.resource(cfg).createOrReplace();
-  }
-
-  @Override
-  public void delete() {
-    logger.atInfo().log("Deleting webhook config %s", cfg);
-    client.resource(cfg).delete();
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/AdmissionWebhookModule.java b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/AdmissionWebhookModule.java
index e151b02..d3d4841 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/AdmissionWebhookModule.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/AdmissionWebhookModule.java
@@ -14,16 +14,15 @@
 
 package com.google.gerrit.k8s.operator.admission;
 
+import com.google.gerrit.k8s.operator.v1alpha.admission.GerritClusterValidationWebhookConfigApplier;
+import com.google.gerrit.k8s.operator.v1alpha.admission.GerritValidationWebhookConfigApplier;
+import com.google.gerrit.k8s.operator.v1alpha.admission.GitGcValidationWebhookConfigApplier;
 import com.google.inject.AbstractModule;
-import com.google.inject.multibindings.Multibinder;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
 
 public class AdmissionWebhookModule extends AbstractModule {
   public void configure() {
-    Multibinder<ValidationWebhookConfigApplier> vwcAppliers =
-        Multibinder.newSetBinder(binder(), ValidationWebhookConfigApplier.class);
-    vwcAppliers.addBinding().to(GerritClusterValidationWebhookConfigApplier.class);
-    vwcAppliers.addBinding().to(GitGcValidationWebhookConfigApplier.class);
-    vwcAppliers.addBinding().to(GerritValidationWebhookConfigApplier.class);
+    install(new FactoryModuleBuilder().build(ValidationWebhookConfigApplier.Factory.class));
 
     bind(ValidationWebhookConfigs.class);
   }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/GerritClusterValidationWebhookConfigApplier.java b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/GerritClusterValidationWebhookConfigApplier.java
deleted file mode 100644
index 9caa323..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/GerritClusterValidationWebhookConfigApplier.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.admission;
-
-import com.google.gerrit.k8s.operator.server.KeyStoreProvider;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.RuleWithOperations;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.RuleWithOperationsBuilder;
-import io.fabric8.kubernetes.client.KubernetesClient;
-import java.util.List;
-
-@Singleton
-public class GerritClusterValidationWebhookConfigApplier
-    extends AbstractValidationWebhookConfigApplier {
-
-  @Inject
-  public GerritClusterValidationWebhookConfigApplier(
-      KubernetesClient client,
-      @Named("Namespace") String namespace,
-      KeyStoreProvider keyStoreProvider) {
-    super(client, namespace, keyStoreProvider);
-  }
-
-  @Override
-  String name() {
-    return "gerrit-cluster";
-  }
-
-  @Override
-  String webhookPath() {
-    return "/admission/gerritcluster";
-  }
-
-  @Override
-  List<RuleWithOperations> rules() {
-    return List.of(
-        new RuleWithOperationsBuilder()
-            .withApiGroups("gerritoperator.google.com")
-            .withApiVersions("*")
-            .withOperations("CREATE", "UPDATE")
-            .withResources("gerritclusters")
-            .withScope("*")
-            .build());
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/GerritValidationWebhookConfigApplier.java b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/GerritValidationWebhookConfigApplier.java
deleted file mode 100644
index 3f6b4a4..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/GerritValidationWebhookConfigApplier.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.admission;
-
-import com.google.gerrit.k8s.operator.server.KeyStoreProvider;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.RuleWithOperations;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.RuleWithOperationsBuilder;
-import io.fabric8.kubernetes.client.KubernetesClient;
-import java.util.List;
-
-@Singleton
-public class GerritValidationWebhookConfigApplier extends AbstractValidationWebhookConfigApplier {
-
-  @Inject
-  public GerritValidationWebhookConfigApplier(
-      KubernetesClient client,
-      @Named("Namespace") String namespace,
-      KeyStoreProvider keyStoreProvider) {
-    super(client, namespace, keyStoreProvider);
-  }
-
-  @Override
-  String name() {
-    return "gerrit";
-  }
-
-  @Override
-  String webhookPath() {
-    return "/admission/gerrit";
-  }
-
-  @Override
-  List<RuleWithOperations> rules() {
-    return List.of(
-        new RuleWithOperationsBuilder()
-            .withApiGroups("gerritoperator.google.com")
-            .withApiVersions("*")
-            .withOperations("CREATE", "UPDATE")
-            .withResources("gerrits")
-            .withScope("*")
-            .build());
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/GitGcValidationWebhookConfigApplier.java b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/GitGcValidationWebhookConfigApplier.java
deleted file mode 100644
index f92d875..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/GitGcValidationWebhookConfigApplier.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.admission;
-
-import com.google.gerrit.k8s.operator.server.KeyStoreProvider;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.RuleWithOperations;
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.RuleWithOperationsBuilder;
-import io.fabric8.kubernetes.client.KubernetesClient;
-import java.util.List;
-
-@Singleton
-public class GitGcValidationWebhookConfigApplier extends AbstractValidationWebhookConfigApplier {
-
-  @Inject
-  public GitGcValidationWebhookConfigApplier(
-      KubernetesClient client,
-      @Named("Namespace") String namespace,
-      KeyStoreProvider keyStoreProvider) {
-    super(client, namespace, keyStoreProvider);
-  }
-
-  @Override
-  String name() {
-    return "gitgc";
-  }
-
-  @Override
-  String webhookPath() {
-    return "/admission/gitgc";
-  }
-
-  @Override
-  List<RuleWithOperations> rules() {
-    return List.of(
-        new RuleWithOperationsBuilder()
-            .withApiGroups("gerritoperator.google.com")
-            .withApiVersions("*")
-            .withOperations("CREATE", "UPDATE")
-            .withResources("gitgcs")
-            .withScope("*")
-            .build());
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/ValidationWebhookConfigApplier.java b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/ValidationWebhookConfigApplier.java
index b8cc44f..443347e 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/ValidationWebhookConfigApplier.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/ValidationWebhookConfigApplier.java
@@ -14,13 +14,131 @@
 
 package com.google.gerrit.k8s.operator.admission;
 
-import io.fabric8.kubernetes.api.model.admissionregistration.v1.ValidatingWebhookConfiguration;
+import static com.google.gerrit.k8s.operator.GerritOperator.SERVICE_NAME;
+import static com.google.gerrit.k8s.operator.GerritOperator.SERVICE_PORT;
 
-public interface ValidationWebhookConfigApplier {
-  /** Builds the ValidatingWebhookConfiguration */
-  ValidatingWebhookConfiguration build() throws Exception;
-  /** Applies the ValidatingWebhookConfiguration to the cluster */
-  void apply() throws Exception;
-  /** Deletes the ValidatingWebhookConfiguration to the cluster */
-  void delete();
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.k8s.operator.server.KeyStoreProvider;
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import com.google.inject.name.Named;
+import io.fabric8.kubernetes.api.model.admissionregistration.v1.RuleWithOperations;
+import io.fabric8.kubernetes.api.model.admissionregistration.v1.RuleWithOperationsBuilder;
+import io.fabric8.kubernetes.api.model.admissionregistration.v1.ValidatingWebhook;
+import io.fabric8.kubernetes.api.model.admissionregistration.v1.ValidatingWebhookBuilder;
+import io.fabric8.kubernetes.api.model.admissionregistration.v1.ValidatingWebhookConfiguration;
+import io.fabric8.kubernetes.api.model.admissionregistration.v1.ValidatingWebhookConfigurationBuilder;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import java.io.IOException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+public class ValidationWebhookConfigApplier {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+  private final KubernetesClient client;
+  private final String namespace;
+  private final KeyStoreProvider keyStoreProvider;
+  private final ValidatingWebhookConfiguration cfg;
+  private final String customResourceName;
+  private final String[] customResourceVersions;
+
+  public interface Factory {
+    ValidationWebhookConfigApplier create(
+        String customResourceName, String[] customResourceVersions);
+  }
+
+  @AssistedInject
+  ValidationWebhookConfigApplier(
+      KubernetesClient client,
+      @Named("Namespace") String namespace,
+      KeyStoreProvider keyStoreProvider,
+      @Assisted String customResourceName,
+      @Assisted String[] customResourceVersions) {
+    this.client = client;
+    this.namespace = namespace;
+    this.keyStoreProvider = keyStoreProvider;
+    this.customResourceName = customResourceName;
+    this.customResourceVersions = customResourceVersions;
+
+    this.cfg = build();
+  }
+
+  public List<RuleWithOperations> rules(String version) {
+    return List.of(
+        new RuleWithOperationsBuilder()
+            .withApiGroups("gerritoperator.google.com")
+            .withApiVersions(version)
+            .withOperations("CREATE", "UPDATE")
+            .withResources(customResourceName)
+            .withScope("*")
+            .build());
+  }
+
+  public List<ValidatingWebhook> webhooks()
+      throws CertificateEncodingException, KeyStoreException, NoSuchAlgorithmException,
+          CertificateException, IOException {
+    List<ValidatingWebhook> webhooks = new ArrayList<>();
+    for (String version : customResourceVersions) {
+      webhooks.add(
+          new ValidatingWebhookBuilder()
+              .withName(customResourceName.toLowerCase() + "." + version + ".validator.google.com")
+              .withAdmissionReviewVersions("v1", "v1beta1")
+              .withNewClientConfig()
+              .withCaBundle(caBundle())
+              .withNewService()
+              .withName(SERVICE_NAME)
+              .withNamespace(namespace)
+              .withPath(
+                  String.format("/admission/%s/%s", version, customResourceName).toLowerCase())
+              .withPort(SERVICE_PORT)
+              .endService()
+              .endClientConfig()
+              .withFailurePolicy("Fail")
+              .withMatchPolicy("Equivalent")
+              .withRules(rules(version))
+              .withTimeoutSeconds(10)
+              .withSideEffects("None")
+              .build());
+    }
+    return webhooks;
+  }
+
+  private String caBundle()
+      throws CertificateEncodingException, KeyStoreException, NoSuchAlgorithmException,
+          CertificateException, IOException {
+    return Base64.getEncoder().encodeToString(keyStoreProvider.getCertificate().getBytes());
+  }
+
+  public ValidatingWebhookConfiguration build() {
+    try {
+      return new ValidatingWebhookConfigurationBuilder()
+          .withNewMetadata()
+          .withName(customResourceName.toLowerCase())
+          .endMetadata()
+          .withWebhooks(webhooks())
+          .build();
+    } catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException e) {
+      throw new RuntimeException(
+          "Failed to deploy ValidationWebhookConfiguration " + customResourceName, e);
+    }
+  }
+
+  public void apply()
+      throws KeyStoreException, NoSuchProviderException, IOException, NoSuchAlgorithmException,
+          CertificateException {
+    logger.atInfo().log("Applying webhook config %s", cfg);
+    client.resource(cfg).createOrReplace();
+  }
+
+  public void delete() {
+    logger.atInfo().log("Deleting webhook config %s", cfg);
+    client.resource(cfg).delete();
+  }
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/ValidationWebhookConfigs.java b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/ValidationWebhookConfigs.java
index a63aa0c..901d15f 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/admission/ValidationWebhookConfigs.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/admission/ValidationWebhookConfigs.java
@@ -14,18 +14,28 @@
 
 package com.google.gerrit.k8s.operator.admission;
 
+import static com.google.gerrit.k8s.operator.Constants.CUSTOM_RESOURCES;
+import static com.google.gerrit.k8s.operator.Constants.VERSIONS;
+
 import com.google.gerrit.k8s.operator.LifecycleManager;
 import com.google.inject.Inject;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 
 public class ValidationWebhookConfigs {
 
-  private final Set<ValidationWebhookConfigApplier> configAppliers;
+  private final List<ValidationWebhookConfigApplier> configAppliers;
 
   @Inject
   public ValidationWebhookConfigs(
-      LifecycleManager lifecycleManager, Set<ValidationWebhookConfigApplier> configAppliers) {
-    this.configAppliers = configAppliers;
+      LifecycleManager lifecycleManager,
+      ValidationWebhookConfigApplier.Factory configApplierFactory) {
+    this.configAppliers = new ArrayList<>();
+
+    for (String customResourceName : CUSTOM_RESOURCES) {
+      this.configAppliers.add(configApplierFactory.create(customResourceName, VERSIONS));
+    }
+
     lifecycleManager.addShutdownHook(
         new Runnable() {
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterDependentResourceNameDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterDependentResourceNameDiscriminator.java
deleted file mode 100644
index 4d6cccb..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterDependentResourceNameDiscriminator.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.cluster;
-
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import io.fabric8.kubernetes.api.model.HasMetadata;
-import io.javaoperatorsdk.operator.api.reconciler.Context;
-import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
-import java.util.Optional;
-
-public class GerritClusterDependentResourceNameDiscriminator<R extends HasMetadata>
-    implements ResourceDiscriminator<R, GerritCluster> {
-
-  private final String nameSuffix;
-
-  public GerritClusterDependentResourceNameDiscriminator(String nameSuffix) {
-    this.nameSuffix = nameSuffix;
-  }
-
-  @Override
-  public Optional<R> distinguish(
-      Class<R> resource, GerritCluster gerritCluster, Context<GerritCluster> context) {
-
-    return context.getSecondaryResources(resource).stream()
-        .filter(
-            v ->
-                v.getMetadata().getNamespace().equals(gerritCluster.getMetadata().getNamespace())
-                    && v.getMetadata()
-                        .getName()
-                        .equals(
-                            String.format(
-                                "%s-%s", gerritCluster.getMetadata().getName(), nameSuffix)))
-        .findFirst();
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterMember.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterMember.java
deleted file mode 100644
index f458e59..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterMember.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (C) 2022 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.k8s.operator.cluster;
-
-import io.fabric8.kubernetes.api.model.Namespaced;
-import io.fabric8.kubernetes.client.CustomResource;
-
-public abstract class GerritClusterMember<S extends GerritClusterMemberSpec, T>
-    extends CustomResource<S, T> implements Namespaced {
-  private static final long serialVersionUID = 1L;
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterMemberDependentResource.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterMemberDependentResource.java
deleted file mode 100644
index 8bd7163..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterMemberDependentResource.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2022 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.k8s.operator.cluster;
-
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import io.fabric8.kubernetes.api.model.HasMetadata;
-import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
-
-public abstract class GerritClusterMemberDependentResource<
-        R extends HasMetadata, P extends GerritClusterMember<? extends GerritClusterMemberSpec, ?>>
-    extends CRUDKubernetesDependentResource<R, P> {
-
-  public GerritClusterMemberDependentResource(Class<R> resourceType) {
-    super(resourceType);
-  }
-
-  protected GerritCluster getGerritCluster(P primary) {
-    GerritCluster gerritCluster =
-        client
-            .resources(GerritCluster.class)
-            .inNamespace(primary.getMetadata().getNamespace())
-            .withName(primary.getSpec().getCluster())
-            .get();
-
-    if (gerritCluster == null) {
-      throw new IllegalStateException("The Gerrit cluster could not be found.");
-    }
-
-    return gerritCluster;
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterMemberSpec.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterMemberSpec.java
deleted file mode 100644
index 7fe7d15..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterMemberSpec.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2022 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.k8s.operator.cluster;
-
-public interface GerritClusterMemberSpec {
-  public String getCluster();
-
-  public void setCluster(String cluster);
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterReconciler.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterReconciler.java
index bd3dae3..c00d51e 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterReconciler.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/GerritClusterReconciler.java
@@ -29,13 +29,13 @@
 import com.google.gerrit.k8s.operator.cluster.dependent.NfsIdmapdConfigMap;
 import com.google.gerrit.k8s.operator.cluster.dependent.NfsWorkaroundCondition;
 import com.google.gerrit.k8s.operator.cluster.dependent.SharedPVC;
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.cluster.model.GerritClusterStatus;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplate;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
-import com.google.gerrit.k8s.operator.receiver.model.Receiver;
-import com.google.gerrit.k8s.operator.receiver.model.ReceiverTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritClusterStatus;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.ReceiverTemplate;
 import com.google.inject.Singleton;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerrit.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerrit.java
index a5ead67..8de0b3e 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerrit.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerrit.java
@@ -14,9 +14,9 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritCondition.java
index bb4af7f..7455aca 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritCondition.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
 import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritNetwork.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritNetwork.java
index 62790f0..232c791 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritNetwork.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritNetwork.java
@@ -14,14 +14,14 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-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.network.model.GerritNetwork;
-import com.google.gerrit.k8s.operator.network.model.GerritNetworkSpec;
-import com.google.gerrit.k8s.operator.network.model.NetworkMember;
-import com.google.gerrit.k8s.operator.network.model.NetworkMemberWithSsh;
-import com.google.gerrit.k8s.operator.receiver.model.ReceiverTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetworkSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.NetworkMember;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.NetworkMemberWithSsh;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.ReceiverTemplate;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritNetworkCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritNetworkCondition.java
index c659f6f..a5b9244 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritNetworkCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedGerritNetworkCondition.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
 import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedReceiver.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedReceiver.java
index 063b652..62618a2 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedReceiver.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedReceiver.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.receiver.model.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedReceiverCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedReceiverCondition.java
index 473fb67..aa27446 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedReceiverCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/ClusterManagedReceiverCondition.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.receiver.model.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
 import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsIdmapdConfigMap.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsIdmapdConfigMap.java
index 3a1defe..623e801 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsIdmapdConfigMap.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsIdmapdConfigMap.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsIdmapdConfigMapDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsIdmapdConfigMapDiscriminator.java
index c3dfcec..17dfea7 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsIdmapdConfigMapDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsIdmapdConfigMapDiscriminator.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.cluster.GerritClusterReconciler.CM_EVENT_SOURCE;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsWorkaroundCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsWorkaroundCondition.java
index 3d8c176..a0cccc0 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsWorkaroundCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/NfsWorkaroundCondition.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.shared.model.NfsWorkaroundConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.NfsWorkaroundConfig;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVC.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVC.java
index c7a9e4f..099afc6 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVC.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVC.java
@@ -14,10 +14,10 @@
 
 package com.google.gerrit.k8s.operator.cluster.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.shared.model.GerritStorageConfig;
-import com.google.gerrit.k8s.operator.shared.model.SharedStorage;
 import com.google.gerrit.k8s.operator.util.CRUDKubernetesDependentPVCResource;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritStorageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.SharedStorage;
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVCDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVCDiscriminator.java
index 652f890..52fe941 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVCDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVCDiscriminator.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.cluster.GerritClusterReconciler.PVC_EVENT_SOURCE;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritReconciler.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritReconciler.java
index 53abee9..04d0a2f 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritReconciler.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/GerritReconciler.java
@@ -23,8 +23,8 @@
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritSecret;
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritService;
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritStatus;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritStatus;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import io.fabric8.kubernetes.api.model.ConfigMap;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ConfigBuilder.java
index 5b7f4d3..ca58e94 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ConfigBuilder.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ConfigBuilder.java
@@ -14,8 +14,7 @@
 
 package com.google.gerrit.k8s.operator.gerrit.config;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
+import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -23,46 +22,19 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
 
-@SuppressWarnings("rawtypes")
 public abstract class ConfigBuilder {
-  private final String configFileName;
 
-  private List<RequiredOption> requiredOptions = new ArrayList<>();
-  private Config config = new Config();
+  private final ImmutableList<RequiredOption<?>> requiredOptions;
+  private final Config config;
 
-  public ConfigBuilder(String configFileName) {
-    this.configFileName = configFileName;
+  ConfigBuilder(Config baseConfig, ImmutableList<RequiredOption<?>> requiredOptions) {
+    this.config = baseConfig;
+    this.requiredOptions = requiredOptions;
   }
 
-  abstract void addRequiredOptions(Gerrit gerrit);
-
-  public ConfigBuilder forGerrit(Gerrit gerrit) {
-    String configText = gerrit.getSpec().getConfigFiles().getOrDefault(configFileName, "");
-    this.config = parseConfig(configText);
-
-    addRequiredOptions(gerrit);
-
-    return this;
-  }
-
-  @VisibleForTesting
-  ConfigBuilder withConfig(String configText) {
-    this.config = parseConfig(configText);
-    return this;
-  }
-
-  void addRequiredOption(RequiredOption opt) {
-    requiredOptions.add(opt);
-  }
-
-  private Config parseConfig(String text) {
-    Config cfg = new Config();
-    try {
-      cfg.fromText(text);
-    } catch (ConfigInvalidException e) {
-      throw new IllegalStateException("Invalid configuration: " + text, e);
-    }
-    return cfg;
+  protected ConfigBuilder(String baseConfig, ImmutableList<RequiredOption<?>> requiredOptions) {
+    this.config = parseConfig(baseConfig);
+    this.requiredOptions = requiredOptions;
   }
 
   public Config build() {
@@ -80,6 +52,20 @@
     new ConfigValidator(requiredOptions).check(config);
   }
 
+  public List<RequiredOption<?>> getRequiredOptions() {
+    return this.requiredOptions;
+  }
+
+  protected Config parseConfig(String text) {
+    Config cfg = new Config();
+    try {
+      cfg.fromText(text);
+    } catch (ConfigInvalidException e) {
+      throw new IllegalStateException("Invalid configuration: " + text, e);
+    }
+    return cfg;
+  }
+
   @SuppressWarnings("unchecked")
   private void setRequiredOptions() {
     for (RequiredOption<?> opt : requiredOptions) {
@@ -102,8 +88,4 @@
       }
     }
   }
-
-  public List<RequiredOption> getRequiredOptions() {
-    return requiredOptions;
-  }
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ConfigValidator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ConfigValidator.java
index 36e64fe..bc952a1 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ConfigValidator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ConfigValidator.java
@@ -19,11 +19,9 @@
 import org.eclipse.jgit.lib.Config;
 
 public class ConfigValidator {
-  @SuppressWarnings("rawtypes")
-  private final List<RequiredOption> requiredOptions;
+  private final List<RequiredOption<?>> requiredOptions;
 
-  @SuppressWarnings("rawtypes")
-  public ConfigValidator(List<RequiredOption> requiredOptions) {
+  public ConfigValidator(List<RequiredOption<?>> requiredOptions) {
     this.requiredOptions = requiredOptions;
   }
 
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
deleted file mode 100644
index 31d2207..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/GerritConfigBuilder.java
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (C) 2022 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.k8s.operator.gerrit.config;
-
-import static com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet.HTTP_PORT;
-import static com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet.SSH_PORT;
-
-import com.google.common.annotations.VisibleForTesting;
-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.GerritTemplateSpec.GerritMode;
-import com.google.gerrit.k8s.operator.shared.model.IngressConfig;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class GerritConfigBuilder extends ConfigBuilder {
-  private static final Pattern PROTOCOL_PATTERN = Pattern.compile("^(https?)://.+");
-
-  public GerritConfigBuilder() {
-    super("gerrit.config");
-  }
-
-  @Override
-  void addRequiredOptions(Gerrit gerrit) {
-    String serverId = gerrit.getSpec().getServerId();
-    if (serverId != null && !serverId.isBlank()) {
-      addRequiredOption(new RequiredOption<String>("gerrit", "serverId", serverId));
-    }
-
-    addRequiredOption(
-        new RequiredOption<String>("container", "javaHome", "/usr/lib/jvm/java-11-openjdk"));
-
-    Set<String> javaOptions = new HashSet<>();
-    javaOptions.add("-Djavax.net.ssl.trustStore=/var/gerrit/etc/keystore");
-    if (gerrit.getSpec().isHighlyAvailablePrimary()) {
-      javaOptions.add("-Djava.net.preferIPv4Stack=true");
-    }
-    if (gerrit.getSpec().getDebug().isEnabled()) {
-      javaOptions.add("-Xdebug");
-      String debugServerCfg = "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000";
-      if (gerrit.getSpec().getDebug().isSuspend()) {
-        debugServerCfg = debugServerCfg + ",suspend=y";
-      } else {
-        debugServerCfg = debugServerCfg + ",suspend=n";
-      }
-      javaOptions.add(debugServerCfg);
-    }
-    addRequiredOption(new RequiredOption<Set<String>>("container", "javaOptions", javaOptions));
-
-    if (gerrit.getSpec().isHighlyAvailablePrimary()) {
-      addRequiredOption(
-          new RequiredOption<Set<String>>(
-              "gerrit",
-              "installModule",
-              Set.of("com.gerritforge.gerrit.globalrefdb.validation.LibModule")));
-      addRequiredOption(
-          new RequiredOption<Set<String>>(
-              "gerrit",
-              "installDbModule",
-              Set.of("com.ericsson.gerrit.plugins.highavailability.ValidationModule")));
-    }
-
-    addRequiredOption(new RequiredOption<String>("container", "user", "gerrit"));
-    addRequiredOption(new RequiredOption<String>("gerrit", "basepath", "git"));
-    addRequiredOption(new RequiredOption<String>("cache", "directory", "cache"));
-    useReplicaMode(gerrit.getSpec().getMode().equals(GerritMode.REPLICA));
-
-    withSshListenAddress(gerrit);
-    IngressConfig ingressConfig = gerrit.getSpec().getIngress();
-    if (ingressConfig.isEnabled()) {
-      withUrl(ingressConfig.getUrl());
-      withSshAdvertisedAddress(gerrit);
-    }
-  }
-
-  @VisibleForTesting
-  GerritConfigBuilder withUrl(String url) {
-    addRequiredOption(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("/");
-    addRequiredOption(
-        new RequiredOption<String>("httpd", "listenUrl", listenUrlBuilder.toString()));
-    return this;
-  }
-
-  private void withSshListenAddress(Gerrit gerrit) {
-    String listenAddress;
-    if (gerrit.isSshEnabled()) {
-      listenAddress = "*:" + SSH_PORT;
-    } else {
-      listenAddress = "off";
-    }
-    addRequiredOption(new RequiredOption<String>("sshd", "listenAddress", listenAddress));
-  }
-
-  private void withSshAdvertisedAddress(Gerrit gerrit) {
-    if (gerrit.isSshEnabled()) {
-      addRequiredOption(
-          new RequiredOption<String>(
-              "sshd",
-              "advertisedAddress",
-              gerrit.getSpec().getIngress().getFullHostnameForService(GerritService.getName(gerrit))
-                  + ":29418"));
-    }
-  }
-
-  private GerritConfigBuilder useReplicaMode(boolean isReplica) {
-    addRequiredOption(new RequiredOption<Boolean>("container", "replica", isReplica));
-    return this;
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/HighAvailabilityPluginConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/HighAvailabilityPluginConfigBuilder.java
deleted file mode 100644
index d5a02d6..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/HighAvailabilityPluginConfigBuilder.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.gerrit.config;
-
-import com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class HighAvailabilityPluginConfigBuilder extends PluginConfigBuilder {
-  public HighAvailabilityPluginConfigBuilder() {
-    super("high-availability");
-  }
-
-  @Override
-  void addRequiredOptions(Gerrit gerrit) {
-    addRequiredOption(
-        new RequiredPluginOption<String>("high-availability", "main", "sharedDirectory", "shared"));
-    addRequiredOption(
-        new RequiredPluginOption<String>("high-availability", "peerInfo", "strategy", "jgroups"));
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "high-availability", "peerInfo", "jgroups", "myUrl", null));
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "high-availability", "jgroups", "clusterName", gerrit.getMetadata().getName()));
-    addRequiredOption(
-        new RequiredPluginOption<Boolean>("high-availability", "jgroups", "kubernetes", true));
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "high-availability",
-            "jgroups",
-            "kubernetes",
-            "namespace",
-            gerrit.getMetadata().getNamespace()));
-    addRequiredOption(
-        new RequiredPluginOption<Set<String>>(
-            "high-availability", "jgroups", "kubernetes", "label", getLabels(gerrit)));
-    addRequiredOption(
-        new RequiredPluginOption<Boolean>("high-availability", "cache", "synchronize", true));
-    addRequiredOption(
-        new RequiredPluginOption<Boolean>("high-availability", "event", "synchronize", true));
-    addRequiredOption(
-        new RequiredPluginOption<Boolean>("high-availability", "index", "synchronize", true));
-    addRequiredOption(
-        new RequiredPluginOption<Boolean>("high-availability", "index", "synchronizeForced", true));
-    addRequiredOption(
-        new RequiredPluginOption<Boolean>("high-availability", "healthcheck", "enable", true));
-    addRequiredOption(
-        new RequiredPluginOption<Boolean>("high-availability", "ref-database", "enabled", true));
-  }
-
-  private static Set<String> getLabels(Gerrit gerrit) {
-    Map<String, String> selectorLabels = GerritStatefulSet.getSelectorLabels(gerrit);
-    Set<String> labels = new HashSet<>();
-    for (Map.Entry<String, String> label : selectorLabels.entrySet()) {
-      labels.add(label.getKey() + "=" + label.getValue());
-    }
-    return labels;
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/PluginConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/PluginConfigBuilder.java
deleted file mode 100644
index 4ad5e17..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/PluginConfigBuilder.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.gerrit.config;
-
-public abstract class PluginConfigBuilder extends ConfigBuilder {
-
-  public PluginConfigBuilder(String pluginName) {
-    super(pluginName + ".config");
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/RequiredPluginOption.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/RequiredPluginOption.java
deleted file mode 100644
index 2e98e27..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/RequiredPluginOption.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.gerrit.config;
-
-public class RequiredPluginOption<T> extends RequiredOption<T> {
-  private final String plugin;
-
-  public RequiredPluginOption(
-      String plugin, String section, String subSection, String key, T expected) {
-    super(section, subSection, key, expected);
-    this.plugin = plugin;
-  }
-
-  public RequiredPluginOption(String plugin, String section, String key, T expected) {
-    super(section, null, key, expected);
-    this.plugin = plugin;
-  }
-
-  public String getPlugin() {
-    return plugin;
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/SpannerRefDbPluginConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/SpannerRefDbPluginConfigBuilder.java
deleted file mode 100644
index 3872225..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/SpannerRefDbPluginConfigBuilder.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.gerrit.config;
-
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-
-public class SpannerRefDbPluginConfigBuilder extends PluginConfigBuilder {
-  public SpannerRefDbPluginConfigBuilder() {
-    super("spanner-refdb");
-  }
-
-  @Override
-  void addRequiredOptions(Gerrit gerrit) {
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "spanner-refdb", "ref-database", "spanner", "useEmulator", "false"));
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "spanner-refdb",
-            "ref-database",
-            "spanner",
-            "projectName",
-            gerrit.getSpec().getRefdb().getSpanner().getProjectName()));
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "spanner-refdb",
-            "ref-database",
-            "spanner",
-            "credentialsPath",
-            "/var/gerrit/etc/gcp-credentials.json"));
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "spanner-refdb",
-            "ref-database",
-            "spanner",
-            "instance",
-            gerrit.getSpec().getRefdb().getSpanner().getInstance()));
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "spanner-refdb",
-            "ref-database",
-            "spanner",
-            "database",
-            gerrit.getSpec().getRefdb().getSpanner().getDatabase()));
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ZookeeperRefDbPluginConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ZookeeperRefDbPluginConfigBuilder.java
deleted file mode 100644
index 0de42eb..0000000
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/config/ZookeeperRefDbPluginConfigBuilder.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2023 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.k8s.operator.gerrit.config;
-
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-
-public class ZookeeperRefDbPluginConfigBuilder extends PluginConfigBuilder {
-  public ZookeeperRefDbPluginConfigBuilder() {
-    super("zookeeper-refdb");
-  }
-
-  @Override
-  void addRequiredOptions(Gerrit gerrit) {
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "zookeeper-refdb",
-            "ref-database",
-            "zookeeper",
-            "connectString",
-            gerrit.getSpec().getRefdb().getZookeeper().getConnectString()));
-    addRequiredOption(
-        new RequiredPluginOption<String>(
-            "zookeeper-refdb",
-            "ref-database",
-            "zookeeper",
-            "rootNode",
-            gerrit.getSpec().getRefdb().getZookeeper().getRootNode()));
-  }
-}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritConfigMap.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritConfigMap.java
index c37d561..22001b9 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritConfigMap.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritConfigMap.java
@@ -14,13 +14,12 @@
 
 package com.google.gerrit.k8s.operator.gerrit.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.gerrit.config.ConfigBuilder;
-import com.google.gerrit.k8s.operator.gerrit.config.GerritConfigBuilder;
-import com.google.gerrit.k8s.operator.gerrit.config.HighAvailabilityPluginConfigBuilder;
-import com.google.gerrit.k8s.operator.gerrit.config.SpannerRefDbPluginConfigBuilder;
-import com.google.gerrit.k8s.operator.gerrit.config.ZookeeperRefDbPluginConfigBuilder;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.gerrit.config.GerritConfigBuilder;
+import com.google.gerrit.k8s.operator.v1alpha.gerrit.config.HighAvailabilityPluginConfigBuilder;
+import com.google.gerrit.k8s.operator.v1alpha.gerrit.config.SpannerRefDbPluginConfigBuilder;
+import com.google.gerrit.k8s.operator.v1alpha.gerrit.config.ZookeeperRefDbPluginConfigBuilder;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
@@ -49,26 +48,23 @@
       configFiles.put("gerrit.config", "");
     }
 
-    ConfigBuilder gerritConfigBuilder = new GerritConfigBuilder().forGerrit(gerrit);
-
-    configFiles.put("gerrit.config", gerritConfigBuilder.build().toText());
+    configFiles.put("gerrit.config", new GerritConfigBuilder(gerrit).build().toText());
 
     if (gerrit.getSpec().isHighlyAvailablePrimary()) {
       configFiles.put(
           "high-availability.config",
-          new HighAvailabilityPluginConfigBuilder().forGerrit(gerrit).build().toText());
+          new HighAvailabilityPluginConfigBuilder(gerrit).build().toText());
     }
 
     switch (gerrit.getSpec().getRefdb().getDatabase()) {
       case ZOOKEEPER:
         configFiles.put(
             "zookeeper-refdb.config",
-            new ZookeeperRefDbPluginConfigBuilder().forGerrit(gerrit).build().toText());
+            new ZookeeperRefDbPluginConfigBuilder(gerrit).build().toText());
         break;
       case SPANNER:
         configFiles.put(
-            "spanner-refdb.config",
-            new SpannerRefDbPluginConfigBuilder().forGerrit(gerrit).build().toText());
+            "spanner-refdb.config", new SpannerRefDbPluginConfigBuilder(gerrit).build().toText());
         break;
       default:
         break;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritConfigMapDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritConfigMapDiscriminator.java
index b486989..6ec6b77 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritConfigMapDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritConfigMapDiscriminator.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.gerrit.dependent;
 
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMap.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMap.java
index 9199fa2..3b9b8b4 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMap.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMap.java
@@ -14,18 +14,18 @@
 
 package com.google.gerrit.k8s.operator.gerrit.dependent;
 
-import static com.google.gerrit.k8s.operator.cluster.model.GerritCluster.PLUGIN_CACHE_MOUNT_PATH;
-import static com.google.gerrit.k8s.operator.shared.model.GlobalRefDbConfig.RefDatabase.SPANNER;
-import static com.google.gerrit.k8s.operator.shared.model.GlobalRefDbConfig.RefDatabase.ZOOKEEPER;
+import static com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster.PLUGIN_CACHE_MOUNT_PATH;
+import static com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GlobalRefDbConfig.RefDatabase.SPANNER;
+import static com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GlobalRefDbConfig.RefDatabase.ZOOKEEPER;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritInitConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritInitConfig;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMapDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMapDiscriminator.java
index 4b630f8..5494f5a 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMapDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritInitConfigMapDiscriminator.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.gerrit.dependent;
 
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritSecret.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritSecret.java
index 669edd0..ac65dcd 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritSecret.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritSecret.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.gerrit.dependent;
 
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
 import io.fabric8.kubernetes.api.model.Secret;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
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 92883f6..888903c 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
@@ -17,10 +17,10 @@
 import static com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet.HTTP_PORT;
 import static com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet.SSH_PORT;
 
-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 com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
 import io.fabric8.kubernetes.api.model.Service;
 import io.fabric8.kubernetes.api.model.ServiceBuilder;
 import io.fabric8.kubernetes.api.model.ServicePort;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritStatefulSet.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritStatefulSet.java
index d12b8f8..d319f4c 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritStatefulSet.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/dependent/GerritStatefulSet.java
@@ -17,11 +17,11 @@
 import static com.google.gerrit.k8s.operator.gerrit.dependent.GerritSecret.CONTEXT_SECRET_VERSION_KEY;
 
 import com.google.common.flogger.FluentLogger;
-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.shared.model.ContainerImageConfig;
-import com.google.gerrit.k8s.operator.shared.model.NfsWorkaroundConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.ContainerImageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.NfsWorkaroundConfig;
 import io.fabric8.kubernetes.api.model.Container;
 import io.fabric8.kubernetes.api.model.ContainerPort;
 import io.fabric8.kubernetes.api.model.EnvVar;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/GitGarbageCollectionReconciler.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/GitGarbageCollectionReconciler.java
index 753ecfd..8d28fea 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/GitGarbageCollectionReconciler.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/GitGarbageCollectionReconciler.java
@@ -15,11 +15,11 @@
 package com.google.gerrit.k8s.operator.gitgc;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
 import com.google.gerrit.k8s.operator.gitgc.dependent.GitGarbageCollectionCronJob;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollection;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollectionStatus;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollectionStatus.GitGcState;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollection;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollectionStatus;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollectionStatus.GitGcState;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import io.fabric8.kubernetes.client.KubernetesClient;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/dependent/GitGarbageCollectionCronJob.java b/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/dependent/GitGarbageCollectionCronJob.java
index 5a39d3a..254bbab 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/dependent/GitGarbageCollectionCronJob.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/dependent/GitGarbageCollectionCronJob.java
@@ -15,9 +15,8 @@
 package com.google.gerrit.k8s.operator.gitgc.dependent;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.k8s.operator.cluster.GerritClusterMemberDependentResource;
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollection;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollection;
 import io.fabric8.kubernetes.api.model.Container;
 import io.fabric8.kubernetes.api.model.ContainerBuilder;
 import io.fabric8.kubernetes.api.model.Volume;
@@ -27,13 +26,14 @@
 import io.fabric8.kubernetes.api.model.batch.v1.JobTemplateSpec;
 import io.fabric8.kubernetes.api.model.batch.v1.JobTemplateSpecBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 public class GitGarbageCollectionCronJob
-    extends GerritClusterMemberDependentResource<CronJob, GitGarbageCollection> {
+    extends CRUDKubernetesDependentResource<CronJob, GitGarbageCollection> {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   public GitGarbageCollectionCronJob() {
@@ -44,7 +44,12 @@
   protected CronJob desired(GitGarbageCollection gitGc, Context<GitGarbageCollection> context) {
     String ns = gitGc.getMetadata().getNamespace();
     String name = gitGc.getMetadata().getName();
-    GerritCluster gerritCluster = getGerritCluster(gitGc);
+    GerritCluster gerritCluster =
+        client
+            .resources(GerritCluster.class)
+            .inNamespace(ns)
+            .withName(gitGc.getSpec().getCluster())
+            .get();
     logger.atInfo().log("Reconciling GitGc with name: %s/%s", ns, name);
 
     Map<String, String> gitGcLabels =
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/GerritClusterIngressCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/GerritClusterIngressCondition.java
index 265c8e1..9077f1b 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/GerritClusterIngressCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/GerritClusterIngressCondition.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/GerritNetworkReconcilerProvider.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/GerritNetworkReconcilerProvider.java
index 5d68e81..a614e92 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/GerritNetworkReconcilerProvider.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/GerritNetworkReconcilerProvider.java
@@ -17,8 +17,8 @@
 import com.google.gerrit.k8s.operator.network.ambassador.GerritAmbassadorReconciler;
 import com.google.gerrit.k8s.operator.network.ingress.GerritIngressReconciler;
 import com.google.gerrit.k8s.operator.network.istio.GerritIstioReconciler;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
 import com.google.gerrit.k8s.operator.network.none.GerritNoIngressReconciler;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.name.Named;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/GerritAmbassadorReconciler.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/GerritAmbassadorReconciler.java
index 5037ea9..34dc9db 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/GerritAmbassadorReconciler.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/GerritAmbassadorReconciler.java
@@ -37,7 +37,7 @@
 import com.google.gerrit.k8s.operator.network.ambassador.dependent.ReceiverMappingCondition;
 import com.google.gerrit.k8s.operator.network.ambassador.dependent.SingleMappingCondition;
 import com.google.gerrit.k8s.operator.network.ambassador.dependent.TLSContextCondition;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import com.google.inject.Singleton;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/AbstractAmbassadorDependentResource.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/AbstractAmbassadorDependentResource.java
index 399a90f..3cc8d4a 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/AbstractAmbassadorDependentResource.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/AbstractAmbassadorDependentResource.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.fabric8.kubernetes.api.model.HasMetadata;
 import io.fabric8.kubernetes.api.model.ObjectMeta;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/CreateHostCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/CreateHostCondition.java
index 1846308..e7f99ec 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/CreateHostCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/CreateHostCondition.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterHost.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterHost.java
index 77a1b46..5f916c4 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterHost.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterHost.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterTLSContext.GERRIT_TLS_CONTEXT;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Host;
 import io.getambassador.v2.HostBuilder;
 import io.getambassador.v2.hostspec.TlsContext;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMapping.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMapping.java
index e6f63ed..733be73 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMapping.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMapping.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
-import com.google.gerrit.k8s.operator.network.model.NetworkMemberWithSsh;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.NetworkMemberWithSsh;
 import io.getambassador.v2.Mapping;
 import io.getambassador.v2.MappingBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingDiscriminator.java
index e4bd776..12d99c3 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingDiscriminator.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMapping.GERRIT_MAPPING;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplica.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplica.java
index 6619c75..8fda99e 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplica.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplica.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.network.Constants.INFO_REFS_PATTERN;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.getambassador.v2.MappingBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplicaDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplicaDiscriminator.java
index 42dd429..9a4da43 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplicaDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingGETReplicaDiscriminator.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingGETReplica.GERRIT_MAPPING_GET_REPLICA;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplica.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplica.java
index 5274f50..1779990 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplica.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplica.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.network.Constants.UPLOAD_PACK_URL_PATTERN;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.getambassador.v2.MappingBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplicaDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplicaDiscriminator.java
index cb1b8d9..a1c02c8 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplicaDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPOSTReplicaDiscriminator.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingPOSTReplica.GERRIT_MAPPING_POST_REPLICA;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimary.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimary.java
index 0c93683..f62b74a 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimary.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimary.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.getambassador.v2.MappingBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimaryDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimaryDiscriminator.java
index abb7b90..0d38c69 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimaryDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingPrimaryDiscriminator.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingPrimary.GERRIT_MAPPING_PRIMARY;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiver.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiver.java
index a35ac1f..2d50b65 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiver.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiver.java
@@ -17,8 +17,8 @@
 import static com.google.gerrit.k8s.operator.network.Constants.PROJECTS_URL_PATTERN;
 import static com.google.gerrit.k8s.operator.network.Constants.RECEIVE_PACK_URL_PATTERN;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
 import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverService;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.getambassador.v2.MappingBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverDiscriminator.java
index f5aa07f..a31e905 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverDiscriminator.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingReceiver.GERRIT_MAPPING_RECEIVER;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGET.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGET.java
index 7f59488..aadd9dc 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGET.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGET.java
@@ -16,8 +16,8 @@
 
 import static com.google.gerrit.k8s.operator.network.Constants.INFO_REFS_PATTERN;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
 import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverService;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.getambassador.v2.MappingBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGETDiscriminator.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGETDiscriminator.java
index 27b36e8..24cf7f8 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGETDiscriminator.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterMappingReceiverGETDiscriminator.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.k8s.operator.network.ambassador.dependent.GerritClusterMappingReceiverGET.GERRIT_MAPPING_RECEIVER_GET;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterTLSContext.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterTLSContext.java
index 52e5b72..7cae8da 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterTLSContext.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterTLSContext.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.TLSContext;
 import io.getambassador.v2.TLSContextBuilder;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/LoadBalanceCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/LoadBalanceCondition.java
index 6f9b3b1..ea9066a 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/LoadBalanceCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/LoadBalanceCondition.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/MappingDependentResourceInterface.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/MappingDependentResourceInterface.java
index 097e972..dc27428 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/MappingDependentResourceInterface.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/MappingDependentResourceInterface.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/ReceiverMappingCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/ReceiverMappingCondition.java
index b9f88fd..a83f984 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/ReceiverMappingCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/ReceiverMappingCondition.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/SingleMappingCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/SingleMappingCondition.java
index 07606c5..52a1f2c 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/SingleMappingCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/SingleMappingCondition.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/TLSContextCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/TLSContextCondition.java
index 1749180..053c559 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/TLSContextCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/TLSContextCondition.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.ambassador.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Mapping;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ingress/GerritIngressReconciler.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ingress/GerritIngressReconciler.java
index 64d7d2b..30c90a9 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ingress/GerritIngressReconciler.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ingress/GerritIngressReconciler.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.k8s.operator.network.GerritClusterIngressCondition;
 import com.google.gerrit.k8s.operator.network.ingress.dependent.GerritClusterIngress;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import com.google.inject.Singleton;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ingress/dependent/GerritClusterIngress.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ingress/dependent/GerritClusterIngress.java
index 5b25e07..a62e298 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/ingress/dependent/GerritClusterIngress.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/ingress/dependent/GerritClusterIngress.java
@@ -14,12 +14,12 @@
 
 package com.google.gerrit.k8s.operator.network.ingress.dependent;
 
-import static com.google.gerrit.k8s.operator.network.model.GerritNetwork.SESSION_COOKIE_NAME;
+import static com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork.SESSION_COOKIE_NAME;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritService;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
 import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverService;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 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;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/GerritIstioReconciler.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/GerritIstioReconciler.java
index adbbf72..4d12941 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/GerritIstioReconciler.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/GerritIstioReconciler.java
@@ -22,7 +22,7 @@
 import com.google.gerrit.k8s.operator.network.istio.dependent.GerritIstioCondition;
 import com.google.gerrit.k8s.operator.network.istio.dependent.GerritIstioDestinationRule;
 import com.google.gerrit.k8s.operator.network.istio.dependent.GerritIstioVirtualService;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import com.google.inject.Singleton;
 import io.fabric8.istio.api.networking.v1beta1.DestinationRule;
 import io.fabric8.istio.api.networking.v1beta1.VirtualService;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioGateway.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioGateway.java
index a300f7d..099a4ed 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioGateway.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioGateway.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.k8s.operator.network.istio.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.fabric8.istio.api.networking.v1beta1.Gateway;
 import io.fabric8.istio.api.networking.v1beta1.GatewayBuilder;
 import io.fabric8.istio.api.networking.v1beta1.Server;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioCondition.java
index eb632e9..ac6c6c3 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioCondition.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioCondition.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.istio.dependent;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.fabric8.istio.api.networking.v1beta1.VirtualService;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioDestinationRule.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioDestinationRule.java
index 087719d..98f8267 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioDestinationRule.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioDestinationRule.java
@@ -14,13 +14,13 @@
 
 package com.google.gerrit.k8s.operator.network.istio.dependent;
 
-import static com.google.gerrit.k8s.operator.network.model.GerritNetwork.SESSION_COOKIE_NAME;
-import static com.google.gerrit.k8s.operator.network.model.GerritNetwork.SESSION_COOKIE_TTL;
+import static com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork.SESSION_COOKIE_NAME;
+import static com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork.SESSION_COOKIE_TTL;
 
-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.GerritTemplate;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.fabric8.istio.api.networking.v1beta1.DestinationRule;
 import io.fabric8.istio.api.networking.v1beta1.DestinationRuleBuilder;
 import io.fabric8.istio.api.networking.v1beta1.LoadBalancerSettingsSimpleLB;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioVirtualService.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioVirtualService.java
index 31c8e18..f0d683c 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioVirtualService.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritIstioVirtualService.java
@@ -14,12 +14,12 @@
 
 package com.google.gerrit.k8s.operator.network.istio.dependent;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritService;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
-import com.google.gerrit.k8s.operator.network.model.NetworkMember;
-import com.google.gerrit.k8s.operator.network.model.NetworkMemberWithSsh;
 import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverService;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.NetworkMember;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.NetworkMemberWithSsh;
 import io.fabric8.istio.api.networking.v1beta1.HTTPMatchRequest;
 import io.fabric8.istio.api.networking.v1beta1.HTTPMatchRequestBuilder;
 import io.fabric8.istio.api.networking.v1beta1.HTTPRoute;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/none/GerritNoIngressReconciler.java b/operator/src/main/java/com/google/gerrit/k8s/operator/network/none/GerritNoIngressReconciler.java
index c184fe9..2179260 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/none/GerritNoIngressReconciler.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/network/none/GerritNoIngressReconciler.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.network.none;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import com.google.inject.Singleton;
 import io.javaoperatorsdk.operator.api.reconciler.Context;
 import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/ReceiverReconciler.java b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/ReceiverReconciler.java
index fd6e59e..b48a05f 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/ReceiverReconciler.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/ReceiverReconciler.java
@@ -17,8 +17,8 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverDeployment;
 import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverService;
-import com.google.gerrit.k8s.operator.receiver.model.Receiver;
-import com.google.gerrit.k8s.operator.receiver.model.ReceiverStatus;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.ReceiverStatus;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import io.fabric8.kubernetes.api.model.Secret;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverDeployment.java b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverDeployment.java
index 0f2a294..16c0072 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverDeployment.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverDeployment.java
@@ -14,10 +14,10 @@
 
 package com.google.gerrit.k8s.operator.receiver.dependent;
 
-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.shared.model.NfsWorkaroundConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.NfsWorkaroundConfig;
 import io.fabric8.kubernetes.api.model.Container;
 import io.fabric8.kubernetes.api.model.ContainerPort;
 import io.fabric8.kubernetes.api.model.Volume;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverSecret.java b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverSecret.java
index d432cc4..ca4700a 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverSecret.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverSecret.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.receiver.dependent;
 
-import com.google.gerrit.k8s.operator.receiver.model.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
 import io.fabric8.kubernetes.api.model.Secret;
 import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
 import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource;
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 307be1a..3ccb644 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
@@ -16,9 +16,9 @@
 
 import static com.google.gerrit.k8s.operator.receiver.dependent.ReceiverDeployment.HTTP_PORT;
 
-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.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
 import io.fabric8.kubernetes.api.model.Service;
 import io.fabric8.kubernetes.api.model.ServiceBuilder;
 import io.fabric8.kubernetes.api.model.ServicePort;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/server/AdmissionWebhookServlet.java b/operator/src/main/java/com/google/gerrit/k8s/operator/server/AdmissionWebhookServlet.java
index 59f8d89..a886988 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/server/AdmissionWebhookServlet.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/server/AdmissionWebhookServlet.java
@@ -20,4 +20,8 @@
   private static final long serialVersionUID = 1L;
 
   public abstract String getName();
+
+  public abstract String getVersion();
+
+  public abstract String getURI();
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/server/HttpServer.java b/operator/src/main/java/com/google/gerrit/k8s/operator/server/HttpServer.java
index e4fb2b2..23b8055 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/server/HttpServer.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/server/HttpServer.java
@@ -62,8 +62,7 @@
 
     ServletHandler servletHandler = new ServletHandler();
     for (AdmissionWebhookServlet servlet : admissionWebhookServlets) {
-      servletHandler.addServletWithMapping(
-          new ServletHolder(servlet), "/admission/" + servlet.getName());
+      servletHandler.addServletWithMapping(new ServletHolder(servlet), servlet.getURI());
     }
     servletHandler.addServletWithMapping(HealthcheckServlet.class, "/health");
     server.setHandler(servletHandler);
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/server/ServerModule.java b/operator/src/main/java/com/google/gerrit/k8s/operator/server/ServerModule.java
index 00de7c7..ae613dd 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/server/ServerModule.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/server/ServerModule.java
@@ -16,6 +16,9 @@
 
 import static com.google.gerrit.k8s.operator.server.FileSystemKeyStoreProvider.KEYSTORE_PATH;
 
+import com.google.gerrit.k8s.operator.v1alpha.admission.servlet.GerritAdmissionWebhook;
+import com.google.gerrit.k8s.operator.v1alpha.admission.servlet.GerritClusterAdmissionWebhook;
+import com.google.gerrit.k8s.operator.v1alpha.admission.servlet.GitGcAdmissionWebhook;
 import com.google.inject.AbstractModule;
 import com.google.inject.multibindings.Multibinder;
 import java.io.File;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/server/ValidatingAdmissionWebhookServlet.java b/operator/src/main/java/com/google/gerrit/k8s/operator/server/ValidatingAdmissionWebhookServlet.java
index 222b187..f6d7c39 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/server/ValidatingAdmissionWebhookServlet.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/server/ValidatingAdmissionWebhookServlet.java
@@ -28,7 +28,7 @@
   private static final long serialVersionUID = 1L;
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
-  abstract Status validate(HasMetadata resource);
+  public abstract Status validate(HasMetadata resource);
 
   @Override
   public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
@@ -53,4 +53,9 @@
     logger.atFine().log(
         "Admission request responded with %s", admissionReq.getResponse().toString());
   }
+
+  @Override
+  public String getURI() {
+    return String.format("/admission/%s/%s", getVersion(), getName());
+  }
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/server/GerritAdmissionWebhook.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/admission/servlet/GerritAdmissionWebhook.java
similarity index 81%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/server/GerritAdmissionWebhook.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/admission/servlet/GerritAdmissionWebhook.java
index e382c6c..f74cb59 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/server/GerritAdmissionWebhook.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/admission/servlet/GerritAdmissionWebhook.java
@@ -12,15 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.server;
+package com.google.gerrit.k8s.operator.v1alpha.admission.servlet;
 
-import static com.google.gerrit.k8s.operator.shared.model.GlobalRefDbConfig.RefDatabase.SPANNER;
-import static com.google.gerrit.k8s.operator.shared.model.GlobalRefDbConfig.RefDatabase.ZOOKEEPER;
+import static com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GlobalRefDbConfig.RefDatabase.SPANNER;
+import static com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GlobalRefDbConfig.RefDatabase.ZOOKEEPER;
 
-import com.google.gerrit.k8s.operator.gerrit.config.GerritConfigBuilder;
 import com.google.gerrit.k8s.operator.gerrit.config.InvalidGerritConfigException;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-import com.google.gerrit.k8s.operator.shared.model.GlobalRefDbConfig;
+import com.google.gerrit.k8s.operator.server.ValidatingAdmissionWebhookServlet;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GlobalRefDbConfig;
+import com.google.gerrit.k8s.operator.v1alpha.gerrit.config.GerritConfigBuilder;
 import com.google.inject.Singleton;
 import io.fabric8.kubernetes.api.model.HasMetadata;
 import io.fabric8.kubernetes.api.model.Status;
@@ -33,7 +34,7 @@
   private static final long serialVersionUID = 1L;
 
   @Override
-  Status validate(HasMetadata resource) {
+  public Status validate(HasMetadata resource) {
     if (!(resource instanceof Gerrit)) {
       return new StatusBuilder()
           .withCode(HttpServletResponse.SC_BAD_REQUEST)
@@ -83,7 +84,7 @@
   }
 
   private void invalidGerritConfiguration(Gerrit gerrit) throws InvalidGerritConfigException {
-    new GerritConfigBuilder().forGerrit(gerrit).validate();
+    new GerritConfigBuilder(gerrit).validate();
   }
 
   private boolean noRefDbConfiguredForHA(Gerrit gerrit) {
@@ -107,4 +108,9 @@
   public String getName() {
     return "gerrit";
   }
+
+  @Override
+  public String getVersion() {
+    return "v1alpha";
+  }
 }
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/v1alpha/admission/servlet/GerritClusterAdmissionWebhook.java
similarity index 86%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhook.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/admission/servlet/GerritClusterAdmissionWebhook.java
index a2229ae..8817559 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/server/GerritClusterAdmissionWebhook.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/admission/servlet/GerritClusterAdmissionWebhook.java
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.server;
+package com.google.gerrit.k8s.operator.v1alpha.admission.servlet;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-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.server.ValidatingAdmissionWebhookServlet;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
 import com.google.inject.Singleton;
 import io.fabric8.kubernetes.api.model.HasMetadata;
 import io.fabric8.kubernetes.api.model.Status;
@@ -28,7 +29,7 @@
   private static final long serialVersionUID = 1L;
 
   @Override
-  Status validate(HasMetadata resource) {
+  public Status validate(HasMetadata resource) {
     if (!(resource instanceof GerritCluster)) {
       return new StatusBuilder()
           .withCode(HttpServletResponse.SC_BAD_REQUEST)
@@ -94,4 +95,9 @@
   public String getName() {
     return "gerritcluster";
   }
+
+  @Override
+  public String getVersion() {
+    return "v1alpha";
+  }
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/server/GitGcAdmissionWebhook.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/admission/servlet/GitGcAdmissionWebhook.java
similarity index 91%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/server/GitGcAdmissionWebhook.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/admission/servlet/GitGcAdmissionWebhook.java
index 6134f17..3198c1a 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/server/GitGcAdmissionWebhook.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/admission/servlet/GitGcAdmissionWebhook.java
@@ -12,10 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.server;
+package com.google.gerrit.k8s.operator.v1alpha.admission.servlet;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollection;
+import com.google.gerrit.k8s.operator.server.ValidatingAdmissionWebhookServlet;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollection;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import io.fabric8.kubernetes.api.model.HasMetadata;
@@ -44,7 +45,7 @@
   }
 
   @Override
-  Status validate(HasMetadata resource) {
+  public Status validate(HasMetadata resource) {
     if (!(resource instanceof GitGarbageCollection)) {
       return new StatusBuilder()
           .withCode(HttpServletResponse.SC_BAD_REQUEST)
@@ -105,4 +106,9 @@
   public String getName() {
     return "gitgc";
   }
+
+  @Override
+  public String getVersion() {
+    return "v1alpha";
+  }
 }
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritCluster.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/cluster/GerritCluster.java
similarity index 94%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritCluster.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/cluster/GerritCluster.java
index 631336b..b88ab09 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritCluster.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/cluster/GerritCluster.java
@@ -12,16 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.cluster.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.cluster;
 
 import static com.google.gerrit.k8s.operator.cluster.dependent.NfsIdmapdConfigMap.NFS_IDMAPD_CM_NAME;
 import static com.google.gerrit.k8s.operator.cluster.dependent.SharedPVC.SHARED_PVC_NAME;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.k8s.operator.cluster.GerritClusterMemberSpec;
-import com.google.gerrit.k8s.operator.shared.model.ContainerImageConfig;
-import com.google.gerrit.k8s.operator.shared.model.SharedStorage.ExternalPVCConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.ContainerImageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.SharedStorage.ExternalPVCConfig;
 import io.fabric8.kubernetes.api.model.Container;
 import io.fabric8.kubernetes.api.model.ContainerBuilder;
 import io.fabric8.kubernetes.api.model.EnvVar;
@@ -175,12 +174,6 @@
   }
 
   @JsonIgnore
-  public static boolean isMemberPartOfCluster(
-      GerritClusterMemberSpec memberSpec, GerritCluster cluster) {
-    return memberSpec.getCluster().equals(cluster.getMetadata().getName());
-  }
-
-  @JsonIgnore
   public Container createNfsInitContainer() {
     return createNfsInitContainer(
         getSpec().getStorage().getStorageClasses().getNfsWorkaround().getIdmapdConfig() != null,
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterSpec.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/cluster/GerritClusterSpec.java
similarity index 79%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterSpec.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/cluster/GerritClusterSpec.java
index e554ad8..956c678 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterSpec.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/cluster/GerritClusterSpec.java
@@ -12,14 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.cluster.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.cluster;
 
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplate;
-import com.google.gerrit.k8s.operator.receiver.model.ReceiverTemplate;
-import com.google.gerrit.k8s.operator.shared.model.ContainerImageConfig;
-import com.google.gerrit.k8s.operator.shared.model.GerritClusterIngressConfig;
-import com.google.gerrit.k8s.operator.shared.model.GerritStorageConfig;
-import com.google.gerrit.k8s.operator.shared.model.GlobalRefDbConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.ReceiverTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.ContainerImageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritClusterIngressConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritStorageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GlobalRefDbConfig;
 import java.util.ArrayList;
 import java.util.List;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterStatus.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/cluster/GerritClusterStatus.java
similarity index 92%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterStatus.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/cluster/GerritClusterStatus.java
index 9cfd647..a0b853d 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/model/GerritClusterStatus.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/cluster/GerritClusterStatus.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.cluster.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.cluster;
 
 import java.util.List;
 import java.util.Map;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/Gerrit.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/Gerrit.java
similarity index 95%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/Gerrit.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/Gerrit.java
index 3c21a0f..4027cdd 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/Gerrit.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/Gerrit.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import io.fabric8.kubernetes.api.model.Namespaced;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritDebugConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritDebugConfig.java
similarity index 93%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritDebugConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritDebugConfig.java
index 69d32ad..7e7a5fd 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritDebugConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritDebugConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 public class GerritDebugConfig {
   private boolean enabled;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritInitConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritInitConfig.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritInitConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritInitConfig.java
index 07d5270..82dc41f 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritInitConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritInitConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import java.util.List;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritModule.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritModule.java
similarity index 95%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritModule.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritModule.java
index 68e4c0f..5b0f241 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritModule.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritModule.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import java.io.Serializable;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritPlugin.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritPlugin.java
similarity index 95%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritPlugin.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritPlugin.java
index 9fd2087..196ecb4 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritPlugin.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritPlugin.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritProbe.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritProbe.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritProbe.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritProbe.java
index e66c786..46733ed 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritProbe.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritProbe.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritSite.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritSite.java
similarity index 93%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritSite.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritSite.java
index 4c908bf..2b604c8 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritSite.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritSite.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 import io.fabric8.kubernetes.api.model.Quantity;
 import java.io.Serializable;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritSpec.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritSpec.java
similarity index 82%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritSpec.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritSpec.java
index 58f5538..ed21087 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritSpec.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritSpec.java
@@ -12,12 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
-import com.google.gerrit.k8s.operator.shared.model.ContainerImageConfig;
-import com.google.gerrit.k8s.operator.shared.model.GerritStorageConfig;
-import com.google.gerrit.k8s.operator.shared.model.GlobalRefDbConfig;
-import com.google.gerrit.k8s.operator.shared.model.IngressConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.ContainerImageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritStorageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GlobalRefDbConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.IngressConfig;
 
 public class GerritSpec extends GerritTemplateSpec {
   private ContainerImageConfig containerImages = new ContainerImageConfig();
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritStatus.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritStatus.java
similarity index 95%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritStatus.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritStatus.java
index aeeb403..f779f75 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritStatus.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritStatus.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 import java.util.HashMap;
 import java.util.Map;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritTemplate.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritTemplate.java
similarity index 88%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritTemplate.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritTemplate.java
index 602431a..1144737 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritTemplate.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritTemplate.java
@@ -12,30 +12,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.shared.model.GlobalRefDbConfig;
-import com.google.gerrit.k8s.operator.shared.model.IngressConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GlobalRefDbConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.IngressConfig;
 import io.fabric8.kubernetes.api.model.KubernetesResource;
 import io.fabric8.kubernetes.api.model.ObjectMeta;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
-import io.sundr.builder.annotations.Buildable;
 
 @JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class)
 @JsonInclude(JsonInclude.Include.NON_NULL)
 @JsonPropertyOrder({"metadata", "spec"})
-@Buildable(
-    editableEnabled = false,
-    validationEnabled = false,
-    generateBuilderPackage = false,
-    lazyCollectionInitEnabled = false,
-    builderPackage = "io.fabric8.kubernetes.api.builder")
 public class GerritTemplate implements KubernetesResource {
   private static final long serialVersionUID = 1L;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritTemplateSpec.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritTemplateSpec.java
similarity index 97%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritTemplateSpec.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritTemplateSpec.java
index 4ae5737..4349cd1 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gerrit/model/GerritTemplateSpec.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gerrit/GerritTemplateSpec.java
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gerrit.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.google.gerrit.k8s.operator.shared.model.HttpSshServiceConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.HttpSshServiceConfig;
 import io.fabric8.kubernetes.api.model.Affinity;
 import io.fabric8.kubernetes.api.model.ResourceRequirements;
 import io.fabric8.kubernetes.api.model.Toleration;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/model/GitGarbageCollection.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gitgc/GitGarbageCollection.java
similarity index 87%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/model/GitGarbageCollection.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gitgc/GitGarbageCollection.java
index c32aa6d..7d268bd 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/model/GitGarbageCollection.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gitgc/GitGarbageCollection.java
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gitgc.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc;
 
-import com.google.gerrit.k8s.operator.cluster.GerritClusterMember;
 import io.fabric8.kubernetes.api.model.Namespaced;
+import io.fabric8.kubernetes.client.CustomResource;
 import io.fabric8.kubernetes.model.annotation.Group;
 import io.fabric8.kubernetes.model.annotation.Plural;
 import io.fabric8.kubernetes.model.annotation.ShortNames;
@@ -28,7 +28,7 @@
 @ShortNames("gitgc")
 @Plural("gitgcs")
 public class GitGarbageCollection
-    extends GerritClusterMember<GitGarbageCollectionSpec, GitGarbageCollectionStatus>
+    extends CustomResource<GitGarbageCollectionSpec, GitGarbageCollectionStatus>
     implements Namespaced {
   private static final long serialVersionUID = 1L;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/model/GitGarbageCollectionSpec.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gitgc/GitGarbageCollectionSpec.java
similarity index 92%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/model/GitGarbageCollectionSpec.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gitgc/GitGarbageCollectionSpec.java
index 5d5f6f5..7648f13 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/model/GitGarbageCollectionSpec.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gitgc/GitGarbageCollectionSpec.java
@@ -12,9 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gitgc.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc;
 
-import com.google.gerrit.k8s.operator.cluster.GerritClusterMemberSpec;
 import io.fabric8.kubernetes.api.model.Affinity;
 import io.fabric8.kubernetes.api.model.ResourceRequirements;
 import io.fabric8.kubernetes.api.model.Toleration;
@@ -23,7 +22,7 @@
 import java.util.Objects;
 import java.util.Set;
 
-public class GitGarbageCollectionSpec implements GerritClusterMemberSpec {
+public class GitGarbageCollectionSpec {
   private String cluster;
   private String schedule;
   private Set<String> projects;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/model/GitGarbageCollectionStatus.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gitgc/GitGarbageCollectionStatus.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/model/GitGarbageCollectionStatus.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gitgc/GitGarbageCollectionStatus.java
index fd36476..ddff4c7 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/gitgc/model/GitGarbageCollectionStatus.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/gitgc/GitGarbageCollectionStatus.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.gitgc.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc;
 
 import java.util.HashSet;
 import java.util.Objects;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/model/GerritNetwork.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/GerritNetwork.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/network/model/GerritNetwork.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/GerritNetwork.java
index e2609df..7596f54 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/model/GerritNetwork.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/GerritNetwork.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.network.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.network;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import io.fabric8.kubernetes.api.model.Namespaced;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/model/GerritNetworkSpec.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/GerritNetworkSpec.java
similarity index 92%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/network/model/GerritNetworkSpec.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/GerritNetworkSpec.java
index 1bfd222..406341f 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/model/GerritNetworkSpec.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/GerritNetworkSpec.java
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.network.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.network;
 
-import com.google.gerrit.k8s.operator.shared.model.GerritClusterIngressConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritClusterIngressConfig;
 import java.util.ArrayList;
 import java.util.List;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/model/NetworkMember.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/NetworkMember.java
similarity index 88%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/network/model/NetworkMember.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/NetworkMember.java
index d5a2fd6..433dbbe 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/model/NetworkMember.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/NetworkMember.java
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.network.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.network;
 
-import com.google.gerrit.k8s.operator.shared.model.HttpServiceConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.HttpServiceConfig;
 
 public class NetworkMember {
   private String name;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/network/model/NetworkMemberWithSsh.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/NetworkMemberWithSsh.java
similarity index 86%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/network/model/NetworkMemberWithSsh.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/NetworkMemberWithSsh.java
index 382c4f6..1668739 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/network/model/NetworkMemberWithSsh.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/network/NetworkMemberWithSsh.java
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.network.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.network;
 
-import com.google.gerrit.k8s.operator.shared.model.HttpSshServiceConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.HttpSshServiceConfig;
 
 public class NetworkMemberWithSsh extends NetworkMember {
   private int sshPort = 29418;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/Receiver.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/Receiver.java
similarity index 94%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/Receiver.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/Receiver.java
index ffda566..bc546df 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/Receiver.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/Receiver.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.receiver.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.receiver;
 
 import io.fabric8.kubernetes.api.model.Namespaced;
 import io.fabric8.kubernetes.client.CustomResource;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverProbe.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverProbe.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverProbe.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverProbe.java
index acf30eb..78aaa58 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverProbe.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverProbe.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.receiver.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.receiver;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.google.gerrit.k8s.operator.receiver.dependent.ReceiverDeployment;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverSpec.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverSpec.java
similarity index 82%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverSpec.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverSpec.java
index 96df251..005fa49 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverSpec.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverSpec.java
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.receiver.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.receiver;
 
-import com.google.gerrit.k8s.operator.shared.model.ContainerImageConfig;
-import com.google.gerrit.k8s.operator.shared.model.IngressConfig;
-import com.google.gerrit.k8s.operator.shared.model.StorageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.ContainerImageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.IngressConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.StorageConfig;
 
 public class ReceiverSpec extends ReceiverTemplateSpec {
   private ContainerImageConfig containerImages = new ContainerImageConfig();
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverStatus.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverStatus.java
similarity index 94%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverStatus.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverStatus.java
index 81fb196..915a62f 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverStatus.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverStatus.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.receiver.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.receiver;
 
 public class ReceiverStatus {
   private boolean ready;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplate.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverTemplate.java
similarity index 85%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplate.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverTemplate.java
index eb932eb..f559419 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplate.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverTemplate.java
@@ -12,30 +12,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.receiver.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.receiver;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.shared.model.IngressConfig;
-import com.google.gerrit.k8s.operator.shared.model.StorageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.IngressConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.StorageConfig;
 import io.fabric8.kubernetes.api.model.KubernetesResource;
 import io.fabric8.kubernetes.api.model.ObjectMeta;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
-import io.sundr.builder.annotations.Buildable;
 
 @JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class)
 @JsonInclude(JsonInclude.Include.NON_NULL)
 @JsonPropertyOrder({"metadata", "spec"})
-@Buildable(
-    editableEnabled = false,
-    validationEnabled = false,
-    generateBuilderPackage = false,
-    lazyCollectionInitEnabled = false,
-    builderPackage = "io.fabric8.kubernetes.api.builder")
 public class ReceiverTemplate implements KubernetesResource {
   private static final long serialVersionUID = 1L;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplateSpec.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverTemplateSpec.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplateSpec.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverTemplateSpec.java
index 704067a..22ee904 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/receiver/model/ReceiverTemplateSpec.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/receiver/ReceiverTemplateSpec.java
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.receiver.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.receiver;
 
-import com.google.gerrit.k8s.operator.shared.model.HttpServiceConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.HttpServiceConfig;
 import io.fabric8.kubernetes.api.model.Affinity;
 import io.fabric8.kubernetes.api.model.IntOrString;
 import io.fabric8.kubernetes.api.model.ResourceRequirements;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/BusyBoxImage.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/BusyBoxImage.java
similarity index 95%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/BusyBoxImage.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/BusyBoxImage.java
index 8adfd70..5c5388f 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/BusyBoxImage.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/BusyBoxImage.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/ContainerImageConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/ContainerImageConfig.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/ContainerImageConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/ContainerImageConfig.java
index 45be615..4f84824 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/ContainerImageConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/ContainerImageConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 import io.fabric8.kubernetes.api.model.LocalObjectReference;
 import java.util.HashSet;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritClusterIngressConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritClusterIngressConfig.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritClusterIngressConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritClusterIngressConfig.java
index d7fd556..a915820 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritClusterIngressConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritClusterIngressConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.util.Map;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressAmbassadorConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritIngressAmbassadorConfig.java
similarity index 93%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressAmbassadorConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritIngressAmbassadorConfig.java
index 375caa8..b34eb67 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressAmbassadorConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritIngressAmbassadorConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 import java.util.List;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressSshConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritIngressSshConfig.java
similarity index 92%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressSshConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritIngressSshConfig.java
index 62ac188..6203803 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressSshConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritIngressSshConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 public class GerritIngressSshConfig {
   private boolean enabled = false;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressTlsConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritIngressTlsConfig.java
similarity index 93%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressTlsConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritIngressTlsConfig.java
index 8a4362a..b4552f5 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritIngressTlsConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritIngressTlsConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 public class GerritIngressTlsConfig {
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritRepositoryConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritRepositoryConfig.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritRepositoryConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritRepositoryConfig.java
index becbbf5..f593c1e 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritRepositoryConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritRepositoryConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritStorageConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritStorageConfig.java
similarity index 94%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritStorageConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritStorageConfig.java
index 25ca54f..d66a0cd 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GerritStorageConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GerritStorageConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 public class GerritStorageConfig extends StorageConfig {
   private PluginCacheConfig pluginCache = new PluginCacheConfig();
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GlobalRefDbConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GlobalRefDbConfig.java
similarity index 95%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GlobalRefDbConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GlobalRefDbConfig.java
index 28b2a81..d338555 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/GlobalRefDbConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/GlobalRefDbConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 public class GlobalRefDbConfig {
   private RefDatabase database = RefDatabase.NONE;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/HttpServiceConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/HttpServiceConfig.java
similarity index 93%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/HttpServiceConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/HttpServiceConfig.java
index e8eb3ae..8e7d651 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/HttpServiceConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/HttpServiceConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 import java.io.Serializable;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/HttpSshServiceConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/HttpSshServiceConfig.java
similarity index 93%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/HttpSshServiceConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/HttpSshServiceConfig.java
index cda6975..8655c32 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/HttpSshServiceConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/HttpSshServiceConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 import java.io.Serializable;
 
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/v1alpha/api/model/shared/IngressConfig.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/IngressConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/IngressConfig.java
index dfbc789..f83b189 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/v1alpha/api/model/shared/IngressConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/NfsWorkaroundConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/NfsWorkaroundConfig.java
similarity index 94%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/NfsWorkaroundConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/NfsWorkaroundConfig.java
index bbce894..ed4a4d1 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/NfsWorkaroundConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/NfsWorkaroundConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 public class NfsWorkaroundConfig {
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/SharedStorage.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/SharedStorage.java
similarity index 96%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/SharedStorage.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/SharedStorage.java
index be68fbf..d2193c1 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/SharedStorage.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/SharedStorage.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 import io.fabric8.kubernetes.api.model.LabelSelector;
 import io.fabric8.kubernetes.api.model.Quantity;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/SpannerRefDbConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/SpannerRefDbConfig.java
similarity index 94%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/SpannerRefDbConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/SpannerRefDbConfig.java
index 73e1ea3..eee7eab 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/SpannerRefDbConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/SpannerRefDbConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 public class SpannerRefDbConfig {
   private String projectName;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/StorageClassConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/StorageClassConfig.java
similarity index 95%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/StorageClassConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/StorageClassConfig.java
index ff11a4f..de4906b 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/StorageClassConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/StorageClassConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 public class StorageClassConfig {
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/StorageConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/StorageConfig.java
similarity index 95%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/StorageConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/StorageConfig.java
index 2551c4b..566ce6e 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/StorageConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/StorageConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 public class StorageConfig {
 
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/ZookeeperRefDbConfig.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/ZookeeperRefDbConfig.java
similarity index 93%
rename from operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/ZookeeperRefDbConfig.java
rename to operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/ZookeeperRefDbConfig.java
index f86ebfd..e90faeb 100644
--- a/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/ZookeeperRefDbConfig.java
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/api/model/shared/ZookeeperRefDbConfig.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.k8s.operator.shared.model;
+package com.google.gerrit.k8s.operator.v1alpha.api.model.shared;
 
 public class ZookeeperRefDbConfig {
   private String connectString;
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/GerritConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/GerritConfigBuilder.java
new file mode 100644
index 0000000..221704d
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/GerritConfigBuilder.java
@@ -0,0 +1,172 @@
+// Copyright (C) 2022 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.k8s.operator.v1alpha.gerrit.config;
+
+import static com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet.HTTP_PORT;
+import static com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet.SSH_PORT;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.k8s.operator.gerrit.config.ConfigBuilder;
+import com.google.gerrit.k8s.operator.gerrit.config.RequiredOption;
+import com.google.gerrit.k8s.operator.gerrit.dependent.GerritService;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.IngressConfig;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class GerritConfigBuilder extends ConfigBuilder {
+  private static final Pattern PROTOCOL_PATTERN = Pattern.compile("^(https?)://.+");
+
+  public GerritConfigBuilder(Gerrit gerrit) {
+    super(
+        gerrit.getSpec().getConfigFiles().getOrDefault("gerrit.config", ""),
+        ImmutableList.copyOf(collectRequiredOptions(gerrit)));
+  }
+
+  private static List<RequiredOption<?>> collectRequiredOptions(Gerrit gerrit) {
+    List<RequiredOption<?>> requiredOptions = new ArrayList<>();
+    requiredOptions.addAll(cacheSection(gerrit));
+    requiredOptions.addAll(containerSection(gerrit));
+    requiredOptions.addAll(gerritSection(gerrit));
+    requiredOptions.addAll(httpdSection(gerrit));
+    requiredOptions.addAll(sshdSection(gerrit));
+    return requiredOptions;
+  }
+
+  private static List<RequiredOption<?>> cacheSection(Gerrit gerrit) {
+    List<RequiredOption<?>> requiredOptions = new ArrayList<>();
+    requiredOptions.add(new RequiredOption<String>("cache", "directory", "cache"));
+    return requiredOptions;
+  }
+
+  private static List<RequiredOption<?>> containerSection(Gerrit gerrit) {
+    List<RequiredOption<?>> requiredOptions = new ArrayList<>();
+    requiredOptions.add(new RequiredOption<String>("container", "user", "gerrit"));
+    requiredOptions.add(
+        new RequiredOption<Boolean>(
+            "container", "replica", gerrit.getSpec().getMode().equals(GerritMode.REPLICA)));
+    requiredOptions.add(
+        new RequiredOption<String>("container", "javaHome", "/usr/lib/jvm/java-11-openjdk"));
+    requiredOptions.add(javaOptions(gerrit));
+    return requiredOptions;
+  }
+
+  private static List<RequiredOption<?>> gerritSection(Gerrit gerrit) {
+    List<RequiredOption<?>> requiredOptions = new ArrayList<>();
+    String serverId = gerrit.getSpec().getServerId();
+    requiredOptions.add(new RequiredOption<String>("gerrit", "basepath", "git"));
+    if (serverId != null && !serverId.isBlank()) {
+      requiredOptions.add(new RequiredOption<String>("gerrit", "serverId", serverId));
+    }
+
+    if (gerrit.getSpec().isHighlyAvailablePrimary()) {
+      requiredOptions.add(
+          new RequiredOption<Set<String>>(
+              "gerrit",
+              "installModule",
+              Set.of("com.gerritforge.gerrit.globalrefdb.validation.LibModule")));
+      requiredOptions.add(
+          new RequiredOption<Set<String>>(
+              "gerrit",
+              "installDbModule",
+              Set.of("com.ericsson.gerrit.plugins.highavailability.ValidationModule")));
+    }
+
+    IngressConfig ingressConfig = gerrit.getSpec().getIngress();
+    if (ingressConfig.isEnabled()) {
+      requiredOptions.add(
+          new RequiredOption<String>("gerrit", "canonicalWebUrl", ingressConfig.getUrl()));
+    }
+
+    return requiredOptions;
+  }
+
+  private static List<RequiredOption<?>> httpdSection(Gerrit gerrit) {
+    List<RequiredOption<?>> requiredOptions = new ArrayList<>();
+    IngressConfig ingressConfig = gerrit.getSpec().getIngress();
+    if (ingressConfig.isEnabled()) {
+      requiredOptions.add(listenUrl(ingressConfig.getUrl()));
+    }
+    return requiredOptions;
+  }
+
+  private static List<RequiredOption<?>> sshdSection(Gerrit gerrit) {
+    List<RequiredOption<?>> requiredOptions = new ArrayList<>();
+    requiredOptions.add(sshListenAddress(gerrit));
+    IngressConfig ingressConfig = gerrit.getSpec().getIngress();
+    if (ingressConfig.isEnabled() && gerrit.isSshEnabled()) {
+      requiredOptions.add(sshAdvertisedAddress(gerrit));
+    }
+    return requiredOptions;
+  }
+
+  private static RequiredOption<Set<String>> javaOptions(Gerrit gerrit) {
+    Set<String> javaOptions = new HashSet<>();
+    javaOptions.add("-Djavax.net.ssl.trustStore=/var/gerrit/etc/keystore");
+    if (gerrit.getSpec().isHighlyAvailablePrimary()) {
+      javaOptions.add("-Djava.net.preferIPv4Stack=true");
+    }
+    if (gerrit.getSpec().getDebug().isEnabled()) {
+      javaOptions.add("-Xdebug");
+      String debugServerCfg = "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000";
+      if (gerrit.getSpec().getDebug().isSuspend()) {
+        debugServerCfg = debugServerCfg + ",suspend=y";
+      } else {
+        debugServerCfg = debugServerCfg + ",suspend=n";
+      }
+      javaOptions.add(debugServerCfg);
+    }
+    return new RequiredOption<Set<String>>("container", "javaOptions", javaOptions);
+  }
+
+  private static RequiredOption<String> listenUrl(String 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("/");
+    return new RequiredOption<String>("httpd", "listenUrl", listenUrlBuilder.toString());
+  }
+
+  private static RequiredOption<String> sshListenAddress(Gerrit gerrit) {
+    String listenAddress;
+    if (gerrit.isSshEnabled()) {
+      listenAddress = "*:" + SSH_PORT;
+    } else {
+      listenAddress = "off";
+    }
+    return new RequiredOption<String>("sshd", "listenAddress", listenAddress);
+  }
+
+  private static RequiredOption<String> sshAdvertisedAddress(Gerrit gerrit) {
+    return new RequiredOption<String>(
+        "sshd",
+        "advertisedAddress",
+        gerrit.getSpec().getIngress().getFullHostnameForService(GerritService.getName(gerrit))
+            + ":29418");
+  }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/HighAvailabilityPluginConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/HighAvailabilityPluginConfigBuilder.java
new file mode 100644
index 0000000..b96a93b
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/HighAvailabilityPluginConfigBuilder.java
@@ -0,0 +1,65 @@
+// Copyright (C) 2023 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.k8s.operator.v1alpha.gerrit.config;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.k8s.operator.gerrit.config.ConfigBuilder;
+import com.google.gerrit.k8s.operator.gerrit.config.RequiredOption;
+import com.google.gerrit.k8s.operator.gerrit.dependent.GerritStatefulSet;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class HighAvailabilityPluginConfigBuilder extends ConfigBuilder {
+  public HighAvailabilityPluginConfigBuilder(Gerrit gerrit) {
+    super(
+        gerrit.getSpec().getConfigFiles().getOrDefault("high-availability.config", ""),
+        ImmutableList.copyOf(collectRequiredOptions(gerrit)));
+  }
+
+  private static List<RequiredOption<?>> collectRequiredOptions(Gerrit gerrit) {
+    List<RequiredOption<?>> requiredOptions = new ArrayList<>();
+    requiredOptions.add(new RequiredOption<String>("main", "sharedDirectory", "shared"));
+    requiredOptions.add(new RequiredOption<String>("peerInfo", "strategy", "jgroups"));
+    requiredOptions.add(new RequiredOption<String>("peerInfo", "jgroups", "myUrl", null));
+    requiredOptions.add(
+        new RequiredOption<String>("jgroups", "clusterName", gerrit.getMetadata().getName()));
+    requiredOptions.add(new RequiredOption<Boolean>("jgroups", "kubernetes", true));
+    requiredOptions.add(
+        new RequiredOption<String>(
+            "jgroups", "kubernetes", "namespace", gerrit.getMetadata().getNamespace()));
+    requiredOptions.add(
+        new RequiredOption<Set<String>>("jgroups", "kubernetes", "label", getLabels(gerrit)));
+    requiredOptions.add(new RequiredOption<Boolean>("cache", "synchronize", true));
+    requiredOptions.add(new RequiredOption<Boolean>("event", "synchronize", true));
+    requiredOptions.add(new RequiredOption<Boolean>("index", "synchronize", true));
+    requiredOptions.add(new RequiredOption<Boolean>("index", "synchronizeForced", true));
+    requiredOptions.add(new RequiredOption<Boolean>("healthcheck", "enable", true));
+    requiredOptions.add(new RequiredOption<Boolean>("ref-database", "enabled", true));
+    return requiredOptions;
+  }
+
+  private static Set<String> getLabels(Gerrit gerrit) {
+    Map<String, String> selectorLabels = GerritStatefulSet.getSelectorLabels(gerrit);
+    Set<String> labels = new HashSet<>();
+    for (Map.Entry<String, String> label : selectorLabels.entrySet()) {
+      labels.add(label.getKey() + "=" + label.getValue());
+    }
+    return labels;
+  }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/SpannerRefDbPluginConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/SpannerRefDbPluginConfigBuilder.java
new file mode 100644
index 0000000..0f3cb30
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/SpannerRefDbPluginConfigBuilder.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 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.k8s.operator.v1alpha.gerrit.config;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.k8s.operator.gerrit.config.ConfigBuilder;
+import com.google.gerrit.k8s.operator.gerrit.config.RequiredOption;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SpannerRefDbPluginConfigBuilder extends ConfigBuilder {
+  public SpannerRefDbPluginConfigBuilder(Gerrit gerrit) {
+    super(
+        gerrit.getSpec().getConfigFiles().getOrDefault("spanner-refdb.config", ""),
+        ImmutableList.copyOf(collectRequiredOptions(gerrit)));
+  }
+
+  private static List<RequiredOption<?>> collectRequiredOptions(Gerrit gerrit) {
+    List<RequiredOption<?>> requiredOptions = new ArrayList<>();
+    requiredOptions.add(
+        new RequiredOption<String>("ref-database", "spanner", "useEmulator", "false"));
+    requiredOptions.add(
+        new RequiredOption<String>(
+            "ref-database",
+            "spanner",
+            "projectName",
+            gerrit.getSpec().getRefdb().getSpanner().getProjectName()));
+    requiredOptions.add(
+        new RequiredOption<String>(
+            "ref-database", "spanner", "credentialsPath", "/var/gerrit/etc/gcp-credentials.json"));
+    requiredOptions.add(
+        new RequiredOption<String>(
+            "ref-database",
+            "spanner",
+            "instance",
+            gerrit.getSpec().getRefdb().getSpanner().getInstance()));
+    requiredOptions.add(
+        new RequiredOption<String>(
+            "ref-database",
+            "spanner",
+            "database",
+            gerrit.getSpec().getRefdb().getSpanner().getDatabase()));
+    return requiredOptions;
+  }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/ZookeeperRefDbPluginConfigBuilder.java b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/ZookeeperRefDbPluginConfigBuilder.java
new file mode 100644
index 0000000..aabb726
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/v1alpha/gerrit/config/ZookeeperRefDbPluginConfigBuilder.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 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.k8s.operator.v1alpha.gerrit.config;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.k8s.operator.gerrit.config.ConfigBuilder;
+import com.google.gerrit.k8s.operator.gerrit.config.RequiredOption;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ZookeeperRefDbPluginConfigBuilder extends ConfigBuilder {
+  public ZookeeperRefDbPluginConfigBuilder(Gerrit gerrit) {
+    super(
+        gerrit.getSpec().getConfigFiles().getOrDefault("zookeeper-refdb.config", ""),
+        ImmutableList.copyOf(collectRequiredOptions(gerrit)));
+  }
+
+  private static List<RequiredOption<?>> collectRequiredOptions(Gerrit gerrit) {
+    List<RequiredOption<?>> requiredOptions = new ArrayList<>();
+    requiredOptions.add(
+        new RequiredOption<String>(
+            "ref-database",
+            "zookeeper",
+            "connectString",
+            gerrit.getSpec().getRefdb().getZookeeper().getConnectString()));
+    requiredOptions.add(
+        new RequiredOption<String>(
+            "ref-database",
+            "zookeeper",
+            "rootNode",
+            gerrit.getSpec().getRefdb().getZookeeper().getRootNode()));
+    return requiredOptions;
+  }
+}
diff --git a/operator/src/main/resources/META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource b/operator/src/main/resources/META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource
index afd1d50..6fb15ac 100644
--- a/operator/src/main/resources/META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource
+++ b/operator/src/main/resources/META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource
@@ -1,5 +1,5 @@
-com.google.gerrit.k8s.operator.cluster.model.GerritCluster
-com.google.gerrit.k8s.operator.gerrit.model.Gerrit
-com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollection
-com.google.gerrit.k8s.operator.receiver.model.Receiver
-com.google.gerrit.k8s.operator.network.model.GerritNetwork
\ No newline at end of file
+com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster
+com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit
+com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollection
+com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver
+com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork
\ No newline at end of file
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/cluster/GerritRepositoryConfigTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/cluster/GerritRepositoryConfigTest.java
index b2a6fc2..5474780 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/cluster/GerritRepositoryConfigTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/cluster/GerritRepositoryConfigTest.java
@@ -18,7 +18,7 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 
-import com.google.gerrit.k8s.operator.shared.model.GerritRepositoryConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritRepositoryConfig;
 import org.junit.jupiter.api.Test;
 
 public class GerritRepositoryConfigTest {
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritWithIngressE2E.java b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritWithIngressE2E.java
index b0fd748..c848aa9 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritWithIngressE2E.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritWithIngressE2E.java
@@ -27,11 +27,11 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.api.GerritApi;
-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.network.IngressType;
 import com.google.gerrit.k8s.operator.test.AbstractGerritOperatorE2ETest;
 import com.google.gerrit.k8s.operator.test.TestGerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
 import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
 import io.fabric8.kubernetes.api.model.networking.v1.IngressLoadBalancerIngress;
 import io.fabric8.kubernetes.api.model.networking.v1.IngressStatus;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritWithIstioE2E.java b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritWithIstioE2E.java
index 9fe2d05..fdf5ccb 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritWithIstioE2E.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/ClusterManagedGerritWithIstioE2E.java
@@ -25,11 +25,11 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.api.GerritApi;
-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.network.IngressType;
 import com.google.gerrit.k8s.operator.test.AbstractGerritOperatorE2ETest;
 import com.google.gerrit.k8s.operator.test.TestGerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
 import org.junit.jupiter.api.Test;
 
 public class ClusterManagedGerritWithIstioE2E extends AbstractGerritOperatorE2ETest {
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/GerritConfigReconciliationE2E.java b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/GerritConfigReconciliationE2E.java
index cb26331..342d524 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/GerritConfigReconciliationE2E.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/GerritConfigReconciliationE2E.java
@@ -20,13 +20,13 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplate;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplateSpec;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplateSpec.GerritMode;
 import com.google.gerrit.k8s.operator.network.IngressType;
-import com.google.gerrit.k8s.operator.shared.model.HttpSshServiceConfig;
 import com.google.gerrit.k8s.operator.test.AbstractGerritOperatorE2ETest;
 import com.google.gerrit.k8s.operator.test.TestGerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.HttpSshServiceConfig;
 import java.util.HashMap;
 import java.util.Map;
 import org.junit.jupiter.api.BeforeEach;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/StandaloneGerritE2E.java b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/StandaloneGerritE2E.java
index 236567d..b7359ab 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/StandaloneGerritE2E.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/StandaloneGerritE2E.java
@@ -16,10 +16,10 @@
 
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplateSpec.GerritMode;
 import com.google.gerrit.k8s.operator.network.IngressType;
 import com.google.gerrit.k8s.operator.test.AbstractGerritOperatorE2ETest;
 import com.google.gerrit.k8s.operator.test.TestGerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
 import org.junit.jupiter.api.Test;
 
 public class StandaloneGerritE2E extends AbstractGerritOperatorE2ETest {
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/config/GerritConfigBuilderTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/config/GerritConfigBuilderTest.java
index fa0fb31..cedcebb 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/config/GerritConfigBuilderTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/gerrit/config/GerritConfigBuilderTest.java
@@ -18,8 +18,10 @@
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.IngressConfig;
+import com.google.gerrit.k8s.operator.v1alpha.gerrit.config.GerritConfigBuilder;
 import java.util.Map;
 import java.util.Set;
 import org.assertj.core.util.Arrays;
@@ -31,7 +33,7 @@
   @Test
   public void emptyGerritConfigContainsAllPresetConfiguration() {
     Gerrit gerrit = createGerrit("");
-    ConfigBuilder cfgBuilder = new GerritConfigBuilder().forGerrit(gerrit);
+    ConfigBuilder cfgBuilder = new GerritConfigBuilder(gerrit);
     Config cfg = cfgBuilder.build();
     for (RequiredOption<?> opt : cfgBuilder.getRequiredOptions()) {
       if (opt.getExpected() instanceof String || opt.getExpected() instanceof Boolean) {
@@ -49,21 +51,28 @@
   @Test
   public void invalidConfigValueIsRejected() {
     Gerrit gerrit = createGerrit("[gerrit]\n  basePath = invalid");
-    assertThrows(
-        IllegalStateException.class, () -> new GerritConfigBuilder().forGerrit(gerrit).build());
+    assertThrows(IllegalStateException.class, () -> new GerritConfigBuilder(gerrit).build());
   }
 
   @Test
   public void validConfigValueIsAccepted() {
     Gerrit gerrit = createGerrit("[gerrit]\n  basePath = git");
-    assertDoesNotThrow(() -> new GerritConfigBuilder().forGerrit(gerrit).build());
+    assertDoesNotThrow(() -> new GerritConfigBuilder(gerrit).build());
   }
 
   @Test
   public void canonicalWebUrlIsConfigured() {
-    String url = "https://gerrit.example.com";
-    Config cfg = new GerritConfigBuilder().withUrl(url).build();
-    assertTrue(cfg.getString("gerrit", null, "canonicalWebUrl").equals(url));
+    IngressConfig ingressConfig = new IngressConfig();
+    ingressConfig.setEnabled(true);
+    ingressConfig.setHost("gerrit.example.com");
+
+    GerritSpec gerritSpec = new GerritSpec();
+    gerritSpec.setIngress(ingressConfig);
+    Gerrit gerrit = new Gerrit();
+    gerrit.setSpec(gerritSpec);
+    Config cfg = new GerritConfigBuilder(gerrit).build();
+    assertTrue(
+        cfg.getString("gerrit", null, "canonicalWebUrl").equals("http://gerrit.example.com"));
   }
 
   private Gerrit createGerrit(String configText) {
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/gitgc/GitGarbageCollectionE2E.java b/operator/src/test/java/com/google/gerrit/k8s/operator/gitgc/GitGarbageCollectionE2E.java
index 3a62cac..7f31ac2 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/gitgc/GitGarbageCollectionE2E.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/gitgc/GitGarbageCollectionE2E.java
@@ -25,11 +25,11 @@
 import static org.junit.jupiter.api.Assertions.assertNull;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollection;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollectionSpec;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollectionStatus;
 import com.google.gerrit.k8s.operator.network.IngressType;
 import com.google.gerrit.k8s.operator.test.AbstractGerritOperatorE2ETest;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollection;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollectionSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollectionStatus;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
 import io.fabric8.kubernetes.api.model.batch.v1.CronJob;
 import io.fabric8.kubernetes.api.model.batch.v1.Job;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterAmbassadorTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterAmbassadorTest.java
index e705dd1..3f5a9cd 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterAmbassadorTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/network/ambassador/dependent/GerritClusterAmbassadorTest.java
@@ -16,7 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.getambassador.v2.Host;
 import io.getambassador.v2.Mapping;
 import io.getambassador.v2.TLSContext;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/network/ingress/dependent/GerritClusterIngressTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/network/ingress/dependent/GerritClusterIngressTest.java
index 7e854ab..a2e6a24 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/network/ingress/dependent/GerritClusterIngressTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/network/ingress/dependent/GerritClusterIngressTest.java
@@ -16,7 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.fabric8.kubernetes.api.model.networking.v1.Ingress;
 import io.javaoperatorsdk.operator.ReconcilerUtils;
 import java.util.stream.Stream;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioTest.java
index 17b27e2..eaed636 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/network/istio/dependent/GerritClusterIstioTest.java
@@ -16,7 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
 import io.fabric8.istio.api.networking.v1beta1.Gateway;
 import io.fabric8.istio.api.networking.v1beta1.VirtualService;
 import io.javaoperatorsdk.operator.ReconcilerUtils;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/receiver/AbstractClusterManagedReceiverE2E.java b/operator/src/test/java/com/google/gerrit/k8s/operator/receiver/AbstractClusterManagedReceiverE2E.java
index 22bb145..557e140 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/receiver/AbstractClusterManagedReceiverE2E.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/receiver/AbstractClusterManagedReceiverE2E.java
@@ -19,14 +19,14 @@
 import static org.hamcrest.Matchers.equalTo;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-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.ReceiverTemplate;
-import com.google.gerrit.k8s.operator.receiver.model.ReceiverTemplateSpec;
 import com.google.gerrit.k8s.operator.test.AbstractGerritOperatorE2ETest;
 import com.google.gerrit.k8s.operator.test.ReceiverUtil;
 import com.google.gerrit.k8s.operator.test.TestGerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.ReceiverTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.ReceiverTemplateSpec;
 import io.fabric8.kubernetes.api.model.ObjectMeta;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
 import java.io.File;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverTest.java
index 488412d..8698b1a 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/receiver/dependent/ReceiverTest.java
@@ -16,7 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.gerrit.k8s.operator.receiver.model.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
 import io.fabric8.kubernetes.api.model.Service;
 import io.fabric8.kubernetes.api.model.apps.Deployment;
 import io.javaoperatorsdk.operator.ReconcilerUtils;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/server/GerritAdmissionWebhookTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/server/GerritAdmissionWebhookTest.java
index be50752..32b342d 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/server/GerritAdmissionWebhookTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/server/GerritAdmissionWebhookTest.java
@@ -19,14 +19,15 @@
 import static org.hamcrest.Matchers.equalTo;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.cluster.model.GerritClusterSpec;
-import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritSpec;
-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.shared.model.GerritClusterIngressConfig;
 import com.google.gerrit.k8s.operator.test.TestAdmissionWebhookServer;
+import com.google.gerrit.k8s.operator.v1alpha.admission.servlet.GerritAdmissionWebhook;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritClusterSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritClusterIngressConfig;
 import io.fabric8.kubernetes.api.model.DefaultKubernetesResourceList;
 import io.fabric8.kubernetes.api.model.HasMetadata;
 import io.fabric8.kubernetes.api.model.ObjectMeta;
@@ -156,7 +157,8 @@
   private HttpURLConnection sendAdmissionRequest(Gerrit gerrit)
       throws MalformedURLException, IOException {
     HttpURLConnection http =
-        (HttpURLConnection) new URL("http://localhost:8080/admission/gerrit").openConnection();
+        (HttpURLConnection)
+            new URL("http://localhost:8080/admission/v1alpha/gerrit").openConnection();
     http.setRequestMethod(HttpMethod.POST.asString());
     http.setRequestProperty("Content-Type", "application/json");
     http.setDoOutput(true);
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 c97ab7a..55526c7 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
@@ -19,17 +19,19 @@
 import static org.hamcrest.Matchers.equalTo;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-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.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 com.google.gerrit.k8s.operator.v1alpha.admission.servlet.GerritAdmissionWebhook;
+import com.google.gerrit.k8s.operator.v1alpha.admission.servlet.GerritClusterAdmissionWebhook;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.ReceiverTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.ReceiverTemplateSpec;
 import io.fabric8.kubernetes.api.model.ObjectMeta;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
 import io.fabric8.kubernetes.api.model.admission.v1.AdmissionRequest;
@@ -169,7 +171,7 @@
       throws MalformedURLException, IOException {
     HttpURLConnection http =
         (HttpURLConnection)
-            new URL("http://localhost:8080/admission/gerritcluster").openConnection();
+            new URL("http://localhost:8080/admission/v1alpha/gerritcluster").openConnection();
     http.setRequestMethod(HttpMethod.POST.asString());
     http.setRequestProperty("Content-Type", "application/json");
     http.setDoOutput(true);
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/server/GitGcAdmissionWebhookTest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/server/GitGcAdmissionWebhookTest.java
index 4f6d46f..5553e95 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/server/GitGcAdmissionWebhookTest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/server/GitGcAdmissionWebhookTest.java
@@ -19,9 +19,11 @@
 import static org.hamcrest.Matchers.equalTo;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollection;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollectionSpec;
 import com.google.gerrit.k8s.operator.test.TestAdmissionWebhookServer;
+import com.google.gerrit.k8s.operator.v1alpha.admission.servlet.GitGcAdmissionWebhook;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollection;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollectionSpec;
 import io.fabric8.kubernetes.api.model.DefaultKubernetesResourceList;
 import io.fabric8.kubernetes.api.model.HasMetadata;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
@@ -63,6 +65,8 @@
   @BeforeAll
   public void setup() throws Exception {
     KubernetesDeserializer.registerCustomKind(
+        "gerritoperator.google.com/v1alpha16", "GerritCluster", GerritCluster.class);
+    KubernetesDeserializer.registerCustomKind(
         "gerritoperator.google.com/v1alpha1", "GitGarbageCollection", GitGarbageCollection.class);
     server = new TestAdmissionWebhookServer();
 
@@ -227,7 +231,8 @@
   private HttpURLConnection sendAdmissionRequest(GitGarbageCollection gitGc)
       throws MalformedURLException, IOException {
     HttpURLConnection http =
-        (HttpURLConnection) new URL("http://localhost:8080/admission/gitgc").openConnection();
+        (HttpURLConnection)
+            new URL("http://localhost:8080/admission/v1alpha/gitgc").openConnection();
     http.setRequestMethod(HttpMethod.POST.asString());
     http.setRequestProperty("Content-Type", "application/json");
     http.setDoOutput(true);
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/test/AbstractGerritOperatorE2ETest.java b/operator/src/test/java/com/google/gerrit/k8s/operator/test/AbstractGerritOperatorE2ETest.java
index bc8780f..ee1aa01 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/test/AbstractGerritOperatorE2ETest.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/test/AbstractGerritOperatorE2ETest.java
@@ -16,16 +16,16 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.k8s.operator.cluster.GerritClusterReconciler;
-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.gitgc.GitGarbageCollectionReconciler;
-import com.google.gerrit.k8s.operator.gitgc.model.GitGarbageCollection;
 import com.google.gerrit.k8s.operator.network.GerritNetworkReconcilerProvider;
 import com.google.gerrit.k8s.operator.network.IngressType;
-import com.google.gerrit.k8s.operator.network.model.GerritNetwork;
 import com.google.gerrit.k8s.operator.receiver.ReceiverReconciler;
-import com.google.gerrit.k8s.operator.receiver.model.Receiver;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gitgc.GitGarbageCollection;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.network.GerritNetwork;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.Receiver;
 import io.fabric8.kubernetes.api.model.Secret;
 import io.fabric8.kubernetes.api.model.SecretBuilder;
 import io.fabric8.kubernetes.client.Config;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/test/ReceiverUtil.java b/operator/src/test/java/com/google/gerrit/k8s/operator/test/ReceiverUtil.java
index 165940c..b6cdc5a 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/test/ReceiverUtil.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/test/ReceiverUtil.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.k8s.operator.test;
 
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
 import io.fabric8.kubernetes.api.model.Secret;
 import io.fabric8.kubernetes.api.model.SecretBuilder;
 import java.net.HttpURLConnection;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestAdmissionWebhookServer.java b/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestAdmissionWebhookServer.java
index c520077..90c55be 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestAdmissionWebhookServer.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestAdmissionWebhookServer.java
@@ -43,7 +43,7 @@
 
   public void registerWebhook(AdmissionWebhookServlet webhook) {
     servletHandler.addServletWithMapping(
-        new ServletHolder(webhook), "/admission/" + webhook.getName());
+        new ServletHolder(webhook), "/admission/" + webhook.getVersion() + "/" + webhook.getName());
   }
 
   public void stop() throws Exception {
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 f3f9546..5762ea5 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
@@ -26,18 +26,18 @@
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritConfigMap;
 import com.google.gerrit.k8s.operator.gerrit.dependent.GerritInitConfigMap;
 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.GerritSite;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritSpec;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplate;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplateSpec;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplateSpec.GerritMode;
-import com.google.gerrit.k8s.operator.shared.model.ContainerImageConfig;
-import com.google.gerrit.k8s.operator.shared.model.GerritRepositoryConfig;
-import com.google.gerrit.k8s.operator.shared.model.GerritStorageConfig;
-import com.google.gerrit.k8s.operator.shared.model.IngressConfig;
-import com.google.gerrit.k8s.operator.shared.model.SharedStorage;
-import com.google.gerrit.k8s.operator.shared.model.StorageClassConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.Gerrit;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritSite;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplateSpec.GerritMode;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.ContainerImageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritRepositoryConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritStorageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.IngressConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.SharedStorage;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.StorageClassConfig;
 import io.fabric8.kubernetes.api.model.LocalObjectReference;
 import io.fabric8.kubernetes.api.model.ObjectMeta;
 import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
diff --git a/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestGerritCluster.java b/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestGerritCluster.java
index a741dd0..ce66ec2 100644
--- a/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestGerritCluster.java
+++ b/operator/src/test/java/com/google/gerrit/k8s/operator/test/TestGerritCluster.java
@@ -24,19 +24,19 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
-import com.google.gerrit.k8s.operator.cluster.model.GerritClusterSpec;
-import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplate;
 import com.google.gerrit.k8s.operator.network.IngressType;
-import com.google.gerrit.k8s.operator.receiver.model.ReceiverTemplate;
-import com.google.gerrit.k8s.operator.shared.model.ContainerImageConfig;
-import com.google.gerrit.k8s.operator.shared.model.GerritClusterIngressConfig;
-import com.google.gerrit.k8s.operator.shared.model.GerritIngressTlsConfig;
-import com.google.gerrit.k8s.operator.shared.model.GerritRepositoryConfig;
-import com.google.gerrit.k8s.operator.shared.model.GerritStorageConfig;
-import com.google.gerrit.k8s.operator.shared.model.NfsWorkaroundConfig;
-import com.google.gerrit.k8s.operator.shared.model.SharedStorage;
-import com.google.gerrit.k8s.operator.shared.model.StorageClassConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritCluster;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.cluster.GerritClusterSpec;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.gerrit.GerritTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.receiver.ReceiverTemplate;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.ContainerImageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritClusterIngressConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritIngressTlsConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritRepositoryConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.GerritStorageConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.NfsWorkaroundConfig;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.SharedStorage;
+import com.google.gerrit.k8s.operator.v1alpha.api.model.shared.StorageClassConfig;
 import com.urswolfer.gerrit.client.rest.GerritAuthData;
 import com.urswolfer.gerrit.client.rest.GerritRestApiFactory;
 import io.fabric8.kubernetes.api.model.LocalObjectReference;