From b2884ba911d51495058c173519c0fc7ad9753859 Mon Sep 17 00:00:00 2001 From: Ed Marks Date: Tue, 17 Mar 2026 17:46:41 +0000 Subject: [PATCH 1/3] feat: allow setting PersistentVolumeClaimRetentionPolicy --- Makefile | 2 +- .../typesensecluster_types_storage.go | 7 +++++- api/v1alpha1/zz_generated.deepcopy.go | 6 +++++ .../templates/typesensecluster-crd.yaml | 23 ++++++++++++++++++- ...pentelekomcloud.com_typesenseclusters.yaml | 22 ++++++++++++++++++ .../samples/ts_v1alpha1_typesensecluster.yaml | 5 +++- .../typesensecluster_statefulset.go | 1 + .../typesensecluster_statefulset_hash.go | 2 +- 8 files changed, 63 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index ef933b8..4f92bfc 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,7 @@ test: manifests generate fmt vet setup-envtest ## Run tests. # The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. # CertManager is installed by default; skip with: # - CERT_MANAGER_INSTALL_SKIP=true -KIND_CLUSTER ?= memcached-operator-test-e2e +KIND_CLUSTER ?= typesense-operator-test-e2e .PHONY: setup-test-e2e setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist diff --git a/api/v1alpha1/typesensecluster_types_storage.go b/api/v1alpha1/typesensecluster_types_storage.go index ec8fd4a..b1a52e5 100644 --- a/api/v1alpha1/typesensecluster_types_storage.go +++ b/api/v1alpha1/typesensecluster_types_storage.go @@ -1,6 +1,9 @@ package v1alpha1 -import "k8s.io/apimachinery/pkg/api/resource" +import ( + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/api/resource" +) type StorageSpec struct { @@ -16,6 +19,8 @@ type StorageSpec struct { AccessMode string `json:"accessMode,omitempty"` Annotations map[string]string `json:"annotations,omitempty"` + + PersistentVolumeClaimRetentionPolicy *appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy `json:"persistentVolumeClaimRetentionPolicy"` } func (s *TypesenseClusterSpec) GetStorage() StorageSpec { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 013c000..19192bc 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1alpha1 import ( + appsv1 "k8s.io/api/apps/v1" "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -241,6 +242,11 @@ func (in *StorageSpec) DeepCopyInto(out *StorageSpec) { (*out)[key] = val } } + if in.PersistentVolumeClaimRetentionPolicy != nil { + in, out := &in.PersistentVolumeClaimRetentionPolicy, &out.PersistentVolumeClaimRetentionPolicy + *out = new(appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageSpec. diff --git a/charts/typesense-operator/templates/typesensecluster-crd.yaml b/charts/typesense-operator/templates/typesensecluster-crd.yaml index 0cbfd8e..1ea1228 100644 --- a/charts/typesense-operator/templates/typesensecluster-crd.yaml +++ b/charts/typesense-operator/templates/typesensecluster-crd.yaml @@ -4271,6 +4271,27 @@ spec: x-kubernetes-int-or-string: true storageClassName: type: string + persistentVolumeClaimRetentionPolicy: + description: |- + StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs + created from the StatefulSet VolumeClaimTemplates. + properties: + whenDeleted: + description: |- + WhenDeleted specifies what happens to PVCs created from StatefulSet + VolumeClaimTemplates when the StatefulSet is deleted. The default policy + of `Retain` causes PVCs to not be affected by StatefulSet deletion. The + `Delete` policy causes those PVCs to be deleted. + type: string + whenScaled: + description: |- + WhenScaled specifies what happens to PVCs created from StatefulSet + VolumeClaimTemplates when the StatefulSet is scaled down. The default + policy of `Retain` causes PVCs to not be affected by a scaledown. The + `Delete` policy causes the associated PVCs for any excess pods above + the replica count to be deleted. + type: string + type: object required: - storageClassName type: object @@ -4580,4 +4601,4 @@ status: kind: "" plural: "" conditions: [] - storedVersions: [] \ No newline at end of file + storedVersions: [] diff --git a/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml b/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml index 336254d..c8797f8 100644 --- a/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml +++ b/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml @@ -4326,6 +4326,27 @@ spec: additionalProperties: type: string type: object + persistentVolumeClaimRetentionPolicy: + description: |- + StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs + created from the StatefulSet VolumeClaimTemplates. + properties: + whenDeleted: + description: |- + WhenDeleted specifies what happens to PVCs created from StatefulSet + VolumeClaimTemplates when the StatefulSet is deleted. The default policy + of `Retain` causes PVCs to not be affected by StatefulSet deletion. The + `Delete` policy causes those PVCs to be deleted. + type: string + whenScaled: + description: |- + WhenScaled specifies what happens to PVCs created from StatefulSet + VolumeClaimTemplates when the StatefulSet is scaled down. The default + policy of `Retain` causes PVCs to not be affected by a scaledown. The + `Delete` policy causes the associated PVCs for any excess pods above + the replica count to be deleted. + type: string + type: object size: anyOf: - type: integer @@ -4336,6 +4357,7 @@ spec: storageClassName: type: string required: + - persistentVolumeClaimRetentionPolicy - storageClassName type: object tolerations: diff --git a/config/samples/ts_v1alpha1_typesensecluster.yaml b/config/samples/ts_v1alpha1_typesensecluster.yaml index 19f1251..4a4a4f3 100644 --- a/config/samples/ts_v1alpha1_typesensecluster.yaml +++ b/config/samples/ts_v1alpha1_typesensecluster.yaml @@ -10,4 +10,7 @@ spec: replicas: 3 storage: size: 100Mi - storageClassName: standard \ No newline at end of file + storageClassName: standard + persistentVolumeClaimRetentionPolicy: + whenDeleted: Delete + whenScaled: Retain diff --git a/internal/controller/typesensecluster_statefulset.go b/internal/controller/typesensecluster_statefulset.go index 244eb1d..2fe80ea 100644 --- a/internal/controller/typesensecluster_statefulset.go +++ b/internal/controller/typesensecluster_statefulset.go @@ -486,6 +486,7 @@ func (r *TypesenseClusterReconciler) buildStatefulSet(ctx context.Context, key c }, }, }, + PersistentVolumeClaimRetentionPolicy: ts.Spec.Storage.PersistentVolumeClaimRetentionPolicy, }, } diff --git a/internal/controller/typesensecluster_statefulset_hash.go b/internal/controller/typesensecluster_statefulset_hash.go index 59ae5dd..9af9da9 100644 --- a/internal/controller/typesensecluster_statefulset_hash.go +++ b/internal/controller/typesensecluster_statefulset_hash.go @@ -43,7 +43,7 @@ func (r *TypesenseClusterReconciler) shouldUpdateStatefulSet(sts *appsv1.Statefu // SpecReplicasChanged if *sts.Spec.Replicas != ts.Spec.Replicas && - (condition.Reason != string(ConditionReasonQuorumDowngraded) || condition.Reason != string(ConditionReasonQuorumQueuedWrites)) { + (condition.Reason != string(ConditionReasonQuorumDowngraded) && condition.Reason != string(ConditionReasonQuorumQueuedWrites)) { triggers = append(triggers, SpecReplicasChanged) update = false scaleOnly = true From 2e3d46f7ac0afad554eae5b2e0f88907758d2436 Mon Sep 17 00:00:00 2001 From: Ed Marks Date: Mon, 13 Apr 2026 12:22:57 +0100 Subject: [PATCH 2/3] chore: rename PersistentVolumeClaimRetentionPolicy -> RetentionPolicy --- api/v1alpha1/typesensecluster_types_storage.go | 2 +- api/v1alpha1/zz_generated.deepcopy.go | 4 ++-- charts/typesense-operator/templates/typesensecluster-crd.yaml | 2 +- .../crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml | 4 ++-- config/samples/ts_v1alpha1_typesensecluster.yaml | 2 +- internal/controller/typesensecluster_statefulset.go | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/v1alpha1/typesensecluster_types_storage.go b/api/v1alpha1/typesensecluster_types_storage.go index b1a52e5..feaedde 100644 --- a/api/v1alpha1/typesensecluster_types_storage.go +++ b/api/v1alpha1/typesensecluster_types_storage.go @@ -20,7 +20,7 @@ type StorageSpec struct { Annotations map[string]string `json:"annotations,omitempty"` - PersistentVolumeClaimRetentionPolicy *appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy `json:"persistentVolumeClaimRetentionPolicy"` + RetentionPolicy *appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy `json:"retentionPolicy"` } func (s *TypesenseClusterSpec) GetStorage() StorageSpec { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 19192bc..21cb0ea 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -242,8 +242,8 @@ func (in *StorageSpec) DeepCopyInto(out *StorageSpec) { (*out)[key] = val } } - if in.PersistentVolumeClaimRetentionPolicy != nil { - in, out := &in.PersistentVolumeClaimRetentionPolicy, &out.PersistentVolumeClaimRetentionPolicy + if in.RetentionPolicy != nil { + in, out := &in.RetentionPolicy, &out.RetentionPolicy *out = new(appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy) **out = **in } diff --git a/charts/typesense-operator/templates/typesensecluster-crd.yaml b/charts/typesense-operator/templates/typesensecluster-crd.yaml index 1ea1228..f460712 100644 --- a/charts/typesense-operator/templates/typesensecluster-crd.yaml +++ b/charts/typesense-operator/templates/typesensecluster-crd.yaml @@ -4271,7 +4271,7 @@ spec: x-kubernetes-int-or-string: true storageClassName: type: string - persistentVolumeClaimRetentionPolicy: + retentionPolicy: description: |- StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs created from the StatefulSet VolumeClaimTemplates. diff --git a/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml b/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml index c8797f8..39dba3a 100644 --- a/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml +++ b/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml @@ -4326,7 +4326,7 @@ spec: additionalProperties: type: string type: object - persistentVolumeClaimRetentionPolicy: + retentionPolicy: description: |- StatefulSetPersistentVolumeClaimRetentionPolicy describes the policy used for PVCs created from the StatefulSet VolumeClaimTemplates. @@ -4357,7 +4357,7 @@ spec: storageClassName: type: string required: - - persistentVolumeClaimRetentionPolicy + - retentionPolicy - storageClassName type: object tolerations: diff --git a/config/samples/ts_v1alpha1_typesensecluster.yaml b/config/samples/ts_v1alpha1_typesensecluster.yaml index 4a4a4f3..4d438d0 100644 --- a/config/samples/ts_v1alpha1_typesensecluster.yaml +++ b/config/samples/ts_v1alpha1_typesensecluster.yaml @@ -11,6 +11,6 @@ spec: storage: size: 100Mi storageClassName: standard - persistentVolumeClaimRetentionPolicy: + retentionPolicy: whenDeleted: Delete whenScaled: Retain diff --git a/internal/controller/typesensecluster_statefulset.go b/internal/controller/typesensecluster_statefulset.go index 2fe80ea..3d8a65f 100644 --- a/internal/controller/typesensecluster_statefulset.go +++ b/internal/controller/typesensecluster_statefulset.go @@ -486,7 +486,7 @@ func (r *TypesenseClusterReconciler) buildStatefulSet(ctx context.Context, key c }, }, }, - PersistentVolumeClaimRetentionPolicy: ts.Spec.Storage.PersistentVolumeClaimRetentionPolicy, + PersistentVolumeClaimRetentionPolicy: ts.Spec.Storage.RetentionPolicy, }, } From 6a1944833eb5c94e2e2080e796c7356a445b167b Mon Sep 17 00:00:00 2001 From: Kyriakos Akriotis Date: Sat, 2 May 2026 17:26:21 +0200 Subject: [PATCH 3/3] draft: added defaults for retention policy, made retention policy optional, reverting policy from delete to retain fails #252 --- .../typesensecluster_types_storage.go | 17 +++++++++++++ ...pentelekomcloud.com_typesenseclusters.yaml | 1 - .../typesensecluster_statefulset.go | 2 +- .../typesensecluster_statefulset_hash.go | 25 ++++++++++++------- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/api/v1alpha1/typesensecluster_types_storage.go b/api/v1alpha1/typesensecluster_types_storage.go index feaedde..b5c4638 100644 --- a/api/v1alpha1/typesensecluster_types_storage.go +++ b/api/v1alpha1/typesensecluster_types_storage.go @@ -18,8 +18,10 @@ type StorageSpec struct { // +kubebuilder:default:=ReadWriteOnce AccessMode string `json:"accessMode,omitempty"` + // +kubebuilder:validation:Optional Annotations map[string]string `json:"annotations,omitempty"` + // +kubebuilder:validation:Optional RetentionPolicy *appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy `json:"retentionPolicy"` } @@ -32,5 +34,20 @@ func (s *TypesenseClusterSpec) GetStorage() StorageSpec { Size: resource.MustParse("100Mi"), StorageClassName: "standard", AccessMode: "ReadWriteOnce", + RetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ + WhenDeleted: appsv1.RetainPersistentVolumeClaimRetentionPolicyType, + WhenScaled: appsv1.RetainPersistentVolumeClaimRetentionPolicyType, + }, + } +} + +func (s *TypesenseClusterSpec) GetStorageRetentionPolicy() *appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy { + if s.Storage != nil && s.Storage.RetentionPolicy != nil { + return s.Storage.RetentionPolicy + } + + return &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ + WhenDeleted: appsv1.RetainPersistentVolumeClaimRetentionPolicyType, + WhenScaled: appsv1.RetainPersistentVolumeClaimRetentionPolicyType, } } diff --git a/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml b/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml index 39dba3a..044ad9a 100644 --- a/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml +++ b/config/crd/bases/ts.opentelekomcloud.com_typesenseclusters.yaml @@ -4357,7 +4357,6 @@ spec: storageClassName: type: string required: - - retentionPolicy - storageClassName type: object tolerations: diff --git a/internal/controller/typesensecluster_statefulset.go b/internal/controller/typesensecluster_statefulset.go index 3d8a65f..da1cdb0 100644 --- a/internal/controller/typesensecluster_statefulset.go +++ b/internal/controller/typesensecluster_statefulset.go @@ -486,7 +486,7 @@ func (r *TypesenseClusterReconciler) buildStatefulSet(ctx context.Context, key c }, }, }, - PersistentVolumeClaimRetentionPolicy: ts.Spec.Storage.RetentionPolicy, + PersistentVolumeClaimRetentionPolicy: ts.Spec.GetStorageRetentionPolicy(), }, } diff --git a/internal/controller/typesensecluster_statefulset_hash.go b/internal/controller/typesensecluster_statefulset_hash.go index 9af9da9..b6683d3 100644 --- a/internal/controller/typesensecluster_statefulset_hash.go +++ b/internal/controller/typesensecluster_statefulset_hash.go @@ -17,15 +17,16 @@ import ( type UpdateStatefulSetTrigger string var ( - SpecReplicasChanged UpdateStatefulSetTrigger = "SpecReplicasChanged" - HashAnnotationChanged UpdateStatefulSetTrigger = "HashAnnotationChanged" - PodAnnotationsChanged UpdateStatefulSetTrigger = "PodAnnotationsChanged" - StatefulSetAnnotationsChanged UpdateStatefulSetTrigger = "StatefulSetAnnotationsChanged" - SpecResourcesChanged UpdateStatefulSetTrigger = "SpecResourcesChanged" - PodSecurityContextChanged UpdateStatefulSetTrigger = "PodSecurityContextChanged" - InvalidContainerCount UpdateStatefulSetTrigger = "InvalidContainerCount" - ContainerSecurityContextChanged UpdateStatefulSetTrigger = "ContainerSecurityContextChanged" - SpecTypesenseVersionChanged UpdateStatefulSetTrigger = "SpecTypesenseVersionChanged" + SpecReplicasChanged UpdateStatefulSetTrigger = "SpecReplicasChanged" + HashAnnotationChanged UpdateStatefulSetTrigger = "HashAnnotationChanged" + PodAnnotationsChanged UpdateStatefulSetTrigger = "PodAnnotationsChanged" + StatefulSetAnnotationsChanged UpdateStatefulSetTrigger = "StatefulSetAnnotationsChanged" + StatefulSetARetentionPolicyChanged UpdateStatefulSetTrigger = "StatefulSetARetentionPolicyChanged" + SpecResourcesChanged UpdateStatefulSetTrigger = "SpecResourcesChanged" + PodSecurityContextChanged UpdateStatefulSetTrigger = "PodSecurityContextChanged" + InvalidContainerCount UpdateStatefulSetTrigger = "InvalidContainerCount" + ContainerSecurityContextChanged UpdateStatefulSetTrigger = "ContainerSecurityContextChanged" + SpecTypesenseVersionChanged UpdateStatefulSetTrigger = "SpecTypesenseVersionChanged" ) func (r *TypesenseClusterReconciler) shouldUpdateStatefulSet(sts *appsv1.StatefulSet, desired *appsv1.StatefulSet, ts *tsv1alpha1.TypesenseCluster) (update bool, scaleOnly bool, triggers []UpdateStatefulSetTrigger) { @@ -71,6 +72,12 @@ func (r *TypesenseClusterReconciler) shouldUpdateStatefulSet(sts *appsv1.Statefu update = true } + // StatefulSetARetentionPolicyChanged + if !apiequality.Semantic.DeepEqual(sts.Spec.PersistentVolumeClaimRetentionPolicy, desired.Spec.PersistentVolumeClaimRetentionPolicy) { + triggers = append(triggers, StatefulSetARetentionPolicyChanged) + update = true + } + //// SpecResourcesChanged //if !apiequality.Semantic.DeepEqual(sts.Spec.Template.Spec.Containers[0].Resources, ts.Spec.GetResources()) { // triggers = append(triggers, SpecResourcesChanged)