Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions api/v1/clusterobjectdeployment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,11 @@ type ClusterObjectDeploymentTemplateSpec struct {
// +listType=atomic
ProgressionProbes []ProgressionProbe `json:"progressionProbes,omitempty"`

// collisionProtection is the default strategy for objects in the revision.
// Objects and phases may override it. When omitted the controller uses Prevent
// in the generated ClusterObjectSet. Omission remains distinct from explicit
// Prevent for template identity; admission does not default this field.
// +optional
// collisionProtection is the required default collision protection strategy for objects in the revision.
// Objects and phases may override it.
// +required
Comment thread
perdasilva marked this conversation as resolved.
// +kubebuilder:validation:Enum=Prevent;IfNoController;None
CollisionProtection CollisionProtection `json:"collisionProtection,omitempty"`
CollisionProtection CollisionProtection `json:"collisionProtection"`
Comment thread
perdasilva marked this conversation as resolved.
}

// ClusterObjectDeploymentStatus reports rollout progress.
Expand Down
30 changes: 18 additions & 12 deletions api/v1/clusterobjectdeployment_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestClusterObjectDeploymentValidity(t *testing.T) {
{name: "collision protection IfNoController", mutate: codCollisionProtection(CollisionProtectionIfNoController), valid: true},
{name: "collision protection None", mutate: codCollisionProtection(CollisionProtectionNone), valid: true},
{name: "collision protection invalid", mutate: codCollisionProtection("Invalid")},
{name: "collision protection empty", mutate: codCollisionProtection("")},
{name: "phases omitted", mutate: func(cod *ClusterObjectDeployment) { cod.Spec.Template.Spec.Phases = nil }},
{name: "phases empty", mutate: func(cod *ClusterObjectDeployment) { cod.Spec.Template.Spec.Phases = []ClusterObjectSetPhase{} }},
{name: "20 phases", mutate: codPhases(20), valid: true},
Expand Down Expand Up @@ -104,24 +105,28 @@ func TestClusterObjectDeploymentValidity(t *testing.T) {
}
}

