From cae91bc1831781f51351c71b9dd5b45ec4ac86aa Mon Sep 17 00:00:00 2001 From: "Per G. da Silva" Date: Tue, 20 Oct 2026 12:05:40 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20make=20ClusterObjectSet=20spec.prog?= =?UTF-8?q?ressionProbes=20immutable=20once=20set?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit progressionProbes is part of the immutable content of a ClusterObjectSet revision, but — unlike spec.phases and spec.collisionProtection — it carried no immutability constraint, so a populated probes list could be changed in place after creation. Add the same immutability rule used by spec.phases: a revision may be created without probes and have them set once, but once set they cannot be modified. Add CRD validation test cases and regenerate the CRD, apply configurations, and experimental manifests. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Per G. da Silva --- api/v1/clusterobjectset_types.go | 4 +++ api/v1/clusterobjectset_types_test.go | 25 +++++++++++++++++++ .../api/v1/clusterobjectsetspec.go | 3 +++ ...peratorframework.io_clusterobjectsets.yaml | 6 +++++ manifests/experimental-e2e.yaml | 6 +++++ manifests/experimental.yaml | 6 +++++ 6 files changed, 50 insertions(+) diff --git a/api/v1/clusterobjectset_types.go b/api/v1/clusterobjectset_types.go index 0b0a93f3d0..4b2c8e0838 100644 --- a/api/v1/clusterobjectset_types.go +++ b/api/v1/clusterobjectset_types.go @@ -131,6 +131,10 @@ type ClusterObjectSetSpec struct { // // The maximum number of probes is 20. // + // Once set, even if empty, the progressionProbes field is immutable, as it is + // part of the immutable content of a revision. + // + // +kubebuilder:validation:XValidation:rule="self == oldSelf || oldSelf.size() == 0", message="progressionProbes is immutable" // +kubebuilder:validation:MinItems=1 // +kubebuilder:validation:MaxItems=20 // +listType=atomic diff --git a/api/v1/clusterobjectset_types_test.go b/api/v1/clusterobjectset_types_test.go index f59251150e..dbb3db6743 100644 --- a/api/v1/clusterobjectset_types_test.go +++ b/api/v1/clusterobjectset_types_test.go @@ -140,6 +140,31 @@ func TestClusterObjectSetImmutability(t *testing.T) { cos.Spec.CollisionProtection = CollisionProtectionNone }, }, + "progressionProbes may be initially unset": { + spec: ClusterObjectSetSpec{ + Group: "test-group", + LifecycleState: ClusterObjectSetLifecycleStateActive, + Revision: 1, + CollisionProtection: CollisionProtectionPrevent, + }, + updateFunc: func(cos *ClusterObjectSet) { + cos.Spec.ProgressionProbes = []ProgressionProbe{validProbe()} + }, + allowed: true, + }, + "progressionProbes are immutable once set": { + spec: ClusterObjectSetSpec{ + Group: "test-group", + LifecycleState: ClusterObjectSetLifecycleStateActive, + Revision: 1, + CollisionProtection: CollisionProtectionPrevent, + ProgressionProbes: []ProgressionProbe{validProbe()}, + }, + updateFunc: func(cos *ClusterObjectSet) { + cos.Spec.ProgressionProbes = []ProgressionProbe{validProbe(), validProbe()} + }, + expectedError: "progressionProbes is immutable", + }, "lifecycleState cannot be un-archived": { spec: ClusterObjectSetSpec{ Group: "test-group", diff --git a/applyconfigurations/api/v1/clusterobjectsetspec.go b/applyconfigurations/api/v1/clusterobjectsetspec.go index 241a21b425..1698ab7208 100644 --- a/applyconfigurations/api/v1/clusterobjectsetspec.go +++ b/applyconfigurations/api/v1/clusterobjectsetspec.go @@ -88,6 +88,9 @@ type ClusterObjectSetSpecApplyConfiguration struct { // // The maximum number of probes is 20. // + // Once set, even if empty, the progressionProbes field is immutable, as it is + // part of the immutable content of a revision. + // // ProgressionProbes []ProgressionProbeApplyConfiguration `json:"progressionProbes,omitempty"` // collisionProtection specifies the default collision protection strategy for all objects diff --git a/helm/olmv1/base/object-controller/crd/experimental/olm.operatorframework.io_clusterobjectsets.yaml b/helm/olmv1/base/object-controller/crd/experimental/olm.operatorframework.io_clusterobjectsets.yaml index 67e2d8e2d0..5d83203f1b 100644 --- a/helm/olmv1/base/object-controller/crd/experimental/olm.operatorframework.io_clusterobjectsets.yaml +++ b/helm/olmv1/base/object-controller/crd/experimental/olm.operatorframework.io_clusterobjectsets.yaml @@ -307,6 +307,9 @@ spec: a ConfigMap if that particular object does not pass the probe check. The maximum number of probes is 20. + + Once set, even if empty, the progressionProbes field is immutable, as it is + part of the immutable content of a revision. items: description: ProgressionProbe provides a custom probe definition, consisting of an object selection method and assertions. @@ -553,6 +556,9 @@ spec: minItems: 1 type: array x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: progressionProbes is immutable + rule: self == oldSelf || oldSelf.size() == 0 revision: description: |- revision is a required, immutable sequence number identifying a specific diff --git a/manifests/experimental-e2e.yaml b/manifests/experimental-e2e.yaml index 355dc36eef..38c406056c 100644 --- a/manifests/experimental-e2e.yaml +++ b/manifests/experimental-e2e.yaml @@ -2279,6 +2279,9 @@ spec: a ConfigMap if that particular object does not pass the probe check. The maximum number of probes is 20. + + Once set, even if empty, the progressionProbes field is immutable, as it is + part of the immutable content of a revision. items: description: ProgressionProbe provides a custom probe definition, consisting of an object selection method and assertions. @@ -2525,6 +2528,9 @@ spec: minItems: 1 type: array x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: progressionProbes is immutable + rule: self == oldSelf || oldSelf.size() == 0 revision: description: |- revision is a required, immutable sequence number identifying a specific diff --git a/manifests/experimental.yaml b/manifests/experimental.yaml index 29028f1ec7..f08813c1a6 100644 --- a/manifests/experimental.yaml +++ b/manifests/experimental.yaml @@ -2240,6 +2240,9 @@ spec: a ConfigMap if that particular object does not pass the probe check. The maximum number of probes is 20. + + Once set, even if empty, the progressionProbes field is immutable, as it is + part of the immutable content of a revision. items: description: ProgressionProbe provides a custom probe definition, consisting of an object selection method and assertions. @@ -2486,6 +2489,9 @@ spec: minItems: 1 type: array x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: progressionProbes is immutable + rule: self == oldSelf || oldSelf.size() == 0 revision: description: |- revision is a required, immutable sequence number identifying a specific