[Operator] Mount shared directory, if HA-plugin is installed
The high-availability plugin uses a directory shared between all sites
to share websessions.
This change adds support to the operator to mount such a directory,
if
- the Gerrit is a primary Gerrit
- the Gerrit asks for at least 2 replicas
- the high-availability plugin is configured to be installed
(it will not be automatically installed)
Change-Id: I44e08d2259f49c593910a80f65f3adb665711f5e
diff --git a/Documentation/operator-api-reference.md b/Documentation/operator-api-reference.md
index 5f688bd..4697adb 100644
--- a/Documentation/operator-api-reference.md
+++ b/Documentation/operator-api-reference.md
@@ -10,38 +10,39 @@
6. [GerritNetwork](#gerritnetwork)
7. [GerritClusterSpec](#gerritclusterspec)
8. [GerritClusterStatus](#gerritclusterstatus)
- 9. [GerritStorageConfig](#gerritstorageconfig)
- 10. [StorageClassConfig](#storageclassconfig)
- 11. [NfsWorkaroundConfig](#nfsworkaroundconfig)
- 12. [SharedStorage](#sharedstorage)
- 13. [OptionalSharedStorage](#optionalsharedstorage)
- 14. [ContainerImageConfig](#containerimageconfig)
- 15. [BusyBoxImage](#busyboximage)
- 16. [GerritRepositoryConfig](#gerritrepositoryconfig)
- 17. [GerritClusterIngressConfig](#gerritclusteringressconfig)
- 18. [GerritIngressTlsConfig](#gerritingresstlsconfig)
- 19. [GerritTemplate](#gerrittemplate)
- 20. [GerritTemplateSpec](#gerrittemplatespec)
- 21. [GerritProbe](#gerritprobe)
- 22. [GerritServiceConfig](#gerritserviceconfig)
- 23. [GerritSite](#gerritsite)
- 24. [GerritPlugin](#gerritplugin)
- 25. [GerritMode](#gerritmode)
- 26. [GerritSpec](#gerritspec)
- 27. [GerritStatus](#gerritstatus)
- 28. [IngressConfig](#ingressconfig)
- 29. [ReceiverTemplate](#receivertemplate)
- 30. [ReceiverTemplateSpec](#receivertemplatespec)
- 31. [ReceiverSpec](#receiverspec)
- 32. [ReceiverStatus](#receiverstatus)
- 33. [ReceiverProbe](#receiverprobe)
- 34. [ReceiverServiceConfig](#receiverserviceconfig)
- 35. [GitGarbageCollectionSpec](#gitgarbagecollectionspec)
- 36. [GitGarbageCollectionStatus](#gitgarbagecollectionstatus)
- 37. [GitGcState](#gitgcstate)
- 38. [GerritNetworkSpec](#gerritnetworkspec)
- 39. [NetworkMember](#networkmember)
- 40. [NetworkMemberWithSsh](#networkmemberwithssh)
+ 9. [StorageConfig](#storageconfig)
+ 10. [GerritStorageConfig](#gerritstorageconfig)
+ 11. [StorageClassConfig](#storageclassconfig)
+ 12. [NfsWorkaroundConfig](#nfsworkaroundconfig)
+ 13. [SharedStorage](#sharedstorage)
+ 14. [OptionalSharedStorage](#optionalsharedstorage)
+ 15. [ContainerImageConfig](#containerimageconfig)
+ 16. [BusyBoxImage](#busyboximage)
+ 17. [GerritRepositoryConfig](#gerritrepositoryconfig)
+ 18. [GerritClusterIngressConfig](#gerritclusteringressconfig)
+ 19. [GerritIngressTlsConfig](#gerritingresstlsconfig)
+ 20. [GerritTemplate](#gerrittemplate)
+ 21. [GerritTemplateSpec](#gerrittemplatespec)
+ 22. [GerritProbe](#gerritprobe)
+ 23. [GerritServiceConfig](#gerritserviceconfig)
+ 24. [GerritSite](#gerritsite)
+ 25. [GerritPlugin](#gerritplugin)
+ 26. [GerritMode](#gerritmode)
+ 27. [GerritSpec](#gerritspec)
+ 28. [GerritStatus](#gerritstatus)
+ 29. [IngressConfig](#ingressconfig)
+ 30. [ReceiverTemplate](#receivertemplate)
+ 31. [ReceiverTemplateSpec](#receivertemplatespec)
+ 32. [ReceiverSpec](#receiverspec)
+ 33. [ReceiverStatus](#receiverstatus)
+ 34. [ReceiverProbe](#receiverprobe)
+ 35. [ReceiverServiceConfig](#receiverserviceconfig)
+ 36. [GitGarbageCollectionSpec](#gitgarbagecollectionspec)
+ 37. [GitGarbageCollectionStatus](#gitgarbagecollectionstatus)
+ 38. [GitGcState](#gitgcstate)
+ 39. [GerritNetworkSpec](#gerritnetworkspec)
+ 40. [NetworkMember](#networkmember)
+ 41. [NetworkMemberWithSsh](#networkmemberwithssh)
## General Remarks
@@ -56,7 +57,7 @@
---
**Group**: gerritoperator.google.com \
-**Version**: v1alpha4 \
+**Version**: v1alpha5 \
**Kind**: GerritCluster
---
@@ -73,7 +74,7 @@
Example:
```yaml
-apiVersion: "gerritoperator.google.com/v1alpha4"
+apiVersion: "gerritoperator.google.com/v1alpha5"
kind: GerritCluster
metadata:
name: gerrit
@@ -113,6 +114,14 @@
volume-type: ssd
aws-availability-zone: us-east-1
+ sharedStorage:
+ size: 1Gi
+ volumeName: ""
+ selector:
+ matchLabels:
+ volume-type: ssd
+ aws-availability-zone: us-east-1
+
logsStorage:
size: 1Gi
volumeName: ""
@@ -324,7 +333,7 @@
---
**Group**: gerritoperator.google.com \
-**Version**: v1alpha5 \
+**Version**: v1alpha6 \
**Kind**: Gerrit
---
@@ -341,7 +350,7 @@
Example:
```yaml
-apiVersion: "gerritoperator.google.com/v1alpha5"
+apiVersion: "gerritoperator.google.com/v1alpha6"
kind: Gerrit
metadata:
name: gerrit
@@ -491,6 +500,14 @@
volume-type: ssd
aws-availability-zone: us-east-1
+ sharedStorage:
+ size: 1Gi
+ volumeName: ""
+ selector:
+ matchLabels:
+ volume-type: ssd
+ aws-availability-zone: us-east-1
+
logsStorage:
size: 1Gi
volumeName: ""
@@ -766,14 +783,22 @@
|---|---|---|
| `members` | `Map<String, List<String>>` | A map listing all Gerrit and Receiver instances managed by the GerritCluster by name |
+## StorageConfig
+
+| Field | Type | Description |
+|------------------------|---------------------------------------------------|---------------------------------------------------------------------------------------------------------|
+| `storageClasses` | [`StorageClassConfig`](#storageclassconfig) | StorageClasses used in the GerritCluster |
+| `gitRepositoryStorage` | [`SharedStorage`](#sharedstorage) | Volume used for storing Git repositories |
+| `logsStorage` | [`SharedStorage`](#sharedstorage) | Volume used for storing logs |
+| `pluginCacheStorage` | [`OptionalSharedStorage`](#optionalsharedstorage) | Volume used for caching downloaded plugin JAR-files (Only used by Gerrit resources. Otherwise ignored.) |
+
## GerritStorageConfig
+Extends [StorageConfig](#StorageConfig).
+
| Field | Type | Description |
|---|---|---|
-| `storageClasses` | [`StorageClassConfig`](#storageclassconfig) | StorageClasses used in the GerritCluster |
-| `gitRepositoryStorage` | [`SharedStorage`](#sharedstorage) | Volume used for storing Git repositories |
-| `logsStorage` | [`SharedStorage`](#sharedstorage) | Volume used for storing logs |
-| `pluginCacheStorage` | [`OptionalSharedStorage`](#optionalsharedstorage) | Volume used for caching downloaded plugin JAR-files (Only used by Gerrit resources. Otherwise ignored.) |
+| `sharedStorage` | [`SharedStorage`](#sharedstorage) | Volume used for resources shared between Gerrit instances except git repositories |
## StorageClassConfig
@@ -968,7 +993,7 @@
| Field | Type | Description |
|---|---|---|
-| `storage` | [`GerritStorageConfig`](#gerritstorageconfig) | Storage used by Gerrit/Receiver instances |
+| `storage` | [`StorageConfig`](#storageconfig) | Storage used by Gerrit/Receiver instances |
| `containerImages` | [`ContainerImageConfig`](#containerimageconfig) | Container images used inside GerritCluster |
| `ingress` | [`IngressConfig`](#ingressconfig) | Ingress configuration for Gerrit |
diff --git a/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/init.py b/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/init.py
index 8266f6f..7cf1646 100755
--- a/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/init.py
+++ b/container-images/gerrit-init/tools/gerrit-initializer/initializer/tasks/init.py
@@ -122,6 +122,10 @@
self._ensure_symlink(f"{MNT_PATH}/git", f"{self.site}/git")
self._ensure_symlink(f"{MNT_PATH}/logs", f"{self.site}/logs")
+ mounted_shared_dir = f"{MNT_PATH}/shared"
+ if not self.is_replica and os.path.exists(mounted_shared_dir):
+ self._ensure_symlink(mounted_shared_dir, f"{self.site}/shared")
+
index_type = self.gerrit_config.get("index.type", default=IndexType.LUCENE.name)
if IndexType[index_type.upper()] is IndexType.ELASTICSEARCH:
self._ensure_symlink(f"{MNT_PATH}/index", f"{self.site}/index")
diff --git a/helm-charts/gerrit-operator-crds/templates/gerritclusters.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/gerritclusters.gerritoperator.google.com-v1.yml
index 10fbbab..99e5161 100644
--- a/helm-charts/gerrit-operator-crds/templates/gerritclusters.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/gerritclusters.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
singular: gerritcluster
scope: Namespaced
versions:
- - name: v1alpha4
+ - name: v1alpha5
schema:
openAPIV3Schema:
properties:
@@ -21,6 +21,36 @@
properties:
storage:
properties:
+ sharedStorage:
+ properties:
+ size:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ volumeName:
+ type: string
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ type: object
storageClasses:
properties:
readWriteOnce:
diff --git a/helm-charts/gerrit-operator-crds/templates/gerrits.gerritoperator.google.com-v1.yml b/helm-charts/gerrit-operator-crds/templates/gerrits.gerritoperator.google.com-v1.yml
index 4991dd5..128a2eb 100644
--- a/helm-charts/gerrit-operator-crds/templates/gerrits.gerritoperator.google.com-v1.yml
+++ b/helm-charts/gerrit-operator-crds/templates/gerrits.gerritoperator.google.com-v1.yml
@@ -13,7 +13,7 @@
singular: gerrit
scope: Namespaced
versions:
- - name: v1alpha5
+ - name: v1alpha6
schema:
openAPIV3Schema:
properties:
@@ -49,6 +49,36 @@
type: object
storage:
properties:
+ sharedStorage:
+ properties:
+ size:
+ anyOf:
+ - type: integer
+ - type: string
+ x-kubernetes-int-or-string: true
+ volumeName:
+ type: string
+ selector:
+ properties:
+ matchExpressions:
+ items:
+ properties:
+ key:
+ type: string
+ operator:
+ type: string
+ values:
+ items:
+ type: string
+ type: array
+ type: object
+ type: array
+ matchLabels:
+ additionalProperties:
+ type: string
+ type: object
+ type: object
+ type: object
storageClasses:
properties:
readWriteOnce:
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 712fdba..0877534 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
@@ -32,6 +32,8 @@
import com.google.gerrit.k8s.operator.cluster.dependent.NfsWorkaroundCondition;
import com.google.gerrit.k8s.operator.cluster.dependent.PluginCacheCondition;
import com.google.gerrit.k8s.operator.cluster.dependent.PluginCachePVC;
+import com.google.gerrit.k8s.operator.cluster.dependent.SharedPVC;
+import com.google.gerrit.k8s.operator.cluster.dependent.SharedPVCCondition;
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;
@@ -65,6 +67,11 @@
type = GitRepositoriesPVC.class,
useEventSourceWithName = PVC_EVENT_SOURCE),
@Dependent(
+ name = "shared-pvc",
+ type = SharedPVC.class,
+ reconcilePrecondition = SharedPVCCondition.class,
+ useEventSourceWithName = PVC_EVENT_SOURCE),
+ @Dependent(
name = "gerrit-logs-pvc",
type = GerritLogsPVC.class,
useEventSourceWithName = PVC_EVENT_SOURCE),
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
new file mode 100644
index 0000000..c7a9e4f
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVC.java
@@ -0,0 +1,54 @@
+// 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.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 io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaimBuilder;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
+import java.util.Map;
+
+@KubernetesDependent(resourceDiscriminator = SharedPVCDiscriminator.class)
+public class SharedPVC extends CRUDKubernetesDependentPVCResource<GerritCluster> {
+
+ public static final String SHARED_PVC_NAME = "shared-pvc";
+
+ @Override
+ protected PersistentVolumeClaim desiredPVC(
+ GerritCluster gerritCluster, Context<GerritCluster> context) {
+ GerritStorageConfig storageConfig = gerritCluster.getSpec().getStorage();
+ SharedStorage sharedStorage = storageConfig.getSharedStorage();
+ return new PersistentVolumeClaimBuilder()
+ .withNewMetadata()
+ .withName(SHARED_PVC_NAME)
+ .withNamespace(gerritCluster.getMetadata().getNamespace())
+ .withLabels(gerritCluster.getLabels("shared-storage", this.getClass().getSimpleName()))
+ .endMetadata()
+ .withNewSpec()
+ .withAccessModes("ReadWriteMany")
+ .withNewResources()
+ .withRequests(Map.of("storage", sharedStorage.getSize()))
+ .endResources()
+ .withStorageClassName(storageConfig.getStorageClasses().getReadWriteMany())
+ .withSelector(sharedStorage.getSelector())
+ .withVolumeName(sharedStorage.getVolumeName())
+ .endSpec()
+ .build();
+ }
+}
diff --git a/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVCCondition.java b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVCCondition.java
new file mode 100644
index 0000000..9b77db7
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVCCondition.java
@@ -0,0 +1,33 @@
+// 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.dependent;
+
+import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
+import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
+
+public class SharedPVCCondition implements Condition<PersistentVolumeClaim, GerritCluster> {
+
+ @Override
+ public boolean isMet(
+ DependentResource<PersistentVolumeClaim, GerritCluster> dependentResource,
+ GerritCluster gerritCluster,
+ Context<GerritCluster> context) {
+ return gerritCluster.getSpec().getGerrits().stream()
+ .anyMatch(g -> g.getSpec().isHighlyAvailablePrimary());
+ }
+}
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
new file mode 100644
index 0000000..652f890
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/cluster/dependent/SharedPVCDiscriminator.java
@@ -0,0 +1,42 @@
+// 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.dependent;
+
+import static com.google.gerrit.k8s.operator.cluster.GerritClusterReconciler.PVC_EVENT_SOURCE;
+
+import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
+import io.javaoperatorsdk.operator.api.reconciler.Context;
+import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
+import io.javaoperatorsdk.operator.processing.event.ResourceID;
+import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
+import java.util.Optional;
+
+public class SharedPVCDiscriminator
+ implements ResourceDiscriminator<PersistentVolumeClaim, GerritCluster> {
+ @Override
+ public Optional<PersistentVolumeClaim> distinguish(
+ Class<PersistentVolumeClaim> resource,
+ GerritCluster primary,
+ Context<GerritCluster> context) {
+ InformerEventSource<PersistentVolumeClaim, GerritCluster> ies =
+ (InformerEventSource<PersistentVolumeClaim, GerritCluster>)
+ context
+ .eventSourceRetriever()
+ .getResourceEventSourceFor(PersistentVolumeClaim.class, PVC_EVENT_SOURCE);
+
+ return ies.get(new ResourceID(SharedPVC.SHARED_PVC_NAME, primary.getMetadata().getNamespace()));
+ }
+}
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/cluster/model/GerritCluster.java
index 6091699..70dd705 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/cluster/model/GerritCluster.java
@@ -17,6 +17,7 @@
import static com.google.gerrit.k8s.operator.cluster.dependent.GerritLogsPVC.LOGS_PVC_NAME;
import static com.google.gerrit.k8s.operator.cluster.dependent.GitRepositoriesPVC.REPOSITORY_PVC_NAME;
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.gerrit.k8s.operator.cluster.GerritClusterMemberSpec;
@@ -42,11 +43,12 @@
import org.apache.commons.lang3.builder.ToStringStyle;
@Group("gerritoperator.google.com")
-@Version("v1alpha4")
+@Version("v1alpha5")
@ShortNames("gclus")
public class GerritCluster extends CustomResource<GerritClusterSpec, GerritClusterStatus>
implements Namespaced {
private static final long serialVersionUID = 2L;
+ private static final String SHARED_VOLUME_NAME = "shared";
private static final String GIT_REPOSITORIES_VOLUME_NAME = "git-repositories";
private static final String LOGS_VOLUME_NAME = "logs";
private static final String NFS_IDMAPD_CONFIG_VOLUME_NAME = "nfs-config";
@@ -102,6 +104,16 @@
}
@JsonIgnore
+ public static Volume getSharedVolume() {
+ return new VolumeBuilder()
+ .withName(SHARED_VOLUME_NAME)
+ .withNewPersistentVolumeClaim()
+ .withClaimName(SHARED_PVC_NAME)
+ .endPersistentVolumeClaim()
+ .build();
+ }
+
+ @JsonIgnore
public static Volume getGitRepositoriesVolume() {
return new VolumeBuilder()
.withName(GIT_REPOSITORIES_VOLUME_NAME)
@@ -125,6 +137,16 @@
}
@JsonIgnore
+ public static VolumeMount getSharedVolumeMount() {
+ return getSharedVolumeMount("/var/mnt/shared");
+ }
+
+ @JsonIgnore
+ public static VolumeMount getSharedVolumeMount(String mountPath) {
+ return new VolumeMountBuilder().withName(SHARED_VOLUME_NAME).withMountPath(mountPath).build();
+ }
+
+ @JsonIgnore
public static Volume getLogsVolume() {
return new VolumeBuilder()
.withName(LOGS_VOLUME_NAME)
@@ -183,22 +205,41 @@
@JsonIgnore
public static Container createNfsInitContainer(
boolean configureIdmapd, ContainerImageConfig imageConfig) {
+ return createNfsInitContainer(configureIdmapd, imageConfig, List.of());
+ }
+
+ @JsonIgnore
+ public static Container createNfsInitContainer(
+ boolean configureIdmapd,
+ ContainerImageConfig imageConfig,
+ List<VolumeMount> additionalVolumeMounts) {
List<VolumeMount> volumeMounts = new ArrayList<>();
volumeMounts.add(getLogsVolumeMount());
volumeMounts.add(getGitRepositoriesVolumeMount());
+ volumeMounts.addAll(additionalVolumeMounts);
+
if (configureIdmapd) {
volumeMounts.add(getNfsImapdConfigVolumeMount());
}
+ StringBuilder args = new StringBuilder();
+ args.append("chown -R ");
+ args.append(GERRIT_FS_UID);
+ args.append(":");
+ args.append(GERRIT_FS_GID);
+ args.append(" ");
+ for (VolumeMount vm : volumeMounts) {
+ args.append(vm.getMountPath());
+ args.append(" ");
+ }
+
return new ContainerBuilder()
.withName("nfs-init")
.withImagePullPolicy(imageConfig.getImagePullPolicy())
.withImage(imageConfig.getBusyBox().getBusyBoxImage())
.withCommand(List.of("sh", "-c"))
- .withArgs(
- String.format(
- "chown -R %d:%d /var/mnt/logs /var/mnt/git", GERRIT_FS_UID, GERRIT_FS_GID))
+ .withArgs(args.toString().trim())
.withEnv(getPodNameEnvVar())
.withVolumeMounts(volumeMounts)
.build();
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 818f7ad..a4afbec 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
@@ -21,6 +21,7 @@
import com.google.gerrit.k8s.operator.cluster.model.GerritCluster;
import com.google.gerrit.k8s.operator.gerrit.GerritReconciler;
import com.google.gerrit.k8s.operator.gerrit.model.Gerrit;
+import com.google.gerrit.k8s.operator.shared.model.ContainerImageConfig;
import com.google.gerrit.k8s.operator.shared.model.NfsWorkaroundConfig;
import io.fabric8.kubernetes.api.model.Container;
import io.fabric8.kubernetes.api.model.ContainerPort;
@@ -66,11 +67,19 @@
NfsWorkaroundConfig nfsWorkaround =
gerrit.getSpec().getStorage().getStorageClasses().getNfsWorkaround();
if (nfsWorkaround.isEnabled() && nfsWorkaround.isChownOnStartup()) {
- initContainers.add(
- GerritCluster.createNfsInitContainer(
- gerrit.getSpec().getStorage().getStorageClasses().getNfsWorkaround().getIdmapdConfig()
- != null,
- gerrit.getSpec().getContainerImages()));
+ boolean hasIdmapdConfig =
+ gerrit.getSpec().getStorage().getStorageClasses().getNfsWorkaround().getIdmapdConfig()
+ != null;
+ ContainerImageConfig images = gerrit.getSpec().getContainerImages();
+
+ if (gerrit.getSpec().isHighlyAvailablePrimary()) {
+
+ initContainers.add(
+ GerritCluster.createNfsInitContainer(
+ hasIdmapdConfig, images, List.of(GerritCluster.getSharedVolumeMount())));
+ } else {
+ initContainers.add(GerritCluster.createNfsInitContainer(hasIdmapdConfig, images));
+ }
}
Map<String, String> replicaSetAnnotations = new HashMap<>();
@@ -194,6 +203,9 @@
private Set<Volume> getVolumes(Gerrit gerrit) {
Set<Volume> volumes = new HashSet<>();
+ if (gerrit.getSpec().isHighlyAvailablePrimary()) {
+ volumes.add(GerritCluster.getSharedVolume());
+ }
volumes.add(GerritCluster.getGitRepositoriesVolume());
volumes.add(GerritCluster.getLogsVolume());
@@ -245,6 +257,9 @@
Set<VolumeMount> volumeMounts = new HashSet<>();
volumeMounts.add(
new VolumeMountBuilder().withName(SITE_VOLUME_NAME).withMountPath("/var/gerrit").build());
+ if (gerrit.getSpec().isHighlyAvailablePrimary()) {
+ volumeMounts.add(GerritCluster.getSharedVolumeMount());
+ }
volumeMounts.add(GerritCluster.getGitRepositoriesVolumeMount());
volumeMounts.add(GerritCluster.getLogsVolumeMount());
volumeMounts.add(
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/gerrit/model/Gerrit.java
index bfe38ee..0064a4c 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/gerrit/model/Gerrit.java
@@ -24,7 +24,7 @@
import org.apache.commons.lang3.builder.ToStringStyle;
@Group("gerritoperator.google.com")
-@Version("v1alpha5")
+@Version("v1alpha6")
@ShortNames("gcr")
public class Gerrit extends CustomResource<GerritSpec, GerritStatus> implements Namespaced {
private static final long serialVersionUID = 2L;
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/gerrit/model/GerritTemplateSpec.java
index fe88fb8..7f8fa92 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/gerrit/model/GerritTemplateSpec.java
@@ -14,6 +14,8 @@
package com.google.gerrit.k8s.operator.gerrit.model;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.gerrit.k8s.operator.gerrit.model.GerritTemplateSpec.GerritMode;
import com.google.gerrit.k8s.operator.shared.model.HttpSshServiceConfig;
import io.fabric8.kubernetes.api.model.Affinity;
import io.fabric8.kubernetes.api.model.ResourceRequirements;
@@ -217,4 +219,11 @@
PRIMARY,
REPLICA
}
+
+ @JsonIgnore
+ public boolean isHighlyAvailablePrimary() {
+ return getPlugins().stream().anyMatch(p -> p.getName().equals("high-availability"))
+ && getMode().equals(GerritMode.PRIMARY)
+ && getReplicas() > 1;
+ }
}
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/receiver/model/ReceiverSpec.java
index bd2d8f3..96df251 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/receiver/model/ReceiverSpec.java
@@ -15,12 +15,12 @@
package com.google.gerrit.k8s.operator.receiver.model;
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.IngressConfig;
+import com.google.gerrit.k8s.operator.shared.model.StorageConfig;
public class ReceiverSpec extends ReceiverTemplateSpec {
private ContainerImageConfig containerImages = new ContainerImageConfig();
- private GerritStorageConfig storage = new GerritStorageConfig();
+ private StorageConfig storage = new StorageConfig();
private IngressConfig ingress = new IngressConfig();
public ReceiverSpec() {}
@@ -37,11 +37,11 @@
this.containerImages = containerImages;
}
- public GerritStorageConfig getStorage() {
+ public StorageConfig getStorage() {
return storage;
}
- public void setStorage(GerritStorageConfig storage) {
+ public void setStorage(StorageConfig storage) {
this.storage = storage;
}
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/receiver/model/ReceiverTemplate.java
index e76f5eb..5c4332a 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/receiver/model/ReceiverTemplate.java
@@ -21,6 +21,7 @@
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 io.fabric8.kubernetes.api.model.KubernetesResource;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
@@ -72,7 +73,7 @@
receiver.setMetadata(getReceiverMetadata(gerritCluster));
ReceiverSpec receiverSpec = new ReceiverSpec(spec);
receiverSpec.setContainerImages(gerritCluster.getSpec().getContainerImages());
- receiverSpec.setStorage(gerritCluster.getSpec().getStorage());
+ receiverSpec.setStorage(new StorageConfig(gerritCluster.getSpec().getStorage()));
IngressConfig ingressConfig = new IngressConfig();
ingressConfig.setHost(gerritCluster.getSpec().getIngress().getHost());
ingressConfig.setTlsEnabled(gerritCluster.getSpec().getIngress().getTls().isEnabled());
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/shared/model/GerritStorageConfig.java
index d90480c..3fc558d 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/shared/model/GerritStorageConfig.java
@@ -14,42 +14,14 @@
package com.google.gerrit.k8s.operator.shared.model;
-public class GerritStorageConfig {
+public class GerritStorageConfig extends StorageConfig {
+ private SharedStorage sharedStorage;
- private StorageClassConfig storageClasses;
- private SharedStorage gitRepositoryStorage;
- private SharedStorage logsStorage;
- private OptionalSharedStorage pluginCacheStorage = new OptionalSharedStorage();
-
- public StorageClassConfig getStorageClasses() {
- return storageClasses;
+ public SharedStorage getSharedStorage() {
+ return sharedStorage;
}
- public SharedStorage getGitRepositoryStorage() {
- return gitRepositoryStorage;
- }
-
- public void setStorageClasses(StorageClassConfig storageClasses) {
- this.storageClasses = storageClasses;
- }
-
- public void setGitRepositoryStorage(SharedStorage gitRepositoryStorage) {
- this.gitRepositoryStorage = gitRepositoryStorage;
- }
-
- public SharedStorage getLogsStorage() {
- return logsStorage;
- }
-
- public void setLogsStorage(SharedStorage logsStorage) {
- this.logsStorage = logsStorage;
- }
-
- public OptionalSharedStorage getPluginCacheStorage() {
- return pluginCacheStorage;
- }
-
- public void setPluginCacheStorage(OptionalSharedStorage pluginCacheStorage) {
- this.pluginCacheStorage = pluginCacheStorage;
+ public void setSharedStorage(SharedStorage sharedStorage) {
+ this.sharedStorage = sharedStorage;
}
}
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/shared/model/StorageConfig.java
new file mode 100644
index 0000000..d7d0eb7
--- /dev/null
+++ b/operator/src/main/java/com/google/gerrit/k8s/operator/shared/model/StorageConfig.java
@@ -0,0 +1,64 @@
+// 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.shared.model;
+
+public class StorageConfig {
+
+ private StorageClassConfig storageClasses;
+ private SharedStorage gitRepositoryStorage;
+ private SharedStorage logsStorage;
+ private OptionalSharedStorage pluginCacheStorage = new OptionalSharedStorage();
+
+ public StorageConfig() {}
+
+ public StorageConfig(GerritStorageConfig gerritStorageConfig) {
+ storageClasses = gerritStorageConfig.getStorageClasses();
+ gitRepositoryStorage = gerritStorageConfig.getGitRepositoryStorage();
+ logsStorage = gerritStorageConfig.getLogsStorage();
+ pluginCacheStorage = gerritStorageConfig.getPluginCacheStorage();
+ }
+
+ public StorageClassConfig getStorageClasses() {
+ return storageClasses;
+ }
+
+ public void setStorageClasses(StorageClassConfig storageClasses) {
+ this.storageClasses = storageClasses;
+ }
+
+ public SharedStorage getGitRepositoryStorage() {
+ return gitRepositoryStorage;
+ }
+
+ public void setGitRepositoryStorage(SharedStorage gitRepositoryStorage) {
+ this.gitRepositoryStorage = gitRepositoryStorage;
+ }
+
+ public SharedStorage getLogsStorage() {
+ return logsStorage;
+ }
+
+ public void setLogsStorage(SharedStorage logsStorage) {
+ this.logsStorage = logsStorage;
+ }
+
+ public OptionalSharedStorage getPluginCacheStorage() {
+ return pluginCacheStorage;
+ }
+
+ public void setPluginCacheStorage(OptionalSharedStorage pluginCacheStorage) {
+ this.pluginCacheStorage = pluginCacheStorage;
+ }
+}