func TestClusterObjectDeploymentDoesNotDefaultTemplate(t *testing.T) {
func TestClusterObjectDeploymentCollisionProtectionIsRequired(t *testing.T) {
c := newClient(t)
cod := newTestClusterObjectDeployment("cod-no-defaults")

// collisionProtection missing rejects the object.
cod := newTestClusterObjectDeployment("cod-cp-required")
cod.Spec.Template.Spec.CollisionProtection = ""
err := c.Create(t.Context(), cod)
require.True(t, apierrors.IsInvalid(err), "expected missing collisionProtection to be invalid, got: %v", err)

// Valid values round-trip correctly; phase and object collisionProtection remain optional.
cod = newTestClusterObjectDeployment("cod-cp-roundtrip")
require.NoError(t, c.Create(t.Context(), cod))
cod = getTestClusterObjectDeployment(t, c, cod.Name)
require.Empty(t, cod.Spec.Template.Spec.CollisionProtection)
require.Equal(t, CollisionProtectionPrevent, cod.Spec.Template.Spec.CollisionProtection)
require.Empty(t, cod.Spec.Template.Spec.Phases[0].CollisionProtection)
require.Empty(t, cod.Spec.Template.Spec.Phases[0].Objects[0].CollisionProtection)

// Omitted and explicitly supplied Prevent remain distinguishable templates.
cod.Spec.Template.Spec.CollisionProtection = CollisionProtectionPrevent
require.NoError(t, c.Update(t.Context(), cod))
cod = getTestClusterObjectDeployment(t, c, cod.Name)
require.Equal(t, CollisionProtectionPrevent, cod.Spec.Template.Spec.CollisionProtection)
cod.Spec.Template.Spec.CollisionProtection = ""
// The field can be changed to another valid value.
cod.Spec.Template.Spec.CollisionProtection = CollisionProtectionNone
require.NoError(t, c.Update(t.Context(), cod))
cod = getTestClusterObjectDeployment(t, c, cod.Name)
require.Empty(t, cod.Spec.Template.Spec.CollisionProtection)
require.Equal(t, CollisionProtectionNone, cod.Spec.Template.Spec.CollisionProtection)
}

func TestClusterObjectDeploymentTemplateIsMutable(t *testing.T) {
Expand Down Expand Up @@ -154,7 +159,6 @@ func TestClusterObjectDeploymentTemplateIsMutable(t *testing.T) {

// Optional fields can also be removed after being set.
cod.Spec.Template.Spec.ProgressionProbes = nil
cod.Spec.Template.Spec.CollisionProtection = ""
require.NoError(t, c.Update(t.Context(), cod))
}

Expand All @@ -166,6 +170,7 @@ func TestClusterObjectDeploymentUpdateValidation(t *testing.T) {
}{
{name: "no phases", mutate: func(cod *ClusterObjectDeployment) { cod.Spec.Template.Spec.Phases = nil }},
{name: "invalid collision protection", mutate: codCollisionProtection("Invalid")},
{name: "empty collision protection", mutate: codCollisionProtection("")},
} {
t.Run(tc.name, func(t *testing.T) {
cod := newTestClusterObjectDeployment(fmt.Sprintf("cod-update-validity-%d", i))
Expand Down Expand Up @@ -306,7 +311,8 @@ func newTestClusterObjectDeployment(name string) *ClusterObjectDeployment {
Spec: ClusterObjectDeploymentSpec{
Template: ClusterObjectDeploymentTemplate{
Spec: ClusterObjectDeploymentTemplateSpec{
Phases: []ClusterObjectSetPhase{{Name: "deploy", Objects: []ClusterObjectSetObject{{Object: configMap()}}}},
CollisionProtection: CollisionProtectionPrevent,
Phases: []ClusterObjectSetPhase{{Name: "deploy", Objects: []ClusterObjectSetObject{{Object: configMap()}}}},
},
},
},
Expand Down
12 changes: 5 additions & 7 deletions api/v1/clusterobjectset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,18 +142,16 @@ type ClusterObjectSetSpec struct {
// <opcon:experimental>
ProgressionProbes []ProgressionProbe `json:"progressionProbes,omitempty"`

// collisionProtection specifies the default collision protection strategy for all objects
// in this revision. Individual phases or objects can override this value.
//
// When set, this value is used as the default for any phase or object that does not
// explicitly specify its own collisionProtection.
// collisionProtection is a required field that specifies the default collision protection
// strategy for all objects in this revision. Individual phases or objects can override this value.
//
// The resolution order is: object > phase > spec
// This value is used as the default for any phase or object that does not explicitly specify
// its own collisionProtection. The resolution order is: object > phase > spec.
//
// +required
// +kubebuilder:validation:Enum=Prevent;IfNoController;None
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="collisionProtection is immutable"
CollisionProtection CollisionProtection `json:"collisionProtection,omitempty"`
CollisionProtection CollisionProtection `json:"collisionProtection"`
}

// ProgressionProbe provides a custom probe definition, consisting of an object selection method and assertions.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 4 additions & 6 deletions applyconfigurations/api/v1/clusterobjectsetspec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/api-reference/olmv1-api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ _Appears in:_
| --- | --- | --- | --- |
| `phases` _[ClusterObjectSetPhase](#clusterobjectsetphase) array_ | phases is an ordered list of 1 to 20 uniquely named phases. Each phase contains<br />at most 50 objects using the existing inline object or immutable Secret reference<br />representation. Phase, object, and probe order participate in template identity. | | MaxItems: 20 <br />MinItems: 1 <br />Required: \{\} <br /> |
| `progressionProbes` _[ProgressionProbe](#progressionprobe) array_ | progressionProbes supplies custom readiness probes for objects in every phase.<br />The existing ClusterObjectSet selector and assertion semantics apply. | | MaxItems: 20 <br />MinItems: 1 <br />Optional: \{\} <br /> |
| `collisionProtection` _[CollisionProtection](#collisionprotection)_ | collisionProtection is the default strategy for objects in the revision.<br />Objects and phases may override it. When omitted the controller uses Prevent<br />in the generated ClusterObjectSet. Omission remains distinct from explicit<br />Prevent for template identity; admission does not default this field. | | Enum: [Prevent IfNoController None] <br />Optional: \{\} <br /> |
| `collisionProtection` _[CollisionProtection](#collisionprotection)_ | collisionProtection is the required default collision protection strategy for objects in the revision.<br />Objects and phases may override it. | | Enum: [Prevent IfNoController None] <br />Required: \{\} <br /> |



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,8 @@ spec:
properties:
collisionProtection:
description: |-
collisionProtection is the default strategy for objects in the revision.
Objects and phases may override it. When omitted the controller uses Prevent
in the generated ClusterObjectSet. Omission remains distinct from explicit
Prevent for template identity; admission does not default this field.
collisionProtection is the required default collision protection strategy for objects in the revision.
Objects and phases may override it.
enum:
- Prevent
- IfNoController
Expand Down Expand Up @@ -469,6 +467,7 @@ spec:
type: array
x-kubernetes-list-type: atomic
required:
- collisionProtection
- phases
type: object
required:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,11 @@ spec:
properties:
collisionProtection:
description: |-
collisionProtection specifies the default collision protection strategy for all objects
in this revision. Individual phases or objects can override this value.
collisionProtection is a required field that specifies the default collision protection
strategy for all objects in this revision. Individual phases or objects can override this value.

When set, this value is used as the default for any phase or object that does not
explicitly specify its own collisionProtection.

The resolution order is: object > phase > spec
This value is used as the default for any phase or object that does not explicitly specify
its own collisionProtection. The resolution order is: object > phase > spec.
enum:
- Prevent
- IfNoController
Expand Down
17 changes: 7 additions & 10 deletions manifests/experimental-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1474,10 +1474,8 @@ spec:
properties:
collisionProtection:
description: |-
collisionProtection is the default strategy for objects in the revision.
Objects and phases may override it. When omitted the controller uses Prevent
in the generated ClusterObjectSet. Omission remains distinct from explicit
Prevent for template identity; admission does not default this field.
collisionProtection is the required default collision protection strategy for objects in the revision.
Objects and phases may override it.
enum:
- Prevent
- IfNoController
Expand Down Expand Up @@ -1880,6 +1878,7 @@ spec:
type: array
x-kubernetes-list-type: atomic
required:
- collisionProtection
- phases
type: object
required:
Expand Down Expand Up @@ -2048,13 +2047,11 @@ spec:
properties:
collisionProtection:
description: |-
collisionProtection specifies the default collision protection strategy for all objects
in this revision. Individual phases or objects can override this value.
collisionProtection is a required field that specifies the default collision protection
strategy for all objects in this revision. Individual phases or objects can override this value.

When set, this value is used as the default for any phase or object that does not
explicitly specify its own collisionProtection.

The resolution order is: object > phase > spec
This value is used as the default for any phase or object that does not explicitly specify
its own collisionProtection. The resolution order is: object > phase > spec.
enum:
- Prevent
- IfNoController
Expand Down
17 changes: 7 additions & 10 deletions manifests/experimental.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1435,10 +1435,8 @@ spec:
properties:
collisionProtection:
description: |-
collisionProtection is the default strategy for objects in the revision.
Objects and phases may override it. When omitted the controller uses Prevent
in the generated ClusterObjectSet. Omission remains distinct from explicit
Prevent for template identity; admission does not default this field.
collisionProtection is the required default collision protection strategy for objects in the revision.
Objects and phases may override it.
enum:
- Prevent
- IfNoController
Expand Down Expand Up @@ -1841,6 +1839,7 @@ spec:
type: array
x-kubernetes-list-type: atomic
required:
- collisionProtection
- phases
type: object
required:
Expand Down Expand Up @@ -2009,13 +2008,11 @@ spec:
properties:
collisionProtection:
description: |-
collisionProtection specifies the default collision protection strategy for all objects
in this revision. Individual phases or objects can override this value.
collisionProtection is a required field that specifies the default collision protection
strategy for all objects in this revision. Individual phases or objects can override this value.

When set, this value is used as the default for any phase or object that does not
explicitly specify its own collisionProtection.

The resolution order is: object > phase > spec
This value is used as the default for any phase or object that does not explicitly specify
its own collisionProtection. The resolution order is: object > phase > spec.
enum:
- Prevent
- IfNoController
Expand Down
Loading