diff --git a/.claude/commands/list-e2e-steps.md b/.claude/commands/list-e2e-steps.md index 8817d4806d..3f78d55d1c 100644 --- a/.claude/commands/list-e2e-steps.md +++ b/.claude/commands/list-e2e-steps.md @@ -66,7 +66,7 @@ Organize steps into these 10 categories. For each step, document: 2. **Catalog Management** - ClusterCatalog creation, updates, image tagging, deletion 3. **ClusterExtension Lifecycle** - Apply, update, remove ClusterExtension resources 4. **ClusterExtension Status & Conditions** - Condition checks, transition times, reconciliation -5. **ClusterExtensionRevision** - Revision-specific condition checks, archival, annotations, labels, active revisions +5. **ClusterObjectSet** - Revision-specific condition checks, archival, annotations, labels, active revisions 6. **Generic Resource Operations** - Get, delete, restore, match arbitrary resources 7. **Test Operator Control** - Marking test-operator deployment ready/not-ready 8. **Metrics** - Fetching and validating Prometheus metrics diff --git a/AGENTS.md b/AGENTS.md index 07dd2e6be8..e71ab54da1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,7 +11,7 @@ operator-controller is the central component of Operator Lifecycle Manager (OLM) install and manage cluster extensions. The project follows a microservices architecture with two main binaries: **operator-controller** - - manages `ClusterExtension` and `ClusterExtensionRevision` CRDs + - manages `ClusterExtension` and `ClusterObjectSet` CRDs - resolves bundles from configured source - unpacks bundles and renders manifests from them - applies manifests with phase-based rollouts @@ -193,7 +193,7 @@ make generate - **Primary CRDs:** - `ClusterExtension` - declares desired extension installations - - `ClusterExtensionRevision` - revision management (experimental) + - `ClusterObjectSet` - revision management (experimental) - `ClusterCatalog` - catalog source definitions - **API domain:** `olm.operatorframework.io` - This is the API group of our user-facing CRDs @@ -204,7 +204,7 @@ make generate Two manifest variants exist: - **Standard:** Production-ready features -- **Experimental:** Features under development/testing (includes `ClusterExtensionRevision` API) +- **Experimental:** Features under development/testing (includes `ClusterObjectSet` API) --- @@ -330,7 +330,7 @@ Two manifest variants exist: **operator-controller:** - `ClusterExtension` controller - manages extension installations -- `ClusterExtensionRevision` controller - manages revision lifecycle +- `ClusterObjectSet` controller - manages revision lifecycle - Resolver - bundle version selection - Applier - applies manifests to cluster - Content Manager - manages extension content diff --git a/api/v1/clusterextension_types.go b/api/v1/clusterextension_types.go index 18f982725b..cf5946a408 100644 --- a/api/v1/clusterextension_types.go +++ b/api/v1/clusterextension_types.go @@ -468,9 +468,9 @@ type BundleMetadata struct { Version string `json:"version"` } -// RevisionStatus defines the observed state of a ClusterExtensionRevision. +// RevisionStatus defines the observed state of a ClusterObjectSet. type RevisionStatus struct { - // name of the ClusterExtensionRevision resource + // name of the ClusterObjectSet resource Name string `json:"name"` // conditions optionally expose Progressing and Available condition of the revision, // in case when it is not yet marked as successfully installed (condition Succeeded is not set to True). @@ -498,7 +498,7 @@ type ClusterExtensionStatus struct { // When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. // When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. // - // When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterExtensionRevisions in active roll out. + // When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterObjectSets in active roll out. // // // When the ClusterExtension is sourced from a catalog, it surfaces deprecation conditions based on catalog metadata. @@ -518,7 +518,7 @@ type ClusterExtensionStatus struct { // +optional Install *ClusterExtensionInstallStatus `json:"install,omitempty"` - // activeRevisions holds a list of currently active (non-archived) ClusterExtensionRevisions, + // activeRevisions holds a list of currently active (non-archived) ClusterObjectSets, // including both installed and rolling out revisions. // +listType=map // +listMapKey=name diff --git a/api/v1/clusterextensionrevision_types.go b/api/v1/clusterobjectset_types.go similarity index 85% rename from api/v1/clusterextensionrevision_types.go rename to api/v1/clusterobjectset_types.go index a0308af06e..a342e02f9e 100644 --- a/api/v1/clusterextensionrevision_types.go +++ b/api/v1/clusterobjectset_types.go @@ -22,25 +22,25 @@ import ( ) const ( - ClusterExtensionRevisionKind = "ClusterExtensionRevision" + ClusterObjectSetKind = "ClusterObjectSet" // Condition Types - ClusterExtensionRevisionTypeAvailable = "Available" - ClusterExtensionRevisionTypeProgressing = "Progressing" - ClusterExtensionRevisionTypeSucceeded = "Succeeded" + ClusterObjectSetTypeAvailable = "Available" + ClusterObjectSetTypeProgressing = "Progressing" + ClusterObjectSetTypeSucceeded = "Succeeded" // Condition Reasons - ClusterExtensionRevisionReasonArchived = "Archived" - ClusterExtensionRevisionReasonBlocked = "Blocked" - ClusterExtensionRevisionReasonProbeFailure = "ProbeFailure" - ClusterExtensionRevisionReasonProbesSucceeded = "ProbesSucceeded" - ClusterExtensionRevisionReasonReconciling = "Reconciling" - ClusterExtensionRevisionReasonRetrying = "Retrying" + ClusterObjectSetReasonArchived = "Archived" + ClusterObjectSetReasonBlocked = "Blocked" + ClusterObjectSetReasonProbeFailure = "ProbeFailure" + ClusterObjectSetReasonProbesSucceeded = "ProbesSucceeded" + ClusterObjectSetReasonReconciling = "Reconciling" + ClusterObjectSetReasonRetrying = "Retrying" ) -// ClusterExtensionRevisionSpec defines the desired state of ClusterExtensionRevision. -type ClusterExtensionRevisionSpec struct { - // lifecycleState specifies the lifecycle state of the ClusterExtensionRevision. +// ClusterObjectSetSpec defines the desired state of ClusterObjectSet. +type ClusterObjectSetSpec struct { + // lifecycleState specifies the lifecycle state of the ClusterObjectSet. // // When set to "Active", the revision is actively managed and reconciled. // When set to "Archived", the revision is inactive and any resources not managed by a subsequent revision are deleted. @@ -56,13 +56,13 @@ type ClusterExtensionRevisionSpec struct { // +required // +kubebuilder:validation:Enum=Active;Archived // +kubebuilder:validation:XValidation:rule="oldSelf == 'Active' || oldSelf == 'Archived' && oldSelf == self", message="cannot un-archive" - LifecycleState ClusterExtensionRevisionLifecycleState `json:"lifecycleState,omitempty"` + LifecycleState ClusterObjectSetLifecycleState `json:"lifecycleState,omitempty"` // revision is a required, immutable sequence number representing a specific revision // of the parent ClusterExtension. // // The revision field must be a positive integer. - // Each ClusterExtensionRevision belonging to the same parent ClusterExtension must have a unique revision number. + // Each ClusterObjectSet belonging to the same parent ClusterExtension must have a unique revision number. // The revision number must always be the previous revision number plus one, or 1 for the first revision. // // +required @@ -93,7 +93,7 @@ type ClusterExtensionRevisionSpec struct { // +listType=map // +listMapKey=name // +optional - Phases []ClusterExtensionRevisionPhase `json:"phases,omitempty"` + Phases []ClusterObjectSetPhase `json:"phases,omitempty"` // progressDeadlineMinutes is an optional field that defines the maximum period // of time in minutes after which an installation should be considered failed and @@ -338,21 +338,21 @@ type FieldValueProbe struct { Value string `json:"value,omitempty"` } -// ClusterExtensionRevisionLifecycleState specifies the lifecycle state of the ClusterExtensionRevision. -type ClusterExtensionRevisionLifecycleState string +// ClusterObjectSetLifecycleState specifies the lifecycle state of the ClusterObjectSet. +type ClusterObjectSetLifecycleState string const ( - // ClusterExtensionRevisionLifecycleStateActive / "Active" is the default lifecycle state. - ClusterExtensionRevisionLifecycleStateActive ClusterExtensionRevisionLifecycleState = "Active" - // ClusterExtensionRevisionLifecycleStateArchived / "Archived" archives the revision for historical or auditing purposes. + // ClusterObjectSetLifecycleStateActive / "Active" is the default lifecycle state. + ClusterObjectSetLifecycleStateActive ClusterObjectSetLifecycleState = "Active" + // ClusterObjectSetLifecycleStateArchived / "Archived" archives the revision for historical or auditing purposes. // The revision is removed from the owner list of all other objects previously under management and all objects // that did not transition to a succeeding revision are deleted. - ClusterExtensionRevisionLifecycleStateArchived ClusterExtensionRevisionLifecycleState = "Archived" + ClusterObjectSetLifecycleStateArchived ClusterObjectSetLifecycleState = "Archived" ) -// ClusterExtensionRevisionPhase represents a group of objects that are applied together. The phase is considered +// ClusterObjectSetPhase represents a group of objects that are applied together. The phase is considered // complete only after all objects pass their status probes. -type ClusterExtensionRevisionPhase struct { +type ClusterObjectSetPhase struct { // name is a required identifier for this phase. // // phase names must follow the DNS label standard as defined in [RFC 1123]. @@ -374,7 +374,7 @@ type ClusterExtensionRevisionPhase struct { // All objects in this list are applied to the cluster in no particular order. The maximum number of objects per phase is 50. // +required // +kubebuilder:validation:MaxItems=50 - Objects []ClusterExtensionRevisionObject `json:"objects"` + Objects []ClusterObjectSetObject `json:"objects"` // collisionProtection specifies the default collision protection strategy for all objects // in this phase. Individual objects can override this value. @@ -390,13 +390,13 @@ type ClusterExtensionRevisionPhase struct { CollisionProtection CollisionProtection `json:"collisionProtection,omitempty"` } -// ClusterExtensionRevisionObject represents a Kubernetes object to be applied as part +// ClusterObjectSetObject represents a Kubernetes object to be applied as part // of a phase, along with its collision protection settings. // // Exactly one of object or ref must be set. // // +kubebuilder:validation:XValidation:rule="has(self.object) != has(self.ref)",message="exactly one of object or ref must be set" -type ClusterExtensionRevisionObject struct { +type ClusterObjectSetObject struct { // object is an optional embedded Kubernetes object to be applied. // // Exactly one of object or ref must be set. @@ -484,27 +484,27 @@ const ( CollisionProtectionNone CollisionProtection = "None" ) -// ClusterExtensionRevisionStatus defines the observed state of a ClusterExtensionRevision. -type ClusterExtensionRevisionStatus struct { +// ClusterObjectSetStatus defines the observed state of a ClusterObjectSet. +type ClusterObjectSetStatus struct { // conditions is an optional list of status conditions describing the state of the - // ClusterExtensionRevision. + // ClusterObjectSet. // // The Progressing condition represents whether the revision is actively rolling out: - // - When status is True and reason is RollingOut, the ClusterExtensionRevision rollout is actively making progress and is in transition. - // - When status is True and reason is Retrying, the ClusterExtensionRevision has encountered an error that could be resolved on subsequent reconciliation attempts. - // - When status is True and reason is Succeeded, the ClusterExtensionRevision has reached the desired state. - // - When status is False and reason is Blocked, the ClusterExtensionRevision has encountered an error that requires manual intervention for recovery. - // - When status is False and reason is Archived, the ClusterExtensionRevision is archived and not being actively reconciled. + // - When status is True and reason is RollingOut, the ClusterObjectSet rollout is actively making progress and is in transition. + // - When status is True and reason is Retrying, the ClusterObjectSet has encountered an error that could be resolved on subsequent reconciliation attempts. + // - When status is True and reason is Succeeded, the ClusterObjectSet has reached the desired state. + // - When status is False and reason is Blocked, the ClusterObjectSet has encountered an error that requires manual intervention for recovery. + // - When status is False and reason is Archived, the ClusterObjectSet is archived and not being actively reconciled. // // The Available condition represents whether the revision has been successfully rolled out and is available: - // - When status is True and reason is ProbesSucceeded, the ClusterExtensionRevision has been successfully rolled out and all objects pass their readiness probes. + // - When status is True and reason is ProbesSucceeded, the ClusterObjectSet has been successfully rolled out and all objects pass their readiness probes. // - When status is False and reason is ProbeFailure, one or more objects are failing their readiness probes during rollout. - // - When status is Unknown and reason is Reconciling, the ClusterExtensionRevision has encountered an error that prevented it from observing the probes. - // - When status is Unknown and reason is Archived, the ClusterExtensionRevision has been archived and its objects have been torn down. - // - When status is Unknown and reason is Migrated, the ClusterExtensionRevision was migrated from an existing release and object status probe results have not yet been observed. + // - When status is Unknown and reason is Reconciling, the ClusterObjectSet has encountered an error that prevented it from observing the probes. + // - When status is Unknown and reason is Archived, the ClusterObjectSet has been archived and its objects have been torn down. + // - When status is Unknown and reason is Migrated, the ClusterObjectSet was migrated from an existing release and object status probe results have not yet been observed. // // The Succeeded condition represents whether the revision has successfully completed its rollout: - // - When status is True and reason is Succeeded, the ClusterExtensionRevision has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. + // - When status is True and reason is Succeeded, the ClusterObjectSet has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. // // +listType=map // +listMapKey=type @@ -521,13 +521,13 @@ type ClusterExtensionRevisionStatus struct { // +kubebuilder:printcolumn:name="Progressing",type=string,JSONPath=`.status.conditions[?(@.type=='Progressing')].status` // +kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp` -// ClusterExtensionRevision represents an immutable snapshot of Kubernetes objects +// ClusterObjectSet represents an immutable snapshot of Kubernetes objects // for a specific version of a ClusterExtension. Each revision contains objects // organized into phases that roll out sequentially. The same object can only be managed by a single revision // at a time. Ownership of objects is transitioned from one revision to the next as the extension is upgraded // or reconfigured. Once the latest revision has rolled out successfully, previous active revisions are archived for // posterity. -type ClusterExtensionRevision struct { +type ClusterObjectSet struct { metav1.TypeMeta `json:",inline"` // metadata is the standard object's metadata. @@ -535,30 +535,30 @@ type ClusterExtensionRevision struct { // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - // spec defines the desired state of the ClusterExtensionRevision. + // spec defines the desired state of the ClusterObjectSet. // +optional - Spec ClusterExtensionRevisionSpec `json:"spec,omitempty"` + Spec ClusterObjectSetSpec `json:"spec,omitempty"` - // status is optional and defines the observed state of the ClusterExtensionRevision. + // status is optional and defines the observed state of the ClusterObjectSet. // +optional - Status ClusterExtensionRevisionStatus `json:"status,omitempty"` + Status ClusterObjectSetStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true -// ClusterExtensionRevisionList contains a list of ClusterExtensionRevision -type ClusterExtensionRevisionList struct { +// ClusterObjectSetList contains a list of ClusterObjectSet +type ClusterObjectSetList struct { metav1.TypeMeta `json:",inline"` // +optional metav1.ListMeta `json:"metadata,omitempty"` - // items is a required list of ClusterExtensionRevision objects. + // items is a required list of ClusterObjectSet objects. // // +required - Items []ClusterExtensionRevision `json:"items"` + Items []ClusterObjectSet `json:"items"` } func init() { - SchemeBuilder.Register(&ClusterExtensionRevision{}, &ClusterExtensionRevisionList{}) + SchemeBuilder.Register(&ClusterObjectSet{}, &ClusterObjectSetList{}) } diff --git a/api/v1/clusterextensionrevision_types_test.go b/api/v1/clusterobjectset_types_test.go similarity index 54% rename from api/v1/clusterextensionrevision_types_test.go rename to api/v1/clusterobjectset_types_test.go index e1412e0e81..6770fbb129 100644 --- a/api/v1/clusterextensionrevision_types_test.go +++ b/api/v1/clusterobjectset_types_test.go @@ -11,101 +11,101 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -func TestClusterExtensionRevisionImmutability(t *testing.T) { +func TestClusterObjectSetImmutability(t *testing.T) { c := newClient(t) ctx := context.Background() i := 0 for name, tc := range map[string]struct { - spec ClusterExtensionRevisionSpec - updateFunc func(*ClusterExtensionRevision) + spec ClusterObjectSetSpec + updateFunc func(*ClusterObjectSet) allowed bool }{ "revision is immutable": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, }, - updateFunc: func(cer *ClusterExtensionRevision) { - cer.Spec.Revision = 2 + updateFunc: func(cos *ClusterObjectSet) { + cos.Spec.Revision = 2 }, }, "phases may be initially empty": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, - Phases: []ClusterExtensionRevisionPhase{}, + Phases: []ClusterObjectSetPhase{}, }, - updateFunc: func(cer *ClusterExtensionRevision) { - cer.Spec.Phases = []ClusterExtensionRevisionPhase{ + updateFunc: func(cos *ClusterObjectSet) { + cos.Spec.Phases = []ClusterObjectSetPhase{ { Name: "foo", - Objects: []ClusterExtensionRevisionObject{}, + Objects: []ClusterObjectSetObject{}, }, } }, allowed: true, }, "phases may be initially unset": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, }, - updateFunc: func(cer *ClusterExtensionRevision) { - cer.Spec.Phases = []ClusterExtensionRevisionPhase{ + updateFunc: func(cos *ClusterObjectSet) { + cos.Spec.Phases = []ClusterObjectSetPhase{ { Name: "foo", - Objects: []ClusterExtensionRevisionObject{}, + Objects: []ClusterObjectSetObject{}, }, } }, allowed: true, }, "phases are immutable if not empty": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "foo", - Objects: []ClusterExtensionRevisionObject{}, + Objects: []ClusterObjectSetObject{}, }, }, }, - updateFunc: func(cer *ClusterExtensionRevision) { - cer.Spec.Phases = []ClusterExtensionRevisionPhase{ + updateFunc: func(cos *ClusterObjectSet) { + cos.Spec.Phases = []ClusterObjectSetPhase{ { Name: "foo2", - Objects: []ClusterExtensionRevisionObject{}, + Objects: []ClusterObjectSetObject{}, }, } }, }, "spec collisionProtection is immutable": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, }, - updateFunc: func(cer *ClusterExtensionRevision) { - cer.Spec.CollisionProtection = CollisionProtectionNone + updateFunc: func(cos *ClusterObjectSet) { + cos.Spec.CollisionProtection = CollisionProtectionNone }, }, } { t.Run(name, func(t *testing.T) { - cer := &ClusterExtensionRevision{ + cos := &ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("foo%d", i), }, Spec: tc.spec, } i = i + 1 - require.NoError(t, c.Create(ctx, cer)) - tc.updateFunc(cer) - err := c.Update(ctx, cer) + require.NoError(t, c.Create(ctx, cos)) + tc.updateFunc(cos) + err := c.Update(ctx, cos) if tc.allowed && err != nil { t.Fatal("expected update to succeed, but got:", err) } @@ -116,67 +116,67 @@ func TestClusterExtensionRevisionImmutability(t *testing.T) { } } -func TestClusterExtensionRevisionValidity(t *testing.T) { +func TestClusterObjectSetValidity(t *testing.T) { c := newClient(t) ctx := context.Background() i := 0 for name, tc := range map[string]struct { - spec ClusterExtensionRevisionSpec + spec ClusterObjectSetSpec valid bool }{ "revision cannot be negative": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: -1, }, valid: false, }, "revision cannot be zero": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, }, valid: false, }, "revision must be positive": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, }, valid: true, }, "lifecycleState must be set": { - spec: ClusterExtensionRevisionSpec{ + spec: ClusterObjectSetSpec{ Revision: 1, }, valid: false, }, "phases must have no more than 20 phases": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, - Phases: make([]ClusterExtensionRevisionPhase, 21), + Phases: make([]ClusterObjectSetPhase, 21), }, valid: false, }, "phases entries must have no more than 50 objects": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "too-many-objects", - Objects: make([]ClusterExtensionRevisionObject, 51), + Objects: make([]ClusterObjectSetObject, 51), }, }, }, valid: false, }, "phases entry names cannot be empty": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "", }, @@ -185,10 +185,10 @@ func TestClusterExtensionRevisionValidity(t *testing.T) { valid: false, }, "phases entry names cannot start with symbols": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "-invalid", }, @@ -197,10 +197,10 @@ func TestClusterExtensionRevisionValidity(t *testing.T) { valid: false, }, "phases entry names cannot start with numeric characters": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "1-invalid", }, @@ -209,60 +209,60 @@ func TestClusterExtensionRevisionValidity(t *testing.T) { valid: false, }, "spec collisionProtection accepts Prevent": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, }, valid: true, }, "spec collisionProtection accepts IfNoController": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionIfNoController, }, valid: true, }, "spec collisionProtection accepts None": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionNone, }, valid: true, }, "spec collisionProtection is required": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, }, valid: false, }, "spec collisionProtection rejects invalid values": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtection("Invalid"), }, valid: false, }, "spec collisionProtection must be set": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, }, valid: false, }, "object collisionProtection is optional": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "deploy", - Objects: []ClusterExtensionRevisionObject{ + Objects: []ClusterObjectSetObject{ { Object: configMap(), }, @@ -273,14 +273,14 @@ func TestClusterExtensionRevisionValidity(t *testing.T) { valid: true, }, "object with inline object is valid": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "deploy", - Objects: []ClusterExtensionRevisionObject{ + Objects: []ClusterObjectSetObject{ { Object: configMap(), }, @@ -291,14 +291,14 @@ func TestClusterExtensionRevisionValidity(t *testing.T) { valid: true, }, "object with ref is valid": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "deploy", - Objects: []ClusterExtensionRevisionObject{ + Objects: []ClusterObjectSetObject{ { Ref: ObjectSourceRef{Name: "my-secret", Key: "my-key"}, }, @@ -309,14 +309,14 @@ func TestClusterExtensionRevisionValidity(t *testing.T) { valid: true, }, "object with both object and ref is invalid": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "deploy", - Objects: []ClusterExtensionRevisionObject{ + Objects: []ClusterObjectSetObject{ { Object: configMap(), Ref: ObjectSourceRef{Name: "my-secret", Key: "my-key"}, @@ -328,14 +328,14 @@ func TestClusterExtensionRevisionValidity(t *testing.T) { valid: false, }, "object with neither object nor ref is invalid": { - spec: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + spec: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: CollisionProtectionPrevent, - Phases: []ClusterExtensionRevisionPhase{ + Phases: []ClusterObjectSetPhase{ { Name: "deploy", - Objects: []ClusterExtensionRevisionObject{ + Objects: []ClusterObjectSetObject{ {}, }, }, @@ -345,14 +345,14 @@ func TestClusterExtensionRevisionValidity(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { - cer := &ClusterExtensionRevision{ + cos := &ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("bar%d", i), }, Spec: tc.spec, } i = i + 1 - err := c.Create(ctx, cer) + err := c.Create(ctx, cos) if tc.valid && err != nil { t.Fatal("expected create to succeed, but got:", err) } diff --git a/api/v1/validation_test.go b/api/v1/validation_test.go index c2f8574930..f56d9c5045 100644 --- a/api/v1/validation_test.go +++ b/api/v1/validation_test.go @@ -34,7 +34,7 @@ func TestValidate(t *testing.T) { } return s } - defaultRevisionSpec := func(s *ClusterExtensionRevisionSpec) *ClusterExtensionRevisionSpec { + defaultRevisionSpec := func(s *ClusterObjectSetSpec) *ClusterObjectSetSpec { s.Revision = 1 s.CollisionProtection = CollisionProtectionPrevent return s @@ -89,55 +89,55 @@ func TestValidate(t *testing.T) { }, want: want{valid: true}, }, - "ClusterExtensionRevision: invalid progress deadline < 10": { + "ClusterObjectSet: invalid progress deadline < 10": { args: args{ - object: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + object: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, ProgressDeadlineMinutes: 9, }, }, want: want{valid: false}, }, - "ClusterExtensionRevision: valid progress deadline = 10": { + "ClusterObjectSet: valid progress deadline = 10": { args: args{ - object: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + object: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, ProgressDeadlineMinutes: 10, }, }, want: want{valid: true}, }, - "ClusterExtensionRevision: valid progress deadline = 360": { + "ClusterObjectSet: valid progress deadline = 360": { args: args{ - object: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + object: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, ProgressDeadlineMinutes: 360, }, }, want: want{valid: true}, }, - "ClusterExtensionRevision: valid progress deadline = 720": { + "ClusterObjectSet: valid progress deadline = 720": { args: args{ - object: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + object: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, ProgressDeadlineMinutes: 720, }, }, want: want{valid: true}, }, - "ClusterExtensionRevision: invalid progress deadline > 720": { + "ClusterObjectSet: invalid progress deadline > 720": { args: args{ - object: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + object: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, ProgressDeadlineMinutes: 721, }, }, want: want{valid: false}, }, - "ClusterExtensionRevision: no progress deadline set": { + "ClusterObjectSet: no progress deadline set": { args: args{ - object: ClusterExtensionRevisionSpec{ - LifecycleState: ClusterExtensionRevisionLifecycleStateActive, + object: ClusterObjectSetSpec{ + LifecycleState: ClusterObjectSetLifecycleStateActive, }, }, want: want{valid: true}, @@ -157,17 +157,17 @@ func TestValidate(t *testing.T) { defaultExtensionSpec(&ce.Spec) } obj = ce - case ClusterExtensionRevisionSpec: - cer := &ClusterExtensionRevision{ + case ClusterObjectSetSpec: + cos := &ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("cer-%d", i), + Name: fmt.Sprintf("cos-%d", i), }, Spec: s, } if !tc.args.skipDefaulting { - defaultRevisionSpec(&cer.Spec) + defaultRevisionSpec(&cos.Spec) } - obj = cer + obj = cos default: t.Fatalf("unknown type %T", s) } diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 4b345e65fc..8d1cea0245 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -358,7 +358,68 @@ func (in *ClusterExtensionList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterExtensionRevision) DeepCopyInto(out *ClusterExtensionRevision) { +func (in *ClusterExtensionSpec) DeepCopyInto(out *ClusterExtensionSpec) { + *out = *in + out.ServiceAccount = in.ServiceAccount + in.Source.DeepCopyInto(&out.Source) + if in.Install != nil { + in, out := &in.Install, &out.Install + *out = new(ClusterExtensionInstallConfig) + (*in).DeepCopyInto(*out) + } + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = new(ClusterExtensionConfig) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionSpec. +func (in *ClusterExtensionSpec) DeepCopy() *ClusterExtensionSpec { + if in == nil { + return nil + } + out := new(ClusterExtensionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Install != nil { + in, out := &in.Install, &out.Install + *out = new(ClusterExtensionInstallStatus) + **out = **in + } + if in.ActiveRevisions != nil { + in, out := &in.ActiveRevisions, &out.ActiveRevisions + *out = make([]RevisionStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionStatus. +func (in *ClusterExtensionStatus) DeepCopy() *ClusterExtensionStatus { + if in == nil { + return nil + } + out := new(ClusterExtensionStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterObjectSet) DeepCopyInto(out *ClusterObjectSet) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) @@ -366,18 +427,18 @@ func (in *ClusterExtensionRevision) DeepCopyInto(out *ClusterExtensionRevision) in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionRevision. -func (in *ClusterExtensionRevision) DeepCopy() *ClusterExtensionRevision { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterObjectSet. +func (in *ClusterObjectSet) DeepCopy() *ClusterObjectSet { if in == nil { return nil } - out := new(ClusterExtensionRevision) + out := new(ClusterObjectSet) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterExtensionRevision) DeepCopyObject() runtime.Object { +func (in *ClusterObjectSet) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -385,31 +446,31 @@ func (in *ClusterExtensionRevision) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterExtensionRevisionList) DeepCopyInto(out *ClusterExtensionRevisionList) { +func (in *ClusterObjectSetList) DeepCopyInto(out *ClusterObjectSetList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]ClusterExtensionRevision, len(*in)) + *out = make([]ClusterObjectSet, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionRevisionList. -func (in *ClusterExtensionRevisionList) DeepCopy() *ClusterExtensionRevisionList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterObjectSetList. +func (in *ClusterObjectSetList) DeepCopy() *ClusterObjectSetList { if in == nil { return nil } - out := new(ClusterExtensionRevisionList) + out := new(ClusterObjectSetList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterExtensionRevisionList) DeepCopyObject() runtime.Object { +func (in *ClusterObjectSetList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -417,50 +478,50 @@ func (in *ClusterExtensionRevisionList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterExtensionRevisionObject) DeepCopyInto(out *ClusterExtensionRevisionObject) { +func (in *ClusterObjectSetObject) DeepCopyInto(out *ClusterObjectSetObject) { *out = *in in.Object.DeepCopyInto(&out.Object) out.Ref = in.Ref } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionRevisionObject. -func (in *ClusterExtensionRevisionObject) DeepCopy() *ClusterExtensionRevisionObject { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterObjectSetObject. +func (in *ClusterObjectSetObject) DeepCopy() *ClusterObjectSetObject { if in == nil { return nil } - out := new(ClusterExtensionRevisionObject) + out := new(ClusterObjectSetObject) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterExtensionRevisionPhase) DeepCopyInto(out *ClusterExtensionRevisionPhase) { +func (in *ClusterObjectSetPhase) DeepCopyInto(out *ClusterObjectSetPhase) { *out = *in if in.Objects != nil { in, out := &in.Objects, &out.Objects - *out = make([]ClusterExtensionRevisionObject, len(*in)) + *out = make([]ClusterObjectSetObject, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionRevisionPhase. -func (in *ClusterExtensionRevisionPhase) DeepCopy() *ClusterExtensionRevisionPhase { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterObjectSetPhase. +func (in *ClusterObjectSetPhase) DeepCopy() *ClusterObjectSetPhase { if in == nil { return nil } - out := new(ClusterExtensionRevisionPhase) + out := new(ClusterObjectSetPhase) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterExtensionRevisionSpec) DeepCopyInto(out *ClusterExtensionRevisionSpec) { +func (in *ClusterObjectSetSpec) DeepCopyInto(out *ClusterObjectSetSpec) { *out = *in if in.Phases != nil { in, out := &in.Phases, &out.Phases - *out = make([]ClusterExtensionRevisionPhase, len(*in)) + *out = make([]ClusterObjectSetPhase, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -474,18 +535,18 @@ func (in *ClusterExtensionRevisionSpec) DeepCopyInto(out *ClusterExtensionRevisi } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionRevisionSpec. -func (in *ClusterExtensionRevisionSpec) DeepCopy() *ClusterExtensionRevisionSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterObjectSetSpec. +func (in *ClusterObjectSetSpec) DeepCopy() *ClusterObjectSetSpec { if in == nil { return nil } - out := new(ClusterExtensionRevisionSpec) + out := new(ClusterObjectSetSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterExtensionRevisionStatus) DeepCopyInto(out *ClusterExtensionRevisionStatus) { +func (in *ClusterObjectSetStatus) DeepCopyInto(out *ClusterObjectSetStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions @@ -496,73 +557,12 @@ func (in *ClusterExtensionRevisionStatus) DeepCopyInto(out *ClusterExtensionRevi } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionRevisionStatus. -func (in *ClusterExtensionRevisionStatus) DeepCopy() *ClusterExtensionRevisionStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterObjectSetStatus. +func (in *ClusterObjectSetStatus) DeepCopy() *ClusterObjectSetStatus { if in == nil { return nil } - out := new(ClusterExtensionRevisionStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterExtensionSpec) DeepCopyInto(out *ClusterExtensionSpec) { - *out = *in - out.ServiceAccount = in.ServiceAccount - in.Source.DeepCopyInto(&out.Source) - if in.Install != nil { - in, out := &in.Install, &out.Install - *out = new(ClusterExtensionInstallConfig) - (*in).DeepCopyInto(*out) - } - if in.Config != nil { - in, out := &in.Config, &out.Config - *out = new(ClusterExtensionConfig) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionSpec. -func (in *ClusterExtensionSpec) DeepCopy() *ClusterExtensionSpec { - if in == nil { - return nil - } - out := new(ClusterExtensionSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterExtensionStatus) DeepCopyInto(out *ClusterExtensionStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Install != nil { - in, out := &in.Install, &out.Install - *out = new(ClusterExtensionInstallStatus) - **out = **in - } - if in.ActiveRevisions != nil { - in, out := &in.ActiveRevisions, &out.ActiveRevisions - *out = make([]RevisionStatus, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExtensionStatus. -func (in *ClusterExtensionStatus) DeepCopy() *ClusterExtensionStatus { - if in == nil { - return nil - } - out := new(ClusterExtensionStatus) + out := new(ClusterObjectSetStatus) in.DeepCopyInto(out) return out } diff --git a/applyconfigurations/api/v1/clusterextensionrevisionstatus.go b/applyconfigurations/api/v1/clusterextensionrevisionstatus.go deleted file mode 100644 index f867971152..0000000000 --- a/applyconfigurations/api/v1/clusterextensionrevisionstatus.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2022. - -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. -*/ -// Code generated by controller-gen-v0.20. DO NOT EDIT. - -package v1 - -import ( - metav1 "k8s.io/client-go/applyconfigurations/meta/v1" -) - -// ClusterExtensionRevisionStatusApplyConfiguration represents a declarative configuration of the ClusterExtensionRevisionStatus type for use -// with apply. -// -// ClusterExtensionRevisionStatus defines the observed state of a ClusterExtensionRevision. -type ClusterExtensionRevisionStatusApplyConfiguration struct { - // conditions is an optional list of status conditions describing the state of the - // ClusterExtensionRevision. - // - // The Progressing condition represents whether the revision is actively rolling out: - // - When status is True and reason is RollingOut, the ClusterExtensionRevision rollout is actively making progress and is in transition. - // - When status is True and reason is Retrying, the ClusterExtensionRevision has encountered an error that could be resolved on subsequent reconciliation attempts. - // - When status is True and reason is Succeeded, the ClusterExtensionRevision has reached the desired state. - // - When status is False and reason is Blocked, the ClusterExtensionRevision has encountered an error that requires manual intervention for recovery. - // - When status is False and reason is Archived, the ClusterExtensionRevision is archived and not being actively reconciled. - // - // The Available condition represents whether the revision has been successfully rolled out and is available: - // - When status is True and reason is ProbesSucceeded, the ClusterExtensionRevision has been successfully rolled out and all objects pass their readiness probes. - // - When status is False and reason is ProbeFailure, one or more objects are failing their readiness probes during rollout. - // - When status is Unknown and reason is Reconciling, the ClusterExtensionRevision has encountered an error that prevented it from observing the probes. - // - When status is Unknown and reason is Archived, the ClusterExtensionRevision has been archived and its objects have been torn down. - // - When status is Unknown and reason is Migrated, the ClusterExtensionRevision was migrated from an existing release and object status probe results have not yet been observed. - // - // The Succeeded condition represents whether the revision has successfully completed its rollout: - // - When status is True and reason is Succeeded, the ClusterExtensionRevision has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. - Conditions []metav1.ConditionApplyConfiguration `json:"conditions,omitempty"` -} - -// ClusterExtensionRevisionStatusApplyConfiguration constructs a declarative configuration of the ClusterExtensionRevisionStatus type for use with -// apply. -func ClusterExtensionRevisionStatus() *ClusterExtensionRevisionStatusApplyConfiguration { - return &ClusterExtensionRevisionStatusApplyConfiguration{} -} - -// WithConditions adds the given value to the Conditions field in the declarative configuration -// and returns the receiver, so that objects can be build by chaining "With" function invocations. -// If called multiple times, values provided by each call will be appended to the Conditions field. -func (b *ClusterExtensionRevisionStatusApplyConfiguration) WithConditions(values ...*metav1.ConditionApplyConfiguration) *ClusterExtensionRevisionStatusApplyConfiguration { - for i := range values { - if values[i] == nil { - panic("nil value passed to WithConditions") - } - b.Conditions = append(b.Conditions, *values[i]) - } - return b -} diff --git a/applyconfigurations/api/v1/clusterextensionstatus.go b/applyconfigurations/api/v1/clusterextensionstatus.go index dd76e76a19..d11ad931dd 100644 --- a/applyconfigurations/api/v1/clusterextensionstatus.go +++ b/applyconfigurations/api/v1/clusterextensionstatus.go @@ -39,7 +39,7 @@ type ClusterExtensionStatusApplyConfiguration struct { // When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. // When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. // - // When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterExtensionRevisions in active roll out. + // When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterObjectSets in active roll out. // // // When the ClusterExtension is sourced from a catalog, it surfaces deprecation conditions based on catalog metadata. @@ -51,7 +51,7 @@ type ClusterExtensionStatusApplyConfiguration struct { Conditions []metav1.ConditionApplyConfiguration `json:"conditions,omitempty"` // install is a representation of the current installation status for this ClusterExtension. Install *ClusterExtensionInstallStatusApplyConfiguration `json:"install,omitempty"` - // activeRevisions holds a list of currently active (non-archived) ClusterExtensionRevisions, + // activeRevisions holds a list of currently active (non-archived) ClusterObjectSets, // including both installed and rolling out revisions. // ActiveRevisions []RevisionStatusApplyConfiguration `json:"activeRevisions,omitempty"` diff --git a/applyconfigurations/api/v1/clusterextensionrevision.go b/applyconfigurations/api/v1/clusterobjectset.go similarity index 72% rename from applyconfigurations/api/v1/clusterextensionrevision.go rename to applyconfigurations/api/v1/clusterobjectset.go index 5ccee31c23..bea5d16ff1 100644 --- a/applyconfigurations/api/v1/clusterextensionrevision.go +++ b/applyconfigurations/api/v1/clusterobjectset.go @@ -23,42 +23,42 @@ import ( metav1 "k8s.io/client-go/applyconfigurations/meta/v1" ) -// ClusterExtensionRevisionApplyConfiguration represents a declarative configuration of the ClusterExtensionRevision type for use +// ClusterObjectSetApplyConfiguration represents a declarative configuration of the ClusterObjectSet type for use // with apply. // -// ClusterExtensionRevision represents an immutable snapshot of Kubernetes objects +// ClusterObjectSet represents an immutable snapshot of Kubernetes objects // for a specific version of a ClusterExtension. Each revision contains objects // organized into phases that roll out sequentially. The same object can only be managed by a single revision // at a time. Ownership of objects is transitioned from one revision to the next as the extension is upgraded // or reconfigured. Once the latest revision has rolled out successfully, previous active revisions are archived for // posterity. -type ClusterExtensionRevisionApplyConfiguration struct { +type ClusterObjectSetApplyConfiguration struct { metav1.TypeMetaApplyConfiguration `json:",inline"` // metadata is the standard object's metadata. // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata *metav1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` - // spec defines the desired state of the ClusterExtensionRevision. - Spec *ClusterExtensionRevisionSpecApplyConfiguration `json:"spec,omitempty"` - // status is optional and defines the observed state of the ClusterExtensionRevision. - Status *ClusterExtensionRevisionStatusApplyConfiguration `json:"status,omitempty"` + // spec defines the desired state of the ClusterObjectSet. + Spec *ClusterObjectSetSpecApplyConfiguration `json:"spec,omitempty"` + // status is optional and defines the observed state of the ClusterObjectSet. + Status *ClusterObjectSetStatusApplyConfiguration `json:"status,omitempty"` } -// ClusterExtensionRevision constructs a declarative configuration of the ClusterExtensionRevision type for use with +// ClusterObjectSet constructs a declarative configuration of the ClusterObjectSet type for use with // apply. -func ClusterExtensionRevision(name string) *ClusterExtensionRevisionApplyConfiguration { - b := &ClusterExtensionRevisionApplyConfiguration{} +func ClusterObjectSet(name string) *ClusterObjectSetApplyConfiguration { + b := &ClusterObjectSetApplyConfiguration{} b.WithName(name) - b.WithKind("ClusterExtensionRevision") + b.WithKind("ClusterObjectSet") b.WithAPIVersion("olm.operatorframework.io/v1") return b } -func (b ClusterExtensionRevisionApplyConfiguration) IsApplyConfiguration() {} +func (b ClusterObjectSetApplyConfiguration) IsApplyConfiguration() {} // WithKind sets the Kind field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Kind field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithKind(value string) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithKind(value string) *ClusterObjectSetApplyConfiguration { b.TypeMetaApplyConfiguration.Kind = &value return b } @@ -66,7 +66,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithKind(value string) *Clu // WithAPIVersion sets the APIVersion field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the APIVersion field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithAPIVersion(value string) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithAPIVersion(value string) *ClusterObjectSetApplyConfiguration { b.TypeMetaApplyConfiguration.APIVersion = &value return b } @@ -74,7 +74,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithAPIVersion(value string // WithName sets the Name field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Name field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithName(value string) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithName(value string) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ObjectMetaApplyConfiguration.Name = &value return b @@ -83,7 +83,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithName(value string) *Clu // WithGenerateName sets the GenerateName field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the GenerateName field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithGenerateName(value string) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithGenerateName(value string) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ObjectMetaApplyConfiguration.GenerateName = &value return b @@ -92,7 +92,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithGenerateName(value stri // WithNamespace sets the Namespace field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Namespace field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithNamespace(value string) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithNamespace(value string) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ObjectMetaApplyConfiguration.Namespace = &value return b @@ -101,7 +101,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithNamespace(value string) // WithUID sets the UID field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the UID field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithUID(value types.UID) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithUID(value types.UID) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ObjectMetaApplyConfiguration.UID = &value return b @@ -110,7 +110,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithUID(value types.UID) *C // WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the ResourceVersion field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithResourceVersion(value string) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithResourceVersion(value string) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ObjectMetaApplyConfiguration.ResourceVersion = &value return b @@ -119,7 +119,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithResourceVersion(value s // WithGeneration sets the Generation field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Generation field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithGeneration(value int64) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithGeneration(value int64) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ObjectMetaApplyConfiguration.Generation = &value return b @@ -128,7 +128,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithGeneration(value int64) // WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the CreationTimestamp field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithCreationTimestamp(value apismetav1.Time) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithCreationTimestamp(value apismetav1.Time) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ObjectMetaApplyConfiguration.CreationTimestamp = &value return b @@ -137,7 +137,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithCreationTimestamp(value // WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the DeletionTimestamp field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithDeletionTimestamp(value apismetav1.Time) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithDeletionTimestamp(value apismetav1.Time) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value return b @@ -146,7 +146,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithDeletionTimestamp(value // WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value return b @@ -156,7 +156,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithDeletionGracePeriodSeco // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, the entries provided by each call will be put on the Labels field, // overwriting an existing map entries in Labels field with the same key. -func (b *ClusterExtensionRevisionApplyConfiguration) WithLabels(entries map[string]string) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithLabels(entries map[string]string) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) @@ -171,7 +171,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithLabels(entries map[stri // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, the entries provided by each call will be put on the Annotations field, // overwriting an existing map entries in Annotations field with the same key. -func (b *ClusterExtensionRevisionApplyConfiguration) WithAnnotations(entries map[string]string) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithAnnotations(entries map[string]string) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) @@ -185,7 +185,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithAnnotations(entries map // WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the OwnerReferences field. -func (b *ClusterExtensionRevisionApplyConfiguration) WithOwnerReferences(values ...*metav1.OwnerReferenceApplyConfiguration) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithOwnerReferences(values ...*metav1.OwnerReferenceApplyConfiguration) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() for i := range values { if values[i] == nil { @@ -199,7 +199,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithOwnerReferences(values // WithFinalizers adds the given value to the Finalizers field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the Finalizers field. -func (b *ClusterExtensionRevisionApplyConfiguration) WithFinalizers(values ...string) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithFinalizers(values ...string) *ClusterObjectSetApplyConfiguration { b.ensureObjectMetaApplyConfigurationExists() for i := range values { b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) @@ -207,7 +207,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithFinalizers(values ...st return b } -func (b *ClusterExtensionRevisionApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { +func (b *ClusterObjectSetApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { if b.ObjectMetaApplyConfiguration == nil { b.ObjectMetaApplyConfiguration = &metav1.ObjectMetaApplyConfiguration{} } @@ -216,7 +216,7 @@ func (b *ClusterExtensionRevisionApplyConfiguration) ensureObjectMetaApplyConfig // WithSpec sets the Spec field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Spec field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithSpec(value *ClusterExtensionRevisionSpecApplyConfiguration) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithSpec(value *ClusterObjectSetSpecApplyConfiguration) *ClusterObjectSetApplyConfiguration { b.Spec = value return b } @@ -224,29 +224,29 @@ func (b *ClusterExtensionRevisionApplyConfiguration) WithSpec(value *ClusterExte // WithStatus sets the Status field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Status field is set to the value of the last call. -func (b *ClusterExtensionRevisionApplyConfiguration) WithStatus(value *ClusterExtensionRevisionStatusApplyConfiguration) *ClusterExtensionRevisionApplyConfiguration { +func (b *ClusterObjectSetApplyConfiguration) WithStatus(value *ClusterObjectSetStatusApplyConfiguration) *ClusterObjectSetApplyConfiguration { b.Status = value return b } // GetKind retrieves the value of the Kind field in the declarative configuration. -func (b *ClusterExtensionRevisionApplyConfiguration) GetKind() *string { +func (b *ClusterObjectSetApplyConfiguration) GetKind() *string { return b.TypeMetaApplyConfiguration.Kind } // GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. -func (b *ClusterExtensionRevisionApplyConfiguration) GetAPIVersion() *string { +func (b *ClusterObjectSetApplyConfiguration) GetAPIVersion() *string { return b.TypeMetaApplyConfiguration.APIVersion } // GetName retrieves the value of the Name field in the declarative configuration. -func (b *ClusterExtensionRevisionApplyConfiguration) GetName() *string { +func (b *ClusterObjectSetApplyConfiguration) GetName() *string { b.ensureObjectMetaApplyConfigurationExists() return b.ObjectMetaApplyConfiguration.Name } // GetNamespace retrieves the value of the Namespace field in the declarative configuration. -func (b *ClusterExtensionRevisionApplyConfiguration) GetNamespace() *string { +func (b *ClusterObjectSetApplyConfiguration) GetNamespace() *string { b.ensureObjectMetaApplyConfigurationExists() return b.ObjectMetaApplyConfiguration.Namespace } diff --git a/applyconfigurations/api/v1/clusterextensionrevisionobject.go b/applyconfigurations/api/v1/clusterobjectsetobject.go similarity index 75% rename from applyconfigurations/api/v1/clusterextensionrevisionobject.go rename to applyconfigurations/api/v1/clusterobjectsetobject.go index 7962712e47..c526fb438c 100644 --- a/applyconfigurations/api/v1/clusterextensionrevisionobject.go +++ b/applyconfigurations/api/v1/clusterobjectsetobject.go @@ -22,14 +22,14 @@ import ( unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) -// ClusterExtensionRevisionObjectApplyConfiguration represents a declarative configuration of the ClusterExtensionRevisionObject type for use +// ClusterObjectSetObjectApplyConfiguration represents a declarative configuration of the ClusterObjectSetObject type for use // with apply. // -// ClusterExtensionRevisionObject represents a Kubernetes object to be applied as part +// ClusterObjectSetObject represents a Kubernetes object to be applied as part // of a phase, along with its collision protection settings. // // Exactly one of object or ref must be set. -type ClusterExtensionRevisionObjectApplyConfiguration struct { +type ClusterObjectSetObjectApplyConfiguration struct { // object is an optional embedded Kubernetes object to be applied. // // Exactly one of object or ref must be set. @@ -62,16 +62,16 @@ type ClusterExtensionRevisionObjectApplyConfiguration struct { CollisionProtection *apiv1.CollisionProtection `json:"collisionProtection,omitempty"` } -// ClusterExtensionRevisionObjectApplyConfiguration constructs a declarative configuration of the ClusterExtensionRevisionObject type for use with +// ClusterObjectSetObjectApplyConfiguration constructs a declarative configuration of the ClusterObjectSetObject type for use with // apply. -func ClusterExtensionRevisionObject() *ClusterExtensionRevisionObjectApplyConfiguration { - return &ClusterExtensionRevisionObjectApplyConfiguration{} +func ClusterObjectSetObject() *ClusterObjectSetObjectApplyConfiguration { + return &ClusterObjectSetObjectApplyConfiguration{} } // WithObject sets the Object field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Object field is set to the value of the last call. -func (b *ClusterExtensionRevisionObjectApplyConfiguration) WithObject(value unstructured.Unstructured) *ClusterExtensionRevisionObjectApplyConfiguration { +func (b *ClusterObjectSetObjectApplyConfiguration) WithObject(value unstructured.Unstructured) *ClusterObjectSetObjectApplyConfiguration { b.Object = &value return b } @@ -79,7 +79,7 @@ func (b *ClusterExtensionRevisionObjectApplyConfiguration) WithObject(value unst // WithRef sets the Ref field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Ref field is set to the value of the last call. -func (b *ClusterExtensionRevisionObjectApplyConfiguration) WithRef(value *ObjectSourceRefApplyConfiguration) *ClusterExtensionRevisionObjectApplyConfiguration { +func (b *ClusterObjectSetObjectApplyConfiguration) WithRef(value *ObjectSourceRefApplyConfiguration) *ClusterObjectSetObjectApplyConfiguration { b.Ref = value return b } @@ -87,7 +87,7 @@ func (b *ClusterExtensionRevisionObjectApplyConfiguration) WithRef(value *Object // WithCollisionProtection sets the CollisionProtection field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the CollisionProtection field is set to the value of the last call. -func (b *ClusterExtensionRevisionObjectApplyConfiguration) WithCollisionProtection(value apiv1.CollisionProtection) *ClusterExtensionRevisionObjectApplyConfiguration { +func (b *ClusterObjectSetObjectApplyConfiguration) WithCollisionProtection(value apiv1.CollisionProtection) *ClusterObjectSetObjectApplyConfiguration { b.CollisionProtection = &value return b } diff --git a/applyconfigurations/api/v1/clusterextensionrevisionphase.go b/applyconfigurations/api/v1/clusterobjectsetphase.go similarity index 72% rename from applyconfigurations/api/v1/clusterextensionrevisionphase.go rename to applyconfigurations/api/v1/clusterobjectsetphase.go index 462b0e7212..de70767a7e 100644 --- a/applyconfigurations/api/v1/clusterextensionrevisionphase.go +++ b/applyconfigurations/api/v1/clusterobjectsetphase.go @@ -21,12 +21,12 @@ import ( apiv1 "github.com/operator-framework/operator-controller/api/v1" ) -// ClusterExtensionRevisionPhaseApplyConfiguration represents a declarative configuration of the ClusterExtensionRevisionPhase type for use +// ClusterObjectSetPhaseApplyConfiguration represents a declarative configuration of the ClusterObjectSetPhase type for use // with apply. // -// ClusterExtensionRevisionPhase represents a group of objects that are applied together. The phase is considered +// ClusterObjectSetPhase represents a group of objects that are applied together. The phase is considered // complete only after all objects pass their status probes. -type ClusterExtensionRevisionPhaseApplyConfiguration struct { +type ClusterObjectSetPhaseApplyConfiguration struct { // name is a required identifier for this phase. // // phase names must follow the DNS label standard as defined in [RFC 1123]. @@ -40,7 +40,7 @@ type ClusterExtensionRevisionPhaseApplyConfiguration struct { // objects is a required list of all Kubernetes objects that belong to this phase. // // All objects in this list are applied to the cluster in no particular order. The maximum number of objects per phase is 50. - Objects []ClusterExtensionRevisionObjectApplyConfiguration `json:"objects,omitempty"` + Objects []ClusterObjectSetObjectApplyConfiguration `json:"objects,omitempty"` // collisionProtection specifies the default collision protection strategy for all objects // in this phase. Individual objects can override this value. // @@ -52,16 +52,16 @@ type ClusterExtensionRevisionPhaseApplyConfiguration struct { CollisionProtection *apiv1.CollisionProtection `json:"collisionProtection,omitempty"` } -// ClusterExtensionRevisionPhaseApplyConfiguration constructs a declarative configuration of the ClusterExtensionRevisionPhase type for use with +// ClusterObjectSetPhaseApplyConfiguration constructs a declarative configuration of the ClusterObjectSetPhase type for use with // apply. -func ClusterExtensionRevisionPhase() *ClusterExtensionRevisionPhaseApplyConfiguration { - return &ClusterExtensionRevisionPhaseApplyConfiguration{} +func ClusterObjectSetPhase() *ClusterObjectSetPhaseApplyConfiguration { + return &ClusterObjectSetPhaseApplyConfiguration{} } // WithName sets the Name field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Name field is set to the value of the last call. -func (b *ClusterExtensionRevisionPhaseApplyConfiguration) WithName(value string) *ClusterExtensionRevisionPhaseApplyConfiguration { +func (b *ClusterObjectSetPhaseApplyConfiguration) WithName(value string) *ClusterObjectSetPhaseApplyConfiguration { b.Name = &value return b } @@ -69,7 +69,7 @@ func (b *ClusterExtensionRevisionPhaseApplyConfiguration) WithName(value string) // WithObjects adds the given value to the Objects field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the Objects field. -func (b *ClusterExtensionRevisionPhaseApplyConfiguration) WithObjects(values ...*ClusterExtensionRevisionObjectApplyConfiguration) *ClusterExtensionRevisionPhaseApplyConfiguration { +func (b *ClusterObjectSetPhaseApplyConfiguration) WithObjects(values ...*ClusterObjectSetObjectApplyConfiguration) *ClusterObjectSetPhaseApplyConfiguration { for i := range values { if values[i] == nil { panic("nil value passed to WithObjects") @@ -82,7 +82,7 @@ func (b *ClusterExtensionRevisionPhaseApplyConfiguration) WithObjects(values ... // WithCollisionProtection sets the CollisionProtection field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the CollisionProtection field is set to the value of the last call. -func (b *ClusterExtensionRevisionPhaseApplyConfiguration) WithCollisionProtection(value apiv1.CollisionProtection) *ClusterExtensionRevisionPhaseApplyConfiguration { +func (b *ClusterObjectSetPhaseApplyConfiguration) WithCollisionProtection(value apiv1.CollisionProtection) *ClusterObjectSetPhaseApplyConfiguration { b.CollisionProtection = &value return b } diff --git a/applyconfigurations/api/v1/clusterextensionrevisionspec.go b/applyconfigurations/api/v1/clusterobjectsetspec.go similarity index 78% rename from applyconfigurations/api/v1/clusterextensionrevisionspec.go rename to applyconfigurations/api/v1/clusterobjectsetspec.go index df638b6127..18354e2663 100644 --- a/applyconfigurations/api/v1/clusterextensionrevisionspec.go +++ b/applyconfigurations/api/v1/clusterobjectsetspec.go @@ -21,12 +21,12 @@ import ( apiv1 "github.com/operator-framework/operator-controller/api/v1" ) -// ClusterExtensionRevisionSpecApplyConfiguration represents a declarative configuration of the ClusterExtensionRevisionSpec type for use +// ClusterObjectSetSpecApplyConfiguration represents a declarative configuration of the ClusterObjectSetSpec type for use // with apply. // -// ClusterExtensionRevisionSpec defines the desired state of ClusterExtensionRevision. -type ClusterExtensionRevisionSpecApplyConfiguration struct { - // lifecycleState specifies the lifecycle state of the ClusterExtensionRevision. +// ClusterObjectSetSpec defines the desired state of ClusterObjectSet. +type ClusterObjectSetSpecApplyConfiguration struct { + // lifecycleState specifies the lifecycle state of the ClusterObjectSet. // // When set to "Active", the revision is actively managed and reconciled. // When set to "Archived", the revision is inactive and any resources not managed by a subsequent revision are deleted. @@ -38,12 +38,12 @@ type ClusterExtensionRevisionSpecApplyConfiguration struct { // It is possible for more than one revision to be "Active" simultaneously. This will occur when // moving from one revision to another. The old revision will not be set to "Archived" until the // new revision has been completely rolled out. - LifecycleState *apiv1.ClusterExtensionRevisionLifecycleState `json:"lifecycleState,omitempty"` + LifecycleState *apiv1.ClusterObjectSetLifecycleState `json:"lifecycleState,omitempty"` // revision is a required, immutable sequence number representing a specific revision // of the parent ClusterExtension. // // The revision field must be a positive integer. - // Each ClusterExtensionRevision belonging to the same parent ClusterExtension must have a unique revision number. + // Each ClusterObjectSet belonging to the same parent ClusterExtension must have a unique revision number. // The revision number must always be the previous revision number plus one, or 1 for the first revision. Revision *int64 `json:"revision,omitempty"` // phases is an optional, immutable list of phases that group objects to be applied together. @@ -63,7 +63,7 @@ type ClusterExtensionRevisionSpecApplyConfiguration struct { // Once set, even if empty, the phases field is immutable. // // Each phase in the list must have a unique name. The maximum number of phases is 20. - Phases []ClusterExtensionRevisionPhaseApplyConfiguration `json:"phases,omitempty"` + Phases []ClusterObjectSetPhaseApplyConfiguration `json:"phases,omitempty"` // progressDeadlineMinutes is an optional field that defines the maximum period // of time in minutes after which an installation should be considered failed and // require manual intervention. This functionality is disabled when no value @@ -95,16 +95,16 @@ type ClusterExtensionRevisionSpecApplyConfiguration struct { CollisionProtection *apiv1.CollisionProtection `json:"collisionProtection,omitempty"` } -// ClusterExtensionRevisionSpecApplyConfiguration constructs a declarative configuration of the ClusterExtensionRevisionSpec type for use with +// ClusterObjectSetSpecApplyConfiguration constructs a declarative configuration of the ClusterObjectSetSpec type for use with // apply. -func ClusterExtensionRevisionSpec() *ClusterExtensionRevisionSpecApplyConfiguration { - return &ClusterExtensionRevisionSpecApplyConfiguration{} +func ClusterObjectSetSpec() *ClusterObjectSetSpecApplyConfiguration { + return &ClusterObjectSetSpecApplyConfiguration{} } // WithLifecycleState sets the LifecycleState field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the LifecycleState field is set to the value of the last call. -func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithLifecycleState(value apiv1.ClusterExtensionRevisionLifecycleState) *ClusterExtensionRevisionSpecApplyConfiguration { +func (b *ClusterObjectSetSpecApplyConfiguration) WithLifecycleState(value apiv1.ClusterObjectSetLifecycleState) *ClusterObjectSetSpecApplyConfiguration { b.LifecycleState = &value return b } @@ -112,7 +112,7 @@ func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithLifecycleState(valu // WithRevision sets the Revision field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Revision field is set to the value of the last call. -func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithRevision(value int64) *ClusterExtensionRevisionSpecApplyConfiguration { +func (b *ClusterObjectSetSpecApplyConfiguration) WithRevision(value int64) *ClusterObjectSetSpecApplyConfiguration { b.Revision = &value return b } @@ -120,7 +120,7 @@ func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithRevision(value int6 // WithPhases adds the given value to the Phases field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the Phases field. -func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithPhases(values ...*ClusterExtensionRevisionPhaseApplyConfiguration) *ClusterExtensionRevisionSpecApplyConfiguration { +func (b *ClusterObjectSetSpecApplyConfiguration) WithPhases(values ...*ClusterObjectSetPhaseApplyConfiguration) *ClusterObjectSetSpecApplyConfiguration { for i := range values { if values[i] == nil { panic("nil value passed to WithPhases") @@ -133,7 +133,7 @@ func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithPhases(values ...*C // WithProgressDeadlineMinutes sets the ProgressDeadlineMinutes field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the ProgressDeadlineMinutes field is set to the value of the last call. -func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithProgressDeadlineMinutes(value int32) *ClusterExtensionRevisionSpecApplyConfiguration { +func (b *ClusterObjectSetSpecApplyConfiguration) WithProgressDeadlineMinutes(value int32) *ClusterObjectSetSpecApplyConfiguration { b.ProgressDeadlineMinutes = &value return b } @@ -141,7 +141,7 @@ func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithProgressDeadlineMin // WithProgressionProbes adds the given value to the ProgressionProbes field in the declarative configuration // and returns the receiver, so that objects can be build by chaining "With" function invocations. // If called multiple times, values provided by each call will be appended to the ProgressionProbes field. -func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithProgressionProbes(values ...*ProgressionProbeApplyConfiguration) *ClusterExtensionRevisionSpecApplyConfiguration { +func (b *ClusterObjectSetSpecApplyConfiguration) WithProgressionProbes(values ...*ProgressionProbeApplyConfiguration) *ClusterObjectSetSpecApplyConfiguration { for i := range values { if values[i] == nil { panic("nil value passed to WithProgressionProbes") @@ -154,7 +154,7 @@ func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithProgressionProbes(v // WithCollisionProtection sets the CollisionProtection field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the CollisionProtection field is set to the value of the last call. -func (b *ClusterExtensionRevisionSpecApplyConfiguration) WithCollisionProtection(value apiv1.CollisionProtection) *ClusterExtensionRevisionSpecApplyConfiguration { +func (b *ClusterObjectSetSpecApplyConfiguration) WithCollisionProtection(value apiv1.CollisionProtection) *ClusterObjectSetSpecApplyConfiguration { b.CollisionProtection = &value return b } diff --git a/applyconfigurations/api/v1/clusterobjectsetstatus.go b/applyconfigurations/api/v1/clusterobjectsetstatus.go new file mode 100644 index 0000000000..987dd98394 --- /dev/null +++ b/applyconfigurations/api/v1/clusterobjectsetstatus.go @@ -0,0 +1,68 @@ +/* +Copyright 2022. + +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. +*/ +// Code generated by controller-gen-v0.20. DO NOT EDIT. + +package v1 + +import ( + metav1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// ClusterObjectSetStatusApplyConfiguration represents a declarative configuration of the ClusterObjectSetStatus type for use +// with apply. +// +// ClusterObjectSetStatus defines the observed state of a ClusterObjectSet. +type ClusterObjectSetStatusApplyConfiguration struct { + // conditions is an optional list of status conditions describing the state of the + // ClusterObjectSet. + // + // The Progressing condition represents whether the revision is actively rolling out: + // - When status is True and reason is RollingOut, the ClusterObjectSet rollout is actively making progress and is in transition. + // - When status is True and reason is Retrying, the ClusterObjectSet has encountered an error that could be resolved on subsequent reconciliation attempts. + // - When status is True and reason is Succeeded, the ClusterObjectSet has reached the desired state. + // - When status is False and reason is Blocked, the ClusterObjectSet has encountered an error that requires manual intervention for recovery. + // - When status is False and reason is Archived, the ClusterObjectSet is archived and not being actively reconciled. + // + // The Available condition represents whether the revision has been successfully rolled out and is available: + // - When status is True and reason is ProbesSucceeded, the ClusterObjectSet has been successfully rolled out and all objects pass their readiness probes. + // - When status is False and reason is ProbeFailure, one or more objects are failing their readiness probes during rollout. + // - When status is Unknown and reason is Reconciling, the ClusterObjectSet has encountered an error that prevented it from observing the probes. + // - When status is Unknown and reason is Archived, the ClusterObjectSet has been archived and its objects have been torn down. + // - When status is Unknown and reason is Migrated, the ClusterObjectSet was migrated from an existing release and object status probe results have not yet been observed. + // + // The Succeeded condition represents whether the revision has successfully completed its rollout: + // - When status is True and reason is Succeeded, the ClusterObjectSet has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. + Conditions []metav1.ConditionApplyConfiguration `json:"conditions,omitempty"` +} + +// ClusterObjectSetStatusApplyConfiguration constructs a declarative configuration of the ClusterObjectSetStatus type for use with +// apply. +func ClusterObjectSetStatus() *ClusterObjectSetStatusApplyConfiguration { + return &ClusterObjectSetStatusApplyConfiguration{} +} + +// WithConditions adds the given value to the Conditions field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Conditions field. +func (b *ClusterObjectSetStatusApplyConfiguration) WithConditions(values ...*metav1.ConditionApplyConfiguration) *ClusterObjectSetStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithConditions") + } + b.Conditions = append(b.Conditions, *values[i]) + } + return b +} diff --git a/applyconfigurations/api/v1/revisionstatus.go b/applyconfigurations/api/v1/revisionstatus.go index d8a2e248f4..f5165c7679 100644 --- a/applyconfigurations/api/v1/revisionstatus.go +++ b/applyconfigurations/api/v1/revisionstatus.go @@ -24,9 +24,9 @@ import ( // RevisionStatusApplyConfiguration represents a declarative configuration of the RevisionStatus type for use // with apply. // -// RevisionStatus defines the observed state of a ClusterExtensionRevision. +// RevisionStatus defines the observed state of a ClusterObjectSet. type RevisionStatusApplyConfiguration struct { - // name of the ClusterExtensionRevision resource + // name of the ClusterObjectSet resource Name *string `json:"name,omitempty"` // conditions optionally expose Progressing and Available condition of the revision, // in case when it is not yet marked as successfully installed (condition Succeeded is not set to True). diff --git a/applyconfigurations/utils.go b/applyconfigurations/utils.go index aec0ecb0a1..7b1373e4a1 100644 --- a/applyconfigurations/utils.go +++ b/applyconfigurations/utils.go @@ -55,20 +55,20 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1.ClusterExtensionInstallConfigApplyConfiguration{} case v1.SchemeGroupVersion.WithKind("ClusterExtensionInstallStatus"): return &apiv1.ClusterExtensionInstallStatusApplyConfiguration{} - case v1.SchemeGroupVersion.WithKind("ClusterExtensionRevision"): - return &apiv1.ClusterExtensionRevisionApplyConfiguration{} - case v1.SchemeGroupVersion.WithKind("ClusterExtensionRevisionObject"): - return &apiv1.ClusterExtensionRevisionObjectApplyConfiguration{} - case v1.SchemeGroupVersion.WithKind("ClusterExtensionRevisionPhase"): - return &apiv1.ClusterExtensionRevisionPhaseApplyConfiguration{} - case v1.SchemeGroupVersion.WithKind("ClusterExtensionRevisionSpec"): - return &apiv1.ClusterExtensionRevisionSpecApplyConfiguration{} - case v1.SchemeGroupVersion.WithKind("ClusterExtensionRevisionStatus"): - return &apiv1.ClusterExtensionRevisionStatusApplyConfiguration{} case v1.SchemeGroupVersion.WithKind("ClusterExtensionSpec"): return &apiv1.ClusterExtensionSpecApplyConfiguration{} case v1.SchemeGroupVersion.WithKind("ClusterExtensionStatus"): return &apiv1.ClusterExtensionStatusApplyConfiguration{} + case v1.SchemeGroupVersion.WithKind("ClusterObjectSet"): + return &apiv1.ClusterObjectSetApplyConfiguration{} + case v1.SchemeGroupVersion.WithKind("ClusterObjectSetObject"): + return &apiv1.ClusterObjectSetObjectApplyConfiguration{} + case v1.SchemeGroupVersion.WithKind("ClusterObjectSetPhase"): + return &apiv1.ClusterObjectSetPhaseApplyConfiguration{} + case v1.SchemeGroupVersion.WithKind("ClusterObjectSetSpec"): + return &apiv1.ClusterObjectSetSpecApplyConfiguration{} + case v1.SchemeGroupVersion.WithKind("ClusterObjectSetStatus"): + return &apiv1.ClusterObjectSetStatusApplyConfiguration{} case v1.SchemeGroupVersion.WithKind("ConditionEqualProbe"): return &apiv1.ConditionEqualProbeApplyConfiguration{} case v1.SchemeGroupVersion.WithKind("CRDUpgradeSafetyPreflightConfig"): diff --git a/cmd/operator-controller/main.go b/cmd/operator-controller/main.go index 4835c49a79..48104537e9 100644 --- a/cmd/operator-controller/main.go +++ b/cmd/operator-controller/main.go @@ -264,7 +264,7 @@ func run() error { } if features.OperatorControllerFeatureGate.Enabled(features.BoxcutterRuntime) { - cacheOptions.ByObject[&ocv1.ClusterExtensionRevision{}] = crcache.ByObject{ + cacheOptions.ByObject[&ocv1.ClusterObjectSet{}] = crcache.ByObject{ Label: k8slabels.Everything(), } } @@ -476,7 +476,7 @@ func run() error { var ctrlBuilderOpts []controllers.ControllerBuilderOption if features.OperatorControllerFeatureGate.Enabled(features.BoxcutterRuntime) { - ctrlBuilderOpts = append(ctrlBuilderOpts, controllers.WithOwns(&ocv1.ClusterExtensionRevision{})) + ctrlBuilderOpts = append(ctrlBuilderOpts, controllers.WithOwns(&ocv1.ClusterObjectSet{})) } ceReconciler := &controllers.ClusterExtensionReconciler{ @@ -680,7 +680,7 @@ func (c *boxcutterReconcilerConfigurator) Configure(ceReconciler *controllers.Cl cerCoreClient, err := corev1client.NewForConfig(c.mgr.GetConfig()) if err != nil { - return fmt.Errorf("unable to create client for ClusterExtensionRevision controller: %w", err) + return fmt.Errorf("unable to create client for ClusterObjectSet controller: %w", err) } cerTokenGetter := authentication.NewTokenGetter(cerCoreClient, authentication.WithExpirationDuration(1*time.Hour)) @@ -697,12 +697,12 @@ func (c *boxcutterReconcilerConfigurator) Configure(ceReconciler *controllers.Cl return fmt.Errorf("unable to create revision engine factory: %w", err) } - if err = (&controllers.ClusterExtensionRevisionReconciler{ + if err = (&controllers.ClusterObjectSetReconciler{ Client: c.mgr.GetClient(), RevisionEngineFactory: revisionEngineFactory, TrackingCache: trackingCache, }).SetupWithManager(c.mgr); err != nil { - return fmt.Errorf("unable to setup ClusterExtensionRevision controller: %w", err) + return fmt.Errorf("unable to setup ClusterObjectSet controller: %w", err) } return nil } diff --git a/config/samples/olm_v1_clusterextension.yaml b/config/samples/olm_v1_clusterextension.yaml index 14c8e167e0..847f73592e 100644 --- a/config/samples/olm_v1_clusterextension.yaml +++ b/config/samples/olm_v1_clusterextension.yaml @@ -33,9 +33,9 @@ rules: resources: [clusterextensions/finalizers] verbs: [update] resourceNames: [argocd] -# Allow ClusterExtensionRevisions to set blockOwnerDeletion ownerReferences +# Allow ClusterObjectSets to set blockOwnerDeletion ownerReferences - apiGroups: [olm.operatorframework.io] - resources: [clusterextensionrevisions/finalizers] + resources: [clusterobjectsets/finalizers] verbs: [update] # Manage ArgoCD CRDs - apiGroups: [apiextensions.k8s.io] diff --git a/docs/api-reference/crd-ref-docs-gen-config.yaml b/docs/api-reference/crd-ref-docs-gen-config.yaml index c8efa15c19..d7eb1e4e33 100644 --- a/docs/api-reference/crd-ref-docs-gen-config.yaml +++ b/docs/api-reference/crd-ref-docs-gen-config.yaml @@ -1,5 +1,5 @@ processor: - ignoreTypes: [ClusterExtensionRevision, ClusterExtensionRevisionList] + ignoreTypes: [ClusterObjectSet, ClusterObjectSetList] ignoreFields: [] render: diff --git a/docs/api-reference/olmv1-api-reference.md b/docs/api-reference/olmv1-api-reference.md index 97a8e5d3b0..5b5eeb2361 100644 --- a/docs/api-reference/olmv1-api-reference.md +++ b/docs/api-reference/olmv1-api-reference.md @@ -379,9 +379,9 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions represents the current state of the ClusterExtension.
The set of condition types which apply to all spec.source variations are Installed and Progressing.
The Installed condition represents whether the bundle has been installed for this ClusterExtension:
- When Installed is True and the Reason is Succeeded, the bundle has been successfully installed.
- When Installed is False and the Reason is Failed, the bundle has failed to install.
The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state.
When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state.
When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts.
When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery.

When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterExtensionRevisions in active roll out.

When the ClusterExtension is sourced from a catalog, it surfaces deprecation conditions based on catalog metadata.
These are indications from a package owner to guide users away from a particular package, channel, or bundle:
- BundleDeprecated is True if the installed bundle is marked deprecated, False if not deprecated, or Unknown if no bundle is installed yet or if catalog data is unavailable.
- ChannelDeprecated is True if any requested channel is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable.
- PackageDeprecated is True if the requested package is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable.
- Deprecated is a rollup condition that is True when any deprecation exists, False when none exist, or Unknown when catalog data is unavailable. | | Optional: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions represents the current state of the ClusterExtension.
The set of condition types which apply to all spec.source variations are Installed and Progressing.
The Installed condition represents whether the bundle has been installed for this ClusterExtension:
- When Installed is True and the Reason is Succeeded, the bundle has been successfully installed.
- When Installed is False and the Reason is Failed, the bundle has failed to install.
The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state.
When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state.
When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts.
When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery.

When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterObjectSets in active roll out.

When the ClusterExtension is sourced from a catalog, it surfaces deprecation conditions based on catalog metadata.
These are indications from a package owner to guide users away from a particular package, channel, or bundle:
- BundleDeprecated is True if the installed bundle is marked deprecated, False if not deprecated, or Unknown if no bundle is installed yet or if catalog data is unavailable.
- ChannelDeprecated is True if any requested channel is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable.
- PackageDeprecated is True if the requested package is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable.
- Deprecated is a rollup condition that is True when any deprecation exists, False when none exist, or Unknown when catalog data is unavailable. | | Optional: \{\}
| | `install` _[ClusterExtensionInstallStatus](#clusterextensioninstallstatus)_ | install is a representation of the current installation status for this ClusterExtension. | | Optional: \{\}
| -| `activeRevisions` _[RevisionStatus](#revisionstatus) array_ | activeRevisions holds a list of currently active (non-archived) ClusterExtensionRevisions,
including both installed and rolling out revisions.
| | Optional: \{\}
| +| `activeRevisions` _[RevisionStatus](#revisionstatus) array_ | activeRevisions holds a list of currently active (non-archived) ClusterObjectSets,
including both installed and rolling out revisions.
| | Optional: \{\}
| @@ -551,7 +551,7 @@ _Appears in:_ -RevisionStatus defines the observed state of a ClusterExtensionRevision. +RevisionStatus defines the observed state of a ClusterObjectSet. @@ -560,7 +560,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `name` _string_ | name of the ClusterExtensionRevision resource | | | +| `name` _string_ | name of the ClusterObjectSet resource | | | | `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | conditions optionally expose Progressing and Available condition of the revision,
in case when it is not yet marked as successfully installed (condition Succeeded is not set to True).
Given that a ClusterExtension should remain available during upgrades, an observer may use these conditions
to get more insights about reasons for its current state. | | Optional: \{\}
| diff --git a/docs/concepts/large-bundle-support.md b/docs/concepts/large-bundle-support.md index 055ec628cb..cf1c247cdc 100644 --- a/docs/concepts/large-bundle-support.md +++ b/docs/concepts/large-bundle-support.md @@ -2,13 +2,13 @@ ## Need -ClusterExtensionRevision (CER) objects embed full Kubernetes manifests inline in +ClusterObjectSet (COS) objects embed full Kubernetes manifests inline in `.spec.phases[].objects[].object`. With up to 20 phases and 50 objects per phase, -the serialized CER can approach or exceed the etcd maximum object size of +the serialized COS can approach or exceed the etcd maximum object size of 1.5 MiB. Large operators shipping many CRDs, Deployments, RBAC rules, and webhook configurations are likely to hit this limit. -When the limit is exceeded, the API server rejects the CER and the extension +When the limit is exceeded, the API server rejects the COS and the extension cannot be installed or upgraded. Today there is no mitigation path other than reducing the number of objects in the bundle. @@ -25,20 +25,20 @@ chains is presented as an alternative. ## Approach: Per-Object Content References Externalize all objects by default. Add an optional `ref` field to -`ClusterExtensionRevisionObject` that points to the object content stored in a +`ClusterObjectSetObject` that points to the object content stored in a Secret. Exactly one of `object` or `ref` must be set. The system uses `ref` for -all objects it creates; users who manually craft CERs may use either `object` or +all objects it creates; users who manually craft COSs may use either `object` or `ref`. ### API change Add a new `ObjectSourceRef` type and a `ref` field to -`ClusterExtensionRevisionObject`. Exactly one of `object` or `ref` must be set, +`ClusterObjectSetObject`. Exactly one of `object` or `ref` must be set, enforced via CEL validation. Both fields are immutable (inherited from the immutability of phases). ```go -type ClusterExtensionRevisionObject struct { +type ClusterObjectSetObject struct { // object is an optional embedded Kubernetes object to be applied. // Exactly one of object or ref must be set. // @@ -63,7 +63,7 @@ type ClusterExtensionRevisionObject struct { } ``` -CEL validation on `ClusterExtensionRevisionObject`: +CEL validation on `ClusterObjectSetObject`: ``` rule: "has(self.object) != has(self.ref)" @@ -130,7 +130,7 @@ kubectl get secret -o jsonpath='{.data.}' | base64 -d | gunzip | jq ### Referenced resource conventions -The CER API does not enforce any particular structure or metadata on the +The COS API does not enforce any particular structure or metadata on the referenced Secret — the `ref` field is a plain pointer. The reconciler only requires that the Secret exists and that the key resolves to valid JSON content (optionally gzip-compressed). Everything else is a convention that the system @@ -139,30 +139,30 @@ follow for consistency and safe lifecycle management. Recommended conventions: -1. **Immutability**: Secrets should set `immutable: true`. Because CER phases +1. **Immutability**: Secrets should set `immutable: true`. Because COS phases are immutable, the content backing a ref should not change after creation. Mutable referenced Secrets are not rejected, but modifying them after the - CER is created leads to undefined behavior. + COS is created leads to undefined behavior. 2. **Owner references**: Referenced Secrets should carry an ownerReference to - the CER so that Kubernetes garbage collection removes them when the CER is + the COS so that Kubernetes garbage collection removes them when the COS is deleted: ```yaml ownerReferences: - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision - name: + kind: ClusterObjectSet + name: uid: controller: true ``` Without an ownerReference, the producer is responsible for cleaning up the - Secret when the CER is deleted. The reconciler does not delete referenced + Secret when the COS is deleted. The reconciler does not delete referenced Secrets itself. 3. **Revision label**: A label identifying the owning revision aids discovery, debugging, and bulk cleanup: ``` - olm.operatorframework.io/revision-name: + olm.operatorframework.io/revision-name: ``` This enables fetching all referenced Secrets for a revision with a single list call: @@ -176,23 +176,23 @@ This reduces the number of Secrets created. ### Controller implementation The ClusterExtension controller externalizes all objects by default. When the -Boxcutter applier creates a CER, every object entry uses `ref` pointing to the +Boxcutter applier creates a COS, every object entry uses `ref` pointing to the object content stored in a Secret. Secrets are created in the system namespace -(`olmv1-system` by default). This keeps CERs uniformly small regardless of +(`olmv1-system` by default). This keeps COSs uniformly small regardless of bundle size. -Users who manually craft CERs may use either inline `object` or `ref` pointing +Users who manually craft COSs may use either inline `object` or `ref` pointing to their own Secrets. Inline `object` is convenient for development, testing, or extensions with very few small objects. Users who prefer to manage their own externalized storage can create Secrets and use `ref` directly. ### Example -A system-created CER with all objects externalized: +A system-created COS with all objects externalized: ```yaml apiVersion: olm.operatorframework.io/v1 -kind: ClusterExtensionRevision +kind: ClusterObjectSet metadata: name: my-extension-1 spec: @@ -232,7 +232,7 @@ metadata: olm.operatorframework.io/revision-name: my-extension-1 ownerReferences: - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet name: my-extension-1 uid: controller: true @@ -250,7 +250,7 @@ metadata: olm.operatorframework.io/revision-name: my-extension-1 ownerReferences: - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet name: my-extension-1 uid: controller: true @@ -267,7 +267,7 @@ metadata: olm.operatorframework.io/revision-name: my-extension-1 ownerReferences: - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet name: my-extension-1 uid: controller: true @@ -276,11 +276,11 @@ data: deployment: ``` -A user-crafted CER with inline objects: +A user-crafted COS with inline objects: ```yaml apiVersion: olm.operatorframework.io/v1 -kind: ClusterExtensionRevision +kind: ClusterObjectSet metadata: name: my-extension-1 spec: @@ -311,8 +311,8 @@ system namespace (`olmv1-system` by default): the hash as the data key. The corresponding `ref.key` is set to the hash. Using the content hash as key ensures that it is a stable, deterministic identifier tied to the exact content — if the content changes, the hash - changes, which in turn changes the key in the CER, guaranteeing that any - content mutation is visible as a CER spec change and triggers a new + changes, which in turn changes the key in the COS, guaranteeing that any + content mutation is visible as a COS spec change and triggers a new revision. 2. When adding an object would push the current Secret beyond 900 KiB (leaving headroom for base64 overhead and metadata), finalize it and start a new one. @@ -321,16 +321,16 @@ system namespace (`olmv1-system` by default): creation fails with a clear error. Multiple Secrets are independent — each is referenced directly by a `ref` in -the CER. There is no linked-list chaining between them. +the COS. There is no linked-list chaining between them. ### Crash-safe creation sequence -The creation sequence ensures that by the time the CER exists and is visible to -the CER reconciler, all referenced Secrets are already present. This avoids +The creation sequence ensures that by the time the COS exists and is visible to +the COS reconciler, all referenced Secrets are already present. This avoids spurious "not found" errors and unnecessary retry loops. ownerReferences require the parent's `uid`, which is only assigned by the API -server at creation time. Since we must create the Secrets before the CER, they +server at creation time. Since we must create the Secrets before the COS, they are initially created without ownerReferences. The Secret `immutable` flag only protects `.data` and `.stringData` — metadata (including ownerReferences) can still be patched after creation. @@ -342,43 +342,43 @@ Step 1: Create Secret(s) with revision label, no ownerReference | ClusterExtension controller detects them on | next reconciliation by listing Secrets with | the revision label and checking whether the - | corresponding CER exists. If not, deletes them. + | corresponding COS exists. If not, deletes them. v -Step 2: Create CER with refs pointing to the Secrets from step 1 +Step 2: Create COS with refs pointing to the Secrets from step 1 | - | crash here → CER exists, Secrets exist, refs resolve. - | CER reconciler can proceed normally. + | crash here → COS exists, Secrets exist, refs resolve. + | COS reconciler can proceed normally. | Secrets have no ownerReferences yet. | ClusterExtension controller retries step 3. v -Step 3: Patch ownerReferences onto Secrets (using CER uid) +Step 3: Patch ownerReferences onto Secrets (using COS uid) | | crash here → Some Secrets have ownerRefs, some don't. | ClusterExtension controller retries patching | the remaining Secrets on next reconciliation. v - Done — CER has refs, all Secrets exist with owner refs. + Done — COS has refs, all Secrets exist with owner refs. ``` Key properties: -- **No reconciler churn**: Referenced Secrets exist before the CER is created. - The CER reconciler never encounters missing Secrets during normal operation. +- **No reconciler churn**: Referenced Secrets exist before the COS is created. + The COS reconciler never encounters missing Secrets during normal operation. - **Orphan cleanup**: Secrets created in step 1 carry the revision label - (`olm.operatorframework.io/revision-name`). Once the CER is created in step 2 + (`olm.operatorframework.io/revision-name`). Once the COS is created in step 2 and ownerReferences are patched in step 3, Kubernetes garbage collection - automatically removes the Secrets when the CER is deleted. If a crash occurs + automatically removes the Secrets when the COS is deleted. If a crash occurs between steps 1 and 2, the ClusterExtension controller detects orphaned - Secrets (those with the revision label but no corresponding CER) and deletes + Secrets (those with the revision label but no corresponding COS) and deletes them on its next reconciliation. - **Idempotent retry**: Secrets are immutable in data. Re-creation of an existing Secret returns AlreadyExists and is skipped. ownerReference patching is idempotent — patching an already-set ownerReference is a no-op. -- **Refs are truly immutable**: Set at CER creation, never modified (inherited +- **Refs are truly immutable**: Set at COS creation, never modified (inherited from phase immutability). -### CER reconciler behavior +### COS reconciler behavior -When processing a CER phase: +When processing a COS phase: - For each object entry in the phase: - If `object` is set, use it directly (current behavior, unchanged). - If `ref` is set, fetch the referenced Secret, read the value at the @@ -389,7 +389,7 @@ When processing a CER phase: rollout semantics are unchanged. Under normal operation, referenced Secrets are guaranteed to exist before the -CER is created (see [Crash-safe creation sequence](#crash-safe-creation-sequence)). +COS is created (see [Crash-safe creation sequence](#crash-safe-creation-sequence)). If a referenced Secret or key is not found — indicating an inconsistent state caused by external modification or a partially completed creation sequence — the reconciler returns a retryable error, allowing the controller to retry on @@ -435,7 +435,7 @@ exclusive. Secrets use a dedicated type `olm.operatorframework.io/revision-phase-data` to distinguish them from user-created Secrets and enable easy identification. -Secret names are derived deterministically from the CER name and a content hash. +Secret names are derived deterministically from the COS name and a content hash. The hash is the first 16 hex characters of the SHA-256 digest of the phases serialized to JSON (before gzip compression). Computing the hash from the JSON serialization rather than the gzip output makes it deterministic regardless of @@ -443,13 +443,13 @@ gzip implementation details. | Secret | Name | |----------|-------------------------------| -| First | `-` | -| Second | `--1` | -| Third | `--2` | -| Nth | `--` | +| First | `-` | +| Second | `--1` | +| Third | `--2` | +| Nth | `--` | The hash is computed from the phases JSON before any Secrets are created, so -`phasesRef.secretRef.name` is known at CER creation time and can be set +`phasesRef.secretRef.name` is known at COS creation time and can be set immediately. ### Secret labeling @@ -457,7 +457,7 @@ immediately. All Secrets in a chain carry a common label identifying the owning revision: ``` -olm.operatorframework.io/revision-name: +olm.operatorframework.io/revision-name: ``` This allows all phase data Secrets for a given revision to be fetched with a @@ -488,7 +488,7 @@ metadata: olm.operatorframework.io/revision-name: my-extension-1 ownerReferences: - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet name: my-extension-1 uid: controller: true @@ -510,7 +510,7 @@ metadata: olm.operatorframework.io/revision-name: my-extension-1 ownerReferences: - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet name: my-extension-1 uid: controller: true @@ -529,7 +529,7 @@ metadata: olm.operatorframework.io/revision-name: my-extension-1 ownerReferences: - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet name: my-extension-1 uid: controller: true @@ -548,7 +548,7 @@ metadata: olm.operatorframework.io/revision-name: my-extension-1 ownerReferences: - apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet name: my-extension-1 uid: controller: true @@ -593,35 +593,35 @@ of Secrets, creation fails with a clear error. ### Crash-safe creation sequence Because the hash is computed from the phases JSON before any Secrets are created, -`phasesRef` can be set at CER creation time: +`phasesRef` can be set at COS creation time: ``` -Step 1: Create CER with phasesRef.secretRef.name = - +Step 1: Create COS with phasesRef.secretRef.name = - | - | crash here → CER exists but Secrets do not. + | crash here → COS exists but Secrets do not. | Reconciler retries Secret creation. v -Step 2: Create Secrets with ownerReferences pointing to the CER +Step 2: Create Secrets with ownerReferences pointing to the COS | | crash here → Partial set of Secrets exists, all with | ownerReferences. Not orphaned — GC'd if - | CER is deleted. Existing immutable Secrets + | COS is deleted. Existing immutable Secrets | are skipped (AlreadyExists), missing ones | are created on next attempt. v - Done — CER has phasesRef, all Secrets exist with owner refs. + Done — COS has phasesRef, all Secrets exist with owner refs. ``` Key properties: -- **No orphaned Secrets**: Every Secret carries an ownerReference to the CER. - Kubernetes garbage collection removes them if the CER is deleted at any point. +- **No orphaned Secrets**: Every Secret carries an ownerReference to the COS. + Kubernetes garbage collection removes them if the COS is deleted at any point. - **Idempotent retry**: Secrets are immutable. Re-creation of an existing Secret returns AlreadyExists and is skipped. Missing Secrets are created on retry. -- **phasesRef is truly immutable**: Set at CER creation, never modified. +- **phasesRef is truly immutable**: Set at COS creation, never modified. -### CER reconciler behavior +### COS reconciler behavior -When reconciling a CER: +When reconciling a COS: - If `phases` is set, use it directly (current behavior, unchanged). - If `phasesRef` is set: 1. Fetch the Secret at `phasesRef.secretRef.name` in @@ -630,7 +630,7 @@ When reconciling a CER: 3. If `data[.nextSecretRef]` exists, fetch the named Secret and repeat from step 2. 4. Gunzip the concatenated buffer. - 5. JSON-deserialize into `[]ClusterExtensionRevisionPhase`. + 5. JSON-deserialize into `[]ClusterObjectSetPhase`. 6. Use identically to inline phases. If a Secret is not yet available, return a retryable error. - If neither is set, skip reconciliation (invalid state). @@ -650,7 +650,7 @@ rollout semantics are unchanged. | **Reconciler complexity** | Secrets are served from cache; no ordering dependency between fetches | Chain traversal — follow `.nextSecretRef` links, concatenate chunks, gunzip, deserialize | | **Compression** | Optional per-object gzip, auto-detected via magic bytes; each object compressed independently | Single gzip stream across all phases; exploits cross-phase redundancy for better ratios | | **Number of Secrets** | One or more, typically one | Typically one Secret for all phases; multiple only when compressed blob exceeds 900 KiB | -| **Crash safety** | 3-step: Secrets → CER → patch ownerRefs; orphan cleanup via revision label | 2-step: CER → Secrets with ownerRefs; simpler but reconciler may see missing Secrets temporarily | +| **Crash safety** | 3-step: Secrets → COS → patch ownerRefs; orphan cleanup via revision label | 2-step: COS → Secrets with ownerRefs; simpler but reconciler may see missing Secrets temporarily | | **Flexibility** | Mixed inline/ref per object within the same phase is possible | All-or-nothing — either all phases inline or all externalized | | **Storage efficiency** | Per-object compression misses cross-object redundancy; potentially more Secrets created in edge cases | Better compression from cross-phase redundancy; fewer Secrets | | **Resource type** | Secret only | Secret only (with dedicated type) | @@ -661,13 +661,13 @@ rollout semantics are unchanged. ## Non-goals -- **Migration of existing inline objects/phases**: Existing CERs using inline +- **Migration of existing inline objects/phases**: Existing COSs using inline `object` or `phases` fields continue to work as-is. There is no automatic migration to `ref` or `phasesRef`. - **System-managed lifecycle for system-created resources**: The OLM creates and owns the referenced Secrets it produces (setting ownerReferences and - immutability). Users who manually craft CERs with `ref` are responsible for + immutability). Users who manually craft COSs with `ref` are responsible for the lifecycle of their own referenced Secrets. - **Cross-revision deduplication**: Objects or phases that are identical between @@ -694,7 +694,7 @@ rollout semantics are unchanged. the ceiling rather than removing it. - **Custom Resource for phase/object storage**: A dedicated CRD (e.g., - `ClusterExtensionRevisionPhaseData`) would provide schema validation and + `ClusterObjectSetPhaseData`) would provide schema validation and a typed API. However, it introduces a new CRD to manage, is subject to the same etcd size limit, and the phase data is opaque to the API server anyway (embedded `unstructured.Unstructured` objects). Secrets are simpler and @@ -711,13 +711,13 @@ rollout semantics are unchanged. **Per-object content references** is the recommended approach for the following reasons: -1. **Phases structure preserved**: The phases array in the CER spec remains +1. **Phases structure preserved**: The phases array in the COS spec remains unchanged. Only individual object entries gain a new resolution path via `ref`. This is a smaller, more targeted API change compared to replacing the entire phases field with `phasesRef`. 2. **Granular flexibility**: Inline `object` and `ref` can be mixed within the - same phase. Users who manually craft CERs can choose either approach + same phase. Users who manually craft COSs can choose either approach per-object. The externalized phases approach is all-or-nothing. 3. **Uniform code path**: By externalizing all objects by default, the system diff --git a/hack/tools/update-crds.sh b/hack/tools/update-crds.sh index e379b59896..e7f3ce1392 100755 --- a/hack/tools/update-crds.sh +++ b/hack/tools/update-crds.sh @@ -7,7 +7,7 @@ set -e # The names of the generated CRDs CE="olm.operatorframework.io_clusterextensions.yaml" CC="olm.operatorframework.io_clustercatalogs.yaml" -CR="olm.operatorframework.io_clusterextensionrevisions.yaml" +CR="olm.operatorframework.io_clusterobjectsets.yaml" # order for modules and crds must match # each item in crds must be unique, and should be associated with a module diff --git a/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensions.yaml b/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensions.yaml index 83631dc27b..aebb9e72be 100644 --- a/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensions.yaml +++ b/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensions.yaml @@ -504,10 +504,10 @@ spec: properties: activeRevisions: description: |- - activeRevisions holds a list of currently active (non-archived) ClusterExtensionRevisions, + activeRevisions holds a list of currently active (non-archived) ClusterObjectSets, including both installed and rolling out revisions. items: - description: RevisionStatus defines the observed state of a ClusterExtensionRevision. + description: RevisionStatus defines the observed state of a ClusterObjectSet. properties: conditions: description: |- @@ -575,7 +575,7 @@ spec: - type x-kubernetes-list-type: map name: - description: name of the ClusterExtensionRevision resource + description: name of the ClusterObjectSet resource type: string required: - name @@ -599,7 +599,7 @@ spec: When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. - When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterExtensionRevisions in active roll out. + When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterObjectSets in active roll out. When the ClusterExtension is sourced from a catalog, it surfaces deprecation conditions based on catalog metadata. These are indications from a package owner to guide users away from a particular package, channel, or bundle: diff --git a/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml b/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterobjectsets.yaml similarity index 94% rename from helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml rename to helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterobjectsets.yaml index 83f989cb9d..4dd703fb2e 100644 --- a/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml +++ b/helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterobjectsets.yaml @@ -5,14 +5,14 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.20.1 olm.operatorframework.io/generator: experimental - name: clusterextensionrevisions.olm.operatorframework.io + name: clusterobjectsets.olm.operatorframework.io spec: group: olm.operatorframework.io names: - kind: ClusterExtensionRevision - listKind: ClusterExtensionRevisionList - plural: clusterextensionrevisions - singular: clusterextensionrevision + kind: ClusterObjectSet + listKind: ClusterObjectSetList + plural: clusterobjectsets + singular: clusterobjectset scope: Cluster versions: - additionalPrinterColumns: @@ -29,7 +29,7 @@ spec: schema: openAPIV3Schema: description: |- - ClusterExtensionRevision represents an immutable snapshot of Kubernetes objects + ClusterObjectSet represents an immutable snapshot of Kubernetes objects for a specific version of a ClusterExtension. Each revision contains objects organized into phases that roll out sequentially. The same object can only be managed by a single revision at a time. Ownership of objects is transitioned from one revision to the next as the extension is upgraded @@ -54,7 +54,7 @@ spec: metadata: type: object spec: - description: spec defines the desired state of the ClusterExtensionRevision. + description: spec defines the desired state of the ClusterObjectSet. properties: collisionProtection: description: |- @@ -75,7 +75,7 @@ spec: rule: self == oldSelf lifecycleState: description: |- - lifecycleState specifies the lifecycle state of the ClusterExtensionRevision. + lifecycleState specifies the lifecycle state of the ClusterObjectSet. When set to "Active", the revision is actively managed and reconciled. When set to "Archived", the revision is inactive and any resources not managed by a subsequent revision are deleted. @@ -116,7 +116,7 @@ spec: Each phase in the list must have a unique name. The maximum number of phases is 20. items: description: |- - ClusterExtensionRevisionPhase represents a group of objects that are applied together. The phase is considered + ClusterObjectSetPhase represents a group of objects that are applied together. The phase is considered complete only after all objects pass their status probes. properties: collisionProtection: @@ -160,7 +160,7 @@ spec: All objects in this list are applied to the cluster in no particular order. The maximum number of objects per phase is 50. items: description: |- - ClusterExtensionRevisionObject represents a Kubernetes object to be applied as part + ClusterObjectSetObject represents a Kubernetes object to be applied as part of a phase, along with its collision protection settings. Exactly one of object or ref must be set. @@ -523,7 +523,7 @@ spec: of the parent ClusterExtension. The revision field must be a positive integer. - Each ClusterExtensionRevision belonging to the same parent ClusterExtension must have a unique revision number. + Each ClusterObjectSet belonging to the same parent ClusterExtension must have a unique revision number. The revision number must always be the previous revision number plus one, or 1 for the first revision. format: int64 minimum: 1 @@ -538,29 +538,29 @@ spec: type: object status: description: status is optional and defines the observed state of the - ClusterExtensionRevision. + ClusterObjectSet. properties: conditions: description: |- conditions is an optional list of status conditions describing the state of the - ClusterExtensionRevision. + ClusterObjectSet. The Progressing condition represents whether the revision is actively rolling out: - - When status is True and reason is RollingOut, the ClusterExtensionRevision rollout is actively making progress and is in transition. - - When status is True and reason is Retrying, the ClusterExtensionRevision has encountered an error that could be resolved on subsequent reconciliation attempts. - - When status is True and reason is Succeeded, the ClusterExtensionRevision has reached the desired state. - - When status is False and reason is Blocked, the ClusterExtensionRevision has encountered an error that requires manual intervention for recovery. - - When status is False and reason is Archived, the ClusterExtensionRevision is archived and not being actively reconciled. + - When status is True and reason is RollingOut, the ClusterObjectSet rollout is actively making progress and is in transition. + - When status is True and reason is Retrying, the ClusterObjectSet has encountered an error that could be resolved on subsequent reconciliation attempts. + - When status is True and reason is Succeeded, the ClusterObjectSet has reached the desired state. + - When status is False and reason is Blocked, the ClusterObjectSet has encountered an error that requires manual intervention for recovery. + - When status is False and reason is Archived, the ClusterObjectSet is archived and not being actively reconciled. The Available condition represents whether the revision has been successfully rolled out and is available: - - When status is True and reason is ProbesSucceeded, the ClusterExtensionRevision has been successfully rolled out and all objects pass their readiness probes. + - When status is True and reason is ProbesSucceeded, the ClusterObjectSet has been successfully rolled out and all objects pass their readiness probes. - When status is False and reason is ProbeFailure, one or more objects are failing their readiness probes during rollout. - - When status is Unknown and reason is Reconciling, the ClusterExtensionRevision has encountered an error that prevented it from observing the probes. - - When status is Unknown and reason is Archived, the ClusterExtensionRevision has been archived and its objects have been torn down. - - When status is Unknown and reason is Migrated, the ClusterExtensionRevision was migrated from an existing release and object status probe results have not yet been observed. + - When status is Unknown and reason is Reconciling, the ClusterObjectSet has encountered an error that prevented it from observing the probes. + - When status is Unknown and reason is Archived, the ClusterObjectSet has been archived and its objects have been torn down. + - When status is Unknown and reason is Migrated, the ClusterObjectSet was migrated from an existing release and object status probe results have not yet been observed. The Succeeded condition represents whether the revision has successfully completed its rollout: - - When status is True and reason is Succeeded, the ClusterExtensionRevision has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. + - When status is True and reason is Succeeded, the ClusterObjectSet has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. items: description: Condition contains details for one aspect of the current state of this API Resource. diff --git a/helm/olmv1/templates/crds/customresourcedefinition-clusterextensionrevisions.olm.operatorframework.io.yml b/helm/olmv1/templates/crds/customresourcedefinition-clusterobjectsets.olm.operatorframework.io.yml similarity index 78% rename from helm/olmv1/templates/crds/customresourcedefinition-clusterextensionrevisions.olm.operatorframework.io.yml rename to helm/olmv1/templates/crds/customresourcedefinition-clusterobjectsets.olm.operatorframework.io.yml index e52614d6e3..11abb0d2ce 100644 --- a/helm/olmv1/templates/crds/customresourcedefinition-clusterextensionrevisions.olm.operatorframework.io.yml +++ b/helm/olmv1/templates/crds/customresourcedefinition-clusterobjectsets.olm.operatorframework.io.yml @@ -1,9 +1,9 @@ {{- if .Values.options.operatorController.enabled }} {{- if (eq .Values.options.featureSet "standard") }} -{{- /* Add when GA: tpl (.Files.Get "base/operator-controller/crd/standard/olm.operatorframework.io_clusterextensionrevisionss.yaml") . */}} +{{- /* Add when GA: tpl (.Files.Get "base/operator-controller/crd/standard/olm.operatorframework.io_clusterobjectsetss.yaml") . */}} {{- else if (eq .Values.options.featureSet "experimental") }} {{- if has "BoxcutterRuntime" .Values.options.operatorController.features.enabled }} -{{ tpl (.Files.Get "base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensionrevisions.yaml") . }} +{{ tpl (.Files.Get "base/operator-controller/crd/experimental/olm.operatorframework.io_clusterobjectsets.yaml") . }} {{- end }} {{- else }} {{- fail "options.featureSet must be set to one of: {standard,experimental}" }} diff --git a/helm/olmv1/templates/rbac/clusterrole-operator-controller-manager-role.yml b/helm/olmv1/templates/rbac/clusterrole-operator-controller-manager-role.yml index 9f187e8924..d6f9e87909 100644 --- a/helm/olmv1/templates/rbac/clusterrole-operator-controller-manager-role.yml +++ b/helm/olmv1/templates/rbac/clusterrole-operator-controller-manager-role.yml @@ -89,7 +89,7 @@ rules: - apiGroups: - olm.operatorframework.io resources: - - clusterextensionrevisions + - clusterobjectsets verbs: - create - delete @@ -101,14 +101,14 @@ rules: - apiGroups: - olm.operatorframework.io resources: - - clusterextensionrevisions/status + - clusterobjectsets/status verbs: - patch - update - apiGroups: - olm.operatorframework.io resources: - - clusterextensionrevisions/finalizers + - clusterobjectsets/finalizers verbs: - update {{- end }} diff --git a/internal/operator-controller/applier/boxcutter.go b/internal/operator-controller/applier/boxcutter.go index 0c3a3119a2..3c7f0c00a6 100644 --- a/internal/operator-controller/applier/boxcutter.go +++ b/internal/operator-controller/applier/boxcutter.go @@ -45,16 +45,16 @@ import ( ) const ( - ClusterExtensionRevisionRetentionLimit = 5 + ClusterObjectSetRetentionLimit = 5 ) -type ClusterExtensionRevisionGenerator interface { - GenerateRevision(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) +type ClusterObjectSetGenerator interface { + GenerateRevision(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) GenerateRevisionFromHelmRelease( ctx context.Context, helmRelease *release.Release, ext *ocv1.ClusterExtension, objectLabels map[string]string, - ) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) + ) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) } type SimpleRevisionGenerator struct { @@ -66,9 +66,9 @@ func (r *SimpleRevisionGenerator) GenerateRevisionFromHelmRelease( ctx context.Context, helmRelease *release.Release, ext *ocv1.ClusterExtension, objectLabels map[string]string, -) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { +) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { docs := splitManifestDocuments(helmRelease.Manifest) - objs := make([]ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration, 0, len(docs)) + objs := make([]ocv1ac.ClusterObjectSetObjectApplyConfiguration, 0, len(docs)) for _, doc := range docs { obj := unstructured.Unstructured{} if err := yaml.Unmarshal([]byte(doc), &obj); err != nil { @@ -92,11 +92,11 @@ func (r *SimpleRevisionGenerator) GenerateRevisionFromHelmRelease( obj.SetAnnotations(mergeStringMaps(obj.GetAnnotations(), annotationUpdates)) } - objs = append(objs, *ocv1ac.ClusterExtensionRevisionObject(). + objs = append(objs, *ocv1ac.ClusterObjectSetObject(). WithObject(obj)) } - rev := r.buildClusterExtensionRevision(objs, ext, map[string]string{ + rev := r.buildClusterObjectSet(objs, ext, map[string]string{ labels.BundleNameKey: helmRelease.Labels[labels.BundleNameKey], labels.PackageNameKey: helmRelease.Labels[labels.PackageNameKey], labels.BundleVersionKey: helmRelease.Labels[labels.BundleVersionKey], @@ -112,7 +112,7 @@ func (r *SimpleRevisionGenerator) GenerateRevision( ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string, -) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { +) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { // extract plain manifests plain, err := r.ManifestProvider.Get(bundleFS, ext) if err != nil { @@ -141,7 +141,7 @@ func (r *SimpleRevisionGenerator) GenerateRevision( } // objectLabels - objs := make([]ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration, 0, len(plain)) + objs := make([]ocv1ac.ClusterObjectSetObjectApplyConfiguration, 0, len(plain)) for _, obj := range plain { obj.SetLabels(mergeStringMaps(obj.GetLabels(), objectLabels)) @@ -174,10 +174,10 @@ func (r *SimpleRevisionGenerator) GenerateRevision( unstr.SetAnnotations(mergeStringMaps(unstr.GetAnnotations(), annotationUpdates)) } - objs = append(objs, *ocv1ac.ClusterExtensionRevisionObject(). + objs = append(objs, *ocv1ac.ClusterObjectSetObject(). WithObject(unstr)) } - rev := r.buildClusterExtensionRevision(objs, ext, revisionAnnotations) + rev := r.buildClusterObjectSet(objs, ext, revisionAnnotations) rev.Spec.WithCollisionProtection(ocv1.CollisionProtectionPrevent) return rev, nil } @@ -224,11 +224,11 @@ func sanitizedUnstructured(ctx context.Context, unstr *unstructured.Unstructured obj["metadata"] = metadataSanitized } -func (r *SimpleRevisionGenerator) buildClusterExtensionRevision( - objects []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration, +func (r *SimpleRevisionGenerator) buildClusterObjectSet( + objects []ocv1ac.ClusterObjectSetObjectApplyConfiguration, ext *ocv1.ClusterExtension, annotations map[string]string, -) *ocv1ac.ClusterExtensionRevisionApplyConfiguration { +) *ocv1ac.ClusterObjectSetApplyConfiguration { if annotations == nil { annotations = make(map[string]string) } @@ -237,15 +237,15 @@ func (r *SimpleRevisionGenerator) buildClusterExtensionRevision( phases := PhaseSort(objects) - spec := ocv1ac.ClusterExtensionRevisionSpec(). - WithLifecycleState(ocv1.ClusterExtensionRevisionLifecycleStateActive). + spec := ocv1ac.ClusterObjectSetSpec(). + WithLifecycleState(ocv1.ClusterObjectSetLifecycleStateActive). WithPhases(phases...). WithProgressionProbes(defaultProgressionProbes...) if p := ext.Spec.ProgressDeadlineMinutes; p > 0 { spec.WithProgressDeadlineMinutes(p) } - return ocv1ac.ClusterExtensionRevision(""). + return ocv1ac.ClusterObjectSet(""). WithAnnotations(annotations). WithLabels(map[string]string{ labels.OwnerKindKey: ocv1.ClusterExtensionKind, @@ -255,10 +255,10 @@ func (r *SimpleRevisionGenerator) buildClusterExtensionRevision( } // BoxcutterStorageMigrator migrates ClusterExtensions from Helm-based storage to -// ClusterExtensionRevision storage, enabling upgrades from older operator-controller versions. +// ClusterObjectSet storage, enabling upgrades from older operator-controller versions. type BoxcutterStorageMigrator struct { ActionClientGetter helmclient.ActionClientGetter - RevisionGenerator ClusterExtensionRevisionGenerator + RevisionGenerator ClusterObjectSetGenerator Client boxcutterStorageMigratorClient Scheme *runtime.Scheme FieldOwner string @@ -271,14 +271,14 @@ type boxcutterStorageMigratorClient interface { Status() client.StatusWriter } -// Migrate creates a ClusterExtensionRevision from an existing Helm release if no revisions exist yet. +// Migrate creates a ClusterObjectSet from an existing Helm release if no revisions exist yet. // The migration is idempotent and skipped if revisions already exist or no Helm release is found. func (m *BoxcutterStorageMigrator) Migrate(ctx context.Context, ext *ocv1.ClusterExtension, objectLabels map[string]string) error { - existingRevisionList := ocv1.ClusterExtensionRevisionList{} + existingRevisionList := ocv1.ClusterObjectSetList{} if err := m.Client.List(ctx, &existingRevisionList, client.MatchingLabels{ labels.OwnerNameKey: ext.Name, }); err != nil { - return fmt.Errorf("listing ClusterExtensionRevisions before attempting migration: %w", err) + return fmt.Errorf("listing ClusterObjectSets before attempting migration: %w", err) } if len(existingRevisionList.Items) != 0 { return m.ensureMigratedRevisionStatus(ctx, existingRevisionList.Items) @@ -352,7 +352,7 @@ func (m *BoxcutterStorageMigrator) Migrate(ctx context.Context, ext *ocv1.Cluste // - Status reporting (installed bundle appears as nil) // - Subsequent upgrades (resolution fails without knowing current version) // - // While the ClusterExtensionRevision controller would eventually reconcile and set this status, + // While the ClusterObjectSet controller would eventually reconcile and set this status, // that creates a timing gap where the ClusterExtension reconciliation happens before the status // is set, causing failures during the OLM upgrade window. // @@ -364,13 +364,13 @@ func (m *BoxcutterStorageMigrator) Migrate(ctx context.Context, ext *ocv1.Cluste // ensureMigratedRevisionStatus checks if revision 1 exists and needs its status set. // This handles the case where revision creation succeeded but status update failed. // Returns nil if no action is needed. -func (m *BoxcutterStorageMigrator) ensureMigratedRevisionStatus(ctx context.Context, revisions []ocv1.ClusterExtensionRevision) error { +func (m *BoxcutterStorageMigrator) ensureMigratedRevisionStatus(ctx context.Context, revisions []ocv1.ClusterObjectSet) error { for i := range revisions { if revisions[i].Spec.Revision != 1 { continue } // Skip if already succeeded - status is already set correctly. - if meta.IsStatusConditionTrue(revisions[i].Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded) { + if meta.IsStatusConditionTrue(revisions[i].Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) { return nil } // Ensure revision 1 status is set correctly, including for previously migrated @@ -413,7 +413,7 @@ func (m *BoxcutterStorageMigrator) findLatestDeployedRelease(ac helmclient.Actio // Returns nil if the status is already set or after successfully setting it. // Only sets status on revisions that were actually migrated from Helm (marked with MigratedFromHelmKey label). func (m *BoxcutterStorageMigrator) ensureRevisionStatus(ctx context.Context, name string) error { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} if err := m.Client.Get(ctx, client.ObjectKey{Name: name}, rev); err != nil { return fmt.Errorf("getting existing revision for status check: %w", err) } @@ -426,13 +426,13 @@ func (m *BoxcutterStorageMigrator) ensureRevisionStatus(ctx context.Context, nam } // Check if status is already set to Succeeded=True - if meta.IsStatusConditionTrue(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded) { + if meta.IsStatusConditionTrue(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) { return nil } // Set the Succeeded status condition meta.SetStatusCondition(&rev.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeSucceeded, + Type: ocv1.ClusterObjectSetTypeSucceeded, Status: metav1.ConditionTrue, Reason: ocv1.ReasonSucceeded, Message: "Revision succeeded - migrated from Helm release", @@ -449,7 +449,7 @@ func (m *BoxcutterStorageMigrator) ensureRevisionStatus(ctx context.Context, nam type Boxcutter struct { Client client.Client Scheme *runtime.Scheme - RevisionGenerator ClusterExtensionRevisionGenerator + RevisionGenerator ClusterObjectSetGenerator Preflights []Preflight PreAuthorizer authorization.PreAuthorizer FieldOwner string @@ -459,7 +459,7 @@ type Boxcutter struct { func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (bool, string, error) { // List existing revisions first to validate cluster connectivity before checking contentFS. // This ensures we fail fast on API errors rather than attempting fallback behavior when - // cluster access is unavailable (since the ClusterExtensionRevision controller also requires + // cluster access is unavailable (since the ClusterObjectSet controller also requires // API access to maintain resources). The revision list is also needed to determine if fallback // is possible when contentFS is nil (at least one revision must exist). existingRevisions, err := bc.getExistingRevisions(ctx, ext.GetName()) @@ -474,7 +474,7 @@ func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust return false, "", fmt.Errorf("catalog content unavailable and no revision installed") } // Returning true here signals that the rollout has succeeded using the current revision. - // This assumes the ClusterExtensionRevision controller is running and will continue to + // This assumes the ClusterObjectSet controller is running and will continue to // reconcile, apply, and maintain the resources defined in that revision via Server-Side Apply, // ensuring the workload keeps running even when catalog access is unavailable. return true, "", nil @@ -498,7 +498,7 @@ func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust WithBlockOwnerDeletion(true). WithController(true)) - currentRevision := &ocv1.ClusterExtensionRevision{} + currentRevision := &ocv1.ClusterObjectSet{} state := StateNeedsInstall if len(existingRevisions) > 0 { currentRevision = &existingRevisions[len(existingRevisions)-1] @@ -508,7 +508,7 @@ func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust // Save inline objects before externalization (needed for preflights + createExternalizedRevision) savedInline := saveInlineObjects(desiredRevision) - // Externalize with CURRENT revision name so refs match existing CER + // Externalize with CURRENT revision name so refs match existing COS phases := extractPhasesForPacking(desiredRevision.Spec.Phases) packer := &SecretPacker{ RevisionName: currentRevision.Name, @@ -577,9 +577,9 @@ func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust return true, "", nil } -// createExternalizedRevision creates a new CER with all objects externalized to Secrets. -// It follows a crash-safe three-step sequence: create Secrets, create CER, patch ownerRefs. -func (bc *Boxcutter) createExternalizedRevision(ctx context.Context, ext *ocv1.ClusterExtension, desiredRevision *ocv1ac.ClusterExtensionRevisionApplyConfiguration, existingRevisions []ocv1.ClusterExtensionRevision) error { +// createExternalizedRevision creates a new COS with all objects externalized to Secrets. +// It follows a crash-safe three-step sequence: create Secrets, create COS, patch ownerRefs. +func (bc *Boxcutter) createExternalizedRevision(ctx context.Context, ext *ocv1.ClusterExtension, desiredRevision *ocv1ac.ClusterObjectSetApplyConfiguration, existingRevisions []ocv1.ClusterObjectSet) error { prevRevisions := existingRevisions revisionNumber := latestRevisionNumber(prevRevisions) + 1 @@ -618,12 +618,12 @@ func (bc *Boxcutter) createExternalizedRevision(ctx context.Context, ext *ocv1.C } } - // Step 2: Create CER with refs via SSA (pre-auth already ran above) + // Step 2: Create COS with refs via SSA (pre-auth already ran above) if err := bc.Client.Apply(ctx, desiredRevision, client.FieldOwner(bc.FieldOwner), client.ForceOwnership); err != nil { return fmt.Errorf("creating new Revision: %w", err) } - // Step 3: Patch ownerReferences onto Secrets using the CER's UID + // Step 3: Patch ownerReferences onto Secrets using the COS's UID if err := bc.patchSecretOwnerReferences(ctx, desiredRevision, packResult.Secrets); err != nil { return fmt.Errorf("patching ownerReferences on ref Secrets: %w", err) } @@ -632,7 +632,7 @@ func (bc *Boxcutter) createExternalizedRevision(ctx context.Context, ext *ocv1.C // runPreAuthorizationChecks runs PreAuthorization checks if the PreAuthorizer is set. An error will be returned if // the ClusterExtension service account does not have the necessary permissions to manage the revision's resources -func (bc *Boxcutter) runPreAuthorizationChecks(ctx context.Context, user user.Info, rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration) error { +func (bc *Boxcutter) runPreAuthorizationChecks(ctx context.Context, user user.Info, rev *ocv1ac.ClusterObjectSetApplyConfiguration) error { if bc.PreAuthorizer == nil { return nil } @@ -647,13 +647,13 @@ func (bc *Boxcutter) runPreAuthorizationChecks(ctx context.Context, user user.In return formatPreAuthorizerOutput(bc.PreAuthorizer.PreAuthorize(ctx, user, manifestReader, revisionManagementPerms(rev))) } -// garbageCollectOldRevisions deletes archived revisions beyond ClusterExtensionRevisionRetentionLimit. +// garbageCollectOldRevisions deletes archived revisions beyond ClusterObjectSetRetentionLimit. // Active revisions are never deleted. revisionList must be sorted oldest to newest. -func (bc *Boxcutter) garbageCollectOldRevisions(ctx context.Context, revisionList []ocv1.ClusterExtensionRevision) error { +func (bc *Boxcutter) garbageCollectOldRevisions(ctx context.Context, revisionList []ocv1.ClusterObjectSet) error { for index, r := range revisionList { // Only delete archived revisions that are beyond the limit - if index < len(revisionList)-ClusterExtensionRevisionRetentionLimit && r.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived { - if err := bc.Client.Delete(ctx, &ocv1.ClusterExtensionRevision{ + if index < len(revisionList)-ClusterObjectSetRetentionLimit && r.Spec.LifecycleState == ocv1.ClusterObjectSetLifecycleStateArchived { + if err := bc.Client.Delete(ctx, &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: r.Name, }, @@ -665,21 +665,21 @@ func (bc *Boxcutter) garbageCollectOldRevisions(ctx context.Context, revisionLis return nil } -// getExistingRevisions returns the list of ClusterExtensionRevisions for a ClusterExtension with name extName in revision order (oldest to newest) -func (bc *Boxcutter) getExistingRevisions(ctx context.Context, extName string) ([]ocv1.ClusterExtensionRevision, error) { - existingRevisionList := &ocv1.ClusterExtensionRevisionList{} +// getExistingRevisions returns the list of ClusterObjectSets for a ClusterExtension with name extName in revision order (oldest to newest) +func (bc *Boxcutter) getExistingRevisions(ctx context.Context, extName string) ([]ocv1.ClusterObjectSet, error) { + existingRevisionList := &ocv1.ClusterObjectSetList{} if err := bc.Client.List(ctx, existingRevisionList, client.MatchingLabels{ labels.OwnerNameKey: extName, }); err != nil { return nil, fmt.Errorf("listing revisions: %w", err) } - slices.SortFunc(existingRevisionList.Items, func(a, b ocv1.ClusterExtensionRevision) int { + slices.SortFunc(existingRevisionList.Items, func(a, b ocv1.ClusterObjectSet) int { return cmp.Compare(a.Spec.Revision, b.Spec.Revision) }) return existingRevisionList.Items, nil } -func latestRevisionNumber(prevRevisions []ocv1.ClusterExtensionRevision) int64 { +func latestRevisionNumber(prevRevisions []ocv1.ClusterObjectSet) int64 { if len(prevRevisions) == 0 { return 0 } @@ -810,7 +810,7 @@ func splitManifestDocuments(file string) []string { } // getObjects returns a slice of all objects in the revision -func getObjects(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration) []client.Object { +func getObjects(rev *ocv1ac.ClusterObjectSetApplyConfiguration) []client.Object { if rev.Spec == nil { return nil } @@ -830,7 +830,7 @@ func getObjects(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration) []client } // revisionManifestReader returns an io.Reader containing all manifests in the revision -func revisionManifestReader(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration) (io.Reader, error) { +func revisionManifestReader(rev *ocv1ac.ClusterObjectSetApplyConfiguration) (io.Reader, error) { printer := printers.YAMLPrinter{} buf := new(bytes.Buffer) for _, obj := range getObjects(rev) { @@ -842,7 +842,7 @@ func revisionManifestReader(rev *ocv1ac.ClusterExtensionRevisionApplyConfigurati return buf, nil } -func revisionManagementPerms(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration) func(user.Info) []authorizer.AttributesRecord { +func revisionManagementPerms(rev *ocv1ac.ClusterObjectSetApplyConfiguration) func(user.Info) []authorizer.AttributesRecord { return func(user user.Info) []authorizer.AttributesRecord { return []authorizer.AttributesRecord{ { @@ -850,7 +850,7 @@ func revisionManagementPerms(rev *ocv1ac.ClusterExtensionRevisionApplyConfigurat Name: *rev.GetName(), APIGroup: ocv1.GroupVersion.Group, APIVersion: ocv1.GroupVersion.Version, - Resource: "clusterextensionrevisions/finalizers", + Resource: "clusterobjectsets/finalizers", ResourceRequest: true, Verb: "update", }, @@ -866,7 +866,7 @@ func mergeStringMaps(m1, m2 map[string]string) map[string]string { } // saveInlineObjects saves Object pointers from each phase/object position. -func saveInlineObjects(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration) [][]*unstructured.Unstructured { +func saveInlineObjects(rev *ocv1ac.ClusterObjectSetApplyConfiguration) [][]*unstructured.Unstructured { saved := make([][]*unstructured.Unstructured, len(rev.Spec.Phases)) for i, p := range rev.Spec.Phases { saved[i] = make([]*unstructured.Unstructured, len(p.Objects)) @@ -878,7 +878,7 @@ func saveInlineObjects(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration) [ } // restoreInlineObjects restores saved inline objects and clears refs. -func restoreInlineObjects(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration, saved [][]*unstructured.Unstructured) { +func restoreInlineObjects(rev *ocv1ac.ClusterObjectSetApplyConfiguration, saved [][]*unstructured.Unstructured) { for i := range saved { for j := range saved[i] { rev.Spec.Phases[i].Objects[j].Object = saved[i][j] @@ -888,16 +888,16 @@ func restoreInlineObjects(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration } // extractPhasesForPacking converts apply configuration phases to API types for SecretPacker. -func extractPhasesForPacking(phases []ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration) []ocv1.ClusterExtensionRevisionPhase { - result := make([]ocv1.ClusterExtensionRevisionPhase, 0, len(phases)) +func extractPhasesForPacking(phases []ocv1ac.ClusterObjectSetPhaseApplyConfiguration) []ocv1.ClusterObjectSetPhase { + result := make([]ocv1.ClusterObjectSetPhase, 0, len(phases)) for _, p := range phases { - phase := ocv1.ClusterExtensionRevisionPhase{} + phase := ocv1.ClusterObjectSetPhase{} if p.Name != nil { phase.Name = *p.Name } - phase.Objects = make([]ocv1.ClusterExtensionRevisionObject, 0, len(p.Objects)) + phase.Objects = make([]ocv1.ClusterObjectSetObject, 0, len(p.Objects)) for _, o := range p.Objects { - obj := ocv1.ClusterExtensionRevisionObject{} + obj := ocv1.ClusterObjectSetObject{} if o.Object != nil { obj.Object = *o.Object } @@ -912,7 +912,7 @@ func extractPhasesForPacking(phases []ocv1ac.ClusterExtensionRevisionPhaseApplyC } // replaceInlineWithRefs replaces inline objects in the apply configuration with refs from the pack result. -func replaceInlineWithRefs(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration, pack *PackResult) { +func replaceInlineWithRefs(rev *ocv1ac.ClusterObjectSetApplyConfiguration, pack *PackResult) { if rev.Spec == nil { return } @@ -931,38 +931,38 @@ func replaceInlineWithRefs(rev *ocv1ac.ClusterExtensionRevisionApplyConfiguratio } } -// patchSecretOwnerReferences fetches the CER to get its UID, then patches ownerReferences onto all Secrets. -func (bc *Boxcutter) patchSecretOwnerReferences(ctx context.Context, rev *ocv1ac.ClusterExtensionRevisionApplyConfiguration, secrets []corev1.Secret) error { +// patchSecretOwnerReferences fetches the COS to get its UID, then patches ownerReferences onto all Secrets. +func (bc *Boxcutter) patchSecretOwnerReferences(ctx context.Context, rev *ocv1ac.ClusterObjectSetApplyConfiguration, secrets []corev1.Secret) error { if len(secrets) == 0 { return nil } - // Fetch the CER to get its UID - cer := &ocv1.ClusterExtensionRevision{} - if err := bc.Client.Get(ctx, client.ObjectKey{Name: *rev.GetName()}, cer); err != nil { - return fmt.Errorf("getting CER %q for ownerReference: %w", *rev.GetName(), err) + // Fetch the COS to get its UID + cos := &ocv1.ClusterObjectSet{} + if err := bc.Client.Get(ctx, client.ObjectKey{Name: *rev.GetName()}, cos); err != nil { + return fmt.Errorf("getting COS %q for ownerReference: %w", *rev.GetName(), err) } - return bc.patchOwnerRefsOnSecrets(ctx, cer.Name, cer.UID, secrets) + return bc.patchOwnerRefsOnSecrets(ctx, cos.Name, cos.UID, secrets) } -// ensureSecretOwnerReferences checks referenced Secrets on an existing CER and patches missing ownerReferences. +// ensureSecretOwnerReferences checks referenced Secrets on an existing COS and patches missing ownerReferences. // This handles crash recovery when Step 3 (patching ownerRefs) failed on a previous reconciliation. -func (bc *Boxcutter) ensureSecretOwnerReferences(ctx context.Context, cer *ocv1.ClusterExtensionRevision) error { +func (bc *Boxcutter) ensureSecretOwnerReferences(ctx context.Context, cos *ocv1.ClusterObjectSet) error { // List Secrets with the revision-name label secretList := &corev1.SecretList{} if err := bc.Client.List(ctx, secretList, client.InNamespace(bc.SystemNamespace), - client.MatchingLabels{labels.RevisionNameKey: cer.Name}, + client.MatchingLabels{labels.RevisionNameKey: cos.Name}, ); err != nil { - return fmt.Errorf("listing ref Secrets for revision %q: %w", cer.Name, err) + return fmt.Errorf("listing ref Secrets for revision %q: %w", cos.Name, err) } var needsPatch []corev1.Secret for _, s := range secretList.Items { hasOwnerRef := false for _, ref := range s.OwnerReferences { - if ref.UID == cer.UID { + if ref.UID == cos.UID { hasOwnerRef = true break } @@ -976,16 +976,16 @@ func (bc *Boxcutter) ensureSecretOwnerReferences(ctx context.Context, cer *ocv1. return nil } - return bc.patchOwnerRefsOnSecrets(ctx, cer.Name, cer.UID, needsPatch) + return bc.patchOwnerRefsOnSecrets(ctx, cos.Name, cos.UID, needsPatch) } -// patchOwnerRefsOnSecrets patches ownerReferences onto the given Secrets, pointing to the CER. -func (bc *Boxcutter) patchOwnerRefsOnSecrets(ctx context.Context, cerName string, cerUID types.UID, secrets []corev1.Secret) error { +// patchOwnerRefsOnSecrets patches ownerReferences onto the given Secrets, pointing to the COS. +func (bc *Boxcutter) patchOwnerRefsOnSecrets(ctx context.Context, cosName string, cosUID types.UID, secrets []corev1.Secret) error { ownerRef := metav1.OwnerReference{ APIVersion: ocv1.GroupVersion.String(), - Kind: ocv1.ClusterExtensionRevisionKind, - Name: cerName, - UID: cerUID, + Kind: ocv1.ClusterObjectSetKind, + Name: cosName, + UID: cosUID, Controller: ptr.To(true), BlockOwnerDeletion: ptr.To(true), } @@ -995,7 +995,7 @@ func (bc *Boxcutter) patchOwnerRefsOnSecrets(ctx context.Context, cerName string // Check if ownerRef already set alreadySet := false for _, ref := range s.OwnerReferences { - if ref.UID == cerUID { + if ref.UID == cosUID { alreadySet = true break } diff --git a/internal/operator-controller/applier/boxcutter_test.go b/internal/operator-controller/applier/boxcutter_test.go index 4ce9c69856..b1360cb47e 100644 --- a/internal/operator-controller/applier/boxcutter_test.go +++ b/internal/operator-controller/applier/boxcutter_test.go @@ -98,7 +98,7 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T) rev, err := g.GenerateRevisionFromHelmRelease(t.Context(), helmRelease, ext, objectLabels) require.NoError(t, err) - expected := ocv1ac.ClusterExtensionRevision("test-123-1"). + expected := ocv1ac.ClusterObjectSet("test-123-1"). WithAnnotations(map[string]string{ "olm.operatorframework.io/bundle-name": "my-bundle", "olm.operatorframework.io/bundle-reference": "bundle-ref", @@ -111,15 +111,15 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T) labels.OwnerKindKey: ocv1.ClusterExtensionKind, labels.OwnerNameKey: "test-123", }). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec(). - WithLifecycleState(ocv1.ClusterExtensionRevisionLifecycleStateActive). + WithSpec(ocv1ac.ClusterObjectSetSpec(). + WithLifecycleState(ocv1.ClusterObjectSetLifecycleStateActive). WithCollisionProtection(ocv1.CollisionProtectionNone). WithRevision(1). WithPhases( - ocv1ac.ClusterExtensionRevisionPhase(). + ocv1ac.ClusterObjectSetPhase(). WithName("configuration"). WithObjects( - ocv1ac.ClusterExtensionRevisionObject(). + ocv1ac.ClusterObjectSetObject(). WithObject(unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", @@ -135,7 +135,7 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T) }, }, }), - ocv1ac.ClusterExtensionRevisionObject(). + ocv1ac.ClusterObjectSetObject(). WithObject(unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", @@ -228,11 +228,11 @@ func Test_SimpleRevisionGenerator_GenerateRevision(t *testing.T) { t.Log("by checking the spec-level collisionProtection is set") require.Equal(t, ptr.To(ocv1.CollisionProtectionPrevent), rev.Spec.CollisionProtection) t.Log("by checking the rendered objects are present in the correct phases") - require.Equal(t, []ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ - *ocv1ac.ClusterExtensionRevisionPhase(). + require.Equal(t, []ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ + *ocv1ac.ClusterObjectSetPhase(). WithName(string(applier.PhaseInfrastructure)). WithObjects( - ocv1ac.ClusterExtensionRevisionObject(). + ocv1ac.ClusterObjectSetObject(). WithObject(unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", @@ -248,10 +248,10 @@ func Test_SimpleRevisionGenerator_GenerateRevision(t *testing.T) { }, }), ), - *ocv1ac.ClusterExtensionRevisionPhase(). + *ocv1ac.ClusterObjectSetPhase(). WithName(string(applier.PhaseDeploy)). WithObjects( - ocv1ac.ClusterExtensionRevisionObject(). + ocv1ac.ClusterObjectSetObject(). WithObject(unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "apps/v1", @@ -539,7 +539,7 @@ func TestBoxcutter_Apply(t *testing.T) { UID: "test-uid", }, } - defaultDesiredRevision := &ocv1.ClusterExtensionRevision{ + defaultDesiredRevision := &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-ext-1", UID: "rev-uid-1", @@ -547,12 +547,12 @@ func TestBoxcutter_Apply(t *testing.T) { labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ + Spec: ocv1.ClusterObjectSetSpec{ Revision: 1, - Phases: []ocv1.ClusterExtensionRevisionPhase{ + Phases: []ocv1.ClusterObjectSetPhase{ { Name: string(applier.PhaseDeploy), - Objects: []ocv1.ClusterExtensionRevisionObject{ + Objects: []ocv1.ClusterObjectSetObject{ { Object: unstructured.Unstructured{ Object: map[string]interface{}{ @@ -573,14 +573,14 @@ func TestBoxcutter_Apply(t *testing.T) { allowedRevisionValue := func(revNum int64) *interceptor.Funcs { return &interceptor.Funcs{ Apply: func(ctx context.Context, client client.WithWatch, obj runtime.ApplyConfiguration, opts ...client.ApplyOption) error { - cer, ok := obj.(*ocv1ac.ClusterExtensionRevisionApplyConfiguration) + cos, ok := obj.(*ocv1ac.ClusterObjectSetApplyConfiguration) if !ok { - return fmt.Errorf("expected ClusterExtensionRevisionApplyConfiguration, got %T", obj) + return fmt.Errorf("expected ClusterObjectSetApplyConfiguration, got %T", obj) } - if cer.Spec == nil || cer.Spec.Revision == nil || *cer.Spec.Revision != revNum { - gk := ocv1.SchemeGroupVersion.WithKind("ClusterExtensionRevision").GroupKind() + if cos.Spec == nil || cos.Spec.Revision == nil || *cos.Spec.Revision != revNum { + gk := ocv1.SchemeGroupVersion.WithKind("ClusterObjectSet").GroupKind() name := "" - if n := cer.GetName(); n != nil { + if n := cos.GetName(); n != nil { name = *n } return apierrors.NewInvalid(gk, name, field.ErrorList{field.Invalid(field.NewPath("spec.phases"), "immutable", "spec.phases is immutable")}) @@ -591,7 +591,7 @@ func TestBoxcutter_Apply(t *testing.T) { } testCases := []struct { name string - mockBuilder applier.ClusterExtensionRevisionGenerator + mockBuilder applier.ClusterObjectSetGenerator existingObjs []client.Object expectedErr string validate func(t *testing.T, c client.Client) @@ -600,18 +600,18 @@ func TestBoxcutter_Apply(t *testing.T) { { name: "first revision", mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { - return ocv1ac.ClusterExtensionRevision(""). + makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). WithAnnotations(revisionAnnotations). WithLabels(map[string]string{ labels.OwnerNameKey: ext.Name, }). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec(). + WithSpec(ocv1ac.ClusterObjectSetSpec(). WithPhases( - ocv1ac.ClusterExtensionRevisionPhase(). + ocv1ac.ClusterObjectSetPhase(). WithName(string(applier.PhaseDeploy)). WithObjects( - ocv1ac.ClusterExtensionRevisionObject(). + ocv1ac.ClusterObjectSetObject(). WithObject(unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", @@ -627,7 +627,7 @@ func TestBoxcutter_Apply(t *testing.T) { }, }, validate: func(t *testing.T, c client.Client) { - revList := &ocv1.ClusterExtensionRevisionList{} + revList := &ocv1.ClusterObjectSetList{} err := c.List(t.Context(), revList, client.MatchingLabels{labels.OwnerNameKey: ext.Name}) require.NoError(t, err) require.Len(t, revList.Items, 1) @@ -643,18 +643,18 @@ func TestBoxcutter_Apply(t *testing.T) { { name: "no change, revision exists", mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { - return ocv1ac.ClusterExtensionRevision(""). + makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). WithAnnotations(revisionAnnotations). WithLabels(map[string]string{ labels.OwnerNameKey: ext.Name, }). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec(). + WithSpec(ocv1ac.ClusterObjectSetSpec(). WithPhases( - ocv1ac.ClusterExtensionRevisionPhase(). + ocv1ac.ClusterObjectSetPhase(). WithName(string(applier.PhaseDeploy)). WithObjects( - ocv1ac.ClusterExtensionRevisionObject(). + ocv1ac.ClusterObjectSetObject(). WithObject(unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", @@ -673,7 +673,7 @@ func TestBoxcutter_Apply(t *testing.T) { defaultDesiredRevision, }, validate: func(t *testing.T, c client.Client) { - revList := &ocv1.ClusterExtensionRevisionList{} + revList := &ocv1.ClusterObjectSetList{} err := c.List(context.Background(), revList, client.MatchingLabels{labels.OwnerNameKey: ext.Name}) require.NoError(t, err) // No new revision should be created @@ -684,18 +684,18 @@ func TestBoxcutter_Apply(t *testing.T) { { name: "new revision created when objects in new revision are different", mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { - return ocv1ac.ClusterExtensionRevision(""). + makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). WithAnnotations(revisionAnnotations). WithLabels(map[string]string{ labels.OwnerNameKey: ext.Name, }). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec(). + WithSpec(ocv1ac.ClusterObjectSetSpec(). WithPhases( - ocv1ac.ClusterExtensionRevisionPhase(). + ocv1ac.ClusterObjectSetPhase(). WithName(string(applier.PhaseDeploy)). WithObjects( - ocv1ac.ClusterExtensionRevisionObject(). + ocv1ac.ClusterObjectSetObject(). WithObject(unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", @@ -715,13 +715,13 @@ func TestBoxcutter_Apply(t *testing.T) { defaultDesiredRevision, }, validate: func(t *testing.T, c client.Client) { - revList := &ocv1.ClusterExtensionRevisionList{} + revList := &ocv1.ClusterObjectSetList{} err := c.List(context.Background(), revList, client.MatchingLabels{labels.OwnerNameKey: ext.Name}) require.NoError(t, err) require.Len(t, revList.Items, 2) // Find the new revision (rev 2) - var newRev ocv1.ClusterExtensionRevision + var newRev ocv1.ClusterObjectSet for _, r := range revList.Items { if r.Spec.Revision == 2 { newRev = r @@ -737,14 +737,14 @@ func TestBoxcutter_Apply(t *testing.T) { { name: "error from GenerateRevision", mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { + makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { return nil, errors.New("render boom") }, }, expectedErr: "render boom", validate: func(t *testing.T, c client.Client) { // Ensure no revisions were created - revList := &ocv1.ClusterExtensionRevisionList{} + revList := &ocv1.ClusterObjectSetList{} err := c.List(context.Background(), revList, client.MatchingLabels{labels.OwnerNameKey: ext.Name}) require.NoError(t, err) assert.Empty(t, revList.Items) @@ -753,217 +753,217 @@ func TestBoxcutter_Apply(t *testing.T) { { name: "keep at most 5 past revisions", mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { - return ocv1ac.ClusterExtensionRevision(""). + makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). WithAnnotations(revisionAnnotations). WithLabels(map[string]string{ labels.OwnerNameKey: ext.Name, }). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec()), nil + WithSpec(ocv1ac.ClusterObjectSetSpec()), nil }, }, existingObjs: []client.Object{ - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-1", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateArchived, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateArchived, Revision: 1, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-2", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateArchived, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateArchived, Revision: 2, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-3", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateArchived, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateArchived, Revision: 3, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-4", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateArchived, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateArchived, Revision: 4, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-5", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateArchived, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateArchived, Revision: 5, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-6", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateArchived, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateArchived, Revision: 6, }, }, }, clientIterceptor: allowedRevisionValue(7), validate: func(t *testing.T, c client.Client) { - rev1 := &ocv1.ClusterExtensionRevision{} + rev1 := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{Name: "rev-1"}, rev1) require.Error(t, err) assert.True(t, apierrors.IsNotFound(err)) // Verify garbage collection: should only keep the limit + 1 (current) revisions - revList := &ocv1.ClusterExtensionRevisionList{} + revList := &ocv1.ClusterObjectSetList{} err = c.List(t.Context(), revList) require.NoError(t, err) - // Should have ClusterExtensionRevisionRetentionLimit (5) + current (1) = 6 revisions max - assert.LessOrEqual(t, len(revList.Items), applier.ClusterExtensionRevisionRetentionLimit+1) + // Should have ClusterObjectSetRetentionLimit (5) + current (1) = 6 revisions max + assert.LessOrEqual(t, len(revList.Items), applier.ClusterObjectSetRetentionLimit+1) }, }, { name: "keep active revisions when they are out of limit", mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { - return ocv1ac.ClusterExtensionRevision(""). + makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). WithAnnotations(revisionAnnotations). WithLabels(map[string]string{ labels.OwnerNameKey: ext.Name, }). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec()), nil + WithSpec(ocv1ac.ClusterObjectSetSpec()), nil }, }, existingObjs: []client.Object{ - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-1", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateArchived, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateArchived, Revision: 1, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-2", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ + Spec: ocv1.ClusterObjectSetSpec{ // index beyond the retention limit but active; should be preserved - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateActive, + LifecycleState: ocv1.ClusterObjectSetLifecycleStateActive, Revision: 2, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-3", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateActive, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateActive, Revision: 3, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-4", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ + Spec: ocv1.ClusterObjectSetSpec{ // archived but should be preserved since it is within the limit - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateArchived, + LifecycleState: ocv1.ClusterObjectSetLifecycleStateArchived, Revision: 4, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-5", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateActive, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateActive, Revision: 5, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-6", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateActive, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateActive, Revision: 6, }, }, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "rev-7", Labels: map[string]string{ labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateActive, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateActive, Revision: 7, }, }, }, clientIterceptor: allowedRevisionValue(8), validate: func(t *testing.T, c client.Client) { - rev1 := &ocv1.ClusterExtensionRevision{} + rev1 := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{Name: "rev-1"}, rev1) require.Error(t, err) assert.True(t, apierrors.IsNotFound(err)) - rev2 := &ocv1.ClusterExtensionRevision{} + rev2 := &ocv1.ClusterObjectSet{} err = c.Get(t.Context(), client.ObjectKey{Name: "rev-2"}, rev2) require.NoError(t, err) // Verify active revisions are kept even if beyond the limit - rev4 := &ocv1.ClusterExtensionRevision{} + rev4 := &ocv1.ClusterObjectSet{} err = c.Get(t.Context(), client.ObjectKey{Name: "rev-4"}, rev4) require.NoError(t, err, "active revision 4 should still exist even though it's beyond the limit") }, @@ -971,18 +971,18 @@ func TestBoxcutter_Apply(t *testing.T) { { name: "annotation-only update (same phases, different annotations)", mockBuilder: &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { - return ocv1ac.ClusterExtensionRevision(""). + makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). WithAnnotations(revisionAnnotations). WithLabels(map[string]string{ labels.OwnerNameKey: ext.Name, }). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec(). + WithSpec(ocv1ac.ClusterObjectSetSpec(). WithPhases( - ocv1ac.ClusterExtensionRevisionPhase(). + ocv1ac.ClusterObjectSetPhase(). WithName(string(applier.PhaseDeploy)). WithObjects( - ocv1ac.ClusterExtensionRevisionObject(). + ocv1ac.ClusterObjectSetObject(). WithObject(unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", @@ -999,7 +999,7 @@ func TestBoxcutter_Apply(t *testing.T) { }, existingObjs: []client.Object{ ext, - &ocv1.ClusterExtensionRevision{ + &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-ext-1", Annotations: map[string]string{ @@ -1010,12 +1010,12 @@ func TestBoxcutter_Apply(t *testing.T) { labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ + Spec: ocv1.ClusterObjectSetSpec{ Revision: 1, - Phases: []ocv1.ClusterExtensionRevisionPhase{ + Phases: []ocv1.ClusterObjectSetPhase{ { Name: string(applier.PhaseDeploy), - Objects: []ocv1.ClusterExtensionRevisionObject{ + Objects: []ocv1.ClusterObjectSetObject{ { Object: unstructured.Unstructured{ Object: map[string]interface{}{ @@ -1034,7 +1034,7 @@ func TestBoxcutter_Apply(t *testing.T) { }, }, validate: func(t *testing.T, c client.Client) { - revList := &ocv1.ClusterExtensionRevisionList{} + revList := &ocv1.ClusterObjectSetList{} err := c.List(context.Background(), revList, client.MatchingLabels{labels.OwnerNameKey: ext.Name}) require.NoError(t, err) // Should still be only 1 revision (in-place update, not new revision) @@ -1128,14 +1128,14 @@ func Test_PreAuthorizer_Integration(t *testing.T) { } fakeClient := fake.NewClientBuilder().WithScheme(testScheme).Build() dummyGenerator := &mockBundleRevisionBuilder{ - makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotation map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { - return ocv1ac.ClusterExtensionRevision(""). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec(). + makeRevisionFunc: func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotation map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { + return ocv1ac.ClusterObjectSet(""). + WithSpec(ocv1ac.ClusterObjectSetSpec(). WithPhases( - ocv1ac.ClusterExtensionRevisionPhase(). + ocv1ac.ClusterObjectSetPhase(). WithName("some-phase"). WithObjects( - ocv1ac.ClusterExtensionRevisionObject(). + ocv1ac.ClusterObjectSetObject(). WithObject(unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", @@ -1178,7 +1178,7 @@ func Test_PreAuthorizer_Integration(t *testing.T) { Name: "test-ext-1", APIGroup: "olm.operatorframework.io", APIVersion: "v1", - Resource: "clusterextensionrevisions/finalizers", + Resource: "clusterobjectsets/finalizers", ResourceRequest: true, Verb: "update", }, perms[0]) @@ -1319,23 +1319,23 @@ func TestBoxcutterStorageMigrator(t *testing.T) { } client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionList"), mock.Anything). + On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). Return(nil) client. - On("Apply", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionApplyConfiguration"), mock.Anything). + On("Apply", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetApplyConfiguration"), mock.Anything). Once(). Run(func(args mock.Arguments) { // Verify the migration marker label is set before apply - rev := args.Get(1).(*ocv1ac.ClusterExtensionRevisionApplyConfiguration) + rev := args.Get(1).(*ocv1ac.ClusterObjectSetApplyConfiguration) require.Equal(t, "true", rev.Labels[labels.MigratedFromHelmKey], "Migration marker label should be set") }). Return(nil) client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevision"), mock.Anything). + On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). Once(). Run(func(args mock.Arguments) { // Simulate Get() returning the created revision with server-managed fields - rev := args.Get(2).(*ocv1.ClusterExtensionRevision) + rev := args.Get(2).(*ocv1.ClusterObjectSet) rev.Name = "test-revision" rev.Generation = 1 rev.ResourceVersion = "1" @@ -1355,10 +1355,10 @@ func TestBoxcutterStorageMigrator(t *testing.T) { require.True(t, statusWriter.updateCalled, "Status().Update() should be called during migration") require.NotNil(t, statusWriter.updatedObj, "Updated object should not be nil") - rev, ok := statusWriter.updatedObj.(*ocv1.ClusterExtensionRevision) - require.True(t, ok, "Updated object should be a ClusterExtensionRevision") + rev, ok := statusWriter.updatedObj.(*ocv1.ClusterObjectSet) + require.True(t, ok, "Updated object should be a ClusterObjectSet") - succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded) + succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) require.NotNil(t, succeededCond, "Succeeded condition should be set") assert.Equal(t, metav1.ConditionTrue, succeededCond.Status, "Succeeded condition should be True") assert.Equal(t, ocv1.ReasonSucceeded, succeededCond.Reason, "Reason should be Succeeded") @@ -1385,7 +1385,7 @@ func TestBoxcutterStorageMigrator(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test123"}, } - existingRev := ocv1.ClusterExtensionRevision{ + existingRev := ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-revision", Generation: 2, @@ -1393,13 +1393,13 @@ func TestBoxcutterStorageMigrator(t *testing.T) { labels.MigratedFromHelmKey: "true", }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ + Spec: ocv1.ClusterObjectSetSpec{ Revision: 1, // Migration creates revision 1 }, - Status: ocv1.ClusterExtensionRevisionStatus{ + Status: ocv1.ClusterObjectSetStatus{ Conditions: []metav1.Condition{ { - Type: ocv1.ClusterExtensionRevisionTypeSucceeded, + Type: ocv1.ClusterObjectSetTypeSucceeded, Status: metav1.ConditionTrue, Reason: ocv1.ReasonSucceeded, }, @@ -1408,10 +1408,10 @@ func TestBoxcutterStorageMigrator(t *testing.T) { } client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionList"), mock.Anything). + On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). Run(func(args mock.Arguments) { - list := args.Get(1).(*ocv1.ClusterExtensionRevisionList) - list.Items = []ocv1.ClusterExtensionRevision{existingRev} + list := args.Get(1).(*ocv1.ClusterObjectSetList) + list.Items = []ocv1.ClusterObjectSet{existingRev} }). Return(nil) @@ -1440,7 +1440,7 @@ func TestBoxcutterStorageMigrator(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "test123"}, } - existingRev := ocv1.ClusterExtensionRevision{ + existingRev := ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-revision", Generation: 2, @@ -1448,24 +1448,24 @@ func TestBoxcutterStorageMigrator(t *testing.T) { labels.MigratedFromHelmKey: "true", }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ + Spec: ocv1.ClusterObjectSetSpec{ Revision: 1, // Migration creates revision 1 }, // Status is empty - simulating the case where creation succeeded but status update failed } client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionList"), mock.Anything). + On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). Run(func(args mock.Arguments) { - list := args.Get(1).(*ocv1.ClusterExtensionRevisionList) - list.Items = []ocv1.ClusterExtensionRevision{existingRev} + list := args.Get(1).(*ocv1.ClusterObjectSetList) + list.Items = []ocv1.ClusterObjectSet{existingRev} }). Return(nil) client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevision"), mock.Anything). + On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). Run(func(args mock.Arguments) { - rev := args.Get(2).(*ocv1.ClusterExtensionRevision) + rev := args.Get(2).(*ocv1.ClusterObjectSet) *rev = existingRev }). Return(nil) @@ -1480,10 +1480,10 @@ func TestBoxcutterStorageMigrator(t *testing.T) { require.True(t, statusWriter.updateCalled, "Status().Update() should be called to set missing status") require.NotNil(t, statusWriter.updatedObj, "Updated object should not be nil") - rev, ok := statusWriter.updatedObj.(*ocv1.ClusterExtensionRevision) - require.True(t, ok, "Updated object should be a ClusterExtensionRevision") + rev, ok := statusWriter.updatedObj.(*ocv1.ClusterObjectSet) + require.True(t, ok, "Updated object should be a ClusterObjectSet") - succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded) + succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) require.NotNil(t, succeededCond, "Succeeded condition should be set") assert.Equal(t, metav1.ConditionTrue, succeededCond.Status, "Succeeded condition should be True") assert.Equal(t, ocv1.ReasonSucceeded, succeededCond.Reason, "Reason should be Succeeded") @@ -1510,7 +1510,7 @@ func TestBoxcutterStorageMigrator(t *testing.T) { // Migrated revision with Succeeded=False (e.g., from a previous failed status update attempt) // This simulates a revision whose Succeeded condition should be corrected from False to True during migration. - existingRev := ocv1.ClusterExtensionRevision{ + existingRev := ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-revision", Generation: 2, @@ -1518,13 +1518,13 @@ func TestBoxcutterStorageMigrator(t *testing.T) { labels.MigratedFromHelmKey: "true", }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ + Spec: ocv1.ClusterObjectSetSpec{ Revision: 1, }, - Status: ocv1.ClusterExtensionRevisionStatus{ + Status: ocv1.ClusterObjectSetStatus{ Conditions: []metav1.Condition{ { - Type: ocv1.ClusterExtensionRevisionTypeSucceeded, + Type: ocv1.ClusterObjectSetTypeSucceeded, Status: metav1.ConditionFalse, // Important: False, not missing Reason: "InProgress", }, @@ -1533,17 +1533,17 @@ func TestBoxcutterStorageMigrator(t *testing.T) { } client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionList"), mock.Anything). + On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). Run(func(args mock.Arguments) { - list := args.Get(1).(*ocv1.ClusterExtensionRevisionList) - list.Items = []ocv1.ClusterExtensionRevision{existingRev} + list := args.Get(1).(*ocv1.ClusterObjectSetList) + list.Items = []ocv1.ClusterObjectSet{existingRev} }). Return(nil) client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevision"), mock.Anything). + On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). Run(func(args mock.Arguments) { - rev := args.Get(2).(*ocv1.ClusterExtensionRevision) + rev := args.Get(2).(*ocv1.ClusterObjectSet) *rev = existingRev }). Return(nil) @@ -1558,10 +1558,10 @@ func TestBoxcutterStorageMigrator(t *testing.T) { require.True(t, statusWriter.updateCalled, "Status().Update() should be called to update False to True") require.NotNil(t, statusWriter.updatedObj, "Updated object should not be nil") - rev, ok := statusWriter.updatedObj.(*ocv1.ClusterExtensionRevision) - require.True(t, ok, "Updated object should be a ClusterExtensionRevision") + rev, ok := statusWriter.updatedObj.(*ocv1.ClusterObjectSet) + require.True(t, ok, "Updated object should be a ClusterObjectSet") - succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded) + succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) require.NotNil(t, succeededCond, "Succeeded condition should be set") assert.Equal(t, metav1.ConditionTrue, succeededCond.Status, "Succeeded condition should be updated to True") assert.Equal(t, ocv1.ReasonSucceeded, succeededCond.Reason, "Reason should be Succeeded") @@ -1588,31 +1588,31 @@ func TestBoxcutterStorageMigrator(t *testing.T) { // Revision 1 created by normal Boxcutter operation (no migration label) // This simulates the first rollout - status should NOT be set as it may still be in progress - existingRev := ocv1.ClusterExtensionRevision{ + existingRev := ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-revision", Generation: 2, // No migration label - this is a normal Boxcutter revision }, - Spec: ocv1.ClusterExtensionRevisionSpec{ + Spec: ocv1.ClusterObjectSetSpec{ Revision: 1, }, } client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionList"), mock.Anything). + On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). Run(func(args mock.Arguments) { - list := args.Get(1).(*ocv1.ClusterExtensionRevisionList) - list.Items = []ocv1.ClusterExtensionRevision{existingRev} + list := args.Get(1).(*ocv1.ClusterObjectSetList) + list.Items = []ocv1.ClusterObjectSet{existingRev} }). Return(nil) // The migration flow calls Get() to re-fetch the revision before checking its status. // Even for non-migrated revisions, Get() is called to determine if status needs to be set. client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevision"), mock.Anything). + On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). Run(func(args mock.Arguments) { - rev := args.Get(2).(*ocv1.ClusterExtensionRevision) + rev := args.Get(2).(*ocv1.ClusterObjectSet) *rev = existingRev }). Return(nil) @@ -1670,23 +1670,23 @@ func TestBoxcutterStorageMigrator(t *testing.T) { } client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionList"), mock.Anything). + On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). Return(nil) client. - On("Apply", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionApplyConfiguration"), mock.Anything). + On("Apply", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetApplyConfiguration"), mock.Anything). Once(). Run(func(args mock.Arguments) { // Verify the migration marker label is set before apply - rev := args.Get(1).(*ocv1ac.ClusterExtensionRevisionApplyConfiguration) + rev := args.Get(1).(*ocv1ac.ClusterObjectSetApplyConfiguration) require.Equal(t, "true", rev.Labels[labels.MigratedFromHelmKey], "Migration marker label should be set") }). Return(nil) client. - On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevision"), mock.Anything). + On("Get", mock.Anything, mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSet"), mock.Anything). Run(func(args mock.Arguments) { - rev := args.Get(2).(*ocv1.ClusterExtensionRevision) + rev := args.Get(2).(*ocv1.ClusterObjectSet) rev.ObjectMeta.Name = "test-revision" rev.ObjectMeta.Generation = 1 rev.ObjectMeta.ResourceVersion = "1" @@ -1711,10 +1711,10 @@ func TestBoxcutterStorageMigrator(t *testing.T) { require.True(t, statusWriter.updateCalled, "Status().Update() should be called during migration") require.NotNil(t, statusWriter.updatedObj, "Updated object should not be nil") - rev, ok := statusWriter.updatedObj.(*ocv1.ClusterExtensionRevision) - require.True(t, ok, "Updated object should be a ClusterExtensionRevision") + rev, ok := statusWriter.updatedObj.(*ocv1.ClusterObjectSet) + require.True(t, ok, "Updated object should be a ClusterObjectSet") - succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded) + succeededCond := apimeta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) require.NotNil(t, succeededCond, "Succeeded condition should be set") assert.Equal(t, metav1.ConditionTrue, succeededCond.Status, "Succeeded condition should be True") }) @@ -1756,7 +1756,7 @@ func TestBoxcutterStorageMigrator(t *testing.T) { } client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionList"), mock.Anything). + On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). Return(nil) err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) @@ -1789,7 +1789,7 @@ func TestBoxcutterStorageMigrator(t *testing.T) { } client. - On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterExtensionRevisionList"), mock.Anything). + On("List", mock.Anything, mock.AnythingOfType("*v1.ClusterObjectSetList"), mock.Anything). Return(nil) err := sm.Migrate(t.Context(), ext, map[string]string{"my-label": "my-value"}) @@ -1799,14 +1799,14 @@ func TestBoxcutterStorageMigrator(t *testing.T) { }) } -// mockBundleRevisionBuilder is a mock implementation of the ClusterExtensionRevisionGenerator for testing. +// mockBundleRevisionBuilder is a mock implementation of the ClusterObjectSetGenerator for testing. type mockBundleRevisionBuilder struct { - makeRevisionFunc func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotation map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) + makeRevisionFunc func(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotation map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) generateRevisionFromHelmReleaseCalled bool helmReleaseUsed *release.Release } -func (m *mockBundleRevisionBuilder) GenerateRevision(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { +func (m *mockBundleRevisionBuilder) GenerateRevision(ctx context.Context, bundleFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { return m.makeRevisionFunc(ctx, bundleFS, ext, objectLabels, revisionAnnotations) } @@ -1814,14 +1814,14 @@ func (m *mockBundleRevisionBuilder) GenerateRevisionFromHelmRelease( ctx context.Context, helmRelease *release.Release, ext *ocv1.ClusterExtension, objectLabels map[string]string, -) (*ocv1ac.ClusterExtensionRevisionApplyConfiguration, error) { +) (*ocv1ac.ClusterObjectSetApplyConfiguration, error) { m.generateRevisionFromHelmReleaseCalled = true m.helmReleaseUsed = helmRelease - return ocv1ac.ClusterExtensionRevision("test-revision"). + return ocv1ac.ClusterObjectSet("test-revision"). WithLabels(map[string]string{ labels.OwnerNameKey: ext.Name, }). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec()), nil + WithSpec(ocv1ac.ClusterObjectSetSpec()), nil } type clientMock struct { diff --git a/internal/operator-controller/applier/externalize_test.go b/internal/operator-controller/applier/externalize_test.go index 7e4cfcfd06..98f165ba11 100644 --- a/internal/operator-controller/applier/externalize_test.go +++ b/internal/operator-controller/applier/externalize_test.go @@ -19,9 +19,9 @@ func TestExtractPhasesForPacking(t *testing.T) { }} cp := ocv1.CollisionProtectionPrevent - acPhases := []ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ - *ocv1ac.ClusterExtensionRevisionPhase().WithName("deploy").WithObjects( - ocv1ac.ClusterExtensionRevisionObject().WithObject(obj).WithCollisionProtection(cp), + acPhases := []ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ + *ocv1ac.ClusterObjectSetPhase().WithName("deploy").WithObjects( + ocv1ac.ClusterObjectSetObject().WithObject(obj).WithCollisionProtection(cp), ), } @@ -46,12 +46,12 @@ func TestReplaceInlineWithRefs(t *testing.T) { "metadata": map[string]interface{}{"name": "cm2"}, }} - rev := ocv1ac.ClusterExtensionRevision("test-rev"). - WithSpec(ocv1ac.ClusterExtensionRevisionSpec(). + rev := ocv1ac.ClusterObjectSet("test-rev"). + WithSpec(ocv1ac.ClusterObjectSetSpec(). WithPhases( - ocv1ac.ClusterExtensionRevisionPhase().WithName("deploy").WithObjects( - ocv1ac.ClusterExtensionRevisionObject().WithObject(obj1), - ocv1ac.ClusterExtensionRevisionObject().WithObject(obj2), + ocv1ac.ClusterObjectSetPhase().WithName("deploy").WithObjects( + ocv1ac.ClusterObjectSetObject().WithObject(obj1), + ocv1ac.ClusterObjectSetObject().WithObject(obj2), ), ), ) @@ -82,7 +82,7 @@ func TestReplaceInlineWithRefs(t *testing.T) { } func TestReplaceInlineWithRefs_NilSpec(t *testing.T) { - rev := ocv1ac.ClusterExtensionRevision("test-rev") + rev := ocv1ac.ClusterObjectSet("test-rev") pack := &PackResult{ Refs: map[[2]int]ocv1.ObjectSourceRef{}, } diff --git a/internal/operator-controller/applier/phase.go b/internal/operator-controller/applier/phase.go index 7a495ee5aa..02ee8decf8 100644 --- a/internal/operator-controller/applier/phase.go +++ b/internal/operator-controller/applier/phase.go @@ -163,7 +163,7 @@ func init() { // to ensure consistent ordering regardless of input order. This is critical for // Helm-to-Boxcutter migration where the same resources may come from different sources // (Helm release manifest vs bundle manifest) and need to produce identical phases. -func compareClusterExtensionRevisionObjectApplyConfigurations(a, b ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration) int { +func compareClusterObjectSetObjectApplyConfigurations(a, b ocv1ac.ClusterObjectSetObjectApplyConfiguration) int { aGVK := a.Object.GroupVersionKind() bGVK := b.Object.GroupVersionKind() @@ -179,9 +179,9 @@ func compareClusterExtensionRevisionObjectApplyConfigurations(a, b ocv1ac.Cluste // PhaseSort takes an unsorted list of objects and organizes them into sorted phases. // Each phase will be applied in order according to DefaultPhaseOrder. Objects // within a single phase are applied simultaneously. -func PhaseSort(unsortedObjs []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration) []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration { - phasesSorted := make([]*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration, 0) - phaseMap := make(map[Phase][]ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration) +func PhaseSort(unsortedObjs []ocv1ac.ClusterObjectSetObjectApplyConfiguration) []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration { + phasesSorted := make([]*ocv1ac.ClusterObjectSetPhaseApplyConfiguration, 0) + phaseMap := make(map[Phase][]ocv1ac.ClusterObjectSetObjectApplyConfiguration) for _, obj := range unsortedObjs { phase := determinePhase(obj.Object.GroupVersionKind().GroupKind()) @@ -191,14 +191,14 @@ func PhaseSort(unsortedObjs []ocv1ac.ClusterExtensionRevisionObjectApplyConfigur for _, phaseName := range defaultPhaseOrder { if objs, ok := phaseMap[phaseName]; ok { // Sort objects within the phase deterministically - slices.SortFunc(objs, compareClusterExtensionRevisionObjectApplyConfigurations) + slices.SortFunc(objs, compareClusterObjectSetObjectApplyConfigurations) // Convert to pointers for WithObjects - objPtrs := make([]*ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration, len(objs)) + objPtrs := make([]*ocv1ac.ClusterObjectSetObjectApplyConfiguration, len(objs)) for i := range objs { objPtrs[i] = &objs[i] } - phasesSorted = append(phasesSorted, ocv1ac.ClusterExtensionRevisionPhase(). + phasesSorted = append(phasesSorted, ocv1ac.ClusterObjectSetPhase(). WithName(string(phaseName)). WithObjects(objPtrs...)) } diff --git a/internal/operator-controller/applier/phase_test.go b/internal/operator-controller/applier/phase_test.go index 8a4554be0e..443d0007ec 100644 --- a/internal/operator-controller/applier/phase_test.go +++ b/internal/operator-controller/applier/phase_test.go @@ -19,12 +19,12 @@ import ( func Test_PhaseSort(t *testing.T) { for _, tt := range []struct { name string - objs []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration - want []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration + objs []ocv1ac.ClusterObjectSetObjectApplyConfiguration + want []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration }{ { name: "single deploy obj", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -34,10 +34,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseDeploy)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -52,7 +52,7 @@ func Test_PhaseSort(t *testing.T) { }, { name: "all phases", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -182,10 +182,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseNamespaces)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -198,7 +198,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhasePolicies)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -211,7 +211,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseIdentity)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -224,7 +224,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseConfiguration)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -237,7 +237,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseStorage)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -250,7 +250,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseCRDs)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -263,7 +263,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseRoles)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -284,7 +284,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseBindings)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -305,7 +305,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseInfrastructure)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -318,7 +318,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseDeploy)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -339,7 +339,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseScaling)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -352,7 +352,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhasePublish)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -365,7 +365,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseAdmission)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -380,7 +380,7 @@ func Test_PhaseSort(t *testing.T) { }, { name: "sorted and batched", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -406,10 +406,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseIdentity)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -422,7 +422,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseConfiguration)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -435,7 +435,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseDeploy)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -450,12 +450,12 @@ func Test_PhaseSort(t *testing.T) { }, { name: "no objects", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{}, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{}, + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{}, + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{}, }, { name: "sort by group within same phase", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -479,10 +479,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseDeploy)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -511,7 +511,7 @@ func Test_PhaseSort(t *testing.T) { }, { name: "sort by version within same group and phase", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -535,10 +535,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseDeploy)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -567,7 +567,7 @@ func Test_PhaseSort(t *testing.T) { }, { name: "sort by kind within same group, version, and phase", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -591,10 +591,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseConfiguration)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -623,7 +623,7 @@ func Test_PhaseSort(t *testing.T) { }, { name: "sort by namespace within same GVK and phase", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -661,10 +661,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseConfiguration)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -707,7 +707,7 @@ func Test_PhaseSort(t *testing.T) { }, { name: "sort by name within same GVK, namespace, and phase", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -745,10 +745,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseConfiguration)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -791,7 +791,7 @@ func Test_PhaseSort(t *testing.T) { }, { name: "comprehensive sorting - all dimensions", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -865,10 +865,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseConfiguration)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -921,7 +921,7 @@ func Test_PhaseSort(t *testing.T) { }, { Name: ptr.To(string(applier.PhaseDeploy)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -952,7 +952,7 @@ func Test_PhaseSort(t *testing.T) { }, { name: "cluster-scoped vs namespaced resources - empty namespace sorts first", - objs: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + objs: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -988,10 +988,10 @@ func Test_PhaseSort(t *testing.T) { }, }, }, - want: []*ocv1ac.ClusterExtensionRevisionPhaseApplyConfiguration{ + want: []*ocv1ac.ClusterObjectSetPhaseApplyConfiguration{ { Name: ptr.To(string(applier.PhaseRoles)), - Objects: []ocv1ac.ClusterExtensionRevisionObjectApplyConfiguration{ + Objects: []ocv1ac.ClusterObjectSetObjectApplyConfiguration{ { Object: &unstructured.Unstructured{ Object: map[string]interface{}{ diff --git a/internal/operator-controller/applier/secretpacker.go b/internal/operator-controller/applier/secretpacker.go index 7e09d77510..ac88e5267c 100644 --- a/internal/operator-controller/applier/secretpacker.go +++ b/internal/operator-controller/applier/secretpacker.go @@ -28,7 +28,7 @@ const ( gzipThreshold = 900 * 1024 ) -// SecretPacker packs serialized objects from CER phases into one or more +// SecretPacker packs serialized objects from COS phases into one or more // immutable Secrets. type SecretPacker struct { RevisionName string @@ -37,19 +37,19 @@ type SecretPacker struct { } // PackResult holds the packed Secrets and the ref entries that should -// replace inline objects in the CER phases. +// replace inline objects in the COS phases. type PackResult struct { - // Secrets to be created before the CER. + // Secrets to be created before the COS. Secrets []corev1.Secret // Refs maps (phaseIndex, objectIndex) to the ObjectSourceRef - // that should replace the inline object in the CER. + // that should replace the inline object in the COS. Refs map[[2]int]ocv1.ObjectSourceRef } -// Pack takes CER phases with inline objects and produces: +// Pack takes COS phases with inline objects and produces: // 1. A set of immutable Secrets containing the serialized objects // 2. A mapping from (phaseIdx, objIdx) to the corresponding ObjectSourceRef -func (p *SecretPacker) Pack(phases []ocv1.ClusterExtensionRevisionPhase) (*PackResult, error) { +func (p *SecretPacker) Pack(phases []ocv1.ClusterObjectSetPhase) (*PackResult, error) { result := &PackResult{ Refs: make(map[[2]int]ocv1.ObjectSourceRef), } diff --git a/internal/operator-controller/applier/secretpacker_test.go b/internal/operator-controller/applier/secretpacker_test.go index b6e44d53d4..fcd19d1823 100644 --- a/internal/operator-controller/applier/secretpacker_test.go +++ b/internal/operator-controller/applier/secretpacker_test.go @@ -32,9 +32,9 @@ func TestSecretPacker_Pack(t *testing.T) { }) t.Run("single object packs into one Secret", func(t *testing.T) { - phases := []ocv1.ClusterExtensionRevisionPhase{{ + phases := []ocv1.ClusterObjectSetPhase{{ Name: "deploy", - Objects: []ocv1.ClusterExtensionRevisionObject{{ + Objects: []ocv1.ClusterObjectSetObject{{ Object: testConfigMap("test-cm", "default"), }}, }} @@ -66,9 +66,9 @@ func TestSecretPacker_Pack(t *testing.T) { }) t.Run("multiple objects fit in one Secret", func(t *testing.T) { - phases := []ocv1.ClusterExtensionRevisionPhase{{ + phases := []ocv1.ClusterObjectSetPhase{{ Name: "deploy", - Objects: []ocv1.ClusterExtensionRevisionObject{ + Objects: []ocv1.ClusterObjectSetObject{ {Object: testConfigMap("cm-1", "default")}, {Object: testConfigMap("cm-2", "default")}, }, @@ -82,14 +82,14 @@ func TestSecretPacker_Pack(t *testing.T) { }) t.Run("objects across multiple phases", func(t *testing.T) { - phases := []ocv1.ClusterExtensionRevisionPhase{ + phases := []ocv1.ClusterObjectSetPhase{ { Name: "crds", - Objects: []ocv1.ClusterExtensionRevisionObject{{Object: testConfigMap("crd-1", "")}}, + Objects: []ocv1.ClusterObjectSetObject{{Object: testConfigMap("crd-1", "")}}, }, { Name: "deploy", - Objects: []ocv1.ClusterExtensionRevisionObject{{Object: testConfigMap("deploy-1", "ns")}}, + Objects: []ocv1.ClusterObjectSetObject{{Object: testConfigMap("deploy-1", "ns")}}, }, } @@ -109,9 +109,9 @@ func TestSecretPacker_Pack(t *testing.T) { }) t.Run("deterministic: same input produces same output", func(t *testing.T) { - phases := []ocv1.ClusterExtensionRevisionPhase{{ + phases := []ocv1.ClusterObjectSetPhase{{ Name: "deploy", - Objects: []ocv1.ClusterExtensionRevisionObject{ + Objects: []ocv1.ClusterObjectSetObject{ {Object: testConfigMap("cm-1", "default")}, }, }} @@ -127,9 +127,9 @@ func TestSecretPacker_Pack(t *testing.T) { }) t.Run("skips ref-only objects", func(t *testing.T) { - phases := []ocv1.ClusterExtensionRevisionPhase{{ + phases := []ocv1.ClusterObjectSetPhase{{ Name: "deploy", - Objects: []ocv1.ClusterExtensionRevisionObject{ + Objects: []ocv1.ClusterObjectSetObject{ {Ref: ocv1.ObjectSourceRef{Name: "existing-secret", Namespace: "ns", Key: "somekey"}}, }, }} @@ -148,9 +148,9 @@ func TestSecretPacker_Pack(t *testing.T) { "bigkey": strings.Repeat("a", gzipThreshold+1), } - phases := []ocv1.ClusterExtensionRevisionPhase{{ + phases := []ocv1.ClusterObjectSetPhase{{ Name: "deploy", - Objects: []ocv1.ClusterExtensionRevisionObject{{Object: largeObj}}, + Objects: []ocv1.ClusterObjectSetObject{{Object: largeObj}}, }} result, err := packer.Pack(phases) @@ -174,9 +174,9 @@ func TestSecretPacker_Pack(t *testing.T) { t.Run("duplicate content objects share key and do not inflate size", func(t *testing.T) { // Two identical objects should produce the same content hash key. // The second occurrence must not double-count the size. - phases := []ocv1.ClusterExtensionRevisionPhase{{ + phases := []ocv1.ClusterObjectSetPhase{{ Name: "deploy", - Objects: []ocv1.ClusterExtensionRevisionObject{ + Objects: []ocv1.ClusterObjectSetObject{ {Object: testConfigMap("same-cm", "default")}, {Object: testConfigMap("same-cm", "default")}, }, @@ -201,9 +201,9 @@ func TestSecretPacker_Pack(t *testing.T) { expectedHash := sha256.Sum256(rawData) expectedKey := base64.RawURLEncoding.EncodeToString(expectedHash[:]) - phases := []ocv1.ClusterExtensionRevisionPhase{{ + phases := []ocv1.ClusterObjectSetPhase{{ Name: "deploy", - Objects: []ocv1.ClusterExtensionRevisionObject{{Object: obj}}, + Objects: []ocv1.ClusterObjectSetObject{{Object: obj}}, }} result, err := packer.Pack(phases) require.NoError(t, err) diff --git a/internal/operator-controller/controllers/boxcutter_reconcile_steps.go b/internal/operator-controller/controllers/boxcutter_reconcile_steps.go index dfb5dad145..63b8c7ddb2 100644 --- a/internal/operator-controller/controllers/boxcutter_reconcile_steps.go +++ b/internal/operator-controller/controllers/boxcutter_reconcile_steps.go @@ -42,19 +42,19 @@ func (d *BoxcutterRevisionStatesGetter) GetRevisionStates(ctx context.Context, e // TODO: boxcutter applier has a nearly identical bit of code for listing and sorting revisions // only difference here is that it sorts in reverse order to start iterating with the most // recent revisions. We should consolidate to avoid code duplication. - existingRevisionList := &ocv1.ClusterExtensionRevisionList{} + existingRevisionList := &ocv1.ClusterObjectSetList{} if err := d.Reader.List(ctx, existingRevisionList, client.MatchingLabels{ labels.OwnerNameKey: ext.Name, }); err != nil { return nil, fmt.Errorf("listing revisions: %w", err) } - slices.SortFunc(existingRevisionList.Items, func(a, b ocv1.ClusterExtensionRevision) int { + slices.SortFunc(existingRevisionList.Items, func(a, b ocv1.ClusterObjectSet) int { return cmp.Compare(a.Spec.Revision, b.Spec.Revision) }) rs := &RevisionStates{} for _, rev := range existingRevisionList.Items { - if rev.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived { + if rev.Spec.LifecycleState == ocv1.ClusterObjectSetLifecycleStateArchived { continue } @@ -72,7 +72,7 @@ func (d *BoxcutterRevisionStatesGetter) GetRevisionStates(ctx context.Context, e }, } - if apimeta.IsStatusConditionTrue(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded) { + if apimeta.IsStatusConditionTrue(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) { rs.Installed = rm } else { rs.RollingOut = append(rs.RollingOut, rm) @@ -128,7 +128,7 @@ func ApplyBundleWithBoxcutter(apply func(ctx context.Context, contentFS fs.FS, e ext.Status.ActiveRevisions = []ocv1.RevisionStatus{} // Mirror Available/Progressing conditions from the installed revision if i := state.revisionStates.Installed; i != nil { - for _, cndType := range []string{ocv1.ClusterExtensionRevisionTypeAvailable, ocv1.ClusterExtensionRevisionTypeProgressing} { + for _, cndType := range []string{ocv1.ClusterObjectSetTypeAvailable, ocv1.ClusterObjectSetTypeProgressing} { if cnd := apimeta.FindStatusCondition(i.Conditions, cndType); cnd != nil { cnd.ObservedGeneration = ext.GetGeneration() apimeta.SetStatusCondition(&ext.Status.Conditions, *cnd) @@ -141,7 +141,7 @@ func ApplyBundleWithBoxcutter(apply func(ctx context.Context, contentFS fs.FS, e } for idx, r := range state.revisionStates.RollingOut { rs := ocv1.RevisionStatus{Name: r.RevisionName} - for _, cndType := range []string{ocv1.ClusterExtensionRevisionTypeAvailable, ocv1.ClusterExtensionRevisionTypeProgressing} { + for _, cndType := range []string{ocv1.ClusterObjectSetTypeAvailable, ocv1.ClusterObjectSetTypeProgressing} { if cnd := apimeta.FindStatusCondition(r.Conditions, cndType); cnd != nil { cnd.ObservedGeneration = ext.GetGeneration() apimeta.SetStatusCondition(&rs.Conditions, *cnd) @@ -149,7 +149,7 @@ func ApplyBundleWithBoxcutter(apply func(ctx context.Context, contentFS fs.FS, e } // Mirror Progressing condition from the latest active revision if idx == len(state.revisionStates.RollingOut)-1 { - if pcnd := apimeta.FindStatusCondition(r.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing); pcnd != nil { + if pcnd := apimeta.FindStatusCondition(r.Conditions, ocv1.ClusterObjectSetTypeProgressing); pcnd != nil { pcnd.ObservedGeneration = ext.GetGeneration() apimeta.SetStatusCondition(&ext.Status.Conditions, *pcnd) } diff --git a/internal/operator-controller/controllers/clusterextension_reconcile_steps.go b/internal/operator-controller/controllers/clusterextension_reconcile_steps.go index 6b80c905b0..48507a2b7c 100644 --- a/internal/operator-controller/controllers/clusterextension_reconcile_steps.go +++ b/internal/operator-controller/controllers/clusterextension_reconcile_steps.go @@ -380,7 +380,7 @@ func UnpackBundle(i imageutil.Puller, cache imageutil.Cache) ReconcileStepFunc { if bundleUnchanged { // Bundle hasn't changed and Pull failed (likely cache miss + catalog unavailable). // This happens in fallback mode after catalog deletion. Set imageFS to nil so the - // applier can maintain the workload using existing Helm release or ClusterExtensionRevision. + // applier can maintain the workload using existing Helm release or ClusterObjectSet. l.V(1).Info("bundle content unavailable but version unchanged, maintaining current installation", "bundle", state.resolvedRevisionMetadata.Name, "version", state.resolvedRevisionMetadata.Version, diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller.go b/internal/operator-controller/controllers/clusterobjectset_controller.go similarity index 71% rename from internal/operator-controller/controllers/clusterextensionrevision_controller.go rename to internal/operator-controller/controllers/clusterobjectset_controller.go index 5886d16650..bca371613c 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller.go +++ b/internal/operator-controller/controllers/clusterobjectset_controller.go @@ -43,12 +43,12 @@ import ( ) const ( - clusterExtensionRevisionTeardownFinalizer = "olm.operatorframework.io/teardown" + clusterObjectSetTeardownFinalizer = "olm.operatorframework.io/teardown" ) -// ClusterExtensionRevisionReconciler actions individual snapshots of ClusterExtensions, +// ClusterObjectSetReconciler actions individual snapshots of ClusterExtensions, // as part of the boxcutter integration. -type ClusterExtensionRevisionReconciler struct { +type ClusterObjectSetReconciler struct { Client client.Client RevisionEngineFactory RevisionEngineFactory TrackingCache trackingCache @@ -62,16 +62,16 @@ type trackingCache interface { Free(ctx context.Context, user client.Object) error } -//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensionrevisions,verbs=get;list;watch;update;patch;create;delete -//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensionrevisions/status,verbs=update;patch -//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensionrevisions/finalizers,verbs=update +//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterobjectsets,verbs=get;list;watch;update;patch;create;delete +//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterobjectsets/status,verbs=update;patch +//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterobjectsets/finalizers,verbs=update //+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch -func (c *ClusterExtensionRevisionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +func (c *ClusterObjectSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { l := log.FromContext(ctx).WithName("cluster-extension-revision") ctx = log.IntoContext(ctx, l) - existingRev := &ocv1.ClusterExtensionRevision{} + existingRev := &ocv1.ClusterObjectSet{} if err := c.Client.Get(ctx, req.NamespacedName, existingRev); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } @@ -83,9 +83,9 @@ func (c *ClusterExtensionRevisionReconciler) Reconcile(ctx context.Context, req res, reconcileErr := c.reconcile(ctx, reconciledRev) if pd := existingRev.Spec.ProgressDeadlineMinutes; pd > 0 { - cnd := meta.FindStatusCondition(reconciledRev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cnd := meta.FindStatusCondition(reconciledRev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) isStillProgressing := cnd != nil && cnd.Status == metav1.ConditionTrue && cnd.Reason != ocv1.ReasonSucceeded - succeeded := meta.IsStatusConditionTrue(reconciledRev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded) + succeeded := meta.IsStatusConditionTrue(reconciledRev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) // check if we reached the progress deadline only if the revision is still progressing and has not succeeded yet if isStillProgressing && !succeeded { timeout := time.Duration(pd) * time.Minute @@ -108,7 +108,7 @@ func (c *ClusterExtensionRevisionReconciler) Reconcile(ctx context.Context, req // Do checks before any Update()s, as Update() may modify the resource structure! updateStatus := !equality.Semantic.DeepEqual(existingRev.Status, reconciledRev.Status) - unexpectedFieldsChanged := checkForUnexpectedClusterExtensionRevisionFieldChange(*existingRev, *reconciledRev) + unexpectedFieldsChanged := checkForUnexpectedClusterObjectSetFieldChange(*existingRev, *reconciledRev) if unexpectedFieldsChanged { panic("spec or metadata changed by reconciler") } @@ -127,8 +127,8 @@ func (c *ClusterExtensionRevisionReconciler) Reconcile(ctx context.Context, req } // Compare resources - ignoring status & metadata.finalizers -func checkForUnexpectedClusterExtensionRevisionFieldChange(a, b ocv1.ClusterExtensionRevision) bool { - a.Status, b.Status = ocv1.ClusterExtensionRevisionStatus{}, ocv1.ClusterExtensionRevisionStatus{} +func checkForUnexpectedClusterObjectSetFieldChange(a, b ocv1.ClusterObjectSet) bool { + a.Status, b.Status = ocv1.ClusterObjectSetStatus{}, ocv1.ClusterObjectSetStatus{} // when finalizers are updated during reconcile, we expect finalizers, managedFields, and resourceVersion // to be updated, so we ignore changes in these fields. @@ -138,54 +138,54 @@ func checkForUnexpectedClusterExtensionRevisionFieldChange(a, b ocv1.ClusterExte return !equality.Semantic.DeepEqual(a.Spec, b.Spec) } -func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer *ocv1.ClusterExtensionRevision) (ctrl.Result, error) { +func (c *ClusterObjectSetReconciler) reconcile(ctx context.Context, cos *ocv1.ClusterObjectSet) (ctrl.Result, error) { l := log.FromContext(ctx) - if !cer.DeletionTimestamp.IsZero() { - return c.delete(ctx, cer) + if !cos.DeletionTimestamp.IsZero() { + return c.delete(ctx, cos) } - phases, opts, err := c.buildBoxcutterPhases(ctx, cer) + phases, opts, err := c.buildBoxcutterPhases(ctx, cos) if err != nil { - setRetryingConditions(cer, err.Error()) + setRetryingConditions(cos, err.Error()) return ctrl.Result{}, fmt.Errorf("converting to boxcutter revision: %v", err) } - siblings, err := c.siblingRevisionNames(ctx, cer) + siblings, err := c.siblingRevisionNames(ctx, cos) if err != nil { - setRetryingConditions(cer, err.Error()) + setRetryingConditions(cos, err.Error()) return ctrl.Result{}, fmt.Errorf("listing sibling revisions: %v", err) } - revisionEngine, err := c.RevisionEngineFactory.CreateRevisionEngine(ctx, cer) + revisionEngine, err := c.RevisionEngineFactory.CreateRevisionEngine(ctx, cos) if err != nil { - setRetryingConditions(cer, err.Error()) + setRetryingConditions(cos, err.Error()) return ctrl.Result{}, fmt.Errorf("failed to create revision engine: %v", err) } revision := boxcutter.NewRevisionWithOwner( - cer.Name, - cer.Spec.Revision, + cos.Name, + cos.Spec.Revision, phases, - cer, + cos, ownerhandling.NewNative(c.Client.Scheme()), ) - if cer.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived { - if err := c.TrackingCache.Free(ctx, cer); err != nil { - markAsAvailableUnknown(cer, ocv1.ClusterExtensionRevisionReasonReconciling, err.Error()) + if cos.Spec.LifecycleState == ocv1.ClusterObjectSetLifecycleStateArchived { + if err := c.TrackingCache.Free(ctx, cos); err != nil { + markAsAvailableUnknown(cos, ocv1.ClusterObjectSetReasonReconciling, err.Error()) return ctrl.Result{}, fmt.Errorf("error stopping informers: %v", err) } - return c.archive(ctx, revisionEngine, cer, revision) + return c.archive(ctx, revisionEngine, cos, revision) } - if err := c.ensureFinalizer(ctx, cer, clusterExtensionRevisionTeardownFinalizer); err != nil { + if err := c.ensureFinalizer(ctx, cos, clusterObjectSetTeardownFinalizer); err != nil { return ctrl.Result{}, fmt.Errorf("error ensuring teardown finalizer: %v", err) } - if err := c.establishWatch(ctx, cer, revision); err != nil { + if err := c.establishWatch(ctx, cos, revision); err != nil { werr := fmt.Errorf("establish watch: %v", err) - setRetryingConditions(cer, werr.Error()) + setRetryingConditions(cos, werr.Error()) return ctrl.Result{}, werr } @@ -195,7 +195,7 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer // Log detailed reconcile reports only in debug mode (V(1)) to reduce verbosity. l.V(1).Info("reconcile report", "report", rres.String()) } - setRetryingConditions(cer, err.Error()) + setRetryingConditions(cos, err.Error()) return ctrl.Result{}, fmt.Errorf("revision reconcile: %v", err) } @@ -203,14 +203,14 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer // TODO: report status, backoff? if verr := rres.GetValidationError(); verr != nil { l.Error(fmt.Errorf("%w", verr), "preflight validation failed, retrying after 10s") - setRetryingConditions(cer, fmt.Sprintf("revision validation error: %s", verr)) + setRetryingConditions(cos, fmt.Sprintf("revision validation error: %s", verr)) return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } for i, pres := range rres.GetPhases() { if verr := pres.GetValidationError(); verr != nil { l.Error(fmt.Errorf("%w", verr), "phase preflight validation failed, retrying after 10s", "phase", i) - setRetryingConditions(cer, fmt.Sprintf("phase %d validation error: %s", i, verr)) + setRetryingConditions(cos, fmt.Sprintf("phase %d validation error: %s", i, verr)) return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } @@ -228,20 +228,20 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer if len(collidingObjs) > 0 { l.Error(fmt.Errorf("object collision detected"), "object collision, retrying after 10s", "phase", i, "collisions", collidingObjs) - setRetryingConditions(cer, fmt.Sprintf("revision object collisions in phase %d\n%s", i, strings.Join(collidingObjs, "\n\n"))) + setRetryingConditions(cos, fmt.Sprintf("revision object collisions in phase %d\n%s", i, strings.Join(collidingObjs, "\n\n"))) return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } } - revVersion := cer.GetAnnotations()[labels.BundleVersionKey] + revVersion := cos.GetAnnotations()[labels.BundleVersionKey] if rres.InTransition() { - markAsProgressing(cer, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion)) + markAsProgressing(cos, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion)) } //nolint:nestif if rres.IsComplete() { // Archive previous revisions - previous, err := c.listPreviousRevisions(ctx, cer) + previous, err := c.listPreviousRevisions(ctx, cos) if err != nil { return ctrl.Result{}, fmt.Errorf("listing previous revisions: %v", err) } @@ -249,24 +249,24 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer patch := []byte(`{"spec":{"lifecycleState":"Archived"}}`) if err := c.Client.Patch(ctx, client.Object(a), client.RawPatch(types.MergePatchType, patch)); err != nil { // TODO: It feels like an error here needs to propagate to a status _somewhere_. - // Not sure the current CER makes sense? But it also feels off to set the CE + // Not sure the current COS makes sense? But it also feels off to set the CE // status from outside the CE reconciler. return ctrl.Result{}, fmt.Errorf("archive previous Revision: %w", err) } } - markAsProgressing(cer, ocv1.ReasonSucceeded, fmt.Sprintf("Revision %s has rolled out.", revVersion)) - markAsAvailable(cer, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, "Objects are available and pass all probes.") + markAsProgressing(cos, ocv1.ReasonSucceeded, fmt.Sprintf("Revision %s has rolled out.", revVersion)) + markAsAvailable(cos, ocv1.ClusterObjectSetReasonProbesSucceeded, "Objects are available and pass all probes.") // We'll probably only want to remove this once we are done updating the ClusterExtension conditions // as its one of the interfaces between the revision and the extension. If we still have the Succeeded for now // that's fine. - meta.SetStatusCondition(&cer.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeSucceeded, + meta.SetStatusCondition(&cos.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterObjectSetTypeSucceeded, Status: metav1.ConditionTrue, Reason: ocv1.ReasonSucceeded, Message: "Revision succeeded rolling out.", - ObservedGeneration: cer.Generation, + ObservedGeneration: cos.Generation, }) } else { var probeFailureMsgs []string @@ -296,58 +296,58 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer } if len(probeFailureMsgs) > 0 { - markAsUnavailable(cer, ocv1.ClusterExtensionRevisionReasonProbeFailure, strings.Join(probeFailureMsgs, "\n")) + markAsUnavailable(cos, ocv1.ClusterObjectSetReasonProbeFailure, strings.Join(probeFailureMsgs, "\n")) } else { - markAsUnavailable(cer, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion)) + markAsUnavailable(cos, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion)) } - if meta.FindStatusCondition(cer.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) == nil { - markAsProgressing(cer, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion)) + if meta.FindStatusCondition(cos.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) == nil { + markAsProgressing(cos, ocv1.ReasonRollingOut, fmt.Sprintf("Revision %s is rolling out.", revVersion)) } } return ctrl.Result{}, nil } -func (c *ClusterExtensionRevisionReconciler) delete(ctx context.Context, cer *ocv1.ClusterExtensionRevision) (ctrl.Result, error) { - if err := c.TrackingCache.Free(ctx, cer); err != nil { - markAsAvailableUnknown(cer, ocv1.ClusterExtensionRevisionReasonReconciling, err.Error()) +func (c *ClusterObjectSetReconciler) delete(ctx context.Context, cos *ocv1.ClusterObjectSet) (ctrl.Result, error) { + if err := c.TrackingCache.Free(ctx, cos); err != nil { + markAsAvailableUnknown(cos, ocv1.ClusterObjectSetReasonReconciling, err.Error()) return ctrl.Result{}, fmt.Errorf("error stopping informers: %v", err) } - if err := c.removeFinalizer(ctx, cer, clusterExtensionRevisionTeardownFinalizer); err != nil { + if err := c.removeFinalizer(ctx, cos, clusterObjectSetTeardownFinalizer); err != nil { return ctrl.Result{}, fmt.Errorf("error removing teardown finalizer: %v", err) } return ctrl.Result{}, nil } -func (c *ClusterExtensionRevisionReconciler) archive(ctx context.Context, revisionEngine RevisionEngine, cer *ocv1.ClusterExtensionRevision, revision boxcutter.RevisionBuilder) (ctrl.Result, error) { +func (c *ClusterObjectSetReconciler) archive(ctx context.Context, revisionEngine RevisionEngine, cos *ocv1.ClusterObjectSet, revision boxcutter.RevisionBuilder) (ctrl.Result, error) { tdres, err := revisionEngine.Teardown(ctx, revision) if err != nil { err = fmt.Errorf("error archiving revision: %v", err) - setRetryingConditions(cer, err.Error()) + setRetryingConditions(cos, err.Error()) return ctrl.Result{}, err } if tdres != nil && !tdres.IsComplete() { - setRetryingConditions(cer, "removing revision resources that are not owned by another revision") + setRetryingConditions(cos, "removing revision resources that are not owned by another revision") return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } // Ensure conditions are set before removing the finalizer when archiving - if markAsArchived(cer) { + if markAsArchived(cos) { return ctrl.Result{}, nil } - if err := c.removeFinalizer(ctx, cer, clusterExtensionRevisionTeardownFinalizer); err != nil { + if err := c.removeFinalizer(ctx, cos, clusterObjectSetTeardownFinalizer); err != nil { return ctrl.Result{}, fmt.Errorf("error removing teardown finalizer: %v", err) } return ctrl.Result{}, nil } -type Sourcerer interface { +type Sourcoser interface { Source(handler handler.EventHandler, predicates ...predicate.Predicate) source.Source } -func (c *ClusterExtensionRevisionReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (c *ClusterObjectSetReconciler) SetupWithManager(mgr ctrl.Manager) error { skipProgressDeadlineExceededPredicate := predicate.Funcs{ UpdateFunc: func(e event.UpdateEvent) bool { - rev, ok := e.ObjectNew.(*ocv1.ClusterExtensionRevision) + rev, ok := e.ObjectNew.(*ocv1.ClusterObjectSet) if !ok { return true } @@ -355,7 +355,7 @@ func (c *ClusterExtensionRevisionReconciler) SetupWithManager(mgr ctrl.Manager) if !rev.DeletionTimestamp.IsZero() { return true } - if cnd := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing); cnd != nil && cnd.Status == metav1.ConditionFalse && cnd.Reason == ocv1.ReasonProgressDeadlineExceeded { + if cnd := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing); cnd != nil && cnd.Status == metav1.ConditionFalse && cnd.Reason == ocv1.ReasonProgressDeadlineExceeded { return false } return true @@ -364,7 +364,7 @@ func (c *ClusterExtensionRevisionReconciler) SetupWithManager(mgr ctrl.Manager) c.Clock = clock.RealClock{} return ctrl.NewControllerManagedBy(mgr). For( - &ocv1.ClusterExtensionRevision{}, + &ocv1.ClusterObjectSet{}, builder.WithPredicates( predicate.ResourceVersionChangedPredicate{}, skipProgressDeadlineExceededPredicate, @@ -372,14 +372,14 @@ func (c *ClusterExtensionRevisionReconciler) SetupWithManager(mgr ctrl.Manager) ). WatchesRawSource( c.TrackingCache.Source( - handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &ocv1.ClusterExtensionRevision{}), + handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &ocv1.ClusterObjectSet{}), predicate.ResourceVersionChangedPredicate{}, ), ). Complete(c) } -func (c *ClusterExtensionRevisionReconciler) establishWatch(ctx context.Context, cer *ocv1.ClusterExtensionRevision, revision boxcutter.RevisionBuilder) error { +func (c *ClusterObjectSetReconciler) establishWatch(ctx context.Context, cos *ocv1.ClusterObjectSet, revision boxcutter.RevisionBuilder) error { gvks := sets.New[schema.GroupVersionKind]() for _, phase := range revision.GetPhases() { for _, obj := range phase.GetObjects() { @@ -387,10 +387,10 @@ func (c *ClusterExtensionRevisionReconciler) establishWatch(ctx context.Context, } } - return c.TrackingCache.Watch(ctx, cer, gvks) + return c.TrackingCache.Watch(ctx, cos, gvks) } -func (c *ClusterExtensionRevisionReconciler) ensureFinalizer( +func (c *ClusterObjectSetReconciler) ensureFinalizer( ctx context.Context, obj client.Object, finalizer string, ) error { if controllerutil.ContainsFinalizer(obj, finalizer) { @@ -414,7 +414,7 @@ func (c *ClusterExtensionRevisionReconciler) ensureFinalizer( return nil } -func (c *ClusterExtensionRevisionReconciler) removeFinalizer(ctx context.Context, obj client.Object, finalizer string) error { +func (c *ClusterObjectSetReconciler) removeFinalizer(ctx context.Context, obj client.Object, finalizer string) error { if !controllerutil.ContainsFinalizer(obj, finalizer) { return nil } @@ -439,33 +439,33 @@ func (c *ClusterExtensionRevisionReconciler) removeFinalizer(ctx context.Context // listPreviousRevisions returns active revisions belonging to the same ClusterExtension with lower revision numbers. // Filters out the current revision, archived revisions, deleting revisions, and revisions with equal or higher numbers. -func (c *ClusterExtensionRevisionReconciler) listPreviousRevisions(ctx context.Context, cer *ocv1.ClusterExtensionRevision) ([]*ocv1.ClusterExtensionRevision, error) { - ownerLabel, ok := cer.Labels[labels.OwnerNameKey] +func (c *ClusterObjectSetReconciler) listPreviousRevisions(ctx context.Context, cos *ocv1.ClusterObjectSet) ([]*ocv1.ClusterObjectSet, error) { + ownerLabel, ok := cos.Labels[labels.OwnerNameKey] if !ok { // No owner label means this revision isn't properly labeled - return empty list return nil, nil } - revList := &ocv1.ClusterExtensionRevisionList{} + revList := &ocv1.ClusterObjectSetList{} if err := c.TrackingCache.List(ctx, revList, client.MatchingLabels{ labels.OwnerNameKey: ownerLabel, }); err != nil { return nil, fmt.Errorf("listing revisions: %w", err) } - previous := make([]*ocv1.ClusterExtensionRevision, 0, len(revList.Items)) + previous := make([]*ocv1.ClusterObjectSet, 0, len(revList.Items)) for i := range revList.Items { r := &revList.Items[i] - if r.Name == cer.Name { + if r.Name == cos.Name { continue } // Skip archived or deleting revisions - if r.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived || + if r.Spec.LifecycleState == ocv1.ClusterObjectSetLifecycleStateArchived || !r.DeletionTimestamp.IsZero() { continue } // Only include revisions with lower revision numbers (actual previous revisions) - if r.Spec.Revision >= cer.Spec.Revision { + if r.Spec.Revision >= cos.Spec.Revision { continue } previous = append(previous, r) @@ -474,8 +474,8 @@ func (c *ClusterExtensionRevisionReconciler) listPreviousRevisions(ctx context.C return previous, nil } -func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Context, cer *ocv1.ClusterExtensionRevision) ([]boxcutter.Phase, []boxcutter.RevisionReconcileOption, error) { - previous, err := c.listPreviousRevisions(ctx, cer) +func (c *ClusterObjectSetReconciler) buildBoxcutterPhases(ctx context.Context, cos *ocv1.ClusterObjectSet) ([]boxcutter.Phase, []boxcutter.RevisionReconcileOption, error) { + previous, err := c.listPreviousRevisions(ctx, cos) if err != nil { return nil, nil, fmt.Errorf("listing previous revisions: %w", err) } @@ -486,7 +486,7 @@ func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Co previousObjs[i] = rev } - progressionProbes, err := buildProgressionProbes(cer.Spec.ProgressionProbes) + progressionProbes, err := buildProgressionProbes(cos.Spec.ProgressionProbes) if err != nil { return nil, nil, err } @@ -497,7 +497,7 @@ func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Co } phases := make([]boxcutter.Phase, 0) - for _, specPhase := range cer.Spec.Phases { + for _, specPhase := range cos.Spec.Phases { objs := make([]client.Object, 0) for _, specObj := range specPhase.Objects { var obj *unstructured.Unstructured @@ -518,10 +518,10 @@ func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Co if objLabels == nil { objLabels = map[string]string{} } - objLabels[labels.OwnerNameKey] = cer.Labels[labels.OwnerNameKey] + objLabels[labels.OwnerNameKey] = cos.Labels[labels.OwnerNameKey] obj.SetLabels(objLabels) - switch cp := EffectiveCollisionProtection(cer.Spec.CollisionProtection, specPhase.CollisionProtection, specObj.CollisionProtection); cp { + switch cp := EffectiveCollisionProtection(cos.Spec.CollisionProtection, specPhase.CollisionProtection, specObj.CollisionProtection); cp { case ocv1.CollisionProtectionIfNoController, ocv1.CollisionProtectionNone: opts = append(opts, boxcutter.WithObjectReconcileOptions( obj, boxcutter.WithCollisionProtection(cp))) @@ -536,7 +536,7 @@ func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Co // resolveObjectRef fetches the referenced Secret, reads the value at the specified key, // auto-detects gzip compression, and deserializes into an unstructured.Unstructured. -func (c *ClusterExtensionRevisionReconciler) resolveObjectRef(ctx context.Context, ref ocv1.ObjectSourceRef) (*unstructured.Unstructured, error) { +func (c *ClusterObjectSetReconciler) resolveObjectRef(ctx context.Context, ref ocv1.ObjectSourceRef) (*unstructured.Unstructured, error) { secret := &corev1.Secret{} key := client.ObjectKey{Name: ref.Name, Namespace: ref.Namespace} if err := c.Client.Get(ctx, key, secret); err != nil { @@ -587,14 +587,14 @@ func EffectiveCollisionProtection(cp ...ocv1.CollisionProtection) ocv1.Collision return ecp } -// siblingRevisionNames returns the names of all ClusterExtensionRevisions that belong to -// the same ClusterExtension as cer. Returns nil when cer has no owner label. -func (c *ClusterExtensionRevisionReconciler) siblingRevisionNames(ctx context.Context, cer *ocv1.ClusterExtensionRevision) (sets.Set[string], error) { - ownerLabel, ok := cer.Labels[labels.OwnerNameKey] +// siblingRevisionNames returns the names of all ClusterObjectSets that belong to +// the same ClusterExtension as cos. Returns nil when cos has no owner label. +func (c *ClusterObjectSetReconciler) siblingRevisionNames(ctx context.Context, cos *ocv1.ClusterObjectSet) (sets.Set[string], error) { + ownerLabel, ok := cos.Labels[labels.OwnerNameKey] if !ok { return nil, nil } - revList := &ocv1.ClusterExtensionRevisionList{} + revList := &ocv1.ClusterObjectSetList{} if err := c.TrackingCache.List(ctx, revList, client.MatchingLabels{ labels.OwnerNameKey: ownerLabel, }); err != nil { @@ -608,13 +608,13 @@ func (c *ClusterExtensionRevisionReconciler) siblingRevisionNames(ctx context.Co } // foreignRevisionController returns the controller OwnerReference when obj is owned by a -// ClusterExtensionRevision that is not in siblings (i.e. belongs to a different ClusterExtension). -// Returns nil when the controller is a sibling or is not a ClusterExtensionRevision. +// ClusterObjectSet that is not in siblings (i.e. belongs to a different ClusterExtension). +// Returns nil when the controller is a sibling or is not a ClusterObjectSet. func foreignRevisionController(obj metav1.Object, siblings sets.Set[string]) *metav1.OwnerReference { refs := obj.GetOwnerReferences() for i := range refs { if refs[i].Controller != nil && *refs[i].Controller && - refs[i].Kind == ocv1.ClusterExtensionRevisionKind && + refs[i].Kind == ocv1.ClusterObjectSetKind && refs[i].APIVersion == ocv1.GroupVersion.String() && !siblings.Has(refs[i].Name) { return &refs[i] @@ -623,7 +623,7 @@ func foreignRevisionController(obj metav1.Object, siblings sets.Set[string]) *me return nil } -// buildProgressionProbes creates a set of boxcutter probes from the fields provided in the CER's spec.progressionProbes. +// buildProgressionProbes creates a set of boxcutter probes from the fields provided in the COS's spec.progressionProbes. // Returns nil and an error if encountered while attempting to build the probes. func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing.And, error) { userProbes := probing.And{} @@ -678,65 +678,65 @@ func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing. return userProbes, nil } -func setRetryingConditions(cer *ocv1.ClusterExtensionRevision, message string) { - markAsProgressing(cer, ocv1.ClusterExtensionRevisionReasonRetrying, message) - if meta.FindStatusCondition(cer.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) != nil { - markAsAvailableUnknown(cer, ocv1.ClusterExtensionRevisionReasonReconciling, message) +func setRetryingConditions(cos *ocv1.ClusterObjectSet, message string) { + markAsProgressing(cos, ocv1.ClusterObjectSetReasonRetrying, message) + if meta.FindStatusCondition(cos.Status.Conditions, ocv1.ClusterObjectSetTypeAvailable) != nil { + markAsAvailableUnknown(cos, ocv1.ClusterObjectSetReasonReconciling, message) } } -func markAsProgressing(cer *ocv1.ClusterExtensionRevision, reason, message string) { - meta.SetStatusCondition(&cer.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeProgressing, +func markAsProgressing(cos *ocv1.ClusterObjectSet, reason, message string) { + meta.SetStatusCondition(&cos.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionTrue, Reason: reason, Message: message, - ObservedGeneration: cer.Generation, + ObservedGeneration: cos.Generation, }) } -func markAsNotProgressing(cer *ocv1.ClusterExtensionRevision, reason, message string) bool { - return meta.SetStatusCondition(&cer.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeProgressing, +func markAsNotProgressing(cos *ocv1.ClusterObjectSet, reason, message string) bool { + return meta.SetStatusCondition(&cos.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionFalse, Reason: reason, Message: message, - ObservedGeneration: cer.Generation, + ObservedGeneration: cos.Generation, }) } -func markAsAvailable(cer *ocv1.ClusterExtensionRevision, reason, message string) bool { - return meta.SetStatusCondition(&cer.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeAvailable, +func markAsAvailable(cos *ocv1.ClusterObjectSet, reason, message string) bool { + return meta.SetStatusCondition(&cos.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterObjectSetTypeAvailable, Status: metav1.ConditionTrue, Reason: reason, Message: message, - ObservedGeneration: cer.Generation, + ObservedGeneration: cos.Generation, }) } -func markAsUnavailable(cer *ocv1.ClusterExtensionRevision, reason, message string) { - meta.SetStatusCondition(&cer.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeAvailable, +func markAsUnavailable(cos *ocv1.ClusterObjectSet, reason, message string) { + meta.SetStatusCondition(&cos.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterObjectSetTypeAvailable, Status: metav1.ConditionFalse, Reason: reason, Message: message, - ObservedGeneration: cer.Generation, + ObservedGeneration: cos.Generation, }) } -func markAsAvailableUnknown(cer *ocv1.ClusterExtensionRevision, reason, message string) bool { - return meta.SetStatusCondition(&cer.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeAvailable, +func markAsAvailableUnknown(cos *ocv1.ClusterObjectSet, reason, message string) bool { + return meta.SetStatusCondition(&cos.Status.Conditions, metav1.Condition{ + Type: ocv1.ClusterObjectSetTypeAvailable, Status: metav1.ConditionUnknown, Reason: reason, Message: message, - ObservedGeneration: cer.Generation, + ObservedGeneration: cos.Generation, }) } -func markAsArchived(cer *ocv1.ClusterExtensionRevision) bool { +func markAsArchived(cos *ocv1.ClusterObjectSet) bool { const msg = "revision is archived" - updated := markAsNotProgressing(cer, ocv1.ClusterExtensionRevisionReasonArchived, msg) - return markAsAvailableUnknown(cer, ocv1.ClusterExtensionRevisionReasonArchived, msg) || updated + updated := markAsNotProgressing(cos, ocv1.ClusterObjectSetReasonArchived, msg) + return markAsAvailableUnknown(cos, ocv1.ClusterObjectSetReasonArchived, msg) || updated } diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller_internal_test.go b/internal/operator-controller/controllers/clusterobjectset_controller_internal_test.go similarity index 82% rename from internal/operator-controller/controllers/clusterextensionrevision_controller_internal_test.go rename to internal/operator-controller/controllers/clusterobjectset_controller_internal_test.go index af4445c6c6..15300f63ca 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller_internal_test.go +++ b/internal/operator-controller/controllers/clusterobjectset_controller_internal_test.go @@ -22,7 +22,7 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/labels" ) -func Test_ClusterExtensionRevisionReconciler_listPreviousRevisions(t *testing.T) { +func Test_ClusterObjectSetReconciler_listPreviousRevisions(t *testing.T) { testScheme := runtime.NewScheme() require.NoError(t, ocv1.AddToScheme(testScheme)) @@ -41,9 +41,9 @@ func Test_ClusterExtensionRevisionReconciler_listPreviousRevisions(t *testing.T) name: "should skip current revision when listing previous", existingObjs: func() []client.Object { ext := newTestClusterExtensionInternal() - rev1 := newTestClusterExtensionRevisionInternal(t, "rev-1") - rev2 := newTestClusterExtensionRevisionInternal(t, "rev-2") - rev3 := newTestClusterExtensionRevisionInternal(t, "rev-3") + rev1 := newTestClusterObjectSetInternal(t, "rev-1") + rev2 := newTestClusterObjectSetInternal(t, "rev-2") + rev3 := newTestClusterObjectSetInternal(t, "rev-3") require.NoError(t, controllerutil.SetControllerReference(ext, rev1, testScheme)) require.NoError(t, controllerutil.SetControllerReference(ext, rev2, testScheme)) require.NoError(t, controllerutil.SetControllerReference(ext, rev3, testScheme)) @@ -60,10 +60,10 @@ func Test_ClusterExtensionRevisionReconciler_listPreviousRevisions(t *testing.T) name: "should drop archived revisions when listing previous", existingObjs: func() []client.Object { ext := newTestClusterExtensionInternal() - rev1 := newTestClusterExtensionRevisionInternal(t, "rev-1") - rev2 := newTestClusterExtensionRevisionInternal(t, "rev-2") - rev2.Spec.LifecycleState = ocv1.ClusterExtensionRevisionLifecycleStateArchived - rev3 := newTestClusterExtensionRevisionInternal(t, "rev-3") + rev1 := newTestClusterObjectSetInternal(t, "rev-1") + rev2 := newTestClusterObjectSetInternal(t, "rev-2") + rev2.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived + rev3 := newTestClusterObjectSetInternal(t, "rev-3") require.NoError(t, controllerutil.SetControllerReference(ext, rev1, testScheme)) require.NoError(t, controllerutil.SetControllerReference(ext, rev2, testScheme)) require.NoError(t, controllerutil.SetControllerReference(ext, rev3, testScheme)) @@ -80,11 +80,11 @@ func Test_ClusterExtensionRevisionReconciler_listPreviousRevisions(t *testing.T) name: "should drop deleting revisions when listing previous", existingObjs: func() []client.Object { ext := newTestClusterExtensionInternal() - rev1 := newTestClusterExtensionRevisionInternal(t, "rev-1") - rev2 := newTestClusterExtensionRevisionInternal(t, "rev-2") + rev1 := newTestClusterObjectSetInternal(t, "rev-1") + rev2 := newTestClusterObjectSetInternal(t, "rev-2") rev2.Finalizers = []string{"test-finalizer"} rev2.DeletionTimestamp = &metav1.Time{Time: time.Now()} - rev3 := newTestClusterExtensionRevisionInternal(t, "rev-3") + rev3 := newTestClusterObjectSetInternal(t, "rev-3") require.NoError(t, controllerutil.SetControllerReference(ext, rev1, testScheme)) require.NoError(t, controllerutil.SetControllerReference(ext, rev2, testScheme)) require.NoError(t, controllerutil.SetControllerReference(ext, rev3, testScheme)) @@ -105,10 +105,10 @@ func Test_ClusterExtensionRevisionReconciler_listPreviousRevisions(t *testing.T) ext2.Name = "test-ext-2" ext2.UID = "test-ext-2" - rev1 := newTestClusterExtensionRevisionInternal(t, "rev-1") - rev2 := newTestClusterExtensionRevisionInternal(t, "rev-2") + rev1 := newTestClusterObjectSetInternal(t, "rev-1") + rev2 := newTestClusterObjectSetInternal(t, "rev-2") rev2.Labels[labels.OwnerNameKey] = "test-ext-2" - rev3 := newTestClusterExtensionRevisionInternal(t, "rev-3") + rev3 := newTestClusterObjectSetInternal(t, "rev-3") require.NoError(t, controllerutil.SetControllerReference(ext, rev1, testScheme)) require.NoError(t, controllerutil.SetControllerReference(ext2, rev2, testScheme)) require.NoError(t, controllerutil.SetControllerReference(ext, rev3, testScheme)) @@ -125,7 +125,7 @@ func Test_ClusterExtensionRevisionReconciler_listPreviousRevisions(t *testing.T) name: "should return empty list when owner label missing", existingObjs: func() []client.Object { ext := newTestClusterExtensionInternal() - rev1 := newTestClusterExtensionRevisionInternal(t, "rev-1") + rev1 := newTestClusterObjectSetInternal(t, "rev-1") delete(rev1.Labels, labels.OwnerNameKey) require.NoError(t, controllerutil.SetControllerReference(ext, rev1, testScheme)) return []client.Object{ext, rev1} @@ -140,12 +140,12 @@ func Test_ClusterExtensionRevisionReconciler_listPreviousRevisions(t *testing.T) WithObjects(tc.existingObjs()...). Build() - reconciler := &ClusterExtensionRevisionReconciler{ + reconciler := &ClusterObjectSetReconciler{ Client: testClient, TrackingCache: &mockTrackingCacheInternal{client: testClient}, } - currentRev := &ocv1.ClusterExtensionRevision{} + currentRev := &ocv1.ClusterObjectSet{} err := testClient.Get(t.Context(), client.ObjectKey{Name: tc.currentRev}, currentRev) require.NoError(t, err) @@ -183,13 +183,13 @@ func newTestClusterExtensionInternal() *ocv1.ClusterExtension { } } -func newTestClusterExtensionRevisionInternal(t *testing.T, name string) *ocv1.ClusterExtensionRevision { +func newTestClusterObjectSetInternal(t *testing.T, name string) *ocv1.ClusterObjectSet { t.Helper() // Extract revision number from name (e.g., "rev-1" -> 1, "test-ext-10" -> 10) revNum := ExtractRevisionNumber(t, name) - rev := &ocv1.ClusterExtensionRevision{ + rev := &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, UID: types.UID(name), @@ -198,17 +198,17 @@ func newTestClusterExtensionRevisionInternal(t *testing.T, name string) *ocv1.Cl labels.OwnerNameKey: "test-ext", }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ + Spec: ocv1.ClusterObjectSetSpec{ Revision: revNum, - Phases: []ocv1.ClusterExtensionRevisionPhase{ + Phases: []ocv1.ClusterObjectSetPhase{ { Name: "everything", - Objects: []ocv1.ClusterExtensionRevisionObject{}, + Objects: []ocv1.ClusterObjectSetObject{}, }, }, }, } - rev.SetGroupVersionKind(ocv1.GroupVersion.WithKind("ClusterExtensionRevision")) + rev.SetGroupVersionKind(ocv1.GroupVersion.WithKind("ClusterObjectSet")) return rev } diff --git a/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go b/internal/operator-controller/controllers/clusterobjectset_controller_test.go similarity index 82% rename from internal/operator-controller/controllers/clusterextensionrevision_controller_test.go rename to internal/operator-controller/controllers/clusterobjectset_controller_test.go index 7864256c7b..ccfb9755ed 100644 --- a/internal/operator-controller/controllers/clusterextensionrevision_controller_test.go +++ b/internal/operator-controller/controllers/clusterobjectset_controller_test.go @@ -35,9 +35,9 @@ import ( "github.com/operator-framework/operator-controller/internal/operator-controller/labels" ) -const clusterExtensionRevisionName = "test-ext-1" +const clusterObjectSetName = "test-ext-1" -func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t *testing.T) { +func Test_ClusterObjectSetReconciler_Reconcile_RevisionReconciliation(t *testing.T) { testScheme := newScheme(t) for _, tc := range []struct { @@ -50,17 +50,17 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t }{ { name: "sets teardown finalizer", - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) require.Contains(t, rev.Finalizers, "olm.operatorframework.io/teardown") @@ -68,71 +68,71 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t }, { name: "Available condition is not updated on error if its not already set", - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, revisionResult: mockRevisionResult{}, revisionReconcileErr: errors.New("some error"), existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeAvailable) require.Nil(t, cond) }, }, { name: "Available condition is updated to Unknown on error if its been already set", - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, revisionResult: mockRevisionResult{}, revisionReconcileErr: errors.New("some error"), existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) meta.SetStatusCondition(&rev1.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeAvailable, + Type: ocv1.ClusterObjectSetTypeAvailable, Status: metav1.ConditionTrue, - Reason: ocv1.ClusterExtensionRevisionReasonProbesSucceeded, + Reason: ocv1.ClusterObjectSetReasonProbesSucceeded, Message: "Revision 1.0.0 is rolled out.", ObservedGeneration: 1, }) return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeAvailable) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonReconciling, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonReconciling, cond.Reason) require.Equal(t, "some error", cond.Message) require.Equal(t, int64(1), cond.ObservedGeneration) }, }, { name: "set Available:False:RollingOut status condition during rollout when no probe failures are detected", - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeAvailable) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) require.Equal(t, ocv1.ReasonRollingOut, cond.Reason) @@ -142,7 +142,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t }, { name: "set Available:False:ProbeFailure condition when probe failures are detected and revision is in transition", - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, revisionResult: mockRevisionResult{ inTransition: true, isComplete: false, @@ -214,26 +214,26 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t }, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeAvailable) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonProbeFailure, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonProbeFailure, cond.Reason) require.Equal(t, "Object Service.v1 my-namespace/my-service: something bad happened and something worse happened\nObject ConfigMap.v1 my-namespace/my-configmap: we have a problem", cond.Message) require.Equal(t, int64(1), cond.ObservedGeneration) }, }, { name: "set Available:False:ProbeFailure condition when probe failures are detected and revision is not in transition", - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, revisionResult: mockRevisionResult{ inTransition: false, isComplete: false, @@ -305,19 +305,19 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t }, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeAvailable) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonProbeFailure, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonProbeFailure, cond.Reason) require.Equal(t, "Object Service.v1 my-namespace/my-service: something bad happened and something worse happened\nObject ConfigMap.v1 my-namespace/my-configmap: we have a problem", cond.Message) require.Equal(t, int64(1), cond.ObservedGeneration) }, @@ -325,22 +325,22 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t { name: "set Progressing:True:Retrying when there's an error reconciling the revision", revisionReconcileErr: errors.New("some error"), - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.TypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonRetrying, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonRetrying, cond.Reason) require.Equal(t, "some error", cond.Message) require.Equal(t, int64(1), cond.ObservedGeneration) }, @@ -350,16 +350,16 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t revisionResult: mockRevisionResult{ inTransition: true, }, - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.TypeProgressing) @@ -376,10 +376,10 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t inTransition: false, isComplete: true, }, - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) meta.SetStatusCondition(&rev1.Status.Conditions, metav1.Condition{ Type: ocv1.TypeProgressing, Status: metav1.ConditionTrue, @@ -390,9 +390,9 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.TypeProgressing) @@ -408,33 +408,33 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t revisionResult: mockRevisionResult{ isComplete: true, }, - reconcilingRevisionName: clusterExtensionRevisionName, + reconcilingRevisionName: clusterObjectSetName, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeAvailable) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonProbesSucceeded, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonProbesSucceeded, cond.Reason) require.Equal(t, "Objects are available and pass all probes.", cond.Message) require.Equal(t, int64(1), cond.ObservedGeneration) - cond = meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cond = meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) require.Equal(t, ocv1.ReasonSucceeded, cond.Reason) require.Equal(t, "Revision 1.0.0 has rolled out.", cond.Message) require.Equal(t, int64(1), cond.ObservedGeneration) - cond = meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeSucceeded) + cond = meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeSucceeded) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) require.Equal(t, ocv1.ReasonSucceeded, cond.Reason) @@ -450,30 +450,30 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t reconcilingRevisionName: "test-ext-3", existingObjs: func() []client.Object { ext := newTestClusterExtension() - prevRev1 := newTestClusterExtensionRevision(t, "test-ext-1", ext, testScheme) - prevRev2 := newTestClusterExtensionRevision(t, "test-ext-2", ext, testScheme) - rev := newTestClusterExtensionRevision(t, "test-ext-3", ext, testScheme) + prevRev1 := newTestClusterObjectSet(t, "test-ext-1", ext, testScheme) + prevRev2 := newTestClusterObjectSet(t, "test-ext-2", ext, testScheme) + rev := newTestClusterObjectSet(t, "test-ext-3", ext, testScheme) return []client.Object{ext, prevRev1, prevRev2, rev} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ Name: "test-ext-1", }, rev) require.NoError(t, err) - require.Equal(t, ocv1.ClusterExtensionRevisionLifecycleStateArchived, rev.Spec.LifecycleState) + require.Equal(t, ocv1.ClusterObjectSetLifecycleStateArchived, rev.Spec.LifecycleState) err = c.Get(t.Context(), client.ObjectKey{ Name: "test-ext-2", }, rev) require.NoError(t, err) - require.Equal(t, ocv1.ClusterExtensionRevisionLifecycleStateArchived, rev.Spec.LifecycleState) + require.Equal(t, ocv1.ClusterObjectSetLifecycleStateArchived, rev.Spec.LifecycleState) err = c.Get(t.Context(), client.ObjectKey{ Name: "test-ext-3", }, rev) require.NoError(t, err) - require.Equal(t, ocv1.ClusterExtensionRevisionLifecycleStateActive, rev.Spec.LifecycleState) + require.Equal(t, ocv1.ClusterObjectSetLifecycleStateActive, rev.Spec.LifecycleState) }, }, } { @@ -481,7 +481,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t // create extension and cluster extension testClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). WithObjects(tc.existingObjs()...). Build() @@ -491,7 +491,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t return tc.revisionResult, tc.revisionReconcileErr }, } - result, err := (&controllers.ClusterExtensionRevisionReconciler{ + result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, TrackingCache: &mockTrackingCache{client: testClient}, @@ -515,10 +515,10 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_RevisionReconciliation(t } } -func Test_ClusterExtensionRevisionReconciler_Reconcile_ValidationError_Retries(t *testing.T) { +func Test_ClusterObjectSetReconciler_Reconcile_ValidationError_Retries(t *testing.T) { const ( - clusterExtensionName = "test-ext" - clusterExtensionRevisionName = "test-ext-1" + clusterExtensionName = "test-ext" + clusterObjectSetName = "test-ext-1" ) testScheme := newScheme(t) @@ -596,12 +596,12 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ValidationError_Retries(t } { t.Run(tc.name, func(t *testing.T) { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) // create extension and cluster extension testClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). WithObjects(ext, rev1). Build() @@ -611,13 +611,13 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ValidationError_Retries(t return tc.revisionResult, nil }, } - result, err := (&controllers.ClusterExtensionRevisionReconciler{ + result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, TrackingCache: &mockTrackingCache{client: testClient}, }).Reconcile(t.Context(), ctrl.Request{ NamespacedName: types.NamespacedName{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, }) @@ -630,9 +630,9 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ValidationError_Retries(t } } -func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) { +func Test_ClusterObjectSetReconciler_Reconcile_ArchivalAndDeletion(t *testing.T) { const ( - clusterExtensionRevisionName = "test-ext-1" + clusterObjectSetName = "test-ext-1" ) testScheme := newScheme(t) @@ -654,16 +654,16 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Finalizers = []string{ "olm.operatorframework.io/teardown", } return []client.Object{ext, rev1} }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) require.NotContains(t, "olm.operatorframework.io/teardown", rev.Finalizers) @@ -677,7 +677,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Finalizers = []string{ "olm.operatorframework.io/teardown", } @@ -689,15 +689,15 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te }, expectedErr: "error stopping informers", validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeAvailable) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonReconciling, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonReconciling, cond.Reason) require.Contains(t, cond.Message, "tracking cache free failed") }, revisionEngineTeardownFn: func(t *testing.T) func(context.Context, machinerytypes.Revision, ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { @@ -709,11 +709,11 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Finalizers = []string{ "olm.operatorframework.io/teardown", } - rev1.Spec.LifecycleState = ocv1.ClusterExtensionRevisionLifecycleStateArchived + rev1.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived return []client.Object{rev1, ext} }, revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { @@ -724,22 +724,22 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te } }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeAvailable) require.NotNil(t, cond) require.Equal(t, metav1.ConditionUnknown, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonArchived, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonArchived, cond.Reason) require.Equal(t, "revision is archived", cond.Message) require.Equal(t, int64(1), cond.ObservedGeneration) - cond = meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cond = meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionFalse, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonArchived, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonArchived, cond.Reason) require.Equal(t, "revision is archived", cond.Message) require.Equal(t, int64(1), cond.ObservedGeneration) }, @@ -749,11 +749,11 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Finalizers = []string{ "olm.operatorframework.io/teardown", } - rev1.Spec.LifecycleState = ocv1.ClusterExtensionRevisionLifecycleStateArchived + rev1.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived return []client.Object{rev1, ext} }, revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { @@ -765,15 +765,15 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te }, expectedResult: ctrl.Result{RequeueAfter: 5 * time.Second}, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonRetrying, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonRetrying, cond.Reason) require.Equal(t, "removing revision resources that are not owned by another revision", cond.Message) // Finalizer should still be present @@ -785,11 +785,11 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Finalizers = []string{ "olm.operatorframework.io/teardown", } - rev1.Spec.LifecycleState = ocv1.ClusterExtensionRevisionLifecycleStateArchived + rev1.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived return []client.Object{rev1, ext} }, revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { @@ -799,15 +799,15 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te }, expectedErr: "error archiving revision", validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonRetrying, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonRetrying, cond.Reason) require.Contains(t, cond.Message, "teardown failed: connection refused") // Finalizer should still be present @@ -819,11 +819,11 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Finalizers = []string{ "olm.operatorframework.io/teardown", } - rev1.Spec.LifecycleState = ocv1.ClusterExtensionRevisionLifecycleStateArchived + rev1.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived return []client.Object{rev1, ext} }, revisionEngineTeardownFn: func(t *testing.T) func(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error) { @@ -832,15 +832,15 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te revisionEngineFactoryErr: fmt.Errorf("token getter failed"), expectedErr: "failed to create revision engine", validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonRetrying, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonRetrying, cond.Reason) require.Contains(t, cond.Message, "token getter failed") // Finalizer should still be present @@ -852,22 +852,22 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te revisionResult: mockRevisionResult{}, existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Finalizers = []string{ "olm.operatorframework.io/teardown", } - rev1.Spec.LifecycleState = ocv1.ClusterExtensionRevisionLifecycleStateArchived + rev1.Spec.LifecycleState = ocv1.ClusterObjectSetLifecycleStateArchived meta.SetStatusCondition(&rev1.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeAvailable, + Type: ocv1.ClusterObjectSetTypeAvailable, Status: metav1.ConditionUnknown, - Reason: ocv1.ClusterExtensionRevisionReasonArchived, + Reason: ocv1.ClusterObjectSetReasonArchived, Message: "revision is archived", ObservedGeneration: rev1.Generation, }) meta.SetStatusCondition(&rev1.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeProgressing, + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionFalse, - Reason: ocv1.ClusterExtensionRevisionReasonArchived, + Reason: ocv1.ClusterObjectSetReasonArchived, Message: "revision is archived", ObservedGeneration: rev1.Generation, }) @@ -881,9 +881,9 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te } }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) require.NotContains(t, rev.Finalizers, "olm.operatorframework.io/teardown") @@ -894,7 +894,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te // create extension and cluster extension testClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). WithObjects(tc.existingObjs()...). Build() @@ -906,7 +906,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te teardown: tc.revisionEngineTeardownFn(t), } factory := &mockRevisionEngineFactory{engine: mockEngine, createErr: tc.revisionEngineFactoryErr} - result, err := (&controllers.ClusterExtensionRevisionReconciler{ + result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: factory, TrackingCache: &mockTrackingCache{ @@ -915,7 +915,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te }, }).Reconcile(t.Context(), ctrl.Request{ NamespacedName: types.NamespacedName{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, }) @@ -933,9 +933,9 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ArchivalAndDeletion(t *te } } -func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testing.T) { +func Test_ClusterObjectSetReconciler_Reconcile_ProgressDeadline(t *testing.T) { const ( - clusterExtensionRevisionName = "test-ext-1" + clusterObjectSetName = "test-ext-1" ) testScheme := newScheme(t) @@ -954,7 +954,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi name: "progressing set to false when progress deadline is exceeded", existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Spec.ProgressDeadlineMinutes = 1 rev1.CreationTimestamp = metav1.NewTime(time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)) return []client.Object{rev1, ext} @@ -965,12 +965,12 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi inTransition: true, }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cnd := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cnd := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) require.Equal(t, metav1.ConditionFalse, cnd.Status) require.Equal(t, ocv1.ReasonProgressDeadlineExceeded, cnd.Reason) }, @@ -979,7 +979,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi name: "requeue after progressDeadline time for final progression deadline check", existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Spec.ProgressDeadlineMinutes = 1 rev1.CreationTimestamp = metav1.NewTime(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)) return []client.Object{rev1, ext} @@ -990,12 +990,12 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi }, reconcileResult: ctrl.Result{RequeueAfter: 62 * time.Second}, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cnd := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cnd := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) require.Equal(t, metav1.ConditionTrue, cnd.Status) require.Equal(t, ocv1.ReasonRollingOut, cnd.Reason) }, @@ -1004,17 +1004,17 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi name: "no progression deadline checks on revision recovery", existingObjs: func() []client.Object { ext := newTestClusterExtension() - rev1 := newTestClusterExtensionRevision(t, clusterExtensionRevisionName, ext, testScheme) + rev1 := newTestClusterObjectSet(t, clusterObjectSetName, ext, testScheme) rev1.Spec.ProgressDeadlineMinutes = 1 rev1.CreationTimestamp = metav1.NewTime(time.Now().Add(-2 * time.Minute)) meta.SetStatusCondition(&rev1.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeProgressing, + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionTrue, Reason: ocv1.ReasonSucceeded, ObservedGeneration: rev1.Generation, }) meta.SetStatusCondition(&rev1.Status.Conditions, metav1.Condition{ - Type: ocv1.ClusterExtensionRevisionTypeSucceeded, + Type: ocv1.ClusterObjectSetTypeSucceeded, Status: metav1.ConditionTrue, Reason: ocv1.ReasonSucceeded, ObservedGeneration: rev1.Generation, @@ -1025,12 +1025,12 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi inTransition: true, }, validate: func(t *testing.T, c client.Client) { - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} err := c.Get(t.Context(), client.ObjectKey{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, rev) require.NoError(t, err) - cnd := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cnd := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) require.Equal(t, metav1.ConditionTrue, cnd.Status) require.Equal(t, ocv1.ReasonRollingOut, cnd.Reason) }, @@ -1040,7 +1040,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi // create extension and cluster extension testClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). WithObjects(tc.existingObjs()...). Build() @@ -1050,7 +1050,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi return tc.revisionResult, nil }, } - result, err := (&controllers.ClusterExtensionRevisionReconciler{ + result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, TrackingCache: &mockTrackingCache{ @@ -1059,7 +1059,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ProgressDeadline(t *testi Clock: tc.clock, }).Reconcile(t.Context(), ctrl.Request{ NamespacedName: types.NamespacedName{ - Name: clusterExtensionRevisionName, + Name: clusterObjectSetName, }, }) require.Equal(t, tc.reconcileResult, result) @@ -1091,13 +1091,13 @@ func newTestClusterExtension() *ocv1.ClusterExtension { } } -func newTestClusterExtensionRevision(t *testing.T, revisionName string, ext *ocv1.ClusterExtension, scheme *runtime.Scheme) *ocv1.ClusterExtensionRevision { +func newTestClusterObjectSet(t *testing.T, revisionName string, ext *ocv1.ClusterExtension, scheme *runtime.Scheme) *ocv1.ClusterObjectSet { t.Helper() // Extract revision number from name (e.g., "rev-1" -> 1, "test-ext-10" -> 10) revNum := controllers.ExtractRevisionNumber(t, revisionName) - rev := &ocv1.ClusterExtensionRevision{ + rev := &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: revisionName, UID: types.UID(revisionName), @@ -1114,13 +1114,13 @@ func newTestClusterExtensionRevision(t *testing.T, revisionName string, ext *ocv labels.OwnerNameKey: ext.Name, }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateActive, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateActive, Revision: revNum, - Phases: []ocv1.ClusterExtensionRevisionPhase{ + Phases: []ocv1.ClusterObjectSetPhase{ { Name: "everything", - Objects: []ocv1.ClusterExtensionRevisionObject{ + Objects: []ocv1.ClusterObjectSetObject{ { Object: unstructured.Unstructured{ Object: map[string]interface{}{ @@ -1160,7 +1160,7 @@ type mockRevisionEngineFactory struct { createErr error } -func (f *mockRevisionEngineFactory) CreateRevisionEngine(ctx context.Context, rev *ocv1.ClusterExtensionRevision) (controllers.RevisionEngine, error) { +func (f *mockRevisionEngineFactory) CreateRevisionEngine(ctx context.Context, rev *ocv1.ClusterObjectSet) (controllers.RevisionEngine, error) { if f.createErr != nil { return nil, f.createErr } @@ -1344,7 +1344,7 @@ func (m *mockTrackingCache) Free(ctx context.Context, user client.Object) error return nil } -func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision(t *testing.T) { +func Test_ClusterObjectSetReconciler_Reconcile_ForeignRevisionCollision(t *testing.T) { testScheme := newScheme(t) for _, tc := range []struct { @@ -1355,7 +1355,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( expectCollision bool }{ { - name: "progressed object owned by a foreign CER is treated as a collision", + name: "progressed object owned by a foreign COS is treated as a collision", reconcilingRevisionName: "ext-B-1", existingObjs: func() []client.Object { extA := &ocv1.ClusterExtension{ @@ -1380,8 +1380,8 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( }, }, } - cerA2 := newTestClusterExtensionRevision(t, "ext-A-2", extA, testScheme) - cerB1 := newTestClusterExtensionRevision(t, "ext-B-1", extB, testScheme) + cerA2 := newTestClusterObjectSet(t, "ext-A-2", extA, testScheme) + cerB1 := newTestClusterObjectSet(t, "ext-B-1", extB, testScheme) return []client.Object{extA, extB, cerA2, cerB1} }, revisionResult: mockRevisionResult{ @@ -1400,7 +1400,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( "ownerReferences": []interface{}{ map[string]interface{}{ "apiVersion": ocv1.GroupVersion.String(), - "kind": ocv1.ClusterExtensionRevisionKind, + "kind": ocv1.ClusterObjectSetKind, "name": "ext-A-2", "uid": "ext-A-2", "controller": true, @@ -1423,7 +1423,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( expectCollision: true, }, { - name: "progressed object owned by a sibling CER is not a collision", + name: "progressed object owned by a sibling COS is not a collision", reconcilingRevisionName: "ext-A-1", existingObjs: func() []client.Object { extA := &ocv1.ClusterExtension{ @@ -1437,8 +1437,8 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( }, }, } - cerA1 := newTestClusterExtensionRevision(t, "ext-A-1", extA, testScheme) - cerA2 := newTestClusterExtensionRevision(t, "ext-A-2", extA, testScheme) + cerA1 := newTestClusterObjectSet(t, "ext-A-1", extA, testScheme) + cerA2 := newTestClusterObjectSet(t, "ext-A-2", extA, testScheme) return []client.Object{extA, cerA1, cerA2} }, revisionResult: mockRevisionResult{ @@ -1457,7 +1457,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( "ownerReferences": []interface{}{ map[string]interface{}{ "apiVersion": ocv1.GroupVersion.String(), - "kind": ocv1.ClusterExtensionRevisionKind, + "kind": ocv1.ClusterObjectSetKind, "name": "ext-A-2", "uid": "ext-A-2", "controller": true, @@ -1480,7 +1480,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( expectCollision: false, }, { - name: "progressed object owned by a non-CER controller is not a collision", + name: "progressed object owned by a non-COS controller is not a collision", reconcilingRevisionName: "ext-B-1", existingObjs: func() []client.Object { extB := &ocv1.ClusterExtension{ @@ -1494,7 +1494,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( }, }, } - cerB1 := newTestClusterExtensionRevision(t, "ext-B-1", extB, testScheme) + cerB1 := newTestClusterObjectSet(t, "ext-B-1", extB, testScheme) return []client.Object{extB, cerB1} }, revisionResult: mockRevisionResult{ @@ -1540,7 +1540,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( t.Run(tc.name, func(t *testing.T) { testClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). WithObjects(tc.existingObjs()...). Build() @@ -1549,7 +1549,7 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( return tc.revisionResult, nil }, } - result, err := (&controllers.ClusterExtensionRevisionReconciler{ + result, err := (&controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, TrackingCache: &mockTrackingCache{client: testClient}, @@ -1563,12 +1563,12 @@ func Test_ClusterExtensionRevisionReconciler_Reconcile_ForeignRevisionCollision( require.Equal(t, ctrl.Result{RequeueAfter: 10 * time.Second}, result) require.NoError(t, err) - rev := &ocv1.ClusterExtensionRevision{} + rev := &ocv1.ClusterObjectSet{} require.NoError(t, testClient.Get(t.Context(), client.ObjectKey{Name: tc.reconcilingRevisionName}, rev)) - cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) + cond := meta.FindStatusCondition(rev.Status.Conditions, ocv1.ClusterObjectSetTypeProgressing) require.NotNil(t, cond) require.Equal(t, metav1.ConditionTrue, cond.Status) - require.Equal(t, ocv1.ClusterExtensionRevisionReasonRetrying, cond.Reason) + require.Equal(t, ocv1.ClusterObjectSetReasonRetrying, cond.Reason) require.Contains(t, cond.Message, "revision object collisions") require.Contains(t, cond.Message, "Conflicting Owner") } else { @@ -1633,11 +1633,11 @@ func Test_effectiveCollisionProtection(t *testing.T) { } } -func Test_ClusterExtensionRevisionReconciler_getScopedClient_Errors(t *testing.T) { +func Test_ClusterObjectSetReconciler_getScopedClient_Errors(t *testing.T) { testScheme := newScheme(t) t.Run("works with serviceAccount annotation and without owner label", func(t *testing.T) { - rev := &ocv1.ClusterExtensionRevision{ + rev := &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-rev-1", UID: types.UID("test-rev-1"), @@ -1648,16 +1648,16 @@ func Test_ClusterExtensionRevisionReconciler_getScopedClient_Errors(t *testing.T labels.BundleVersionKey: "1.0.0", }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateActive, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateActive, Revision: 1, - Phases: []ocv1.ClusterExtensionRevisionPhase{}, + Phases: []ocv1.ClusterObjectSetPhase{}, }, } testClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). WithObjects(rev). Build() @@ -1667,7 +1667,7 @@ func Test_ClusterExtensionRevisionReconciler_getScopedClient_Errors(t *testing.T }, } - reconciler := &controllers.ClusterExtensionRevisionReconciler{ + reconciler := &controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, TrackingCache: &mockTrackingCache{client: testClient}, @@ -1681,17 +1681,17 @@ func Test_ClusterExtensionRevisionReconciler_getScopedClient_Errors(t *testing.T }) t.Run("missing serviceAccount annotation", func(t *testing.T) { - rev := &ocv1.ClusterExtensionRevision{ + rev := &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: "test-rev-1", Annotations: map[string]string{ labels.BundleVersionKey: "1.0.0", }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateActive, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateActive, Revision: 1, - Phases: []ocv1.ClusterExtensionRevisionPhase{}, + Phases: []ocv1.ClusterObjectSetPhase{}, }, } @@ -1704,7 +1704,7 @@ func Test_ClusterExtensionRevisionReconciler_getScopedClient_Errors(t *testing.T createErr: errors.New("missing serviceAccount name annotation"), } - reconciler := &controllers.ClusterExtensionRevisionReconciler{ + reconciler := &controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: failingFactory, TrackingCache: &mockTrackingCache{client: testClient}, @@ -1720,7 +1720,7 @@ func Test_ClusterExtensionRevisionReconciler_getScopedClient_Errors(t *testing.T t.Run("factory fails to create engine", func(t *testing.T) { ext := newTestClusterExtension() - rev := newTestClusterExtensionRevision(t, "test-rev", ext, testScheme) + rev := newTestClusterObjectSet(t, "test-rev", ext, testScheme) testClient := fake.NewClientBuilder(). WithScheme(testScheme). @@ -1731,7 +1731,7 @@ func Test_ClusterExtensionRevisionReconciler_getScopedClient_Errors(t *testing.T createErr: errors.New("token getter failed"), } - reconciler := &controllers.ClusterExtensionRevisionReconciler{ + reconciler := &controllers.ClusterObjectSetReconciler{ Client: testClient, RevisionEngineFactory: failingFactory, TrackingCache: &mockTrackingCache{client: testClient}, diff --git a/internal/operator-controller/controllers/common_controller.go b/internal/operator-controller/controllers/common_controller.go index e1991e4d16..46197c6c36 100644 --- a/internal/operator-controller/controllers/common_controller.go +++ b/internal/operator-controller/controllers/common_controller.go @@ -96,8 +96,8 @@ func determineFailureReason(rollingRevisions []*RevisionMetadata) string { // Check if the LATEST rolling revision indicates an error (Retrying reason) // Latest revision is the last element in the array (sorted ascending by Spec.Revision) latestRevision := rollingRevisions[len(rollingRevisions)-1] - progressingCond := apimeta.FindStatusCondition(latestRevision.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing) - if progressingCond != nil && progressingCond.Reason == string(ocv1.ClusterExtensionRevisionReasonRetrying) { + progressingCond := apimeta.FindStatusCondition(latestRevision.Conditions, ocv1.ClusterObjectSetTypeProgressing) + if progressingCond != nil && progressingCond.Reason == string(ocv1.ClusterObjectSetReasonRetrying) { // Retrying indicates an error occurred (config, apply, validation, etc.) // Use Failed for semantic correctness: installation failed due to error return ocv1.ReasonFailed diff --git a/internal/operator-controller/controllers/common_controller_test.go b/internal/operator-controller/controllers/common_controller_test.go index 2055a2b891..6edf8751ba 100644 --- a/internal/operator-controller/controllers/common_controller_test.go +++ b/internal/operator-controller/controllers/common_controller_test.go @@ -325,9 +325,9 @@ func TestSetInstalledStatusFromRevisionStates_ConfigValidationError(t *testing.T RevisionName: "rev-1", Conditions: []metav1.Condition{ { - Type: ocv1.ClusterExtensionRevisionTypeProgressing, + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionTrue, - Reason: ocv1.ClusterExtensionRevisionReasonRetrying, + Reason: ocv1.ClusterObjectSetReasonRetrying, Message: "some error occurred", }, }, @@ -349,7 +349,7 @@ func TestSetInstalledStatusFromRevisionStates_ConfigValidationError(t *testing.T RevisionName: "rev-1", Conditions: []metav1.Condition{ { - Type: ocv1.ClusterExtensionRevisionTypeProgressing, + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionTrue, Reason: ocv1.ReasonRollingOut, Message: "Revision is rolling out", @@ -360,9 +360,9 @@ func TestSetInstalledStatusFromRevisionStates_ConfigValidationError(t *testing.T RevisionName: "rev-2", Conditions: []metav1.Condition{ { - Type: ocv1.ClusterExtensionRevisionTypeProgressing, + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionTrue, - Reason: ocv1.ClusterExtensionRevisionReasonRetrying, + Reason: ocv1.ClusterObjectSetReasonRetrying, Message: "validation error occurred", }, }, @@ -384,7 +384,7 @@ func TestSetInstalledStatusFromRevisionStates_ConfigValidationError(t *testing.T RevisionName: "rev-1", Conditions: []metav1.Condition{ { - Type: ocv1.ClusterExtensionRevisionTypeProgressing, + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionTrue, Reason: ocv1.ReasonRollingOut, Message: "Revision is rolling out", @@ -408,9 +408,9 @@ func TestSetInstalledStatusFromRevisionStates_ConfigValidationError(t *testing.T RevisionName: "rev-1", Conditions: []metav1.Condition{ { - Type: ocv1.ClusterExtensionRevisionTypeProgressing, + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionTrue, - Reason: ocv1.ClusterExtensionRevisionReasonRetrying, + Reason: ocv1.ClusterObjectSetReasonRetrying, Message: "old error that was superseded", }, }, @@ -419,7 +419,7 @@ func TestSetInstalledStatusFromRevisionStates_ConfigValidationError(t *testing.T RevisionName: "rev-2", Conditions: []metav1.Condition{ { - Type: ocv1.ClusterExtensionRevisionTypeProgressing, + Type: ocv1.ClusterObjectSetTypeProgressing, Status: metav1.ConditionTrue, Reason: ocv1.ReasonRollingOut, Message: "Latest revision is rolling out healthy", diff --git a/internal/operator-controller/controllers/resolve_ref_test.go b/internal/operator-controller/controllers/resolve_ref_test.go index eab8c51a1b..da3465ca7b 100644 --- a/internal/operator-controller/controllers/resolve_ref_test.go +++ b/internal/operator-controller/controllers/resolve_ref_test.go @@ -56,7 +56,7 @@ func TestResolveObjectRef_PlainJSON(t *testing.T) { }, } - cer := newRefTestCER("ref-plain-1", ocv1.ObjectSourceRef{ + cos := newRefTestCOS("ref-plain-1", ocv1.ObjectSourceRef{ Name: "test-secret", Namespace: "olmv1-system", Key: "my-key", @@ -64,8 +64,8 @@ func TestResolveObjectRef_PlainJSON(t *testing.T) { fakeClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithObjects(secret, cer). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithObjects(secret, cos). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). Build() mockEngine := &mockRevisionEngine{ @@ -73,7 +73,7 @@ func TestResolveObjectRef_PlainJSON(t *testing.T) { return mockRevisionResult{}, nil }, } - reconciler := &controllers.ClusterExtensionRevisionReconciler{ + reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, TrackingCache: &mockTrackingCache{client: fakeClient}, @@ -81,7 +81,7 @@ func TestResolveObjectRef_PlainJSON(t *testing.T) { } _, err = reconciler.Reconcile(context.Background(), ctrl.Request{ - NamespacedName: types.NamespacedName{Name: cer.Name}, + NamespacedName: types.NamespacedName{Name: cos.Name}, }) require.NoError(t, err) } @@ -117,7 +117,7 @@ func TestResolveObjectRef_GzipCompressed(t *testing.T) { }, } - cer := newRefTestCER("ref-gzip-1", ocv1.ObjectSourceRef{ + cos := newRefTestCOS("ref-gzip-1", ocv1.ObjectSourceRef{ Name: "test-secret-gz", Namespace: "olmv1-system", Key: "my-key", @@ -125,8 +125,8 @@ func TestResolveObjectRef_GzipCompressed(t *testing.T) { fakeClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithObjects(secret, cer). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithObjects(secret, cos). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). Build() mockEngine := &mockRevisionEngine{ @@ -134,7 +134,7 @@ func TestResolveObjectRef_GzipCompressed(t *testing.T) { return mockRevisionResult{}, nil }, } - reconciler := &controllers.ClusterExtensionRevisionReconciler{ + reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: mockEngine}, TrackingCache: &mockTrackingCache{client: fakeClient}, @@ -142,7 +142,7 @@ func TestResolveObjectRef_GzipCompressed(t *testing.T) { } _, err = reconciler.Reconcile(context.Background(), ctrl.Request{ - NamespacedName: types.NamespacedName{Name: cer.Name}, + NamespacedName: types.NamespacedName{Name: cos.Name}, }) require.NoError(t, err) } @@ -150,7 +150,7 @@ func TestResolveObjectRef_GzipCompressed(t *testing.T) { func TestResolveObjectRef_SecretNotFound(t *testing.T) { testScheme := newSchemeWithCoreV1(t) - cer := newRefTestCER("ref-notfound-1", ocv1.ObjectSourceRef{ + cos := newRefTestCOS("ref-notfound-1", ocv1.ObjectSourceRef{ Name: "nonexistent-secret", Namespace: "olmv1-system", Key: "my-key", @@ -158,11 +158,11 @@ func TestResolveObjectRef_SecretNotFound(t *testing.T) { fakeClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithObjects(cer). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithObjects(cos). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). Build() - reconciler := &controllers.ClusterExtensionRevisionReconciler{ + reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: &mockRevisionEngine{}}, TrackingCache: &mockTrackingCache{client: fakeClient}, @@ -170,7 +170,7 @@ func TestResolveObjectRef_SecretNotFound(t *testing.T) { } _, err := reconciler.Reconcile(context.Background(), ctrl.Request{ - NamespacedName: types.NamespacedName{Name: cer.Name}, + NamespacedName: types.NamespacedName{Name: cos.Name}, }) require.Error(t, err) assert.Contains(t, err.Error(), "resolving ref") @@ -189,7 +189,7 @@ func TestResolveObjectRef_KeyNotFound(t *testing.T) { }, } - cer := newRefTestCER("ref-nokey-1", ocv1.ObjectSourceRef{ + cos := newRefTestCOS("ref-nokey-1", ocv1.ObjectSourceRef{ Name: "test-secret-nokey", Namespace: "olmv1-system", Key: "missing-key", @@ -197,11 +197,11 @@ func TestResolveObjectRef_KeyNotFound(t *testing.T) { fakeClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithObjects(secret, cer). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithObjects(secret, cos). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). Build() - reconciler := &controllers.ClusterExtensionRevisionReconciler{ + reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: &mockRevisionEngine{}}, TrackingCache: &mockTrackingCache{client: fakeClient}, @@ -209,7 +209,7 @@ func TestResolveObjectRef_KeyNotFound(t *testing.T) { } _, err := reconciler.Reconcile(context.Background(), ctrl.Request{ - NamespacedName: types.NamespacedName{Name: cer.Name}, + NamespacedName: types.NamespacedName{Name: cos.Name}, }) require.Error(t, err) assert.Contains(t, err.Error(), "key") @@ -228,7 +228,7 @@ func TestResolveObjectRef_InvalidJSON(t *testing.T) { }, } - cer := newRefTestCER("ref-invalid-1", ocv1.ObjectSourceRef{ + cos := newRefTestCOS("ref-invalid-1", ocv1.ObjectSourceRef{ Name: "test-secret-invalid", Namespace: "olmv1-system", Key: "my-key", @@ -236,11 +236,11 @@ func TestResolveObjectRef_InvalidJSON(t *testing.T) { fakeClient := fake.NewClientBuilder(). WithScheme(testScheme). - WithObjects(secret, cer). - WithStatusSubresource(&ocv1.ClusterExtensionRevision{}). + WithObjects(secret, cos). + WithStatusSubresource(&ocv1.ClusterObjectSet{}). Build() - reconciler := &controllers.ClusterExtensionRevisionReconciler{ + reconciler := &controllers.ClusterObjectSetReconciler{ Client: fakeClient, RevisionEngineFactory: &mockRevisionEngineFactory{engine: &mockRevisionEngine{}}, TrackingCache: &mockTrackingCache{client: fakeClient}, @@ -248,14 +248,14 @@ func TestResolveObjectRef_InvalidJSON(t *testing.T) { } _, err := reconciler.Reconcile(context.Background(), ctrl.Request{ - NamespacedName: types.NamespacedName{Name: cer.Name}, + NamespacedName: types.NamespacedName{Name: cos.Name}, }) require.Error(t, err) assert.Contains(t, err.Error(), "unmarshal") } -func newRefTestCER(name string, ref ocv1.ObjectSourceRef) *ocv1.ClusterExtensionRevision { - cer := &ocv1.ClusterExtensionRevision{ +func newRefTestCOS(name string, ref ocv1.ObjectSourceRef) *ocv1.ClusterObjectSet { + cos := &ocv1.ClusterObjectSet{ ObjectMeta: metav1.ObjectMeta{ Name: name, UID: types.UID(name), @@ -263,14 +263,14 @@ func newRefTestCER(name string, ref ocv1.ObjectSourceRef) *ocv1.ClusterExtension labels.OwnerNameKey: "test-ext", }, }, - Spec: ocv1.ClusterExtensionRevisionSpec{ - LifecycleState: ocv1.ClusterExtensionRevisionLifecycleStateActive, + Spec: ocv1.ClusterObjectSetSpec{ + LifecycleState: ocv1.ClusterObjectSetLifecycleStateActive, Revision: 1, CollisionProtection: ocv1.CollisionProtectionPrevent, - Phases: []ocv1.ClusterExtensionRevisionPhase{ + Phases: []ocv1.ClusterObjectSetPhase{ { Name: "deploy", - Objects: []ocv1.ClusterExtensionRevisionObject{ + Objects: []ocv1.ClusterObjectSetObject{ { Ref: ref, }, @@ -279,6 +279,6 @@ func newRefTestCER(name string, ref ocv1.ObjectSourceRef) *ocv1.ClusterExtension }, }, } - cer.SetGroupVersionKind(ocv1.GroupVersion.WithKind("ClusterExtensionRevision")) - return cer + cos.SetGroupVersionKind(ocv1.GroupVersion.WithKind("ClusterObjectSet")) + return cos } diff --git a/internal/operator-controller/controllers/revision_engine_factory.go b/internal/operator-controller/controllers/revision_engine_factory.go index f87d930b38..b63536d70a 100644 --- a/internal/operator-controller/controllers/revision_engine_factory.go +++ b/internal/operator-controller/controllers/revision_engine_factory.go @@ -1,6 +1,6 @@ //go:build !standard -// This file is excluded from standard builds because ClusterExtensionRevision +// This file is excluded from standard builds because ClusterObjectSet // is an experimental feature. Standard builds use Helm-based applier only. // The experimental build includes BoxcutterRuntime which requires these factories // for serviceAccount-scoped client creation and RevisionEngine instantiation. @@ -35,9 +35,9 @@ type RevisionEngine interface { Reconcile(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error) } -// RevisionEngineFactory creates a RevisionEngine for a ClusterExtensionRevision. +// RevisionEngineFactory creates a RevisionEngine for a ClusterObjectSet. type RevisionEngineFactory interface { - CreateRevisionEngine(ctx context.Context, rev *ocv1.ClusterExtensionRevision) (RevisionEngine, error) + CreateRevisionEngine(ctx context.Context, rev *ocv1.ClusterObjectSet) (RevisionEngine, error) } // defaultRevisionEngineFactory creates boxcutter RevisionEngines with serviceAccount-scoped clients. @@ -51,9 +51,9 @@ type defaultRevisionEngineFactory struct { TokenGetter *authentication.TokenGetter } -// CreateRevisionEngine constructs a boxcutter RevisionEngine for the given ClusterExtensionRevision. +// CreateRevisionEngine constructs a boxcutter RevisionEngine for the given ClusterObjectSet. // It reads the ServiceAccount from annotations and creates a scoped client. -func (f *defaultRevisionEngineFactory) CreateRevisionEngine(_ context.Context, rev *ocv1.ClusterExtensionRevision) (RevisionEngine, error) { +func (f *defaultRevisionEngineFactory) CreateRevisionEngine(_ context.Context, rev *ocv1.ClusterObjectSet) (RevisionEngine, error) { saNamespace, saName, err := f.getServiceAccount(rev) if err != nil { return nil, err @@ -77,7 +77,7 @@ func (f *defaultRevisionEngineFactory) CreateRevisionEngine(_ context.Context, r ), nil } -func (f *defaultRevisionEngineFactory) getServiceAccount(rev *ocv1.ClusterExtensionRevision) (string, string, error) { +func (f *defaultRevisionEngineFactory) getServiceAccount(rev *ocv1.ClusterObjectSet) (string, string, error) { annotations := rev.GetAnnotations() if annotations == nil { return "", "", fmt.Errorf("revision %q is missing required annotations", rev.Name) diff --git a/internal/operator-controller/labels/labels.go b/internal/operator-controller/labels/labels.go index 9020dec658..27d34e9a80 100644 --- a/internal/operator-controller/labels/labels.go +++ b/internal/operator-controller/labels/labels.go @@ -2,51 +2,51 @@ package labels const ( // OwnerKindKey is the label key used to record the kind of the owner - // resource responsible for creating or managing a ClusterExtensionRevision. + // resource responsible for creating or managing a ClusterObjectSet. OwnerKindKey = "olm.operatorframework.io/owner-kind" // OwnerNameKey is the label key used to record the name of the owner - // resource responsible for creating or managing a ClusterExtensionRevision. + // resource responsible for creating or managing a ClusterObjectSet. OwnerNameKey = "olm.operatorframework.io/owner-name" // PackageNameKey is the label key used to record the package name - // associated with a ClusterExtensionRevision. + // associated with a ClusterObjectSet. PackageNameKey = "olm.operatorframework.io/package-name" // BundleNameKey is the label key used to record the bundle name - // associated with a ClusterExtensionRevision. + // associated with a ClusterObjectSet. BundleNameKey = "olm.operatorframework.io/bundle-name" // BundleVersionKey is the label key used to record the bundle version - // associated with a ClusterExtensionRevision. + // associated with a ClusterObjectSet. BundleVersionKey = "olm.operatorframework.io/bundle-version" // BundleReferenceKey is the label key used to record an external reference // (such as an image or catalog reference) to the bundle for a - // ClusterExtensionRevision. + // ClusterObjectSet. BundleReferenceKey = "olm.operatorframework.io/bundle-reference" // ServiceAccountNameKey is the annotation key used to record the name of // the ServiceAccount configured on the owning ClusterExtension. It is - // applied as an annotation on ClusterExtensionRevision resources to + // applied as an annotation on ClusterObjectSet resources to // capture which ServiceAccount was used for their lifecycle operations. ServiceAccountNameKey = "olm.operatorframework.io/service-account-name" // ServiceAccountNamespaceKey is the annotation key used to record the // namespace of the ServiceAccount configured on the owning // ClusterExtension. It is applied as an annotation on - // ClusterExtensionRevision resources together with ServiceAccountNameKey + // ClusterObjectSet resources together with ServiceAccountNameKey // so that the effective ServiceAccount identity used for - // ClusterExtensionRevision operations is preserved. + // ClusterObjectSet operations is preserved. ServiceAccountNamespaceKey = "olm.operatorframework.io/service-account-namespace" // RevisionNameKey is the label key used to record the name of the - // ClusterExtensionRevision that owns or references a resource (e.g. a + // ClusterObjectSet that owns or references a resource (e.g. a // ref Secret). It enables efficient listing of all resources associated // with a specific revision. RevisionNameKey = "olm.operatorframework.io/revision-name" - // MigratedFromHelmKey is the label key used to mark ClusterExtensionRevisions + // MigratedFromHelmKey is the label key used to mark ClusterObjectSets // that were created during migration from Helm releases. This label is used // to distinguish migrated revisions from those created by normal Boxcutter operation. MigratedFromHelmKey = "olm.operatorframework.io/migrated-from-helm" diff --git a/internal/shared/util/test/artifacts.go b/internal/shared/util/test/artifacts.go index dc10546923..727b915e71 100644 --- a/internal/shared/util/test/artifacts.go +++ b/internal/shared/util/test/artifacts.go @@ -25,7 +25,7 @@ import ( // CollectTestArtifacts gets all the artifacts from the test run and saves them to the artifact path. // Currently, it saves: // - clusterextensions -// - clusterextensionrevisions +// - clusterobjectsets // - pods logs // - deployments // - catalogsources @@ -70,20 +70,20 @@ func CollectTestArtifacts(t *testing.T, artifactName string, c client.Client, cf } } - // get all cluster extension revisions save them to the artifact path. - clusterExtensionRevisions := ocv1.ClusterExtensionRevisionList{} - if err := c.List(context.Background(), &clusterExtensionRevisions); err != nil { - fmt.Printf("Failed to list cluster extensions: %v", err) + // get all cluster object sets save them to the artifact path. + clusterObjectSets := ocv1.ClusterObjectSetList{} + if err := c.List(context.Background(), &clusterObjectSets); err != nil { + fmt.Printf("Failed to list cluster object sets: %v", err) } - for _, cer := range clusterExtensionRevisions.Items { - // Save cluster extension to artifact path - clusterExtensionYaml, err := yaml.Marshal(cer) + for _, cos := range clusterObjectSets.Items { + // Save cluster object set to artifact path + clusterObjectSetYaml, err := yaml.Marshal(cos) if err != nil { - fmt.Printf("Failed to marshal cluster extension: %v", err) + fmt.Printf("Failed to marshal cluster object set: %v", err) continue } - if err := os.WriteFile(filepath.Join(artifactPath, cer.Name+"-clusterextensionrevision.yaml"), clusterExtensionYaml, 0600); err != nil { - fmt.Printf("Failed to write cluster extension to file: %v", err) + if err := os.WriteFile(filepath.Join(artifactPath, cos.Name+"-clusterobjectset.yaml"), clusterObjectSetYaml, 0600); err != nil { + fmt.Printf("Failed to write cluster object set to file: %v", err) } } diff --git a/manifests/experimental-e2e.yaml b/manifests/experimental-e2e.yaml index 86af261153..45d773af4d 100644 --- a/manifests/experimental-e2e.yaml +++ b/manifests/experimental-e2e.yaml @@ -610,26 +610,32 @@ spec: subresources: status: {} --- -# Source: olmv1/templates/crds/customresourcedefinition-clusterextensionrevisions.olm.operatorframework.io.yml +# Source: olmv1/templates/crds/customresourcedefinition-clusterextensions.olm.operatorframework.io.yml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.20.1 olm.operatorframework.io/generator: experimental - name: clusterextensionrevisions.olm.operatorframework.io + name: clusterextensions.olm.operatorframework.io spec: group: olm.operatorframework.io names: - kind: ClusterExtensionRevision - listKind: ClusterExtensionRevisionList - plural: clusterextensionrevisions - singular: clusterextensionrevision + kind: ClusterExtension + listKind: ClusterExtensionList + plural: clusterextensions + singular: clusterextension scope: Cluster versions: - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=='Available')].status - name: Available + - jsonPath: .status.install.bundle.name + name: Installed Bundle + type: string + - jsonPath: .status.install.bundle.version + name: Version + type: string + - jsonPath: .status.conditions[?(@.type=='Installed')].status + name: Installed type: string - jsonPath: .status.conditions[?(@.type=='Progressing')].status name: Progressing @@ -640,13 +646,7 @@ spec: name: v1 schema: openAPIV3Schema: - description: |- - ClusterExtensionRevision represents an immutable snapshot of Kubernetes objects - for a specific version of a ClusterExtension. Each revision contains objects - organized into phases that roll out sequentially. The same object can only be managed by a single revision - at a time. Ownership of objects is transitioned from one revision to the next as the extension is upgraded - or reconfigured. Once the latest revision has rolled out successfully, previous active revisions are archived for - posterity. + description: ClusterExtension is the Schema for the clusterextensions API properties: apiVersion: description: |- @@ -666,200 +666,117 @@ spec: metadata: type: object spec: - description: spec defines the desired state of the ClusterExtensionRevision. + description: spec is an optional field that defines the desired state + of the ClusterExtension. properties: - collisionProtection: + config: description: |- - 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. + config is optional and specifies bundle-specific configuration. + Configuration is bundle-specific and a bundle may provide a configuration schema. + When not specified, the default configuration of the resolved bundle is used. - The resolution order is: object > phase > spec - enum: - - Prevent - - IfNoController - - None - type: string - x-kubernetes-validations: - - message: collisionProtection is immutable - rule: self == oldSelf - lifecycleState: - description: |- - lifecycleState specifies the lifecycle state of the ClusterExtensionRevision. + config is validated against a configuration schema provided by the resolved bundle. If the bundle does not provide + a configuration schema the bundle is deemed to not be configurable. More information on how + to configure bundles can be found in the OLM documentation associated with your current OLM version. + properties: + configType: + description: |- + configType is required and specifies the type of configuration source. - When set to "Active", the revision is actively managed and reconciled. - When set to "Archived", the revision is inactive and any resources not managed by a subsequent revision are deleted. - The revision is removed from the owner list of all objects previously under management. - All objects that did not transition to a succeeding revision are deleted. + The only allowed value is "Inline". - Once a revision is set to "Archived", it cannot be un-archived. + When set to "Inline", the cluster extension configuration is defined inline within the ClusterExtension resource. + enum: + - Inline + type: string + inline: + description: |- + inline contains JSON or YAML values specified directly in the ClusterExtension. - It is possible for more than one revision to be "Active" simultaneously. This will occur when - moving from one revision to another. The old revision will not be set to "Archived" until the - new revision has been completely rolled out. - enum: - - Active - - Archived - type: string + It is used to specify arbitrary configuration values for the ClusterExtension. + It must be set if configType is 'Inline' and must be a valid JSON/YAML object containing at least one property. + The configuration values are validated at runtime against a JSON schema provided by the bundle. + minProperties: 1 + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configType + type: object x-kubernetes-validations: - - message: cannot un-archive - rule: oldSelf == 'Active' || oldSelf == 'Archived' && oldSelf == - self - phases: + - message: inline is required when configType is Inline, and forbidden + otherwise + rule: 'has(self.configType) && self.configType == ''Inline'' ?has(self.inline) + : !has(self.inline)' + install: description: |- - phases is an optional, immutable list of phases that group objects to be applied together. - - Objects are organized into phases based on their Group-Kind. Common phases include: - - namespaces: Namespace objects - - policies: ResourceQuota, LimitRange, NetworkPolicy objects - - rbac: ServiceAccount, Role, RoleBinding, ClusterRole, ClusterRoleBinding objects - - crds: CustomResourceDefinition objects - - storage: PersistentVolume, PersistentVolumeClaim, StorageClass objects - - deploy: Deployment, StatefulSet, DaemonSet, Service, ConfigMap, Secret objects - - publish: Ingress, APIService, Route, Webhook objects - - All objects in a phase are applied in no particular order. - The revision progresses to the next phase only after all objects in the current phase pass their readiness probes. - - Once set, even if empty, the phases field is immutable. - - Each phase in the list must have a unique name. The maximum number of phases is 20. - items: - description: |- - ClusterExtensionRevisionPhase represents a group of objects that are applied together. The phase is considered - complete only after all objects pass their status probes. - properties: - collisionProtection: - description: |- - collisionProtection specifies the default collision protection strategy for all objects - in this phase. Individual objects can override this value. - - When set, this value is used as the default for any object in this phase that does not - explicitly specify its own collisionProtection. - - When omitted, we use .spec.collistionProtection as the default for any object in this phase that does not - explicitly specify its own collisionProtection. - enum: - - Prevent - - IfNoController - - None - type: string - name: - description: |- - name is a required identifier for this phase. - - phase names must follow the DNS label standard as defined in [RFC 1123]. - They must contain only lowercase alphanumeric characters or hyphens (-), - start and end with an alphanumeric character, and be no longer than 63 characters. - - Common phase names include: namespaces, policies, rbac, crds, storage, deploy, publish. - - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 63 - minLength: 1 - type: string - x-kubernetes-validations: - - message: the value must consist of only lowercase alphanumeric - characters and hyphens, and must start with an alphabetic - character and end with an alphanumeric character. - rule: '!format.dns1123Label().validate(self).hasValue()' - objects: - description: |- - objects is a required list of all Kubernetes objects that belong to this phase. + install is optional and configures installation options for the ClusterExtension, + such as the pre-flight check configuration. + properties: + preflight: + description: |- + preflight is optional and configures the checks that run before installation or upgrade + of the content for the package specified in the packageName field. - All objects in this list are applied to the cluster in no particular order. The maximum number of objects per phase is 50. - items: + When specified, it replaces the default preflight configuration for install/upgrade actions. + When not specified, the default configuration is used. + properties: + crdUpgradeSafety: description: |- - ClusterExtensionRevisionObject represents a Kubernetes object to be applied as part - of a phase, along with its collision protection settings. + crdUpgradeSafety configures the CRD Upgrade Safety pre-flight checks that run + before upgrades of installed content. - Exactly one of object or ref must be set. + The CRD Upgrade Safety pre-flight check safeguards from unintended consequences of upgrading a CRD, + such as data loss. properties: - collisionProtection: + enforcement: description: |- - collisionProtection controls whether the operator can adopt and modify objects - that already exist on the cluster. - - Allowed values are: "Prevent", "IfNoController", and "None". - - When set to "Prevent", the operator only manages objects it created itself. - This prevents ownership collisions. + enforcement is required and configures the state of the CRD Upgrade Safety pre-flight check. - When set to "IfNoController", the operator can adopt and modify pre-existing objects - that are not owned by another controller. - This is useful for taking over management of manually-created resources. + Allowed values are "None" or "Strict". The default value is "Strict". - When set to "None", the operator can adopt and modify any pre-existing object, even if - owned by another controller. - Use this setting with extreme caution as it may cause multiple controllers to fight over - the same resource, resulting in increased load on the API server and etcd. + When set to "None", the CRD Upgrade Safety pre-flight check is skipped during an upgrade operation. + Use this option with caution as unintended consequences such as data loss can occur. - When omitted, the value is inherited from the phase, then spec. + When set to "Strict", the CRD Upgrade Safety pre-flight check runs during an upgrade operation. enum: - - Prevent - - IfNoController - None + - Strict type: string - object: - description: |- - object is an optional embedded Kubernetes object to be applied. - - Exactly one of object or ref must be set. + required: + - enforcement + type: object + required: + - crdUpgradeSafety + type: object + x-kubernetes-validations: + - message: at least one of [crdUpgradeSafety] are required when + preflight is specified + rule: has(self.crdUpgradeSafety) + type: object + x-kubernetes-validations: + - message: at least one of [preflight] are required when install is + specified + rule: has(self.preflight) + namespace: + description: |- + namespace specifies a Kubernetes namespace. + This is the namespace where the provided ServiceAccount must exist. + It also designates the default namespace where namespace-scoped resources for the extension are applied to the cluster. + Some extensions may contain namespace-scoped resources to be applied in other namespaces. + This namespace must exist. - This object must be a valid Kubernetes resource with apiVersion, kind, and metadata fields. - type: object - x-kubernetes-embedded-resource: true - x-kubernetes-preserve-unknown-fields: true - ref: - description: |- - ref is an optional reference to a Secret that holds the serialized - object manifest. + The namespace field is required, immutable, and follows the DNS label standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character, + and be no longer than 63 characters. - Exactly one of object or ref must be set. - properties: - key: - description: |- - key is the data key within the referenced Secret containing the - object manifest content. The value at this key must be a - JSON-serialized Kubernetes object manifest. - maxLength: 253 - minLength: 1 - type: string - name: - description: name is the name of the referenced Secret. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - namespace is the namespace of the referenced Secret. - When empty, defaults to the OLM system namespace during ref resolution. - maxLength: 63 - type: string - required: - - key - - name - type: object - type: object - x-kubernetes-validations: - - message: exactly one of object or ref must be set - rule: has(self.object) != has(self.ref) - maxItems: 50 - type: array - required: - - name - - objects - type: object - maxItems: 20 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 63 + type: string x-kubernetes-validations: - - message: phases is immutable - rule: self == oldSelf || oldSelf.size() == 0 + - message: namespace is immutable + rule: self == oldSelf + - message: namespace must be a valid DNS1123 label + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") progressDeadlineMinutes: description: |- progressDeadlineMinutes is an optional field that defines the maximum period @@ -870,983 +787,1104 @@ spec: maximum: 720 minimum: 10 type: integer - progressionProbes: + serviceAccount: description: |- - progressionProbes is an optional field which provides the ability to define custom readiness probes - for objects defined within spec.phases. As documented in that field, most kubernetes-native objects - within the phases already have some kind of readiness check built-in, but this field allows for checks - which are tailored to the objects being rolled out - particularly custom resources. + serviceAccount specifies a ServiceAccount used to perform all interactions with the cluster + that are required to manage the extension. + The ServiceAccount must be configured with the necessary permissions to perform these interactions. + The ServiceAccount must exist in the namespace referenced in the spec. + The serviceAccount field is required. + properties: + name: + description: |- + name is a required, immutable reference to the name of the ServiceAccount used for installation + and management of the content for the package specified in the packageName field. - Probes defined within the progressionProbes list will apply to every phase in the revision. However, the probes will only - execute against phase objects which are a match for the provided selector type. For instance, a probe using a GroupKind selector - for ConfigMaps will automatically be considered to have passed for any non-ConfigMap object, but will halt any phase containing - a ConfigMap if that particular object does not pass the probe check. + This ServiceAccount must exist in the installNamespace. - The maximum number of probes is 20. - items: - description: ProgressionProbe provides a custom probe definition, - consisting of an object selection method and assertions. - properties: - assertions: - description: |- - assertions is a required list of checks which will run against the objects selected by the selector. If - one or more assertions fail then the phase within which the object lives will be not be considered - 'Ready', blocking rollout of all subsequent phases. - items: - description: Assertion is a discriminated union which defines - the probe type and definition used as an assertion. - properties: - conditionEqual: - description: conditionEqual contains the expected condition - type and status. - properties: - status: - description: |- - status sets the expected condition status. + The name field follows the DNS subdomain standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), + start and end with an alphanumeric character, and be no longer than 253 characters. - Allowed values are "True" and "False". - enum: - - "True" - - "False" - type: string - type: - description: type sets the expected condition type, - i.e. "Ready". - maxLength: 200 - minLength: 1 - type: string - required: - - status - - type - type: object - fieldValue: - description: fieldValue contains the expected field path - and value found within. - properties: - fieldPath: - description: |- - fieldPath sets the field path for the field to check, i.e. "status.phase". The probe will fail - if the path does not exist. - maxLength: 200 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must contain a valid field path. valid - fields contain upper or lower-case alphanumeric - characters separated by the "." character. - rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') - value: - description: value sets the expected value found at - fieldPath, i.e. "Bound". - maxLength: 200 - minLength: 1 - type: string - required: - - fieldPath - - value - type: object - fieldsEqual: - description: fieldsEqual contains the two field paths - whose values are expected to match. - properties: - fieldA: - description: |- - fieldA sets the field path for the first field, i.e. "spec.replicas". The probe will fail - if the path does not exist. - maxLength: 200 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must contain a valid field path. valid - fields contain upper or lower-case alphanumeric - characters separated by the "." character. - rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') - fieldB: - description: |- - fieldB sets the field path for the second field, i.e. "status.readyReplicas". The probe will fail - if the path does not exist. - maxLength: 200 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must contain a valid field path. valid - fields contain upper or lower-case alphanumeric - characters separated by the "." character. - rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') - required: - - fieldA - - fieldB - type: object - type: - description: |- - type is a required field which specifies the type of probe to use. + Some examples of valid values are: + - some-serviceaccount + - 123-serviceaccount + - 1-serviceaccount-2 + - someserviceaccount + - some.serviceaccount - The allowed probe types are "ConditionEqual", "FieldsEqual", and "FieldValue". + Some examples of invalid values are: + - -some-serviceaccount + - some-serviceaccount- - When set to "ConditionEqual", the probe checks objects that have reached a condition of specified type and status. - When set to "FieldsEqual", the probe checks that the values found at two provided field paths are matching. - When set to "FieldValue", the probe checks that the value found at the provided field path matches what was specified. - enum: - - ConditionEqual - - FieldsEqual - - FieldValue - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: conditionEqual is required when type is ConditionEqual, - and forbidden otherwise - rule: 'self.type == ''ConditionEqual'' ?has(self.conditionEqual) - : !has(self.conditionEqual)' - - message: fieldsEqual is required when type is FieldsEqual, - and forbidden otherwise - rule: 'self.type == ''FieldsEqual'' ?has(self.fieldsEqual) - : !has(self.fieldsEqual)' - - message: fieldValue is required when type is FieldValue, - and forbidden otherwise - rule: 'self.type == ''FieldValue'' ?has(self.fieldValue) - : !has(self.fieldValue)' - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - selector: - description: |- - selector is a required field which defines the method by which we select objects to apply the below - assertions to. Any object which matches the defined selector will have all the associated assertions - applied against it. + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 253 + type: string + x-kubernetes-validations: + - message: name is immutable + rule: self == oldSelf + - message: name must be a valid DNS1123 subdomain. It must contain + only lowercase alphanumeric characters, hyphens (-) or periods + (.), start and end with an alphanumeric character, and be + no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + required: + - name + type: object + source: + description: |- + source is required and selects the installation source of content for this ClusterExtension. + Set the sourceType field to perform the selection. - If no objects within a phase are selected by the provided selector, then all assertions defined here - are considered to have succeeded. - properties: - groupKind: - description: |- - groupKind specifies the group and kind of objects to select. + Catalog is currently the only implemented sourceType. + Setting sourceType to "Catalog" requires the catalog field to also be defined. - Required when type is "GroupKind". + Below is a minimal example of a source definition (in yaml): - Uses the Kubernetes format specified here: - https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#GroupKind - properties: - group: - type: string - kind: - type: string - required: - - group - - kind - type: object - label: - description: |- - label is the label selector definition. + source: + sourceType: Catalog + catalog: + packageName: example-package + properties: + catalog: + description: |- + catalog configures how information is sourced from a catalog. + It is required when sourceType is "Catalog", and forbidden otherwise. + properties: + channels: + description: |- + channels is optional and specifies a set of channels belonging to the package + specified in the packageName field. - Required when type is "Label". + A channel is a package-author-defined stream of updates for an extension. - A probe using a Label selector will be executed against every object matching the labels or expressions; you must use care - when using this type of selector. For example, if multiple Kind objects are selected via labels then the probe is - likely to fail because the values of different Kind objects rarely share the same schema. + Each channel in the list must follow the DNS subdomain standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), + start and end with an alphanumeric character, and be no longer than 253 characters. + You can specify no more than 256 channels. - The LabelSelector field uses the following Kubernetes format: - https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#LabelSelector - Requires exactly one of matchLabels or matchExpressions. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - x-kubernetes-validations: - - message: exactly one of matchLabels or matchExpressions - must be set - rule: (has(self.matchExpressions) && !has(self.matchLabels)) - || (!has(self.matchExpressions) && has(self.matchLabels)) - type: - description: |- - type is a required field which specifies the type of selector to use. + When specified, it constrains the set of installable bundles and the automated upgrade path. + This constraint is an AND operation with the version field. For example: + - Given channel is set to "foo" + - Given version is set to ">=1.0.0, <1.5.0" + - Only bundles that exist in channel "foo" AND satisfy the version range comparison are considered installable + - Automatic upgrades are constrained to upgrade edges defined by the selected channel - The allowed selector types are "GroupKind" and "Label". + When unspecified, upgrade edges across all channels are used to identify valid automatic upgrade paths. - When set to "GroupKind", all objects which match the specified group and kind will be selected. - When set to "Label", all objects which match the specified labels and/or expressions will be selected. - enum: - - GroupKind - - Label + Some examples of valid values are: + - 1.1.x + - alpha + - stable + - stable-v1 + - v1-stable + - dev-preview + - preview + - community + + Some examples of invalid values are: + - -some-channel + - some-channel- + - thisisareallylongchannelnamethatisgreaterthanthemaximumlength + - original_40 + - --default-channel + + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + items: + maxLength: 253 type: string - required: - - type - type: object - x-kubernetes-validations: - - message: groupKind is required when type is GroupKind, and - forbidden otherwise - rule: 'self.type == ''GroupKind'' ?has(self.groupKind) : !has(self.groupKind)' - - message: label is required when type is Label, and forbidden - otherwise - rule: 'self.type == ''Label'' ?has(self.label) : !has(self.label)' - required: - - assertions - - selector - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - revision: - description: |- - revision is a required, immutable sequence number representing a specific revision - of the parent ClusterExtension. + x-kubernetes-validations: + - message: channels entries must be valid DNS1123 subdomains + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + maxItems: 256 + type: array + packageName: + description: |- + packageName specifies the name of the package to be installed and is used to filter + the content from catalogs. - The revision field must be a positive integer. - Each ClusterExtensionRevision belonging to the same parent ClusterExtension must have a unique revision number. - The revision number must always be the previous revision number plus one, or 1 for the first revision. - format: int64 - minimum: 1 - type: integer - x-kubernetes-validations: - - message: revision is immutable - rule: self == oldSelf - required: - - collisionProtection - - lifecycleState - - revision - type: object - status: - description: status is optional and defines the observed state of the - ClusterExtensionRevision. - properties: - conditions: - description: |- - conditions is an optional list of status conditions describing the state of the - ClusterExtensionRevision. + It is required, immutable, and follows the DNS subdomain standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), + start and end with an alphanumeric character, and be no longer than 253 characters. - The Progressing condition represents whether the revision is actively rolling out: - - When status is True and reason is RollingOut, the ClusterExtensionRevision rollout is actively making progress and is in transition. - - When status is True and reason is Retrying, the ClusterExtensionRevision has encountered an error that could be resolved on subsequent reconciliation attempts. - - When status is True and reason is Succeeded, the ClusterExtensionRevision has reached the desired state. - - When status is False and reason is Blocked, the ClusterExtensionRevision has encountered an error that requires manual intervention for recovery. - - When status is False and reason is Archived, the ClusterExtensionRevision is archived and not being actively reconciled. + Some examples of valid values are: + - some-package + - 123-package + - 1-package-2 + - somepackage - The Available condition represents whether the revision has been successfully rolled out and is available: - - When status is True and reason is ProbesSucceeded, the ClusterExtensionRevision has been successfully rolled out and all objects pass their readiness probes. - - When status is False and reason is ProbeFailure, one or more objects are failing their readiness probes during rollout. - - When status is Unknown and reason is Reconciling, the ClusterExtensionRevision has encountered an error that prevented it from observing the probes. - - When status is Unknown and reason is Archived, the ClusterExtensionRevision has been archived and its objects have been torn down. - - When status is Unknown and reason is Migrated, the ClusterExtensionRevision was migrated from an existing release and object status probe results have not yet been observed. + Some examples of invalid values are: + - -some-package + - some-package- + - thisisareallylongpackagenamethatisgreaterthanthemaximumlength + - some.package - The Succeeded condition represents whether the revision has successfully completed its rollout: - - When status is True and reason is Succeeded, the ClusterExtensionRevision has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -# Source: olmv1/templates/crds/customresourcedefinition-clusterextensions.olm.operatorframework.io.yml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.20.1 - olm.operatorframework.io/generator: experimental - name: clusterextensions.olm.operatorframework.io -spec: - group: olm.operatorframework.io - names: - kind: ClusterExtension - listKind: ClusterExtensionList - plural: clusterextensions - singular: clusterextension - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .status.install.bundle.name - name: Installed Bundle - type: string - - jsonPath: .status.install.bundle.version - name: Version - type: string - - jsonPath: .status.conditions[?(@.type=='Installed')].status - name: Installed - type: string - - jsonPath: .status.conditions[?(@.type=='Progressing')].status - name: Progressing - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: ClusterExtension is the Schema for the clusterextensions API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: spec is an optional field that defines the desired state - of the ClusterExtension. - properties: - config: - description: |- - config is optional and specifies bundle-specific configuration. - Configuration is bundle-specific and a bundle may provide a configuration schema. - When not specified, the default configuration of the resolved bundle is used. + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 253 + type: string + x-kubernetes-validations: + - message: packageName is immutable + rule: self == oldSelf + - message: packageName must be a valid DNS1123 subdomain. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric + character, and be no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + selector: + description: |- + selector is optional and filters the set of ClusterCatalogs used in the bundle selection process. + + When unspecified, all ClusterCatalogs are used in the bundle selection process. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + upgradeConstraintPolicy: + default: CatalogProvided + description: |- + upgradeConstraintPolicy is optional and controls whether the upgrade paths defined in the catalog + are enforced for the package referenced in the packageName field. + + Allowed values are "CatalogProvided", "SelfCertified", or omitted. + + When set to "CatalogProvided", automatic upgrades only occur when upgrade constraints specified by the package + author are met. + + When set to "SelfCertified", the upgrade constraints specified by the package author are ignored. + This allows upgrades and downgrades to any version of the package. + This is considered a dangerous operation as it can lead to unknown and potentially disastrous outcomes, + such as data loss. + Use this option only if you have independently verified the changes. + + When omitted, the default value is "CatalogProvided". + enum: + - CatalogProvided + - SelfCertified + type: string + version: + description: |- + version is an optional semver constraint (a specific version or range of versions). + When unspecified, the latest version available is installed. - config is validated against a configuration schema provided by the resolved bundle. If the bundle does not provide - a configuration schema the bundle is deemed to not be configurable. More information on how - to configure bundles can be found in the OLM documentation associated with your current OLM version. - properties: - configType: + Acceptable version ranges are no longer than 64 characters. + Version ranges are composed of comma- or space-delimited values and one or more comparison operators, + known as comparison strings. + You can add additional comparison strings using the OR operator (||). + + # Range Comparisons + + To specify a version range, you can use a comparison string like ">=3.0, + <3.6". When specifying a range, automatic updates will occur within that + range. The example comparison string means "install any version greater than + or equal to 3.0.0 but less than 3.6.0.". It also states intent that if any + upgrades are available within the version range after initial installation, + those upgrades should be automatically performed. + + # Pinned Versions + + To specify an exact version to install you can use a version range that + "pins" to a specific version. When pinning to a specific version, no + automatic updates will occur. An example of a pinned version range is + "0.6.0", which means "only install version 0.6.0 and never + upgrade from this version". + + # Basic Comparison Operators + + The basic comparison operators and their meanings are: + - "=", equal (not aliased to an operator) + - "!=", not equal + - "<", less than + - ">", greater than + - ">=", greater than OR equal to + - "<=", less than OR equal to + + # Wildcard Comparisons + + You can use the "x", "X", and "*" characters as wildcard characters in all + comparison operations. Some examples of using the wildcard characters: + - "1.2.x", "1.2.X", and "1.2.*" is equivalent to ">=1.2.0, < 1.3.0" + - ">= 1.2.x", ">= 1.2.X", and ">= 1.2.*" is equivalent to ">= 1.2.0" + - "<= 2.x", "<= 2.X", and "<= 2.*" is equivalent to "< 3" + - "x", "X", and "*" is equivalent to ">= 0.0.0" + + # Patch Release Comparisons + + When you want to specify a minor version up to the next major version you + can use the "~" character to perform patch comparisons. Some examples: + - "~1.2.3" is equivalent to ">=1.2.3, <1.3.0" + - "~1" and "~1.x" is equivalent to ">=1, <2" + - "~2.3" is equivalent to ">=2.3, <2.4" + - "~1.2.x" is equivalent to ">=1.2.0, <1.3.0" + + # Major Release Comparisons + + You can use the "^" character to make major release comparisons after a + stable 1.0.0 version is published. If there is no stable version published, // minor versions define the stability level. Some examples: + - "^1.2.3" is equivalent to ">=1.2.3, <2.0.0" + - "^1.2.x" is equivalent to ">=1.2.0, <2.0.0" + - "^2.3" is equivalent to ">=2.3, <3" + - "^2.x" is equivalent to ">=2.0.0, <3" + - "^0.2.3" is equivalent to ">=0.2.3, <0.3.0" + - "^0.2" is equivalent to ">=0.2.0, <0.3.0" + - "^0.0.3" is equvalent to ">=0.0.3, <0.0.4" + - "^0.0" is equivalent to ">=0.0.0, <0.1.0" + - "^0" is equivalent to ">=0.0.0, <1.0.0" + + # OR Comparisons + You can use the "||" character to represent an OR operation in the version + range. Some examples: + - ">=1.2.3, <2.0.0 || >3.0.0" + - "^0 || ^3 || ^5" + + For more information on semver, please see https://semver.org/ + maxLength: 64 + type: string + x-kubernetes-validations: + - message: invalid version expression + rule: self.matches("^(\\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|[x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*]))?(\\.(0|[1-9]\\d*|x|X|\\*))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)((?:\\s+|,\\s*|\\s*\\|\\|\\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*))?(\\.(0|[1-9]\\d*|x|X|\\*]))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)*$") + required: + - packageName + type: object + sourceType: description: |- - configType is required and specifies the type of configuration source. + sourceType is required and specifies the type of install source. - The only allowed value is "Inline". + The only allowed value is "Catalog". - When set to "Inline", the cluster extension configuration is defined inline within the ClusterExtension resource. + When set to "Catalog", information for determining the appropriate bundle of content to install + is fetched from ClusterCatalog resources on the cluster. + When using the Catalog sourceType, the catalog field must also be set. enum: - - Inline + - Catalog type: string - inline: - description: |- - inline contains JSON or YAML values specified directly in the ClusterExtension. - - It is used to specify arbitrary configuration values for the ClusterExtension. - It must be set if configType is 'Inline' and must be a valid JSON/YAML object containing at least one property. - The configuration values are validated at runtime against a JSON schema provided by the bundle. - minProperties: 1 - type: object - x-kubernetes-preserve-unknown-fields: true required: - - configType + - sourceType type: object x-kubernetes-validations: - - message: inline is required when configType is Inline, and forbidden + - message: catalog is required when sourceType is Catalog, and forbidden otherwise - rule: 'has(self.configType) && self.configType == ''Inline'' ?has(self.inline) - : !has(self.inline)' - install: + rule: 'has(self.sourceType) && self.sourceType == ''Catalog'' ? + has(self.catalog) : !has(self.catalog)' + required: + - namespace + - serviceAccount + - source + type: object + status: + description: status is an optional field that defines the observed state + of the ClusterExtension. + properties: + activeRevisions: description: |- - install is optional and configures installation options for the ClusterExtension, - such as the pre-flight check configuration. - properties: - preflight: - description: |- - preflight is optional and configures the checks that run before installation or upgrade - of the content for the package specified in the packageName field. - - When specified, it replaces the default preflight configuration for install/upgrade actions. - When not specified, the default configuration is used. - properties: - crdUpgradeSafety: - description: |- - crdUpgradeSafety configures the CRD Upgrade Safety pre-flight checks that run - before upgrades of installed content. - - The CRD Upgrade Safety pre-flight check safeguards from unintended consequences of upgrading a CRD, - such as data loss. + activeRevisions holds a list of currently active (non-archived) ClusterObjectSets, + including both installed and rolling out revisions. + items: + description: RevisionStatus defines the observed state of a ClusterObjectSet. + properties: + conditions: + description: |- + conditions optionally expose Progressing and Available condition of the revision, + in case when it is not yet marked as successfully installed (condition Succeeded is not set to True). + Given that a ClusterExtension should remain available during upgrades, an observer may use these conditions + to get more insights about reasons for its current state. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. properties: - enforcement: + lastTransitionTime: description: |- - enforcement is required and configures the state of the CRD Upgrade Safety pre-flight check. - - Allowed values are "None" or "Strict". The default value is "Strict". - - When set to "None", the CRD Upgrade Safety pre-flight check is skipped during an upgrade operation. - Use this option with caution as unintended consequences such as data loss can occur. - - When set to "Strict", the CRD Upgrade Safety pre-flight check runs during an upgrade operation. + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. enum: - - None - - Strict + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - - enforcement - type: object - required: - - crdUpgradeSafety - type: object - x-kubernetes-validations: - - message: at least one of [crdUpgradeSafety] are required when - preflight is specified - rule: has(self.crdUpgradeSafety) - type: object - x-kubernetes-validations: - - message: at least one of [preflight] are required when install is - specified - rule: has(self.preflight) - namespace: - description: |- - namespace specifies a Kubernetes namespace. - This is the namespace where the provided ServiceAccount must exist. - It also designates the default namespace where namespace-scoped resources for the extension are applied to the cluster. - Some extensions may contain namespace-scoped resources to be applied in other namespaces. - This namespace must exist. - - The namespace field is required, immutable, and follows the DNS label standard as defined in [RFC 1123]. - It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character, - and be no longer than 63 characters. - - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 63 - type: string - x-kubernetes-validations: - - message: namespace is immutable - rule: self == oldSelf - - message: namespace must be a valid DNS1123 label - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") - progressDeadlineMinutes: - description: |- - progressDeadlineMinutes is an optional field that defines the maximum period - of time in minutes after which an installation should be considered failed and - require manual intervention. This functionality is disabled when no value - is provided. The minimum period is 10 minutes, and the maximum is 720 minutes (12 hours). - format: int32 - maximum: 720 - minimum: 10 - type: integer - serviceAccount: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + name: + description: name of the ClusterObjectSet resource + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + conditions: description: |- - serviceAccount specifies a ServiceAccount used to perform all interactions with the cluster - that are required to manage the extension. - The ServiceAccount must be configured with the necessary permissions to perform these interactions. - The ServiceAccount must exist in the namespace referenced in the spec. - The serviceAccount field is required. - properties: - name: - description: |- - name is a required, immutable reference to the name of the ServiceAccount used for installation - and management of the content for the package specified in the packageName field. - - This ServiceAccount must exist in the installNamespace. - - The name field follows the DNS subdomain standard as defined in [RFC 1123]. - It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), - start and end with an alphanumeric character, and be no longer than 253 characters. - - Some examples of valid values are: - - some-serviceaccount - - 123-serviceaccount - - 1-serviceaccount-2 - - someserviceaccount - - some.serviceaccount + conditions represents the current state of the ClusterExtension. - Some examples of invalid values are: - - -some-serviceaccount - - some-serviceaccount- + The set of condition types which apply to all spec.source variations are Installed and Progressing. - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 253 - type: string - x-kubernetes-validations: - - message: name is immutable - rule: self == oldSelf - - message: name must be a valid DNS1123 subdomain. It must contain - only lowercase alphanumeric characters, hyphens (-) or periods - (.), start and end with an alphanumeric character, and be - no longer than 253 characters - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") - required: - - name - type: object - source: - description: |- - source is required and selects the installation source of content for this ClusterExtension. - Set the sourceType field to perform the selection. + The Installed condition represents whether the bundle has been installed for this ClusterExtension: + - When Installed is True and the Reason is Succeeded, the bundle has been successfully installed. + - When Installed is False and the Reason is Failed, the bundle has failed to install. - Catalog is currently the only implemented sourceType. - Setting sourceType to "Catalog" requires the catalog field to also be defined. + The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. + When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. + When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. + When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. - Below is a minimal example of a source definition (in yaml): + When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterObjectSets in active roll out. - source: - sourceType: Catalog - catalog: - packageName: example-package + When the ClusterExtension is sourced from a catalog, it surfaces deprecation conditions based on catalog metadata. + These are indications from a package owner to guide users away from a particular package, channel, or bundle: + - BundleDeprecated is True if the installed bundle is marked deprecated, False if not deprecated, or Unknown if no bundle is installed yet or if catalog data is unavailable. + - ChannelDeprecated is True if any requested channel is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable. + - PackageDeprecated is True if the requested package is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable. + - Deprecated is a rollup condition that is True when any deprecation exists, False when none exist, or Unknown when catalog data is unavailable. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + install: + description: install is a representation of the current installation + status for this ClusterExtension. properties: - catalog: + bundle: description: |- - catalog configures how information is sourced from a catalog. - It is required when sourceType is "Catalog", and forbidden otherwise. + bundle is required and represents the identifying attributes of a bundle. + + A "bundle" is a versioned set of content that represents the resources that need to be applied + to a cluster to install a package. properties: - channels: + name: description: |- - channels is optional and specifies a set of channels belonging to the package - specified in the packageName field. - - A channel is a package-author-defined stream of updates for an extension. - - Each channel in the list must follow the DNS subdomain standard as defined in [RFC 1123]. + name is required and follows the DNS subdomain standard as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters. - You can specify no more than 256 channels. - - When specified, it constrains the set of installable bundles and the automated upgrade path. - This constraint is an AND operation with the version field. For example: - - Given channel is set to "foo" - - Given version is set to ">=1.0.0, <1.5.0" - - Only bundles that exist in channel "foo" AND satisfy the version range comparison are considered installable - - Automatic upgrades are constrained to upgrade edges defined by the selected channel - - When unspecified, upgrade edges across all channels are used to identify valid automatic upgrade paths. - - Some examples of valid values are: - - 1.1.x - - alpha - - stable - - stable-v1 - - v1-stable - - dev-preview - - preview - - community - - Some examples of invalid values are: - - -some-channel - - some-channel- - - thisisareallylongchannelnamethatisgreaterthanthemaximumlength - - original_40 - - --default-channel - - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - items: - maxLength: 253 - type: string - x-kubernetes-validations: - - message: channels entries must be valid DNS1123 subdomains - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") - maxItems: 256 - type: array - packageName: + type: string + x-kubernetes-validations: + - message: packageName must be a valid DNS1123 subdomain. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric + character, and be no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + version: description: |- - packageName specifies the name of the package to be installed and is used to filter - the content from catalogs. - - It is required, immutable, and follows the DNS subdomain standard as defined in [RFC 1123]. - It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), - start and end with an alphanumeric character, and be no longer than 253 characters. - - Some examples of valid values are: - - some-package - - 123-package - - 1-package-2 - - somepackage + version is required and references the version that this bundle represents. + It follows the semantic versioning standard as defined in https://semver.org/. + type: string + x-kubernetes-validations: + - message: version must be well-formed semver + rule: self.matches("^([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-([-0-9A-Za-z]+(\\.[-0-9A-Za-z]+)*))?(\\+([-0-9A-Za-z]+(-\\.[-0-9A-Za-z]+)*))?") + required: + - name + - version + type: object + required: + - bundle + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +# Source: olmv1/templates/crds/customresourcedefinition-clusterobjectsets.olm.operatorframework.io.yml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + olm.operatorframework.io/generator: experimental + name: clusterobjectsets.olm.operatorframework.io +spec: + group: olm.operatorframework.io + names: + kind: ClusterObjectSet + listKind: ClusterObjectSetList + plural: clusterobjectsets + singular: clusterobjectset + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Available')].status + name: Available + type: string + - jsonPath: .status.conditions[?(@.type=='Progressing')].status + name: Progressing + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + ClusterObjectSet represents an immutable snapshot of Kubernetes objects + for a specific version of a ClusterExtension. Each revision contains objects + organized into phases that roll out sequentially. The same object can only be managed by a single revision + at a time. Ownership of objects is transitioned from one revision to the next as the extension is upgraded + or reconfigured. Once the latest revision has rolled out successfully, previous active revisions are archived for + posterity. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: spec defines the desired state of the ClusterObjectSet. + properties: + collisionProtection: + description: |- + collisionProtection specifies the default collision protection strategy for all objects + in this revision. Individual phases or objects can override this value. - Some examples of invalid values are: - - -some-package - - some-package- - - thisisareallylongpackagenamethatisgreaterthanthemaximumlength - - some.package + When set, this value is used as the default for any phase or object that does not + explicitly specify its own collisionProtection. - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 253 - type: string - x-kubernetes-validations: - - message: packageName is immutable - rule: self == oldSelf - - message: packageName must be a valid DNS1123 subdomain. - It must contain only lowercase alphanumeric characters, - hyphens (-) or periods (.), start and end with an alphanumeric - character, and be no longer than 253 characters - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") - selector: - description: |- - selector is optional and filters the set of ClusterCatalogs used in the bundle selection process. + The resolution order is: object > phase > spec + enum: + - Prevent + - IfNoController + - None + type: string + x-kubernetes-validations: + - message: collisionProtection is immutable + rule: self == oldSelf + lifecycleState: + description: |- + lifecycleState specifies the lifecycle state of the ClusterObjectSet. - When unspecified, all ClusterCatalogs are used in the bundle selection process. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - upgradeConstraintPolicy: - default: CatalogProvided - description: |- - upgradeConstraintPolicy is optional and controls whether the upgrade paths defined in the catalog - are enforced for the package referenced in the packageName field. + When set to "Active", the revision is actively managed and reconciled. + When set to "Archived", the revision is inactive and any resources not managed by a subsequent revision are deleted. + The revision is removed from the owner list of all objects previously under management. + All objects that did not transition to a succeeding revision are deleted. - Allowed values are "CatalogProvided", "SelfCertified", or omitted. + Once a revision is set to "Archived", it cannot be un-archived. - When set to "CatalogProvided", automatic upgrades only occur when upgrade constraints specified by the package - author are met. + It is possible for more than one revision to be "Active" simultaneously. This will occur when + moving from one revision to another. The old revision will not be set to "Archived" until the + new revision has been completely rolled out. + enum: + - Active + - Archived + type: string + x-kubernetes-validations: + - message: cannot un-archive + rule: oldSelf == 'Active' || oldSelf == 'Archived' && oldSelf == + self + phases: + description: |- + phases is an optional, immutable list of phases that group objects to be applied together. - When set to "SelfCertified", the upgrade constraints specified by the package author are ignored. - This allows upgrades and downgrades to any version of the package. - This is considered a dangerous operation as it can lead to unknown and potentially disastrous outcomes, - such as data loss. - Use this option only if you have independently verified the changes. + Objects are organized into phases based on their Group-Kind. Common phases include: + - namespaces: Namespace objects + - policies: ResourceQuota, LimitRange, NetworkPolicy objects + - rbac: ServiceAccount, Role, RoleBinding, ClusterRole, ClusterRoleBinding objects + - crds: CustomResourceDefinition objects + - storage: PersistentVolume, PersistentVolumeClaim, StorageClass objects + - deploy: Deployment, StatefulSet, DaemonSet, Service, ConfigMap, Secret objects + - publish: Ingress, APIService, Route, Webhook objects - When omitted, the default value is "CatalogProvided". - enum: - - CatalogProvided - - SelfCertified - type: string - version: - description: |- - version is an optional semver constraint (a specific version or range of versions). - When unspecified, the latest version available is installed. + All objects in a phase are applied in no particular order. + The revision progresses to the next phase only after all objects in the current phase pass their readiness probes. - Acceptable version ranges are no longer than 64 characters. - Version ranges are composed of comma- or space-delimited values and one or more comparison operators, - known as comparison strings. - You can add additional comparison strings using the OR operator (||). + Once set, even if empty, the phases field is immutable. - # Range Comparisons + Each phase in the list must have a unique name. The maximum number of phases is 20. + items: + description: |- + ClusterObjectSetPhase represents a group of objects that are applied together. The phase is considered + complete only after all objects pass their status probes. + properties: + collisionProtection: + description: |- + collisionProtection specifies the default collision protection strategy for all objects + in this phase. Individual objects can override this value. - To specify a version range, you can use a comparison string like ">=3.0, - <3.6". When specifying a range, automatic updates will occur within that - range. The example comparison string means "install any version greater than - or equal to 3.0.0 but less than 3.6.0.". It also states intent that if any - upgrades are available within the version range after initial installation, - those upgrades should be automatically performed. + When set, this value is used as the default for any object in this phase that does not + explicitly specify its own collisionProtection. - # Pinned Versions + When omitted, we use .spec.collistionProtection as the default for any object in this phase that does not + explicitly specify its own collisionProtection. + enum: + - Prevent + - IfNoController + - None + type: string + name: + description: |- + name is a required identifier for this phase. - To specify an exact version to install you can use a version range that - "pins" to a specific version. When pinning to a specific version, no - automatic updates will occur. An example of a pinned version range is - "0.6.0", which means "only install version 0.6.0 and never - upgrade from this version". + phase names must follow the DNS label standard as defined in [RFC 1123]. + They must contain only lowercase alphanumeric characters or hyphens (-), + start and end with an alphanumeric character, and be no longer than 63 characters. - # Basic Comparison Operators + Common phase names include: namespaces, policies, rbac, crds, storage, deploy, publish. - The basic comparison operators and their meanings are: - - "=", equal (not aliased to an operator) - - "!=", not equal - - "<", less than - - ">", greater than - - ">=", greater than OR equal to - - "<=", less than OR equal to + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 63 + minLength: 1 + type: string + x-kubernetes-validations: + - message: the value must consist of only lowercase alphanumeric + characters and hyphens, and must start with an alphabetic + character and end with an alphanumeric character. + rule: '!format.dns1123Label().validate(self).hasValue()' + objects: + description: |- + objects is a required list of all Kubernetes objects that belong to this phase. - # Wildcard Comparisons + All objects in this list are applied to the cluster in no particular order. The maximum number of objects per phase is 50. + items: + description: |- + ClusterObjectSetObject represents a Kubernetes object to be applied as part + of a phase, along with its collision protection settings. - You can use the "x", "X", and "*" characters as wildcard characters in all - comparison operations. Some examples of using the wildcard characters: - - "1.2.x", "1.2.X", and "1.2.*" is equivalent to ">=1.2.0, < 1.3.0" - - ">= 1.2.x", ">= 1.2.X", and ">= 1.2.*" is equivalent to ">= 1.2.0" - - "<= 2.x", "<= 2.X", and "<= 2.*" is equivalent to "< 3" - - "x", "X", and "*" is equivalent to ">= 0.0.0" + Exactly one of object or ref must be set. + properties: + collisionProtection: + description: |- + collisionProtection controls whether the operator can adopt and modify objects + that already exist on the cluster. - # Patch Release Comparisons + Allowed values are: "Prevent", "IfNoController", and "None". - When you want to specify a minor version up to the next major version you - can use the "~" character to perform patch comparisons. Some examples: - - "~1.2.3" is equivalent to ">=1.2.3, <1.3.0" - - "~1" and "~1.x" is equivalent to ">=1, <2" - - "~2.3" is equivalent to ">=2.3, <2.4" - - "~1.2.x" is equivalent to ">=1.2.0, <1.3.0" + When set to "Prevent", the operator only manages objects it created itself. + This prevents ownership collisions. - # Major Release Comparisons + When set to "IfNoController", the operator can adopt and modify pre-existing objects + that are not owned by another controller. + This is useful for taking over management of manually-created resources. - You can use the "^" character to make major release comparisons after a - stable 1.0.0 version is published. If there is no stable version published, // minor versions define the stability level. Some examples: - - "^1.2.3" is equivalent to ">=1.2.3, <2.0.0" - - "^1.2.x" is equivalent to ">=1.2.0, <2.0.0" - - "^2.3" is equivalent to ">=2.3, <3" - - "^2.x" is equivalent to ">=2.0.0, <3" - - "^0.2.3" is equivalent to ">=0.2.3, <0.3.0" - - "^0.2" is equivalent to ">=0.2.0, <0.3.0" - - "^0.0.3" is equvalent to ">=0.0.3, <0.0.4" - - "^0.0" is equivalent to ">=0.0.0, <0.1.0" - - "^0" is equivalent to ">=0.0.0, <1.0.0" + When set to "None", the operator can adopt and modify any pre-existing object, even if + owned by another controller. + Use this setting with extreme caution as it may cause multiple controllers to fight over + the same resource, resulting in increased load on the API server and etcd. - # OR Comparisons - You can use the "||" character to represent an OR operation in the version - range. Some examples: - - ">=1.2.3, <2.0.0 || >3.0.0" - - "^0 || ^3 || ^5" + When omitted, the value is inherited from the phase, then spec. + enum: + - Prevent + - IfNoController + - None + type: string + object: + description: |- + object is an optional embedded Kubernetes object to be applied. - For more information on semver, please see https://semver.org/ - maxLength: 64 - type: string - x-kubernetes-validations: - - message: invalid version expression - rule: self.matches("^(\\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|[x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*]))?(\\.(0|[1-9]\\d*|x|X|\\*))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)((?:\\s+|,\\s*|\\s*\\|\\|\\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*))?(\\.(0|[1-9]\\d*|x|X|\\*]))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)*$") - required: - - packageName - type: object - sourceType: - description: |- - sourceType is required and specifies the type of install source. + Exactly one of object or ref must be set. - The only allowed value is "Catalog". + This object must be a valid Kubernetes resource with apiVersion, kind, and metadata fields. + type: object + x-kubernetes-embedded-resource: true + x-kubernetes-preserve-unknown-fields: true + ref: + description: |- + ref is an optional reference to a Secret that holds the serialized + object manifest. - When set to "Catalog", information for determining the appropriate bundle of content to install - is fetched from ClusterCatalog resources on the cluster. - When using the Catalog sourceType, the catalog field must also be set. - enum: - - Catalog - type: string - required: - - sourceType - type: object + Exactly one of object or ref must be set. + properties: + key: + description: |- + key is the data key within the referenced Secret containing the + object manifest content. The value at this key must be a + JSON-serialized Kubernetes object manifest. + maxLength: 253 + minLength: 1 + type: string + name: + description: name is the name of the referenced Secret. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + namespace is the namespace of the referenced Secret. + When empty, defaults to the OLM system namespace during ref resolution. + maxLength: 63 + type: string + required: + - key + - name + type: object + type: object + x-kubernetes-validations: + - message: exactly one of object or ref must be set + rule: has(self.object) != has(self.ref) + maxItems: 50 + type: array + required: + - name + - objects + type: object + maxItems: 20 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map x-kubernetes-validations: - - message: catalog is required when sourceType is Catalog, and forbidden - otherwise - rule: 'has(self.sourceType) && self.sourceType == ''Catalog'' ? - has(self.catalog) : !has(self.catalog)' - required: - - namespace - - serviceAccount - - source - type: object - status: - description: status is an optional field that defines the observed state - of the ClusterExtension. - properties: - activeRevisions: + - message: phases is immutable + rule: self == oldSelf || oldSelf.size() == 0 + progressDeadlineMinutes: description: |- - activeRevisions holds a list of currently active (non-archived) ClusterExtensionRevisions, - including both installed and rolling out revisions. + progressDeadlineMinutes is an optional field that defines the maximum period + of time in minutes after which an installation should be considered failed and + require manual intervention. This functionality is disabled when no value + is provided. The minimum period is 10 minutes, and the maximum is 720 minutes (12 hours). + format: int32 + maximum: 720 + minimum: 10 + type: integer + progressionProbes: + description: |- + progressionProbes is an optional field which provides the ability to define custom readiness probes + for objects defined within spec.phases. As documented in that field, most kubernetes-native objects + within the phases already have some kind of readiness check built-in, but this field allows for checks + which are tailored to the objects being rolled out - particularly custom resources. + + Probes defined within the progressionProbes list will apply to every phase in the revision. However, the probes will only + execute against phase objects which are a match for the provided selector type. For instance, a probe using a GroupKind selector + for ConfigMaps will automatically be considered to have passed for any non-ConfigMap object, but will halt any phase containing + a ConfigMap if that particular object does not pass the probe check. + + The maximum number of probes is 20. items: - description: RevisionStatus defines the observed state of a ClusterExtensionRevision. + description: ProgressionProbe provides a custom probe definition, + consisting of an object selection method and assertions. properties: - conditions: + assertions: description: |- - conditions optionally expose Progressing and Available condition of the revision, - in case when it is not yet marked as successfully installed (condition Succeeded is not set to True). - Given that a ClusterExtension should remain available during upgrades, an observer may use these conditions - to get more insights about reasons for its current state. + assertions is a required list of checks which will run against the objects selected by the selector. If + one or more assertions fail then the phase within which the object lives will be not be considered + 'Ready', blocking rollout of all subsequent phases. items: - description: Condition contains details for one aspect of - the current state of this API Resource. + description: Assertion is a discriminated union which defines + the probe type and definition used as an assertion. properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: + conditionEqual: + description: conditionEqual contains the expected condition + type and status. + properties: + status: + description: |- + status sets the expected condition status. + + Allowed values are "True" and "False". + enum: + - "True" + - "False" + type: string + type: + description: type sets the expected condition type, + i.e. "Ready". + maxLength: 200 + minLength: 1 + type: string + required: + - status + - type + type: object + fieldValue: + description: fieldValue contains the expected field path + and value found within. + properties: + fieldPath: + description: |- + fieldPath sets the field path for the field to check, i.e. "status.phase". The probe will fail + if the path does not exist. + maxLength: 200 + minLength: 1 + type: string + x-kubernetes-validations: + - message: must contain a valid field path. valid + fields contain upper or lower-case alphanumeric + characters separated by the "." character. + rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') + value: + description: value sets the expected value found at + fieldPath, i.e. "Bound". + maxLength: 200 + minLength: 1 + type: string + required: + - fieldPath + - value + type: object + fieldsEqual: + description: fieldsEqual contains the two field paths + whose values are expected to match. + properties: + fieldA: + description: |- + fieldA sets the field path for the first field, i.e. "spec.replicas". The probe will fail + if the path does not exist. + maxLength: 200 + minLength: 1 + type: string + x-kubernetes-validations: + - message: must contain a valid field path. valid + fields contain upper or lower-case alphanumeric + characters separated by the "." character. + rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') + fieldB: + description: |- + fieldB sets the field path for the second field, i.e. "status.readyReplicas". The probe will fail + if the path does not exist. + maxLength: 200 + minLength: 1 + type: string + x-kubernetes-validations: + - message: must contain a valid field path. valid + fields contain upper or lower-case alphanumeric + characters separated by the "." character. + rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') + required: + - fieldA + - fieldB + type: object + type: description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, - Unknown. + type is a required field which specifies the type of probe to use. + + The allowed probe types are "ConditionEqual", "FieldsEqual", and "FieldValue". + + When set to "ConditionEqual", the probe checks objects that have reached a condition of specified type and status. + When set to "FieldsEqual", the probe checks that the values found at two provided field paths are matching. + When set to "FieldValue", the probe checks that the value found at the provided field path matches what was specified. enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + - ConditionEqual + - FieldsEqual + - FieldValue type: string required: - - lastTransitionTime - - message - - reason - - status - type type: object + x-kubernetes-validations: + - message: conditionEqual is required when type is ConditionEqual, + and forbidden otherwise + rule: 'self.type == ''ConditionEqual'' ?has(self.conditionEqual) + : !has(self.conditionEqual)' + - message: fieldsEqual is required when type is FieldsEqual, + and forbidden otherwise + rule: 'self.type == ''FieldsEqual'' ?has(self.fieldsEqual) + : !has(self.fieldsEqual)' + - message: fieldValue is required when type is FieldValue, + and forbidden otherwise + rule: 'self.type == ''FieldValue'' ?has(self.fieldValue) + : !has(self.fieldValue)' + maxItems: 20 + minItems: 1 type: array - x-kubernetes-list-map-keys: + x-kubernetes-list-type: atomic + selector: + description: |- + selector is a required field which defines the method by which we select objects to apply the below + assertions to. Any object which matches the defined selector will have all the associated assertions + applied against it. + + If no objects within a phase are selected by the provided selector, then all assertions defined here + are considered to have succeeded. + properties: + groupKind: + description: |- + groupKind specifies the group and kind of objects to select. + + Required when type is "GroupKind". + + Uses the Kubernetes format specified here: + https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#GroupKind + properties: + group: + type: string + kind: + type: string + required: + - group + - kind + type: object + label: + description: |- + label is the label selector definition. + + Required when type is "Label". + + A probe using a Label selector will be executed against every object matching the labels or expressions; you must use care + when using this type of selector. For example, if multiple Kind objects are selected via labels then the probe is + likely to fail because the values of different Kind objects rarely share the same schema. + + The LabelSelector field uses the following Kubernetes format: + https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#LabelSelector + Requires exactly one of matchLabels or matchExpressions. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + x-kubernetes-validations: + - message: exactly one of matchLabels or matchExpressions + must be set + rule: (has(self.matchExpressions) && !has(self.matchLabels)) + || (!has(self.matchExpressions) && has(self.matchLabels)) + type: + description: |- + type is a required field which specifies the type of selector to use. + + The allowed selector types are "GroupKind" and "Label". + + When set to "GroupKind", all objects which match the specified group and kind will be selected. + When set to "Label", all objects which match the specified labels and/or expressions will be selected. + enum: + - GroupKind + - Label + type: string + required: - type - x-kubernetes-list-type: map - name: - description: name of the ClusterExtensionRevision resource - type: string + type: object + x-kubernetes-validations: + - message: groupKind is required when type is GroupKind, and + forbidden otherwise + rule: 'self.type == ''GroupKind'' ?has(self.groupKind) : !has(self.groupKind)' + - message: label is required when type is Label, and forbidden + otherwise + rule: 'self.type == ''Label'' ?has(self.label) : !has(self.label)' required: - - name + - assertions + - selector type: object + maxItems: 20 + minItems: 1 type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - conditions: + x-kubernetes-list-type: atomic + revision: description: |- - conditions represents the current state of the ClusterExtension. - - The set of condition types which apply to all spec.source variations are Installed and Progressing. + revision is a required, immutable sequence number representing a specific revision + of the parent ClusterExtension. - The Installed condition represents whether the bundle has been installed for this ClusterExtension: - - When Installed is True and the Reason is Succeeded, the bundle has been successfully installed. - - When Installed is False and the Reason is Failed, the bundle has failed to install. + The revision field must be a positive integer. + Each ClusterObjectSet belonging to the same parent ClusterExtension must have a unique revision number. + The revision number must always be the previous revision number plus one, or 1 for the first revision. + format: int64 + minimum: 1 + type: integer + x-kubernetes-validations: + - message: revision is immutable + rule: self == oldSelf + required: + - collisionProtection + - lifecycleState + - revision + type: object + status: + description: status is optional and defines the observed state of the + ClusterObjectSet. + properties: + conditions: + description: |- + conditions is an optional list of status conditions describing the state of the + ClusterObjectSet. - The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. - When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. - When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. - When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. + The Progressing condition represents whether the revision is actively rolling out: + - When status is True and reason is RollingOut, the ClusterObjectSet rollout is actively making progress and is in transition. + - When status is True and reason is Retrying, the ClusterObjectSet has encountered an error that could be resolved on subsequent reconciliation attempts. + - When status is True and reason is Succeeded, the ClusterObjectSet has reached the desired state. + - When status is False and reason is Blocked, the ClusterObjectSet has encountered an error that requires manual intervention for recovery. + - When status is False and reason is Archived, the ClusterObjectSet is archived and not being actively reconciled. - When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterExtensionRevisions in active roll out. + The Available condition represents whether the revision has been successfully rolled out and is available: + - When status is True and reason is ProbesSucceeded, the ClusterObjectSet has been successfully rolled out and all objects pass their readiness probes. + - When status is False and reason is ProbeFailure, one or more objects are failing their readiness probes during rollout. + - When status is Unknown and reason is Reconciling, the ClusterObjectSet has encountered an error that prevented it from observing the probes. + - When status is Unknown and reason is Archived, the ClusterObjectSet has been archived and its objects have been torn down. + - When status is Unknown and reason is Migrated, the ClusterObjectSet was migrated from an existing release and object status probe results have not yet been observed. - When the ClusterExtension is sourced from a catalog, it surfaces deprecation conditions based on catalog metadata. - These are indications from a package owner to guide users away from a particular package, channel, or bundle: - - BundleDeprecated is True if the installed bundle is marked deprecated, False if not deprecated, or Unknown if no bundle is installed yet or if catalog data is unavailable. - - ChannelDeprecated is True if any requested channel is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable. - - PackageDeprecated is True if the requested package is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable. - - Deprecated is a rollup condition that is True when any deprecation exists, False when none exist, or Unknown when catalog data is unavailable. + The Succeeded condition represents whether the revision has successfully completed its rollout: + - When status is True and reason is Succeeded, the ClusterObjectSet has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. items: description: Condition contains details for one aspect of the current state of this API Resource. @@ -1905,44 +1943,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - install: - description: install is a representation of the current installation - status for this ClusterExtension. - properties: - bundle: - description: |- - bundle is required and represents the identifying attributes of a bundle. - - A "bundle" is a versioned set of content that represents the resources that need to be applied - to a cluster to install a package. - properties: - name: - description: |- - name is required and follows the DNS subdomain standard as defined in [RFC 1123]. - It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), - start and end with an alphanumeric character, and be no longer than 253 characters. - type: string - x-kubernetes-validations: - - message: packageName must be a valid DNS1123 subdomain. - It must contain only lowercase alphanumeric characters, - hyphens (-) or periods (.), start and end with an alphanumeric - character, and be no longer than 253 characters - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") - version: - description: |- - version is required and references the version that this bundle represents. - It follows the semantic versioning standard as defined in https://semver.org/. - type: string - x-kubernetes-validations: - - message: version must be well-formed semver - rule: self.matches("^([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-([-0-9A-Za-z]+(\\.[-0-9A-Za-z]+)*))?(\\+([-0-9A-Za-z]+(-\\.[-0-9A-Za-z]+)*))?") - required: - - name - - version - type: object - required: - - bundle - type: object type: object type: object served: true @@ -2168,7 +2168,7 @@ rules: - apiGroups: - olm.operatorframework.io resources: - - clusterextensionrevisions + - clusterobjectsets verbs: - create - delete @@ -2180,14 +2180,14 @@ rules: - apiGroups: - olm.operatorframework.io resources: - - clusterextensionrevisions/status + - clusterobjectsets/status verbs: - patch - update - apiGroups: - olm.operatorframework.io resources: - - clusterextensionrevisions/finalizers + - clusterobjectsets/finalizers verbs: - update --- diff --git a/manifests/experimental.yaml b/manifests/experimental.yaml index 5a31d93419..9f8f6d8a9a 100644 --- a/manifests/experimental.yaml +++ b/manifests/experimental.yaml @@ -571,26 +571,32 @@ spec: subresources: status: {} --- -# Source: olmv1/templates/crds/customresourcedefinition-clusterextensionrevisions.olm.operatorframework.io.yml +# Source: olmv1/templates/crds/customresourcedefinition-clusterextensions.olm.operatorframework.io.yml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.20.1 olm.operatorframework.io/generator: experimental - name: clusterextensionrevisions.olm.operatorframework.io + name: clusterextensions.olm.operatorframework.io spec: group: olm.operatorframework.io names: - kind: ClusterExtensionRevision - listKind: ClusterExtensionRevisionList - plural: clusterextensionrevisions - singular: clusterextensionrevision + kind: ClusterExtension + listKind: ClusterExtensionList + plural: clusterextensions + singular: clusterextension scope: Cluster versions: - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=='Available')].status - name: Available + - jsonPath: .status.install.bundle.name + name: Installed Bundle + type: string + - jsonPath: .status.install.bundle.version + name: Version + type: string + - jsonPath: .status.conditions[?(@.type=='Installed')].status + name: Installed type: string - jsonPath: .status.conditions[?(@.type=='Progressing')].status name: Progressing @@ -601,13 +607,7 @@ spec: name: v1 schema: openAPIV3Schema: - description: |- - ClusterExtensionRevision represents an immutable snapshot of Kubernetes objects - for a specific version of a ClusterExtension. Each revision contains objects - organized into phases that roll out sequentially. The same object can only be managed by a single revision - at a time. Ownership of objects is transitioned from one revision to the next as the extension is upgraded - or reconfigured. Once the latest revision has rolled out successfully, previous active revisions are archived for - posterity. + description: ClusterExtension is the Schema for the clusterextensions API properties: apiVersion: description: |- @@ -627,200 +627,117 @@ spec: metadata: type: object spec: - description: spec defines the desired state of the ClusterExtensionRevision. + description: spec is an optional field that defines the desired state + of the ClusterExtension. properties: - collisionProtection: + config: description: |- - 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. + config is optional and specifies bundle-specific configuration. + Configuration is bundle-specific and a bundle may provide a configuration schema. + When not specified, the default configuration of the resolved bundle is used. - The resolution order is: object > phase > spec - enum: - - Prevent - - IfNoController - - None - type: string - x-kubernetes-validations: - - message: collisionProtection is immutable - rule: self == oldSelf - lifecycleState: - description: |- - lifecycleState specifies the lifecycle state of the ClusterExtensionRevision. + config is validated against a configuration schema provided by the resolved bundle. If the bundle does not provide + a configuration schema the bundle is deemed to not be configurable. More information on how + to configure bundles can be found in the OLM documentation associated with your current OLM version. + properties: + configType: + description: |- + configType is required and specifies the type of configuration source. - When set to "Active", the revision is actively managed and reconciled. - When set to "Archived", the revision is inactive and any resources not managed by a subsequent revision are deleted. - The revision is removed from the owner list of all objects previously under management. - All objects that did not transition to a succeeding revision are deleted. + The only allowed value is "Inline". - Once a revision is set to "Archived", it cannot be un-archived. + When set to "Inline", the cluster extension configuration is defined inline within the ClusterExtension resource. + enum: + - Inline + type: string + inline: + description: |- + inline contains JSON or YAML values specified directly in the ClusterExtension. - It is possible for more than one revision to be "Active" simultaneously. This will occur when - moving from one revision to another. The old revision will not be set to "Archived" until the - new revision has been completely rolled out. - enum: - - Active - - Archived - type: string + It is used to specify arbitrary configuration values for the ClusterExtension. + It must be set if configType is 'Inline' and must be a valid JSON/YAML object containing at least one property. + The configuration values are validated at runtime against a JSON schema provided by the bundle. + minProperties: 1 + type: object + x-kubernetes-preserve-unknown-fields: true + required: + - configType + type: object x-kubernetes-validations: - - message: cannot un-archive - rule: oldSelf == 'Active' || oldSelf == 'Archived' && oldSelf == - self - phases: + - message: inline is required when configType is Inline, and forbidden + otherwise + rule: 'has(self.configType) && self.configType == ''Inline'' ?has(self.inline) + : !has(self.inline)' + install: description: |- - phases is an optional, immutable list of phases that group objects to be applied together. - - Objects are organized into phases based on their Group-Kind. Common phases include: - - namespaces: Namespace objects - - policies: ResourceQuota, LimitRange, NetworkPolicy objects - - rbac: ServiceAccount, Role, RoleBinding, ClusterRole, ClusterRoleBinding objects - - crds: CustomResourceDefinition objects - - storage: PersistentVolume, PersistentVolumeClaim, StorageClass objects - - deploy: Deployment, StatefulSet, DaemonSet, Service, ConfigMap, Secret objects - - publish: Ingress, APIService, Route, Webhook objects - - All objects in a phase are applied in no particular order. - The revision progresses to the next phase only after all objects in the current phase pass their readiness probes. - - Once set, even if empty, the phases field is immutable. - - Each phase in the list must have a unique name. The maximum number of phases is 20. - items: - description: |- - ClusterExtensionRevisionPhase represents a group of objects that are applied together. The phase is considered - complete only after all objects pass their status probes. - properties: - collisionProtection: - description: |- - collisionProtection specifies the default collision protection strategy for all objects - in this phase. Individual objects can override this value. - - When set, this value is used as the default for any object in this phase that does not - explicitly specify its own collisionProtection. - - When omitted, we use .spec.collistionProtection as the default for any object in this phase that does not - explicitly specify its own collisionProtection. - enum: - - Prevent - - IfNoController - - None - type: string - name: - description: |- - name is a required identifier for this phase. - - phase names must follow the DNS label standard as defined in [RFC 1123]. - They must contain only lowercase alphanumeric characters or hyphens (-), - start and end with an alphanumeric character, and be no longer than 63 characters. - - Common phase names include: namespaces, policies, rbac, crds, storage, deploy, publish. - - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 63 - minLength: 1 - type: string - x-kubernetes-validations: - - message: the value must consist of only lowercase alphanumeric - characters and hyphens, and must start with an alphabetic - character and end with an alphanumeric character. - rule: '!format.dns1123Label().validate(self).hasValue()' - objects: - description: |- - objects is a required list of all Kubernetes objects that belong to this phase. + install is optional and configures installation options for the ClusterExtension, + such as the pre-flight check configuration. + properties: + preflight: + description: |- + preflight is optional and configures the checks that run before installation or upgrade + of the content for the package specified in the packageName field. - All objects in this list are applied to the cluster in no particular order. The maximum number of objects per phase is 50. - items: + When specified, it replaces the default preflight configuration for install/upgrade actions. + When not specified, the default configuration is used. + properties: + crdUpgradeSafety: description: |- - ClusterExtensionRevisionObject represents a Kubernetes object to be applied as part - of a phase, along with its collision protection settings. + crdUpgradeSafety configures the CRD Upgrade Safety pre-flight checks that run + before upgrades of installed content. - Exactly one of object or ref must be set. + The CRD Upgrade Safety pre-flight check safeguards from unintended consequences of upgrading a CRD, + such as data loss. properties: - collisionProtection: + enforcement: description: |- - collisionProtection controls whether the operator can adopt and modify objects - that already exist on the cluster. - - Allowed values are: "Prevent", "IfNoController", and "None". - - When set to "Prevent", the operator only manages objects it created itself. - This prevents ownership collisions. + enforcement is required and configures the state of the CRD Upgrade Safety pre-flight check. - When set to "IfNoController", the operator can adopt and modify pre-existing objects - that are not owned by another controller. - This is useful for taking over management of manually-created resources. + Allowed values are "None" or "Strict". The default value is "Strict". - When set to "None", the operator can adopt and modify any pre-existing object, even if - owned by another controller. - Use this setting with extreme caution as it may cause multiple controllers to fight over - the same resource, resulting in increased load on the API server and etcd. + When set to "None", the CRD Upgrade Safety pre-flight check is skipped during an upgrade operation. + Use this option with caution as unintended consequences such as data loss can occur. - When omitted, the value is inherited from the phase, then spec. + When set to "Strict", the CRD Upgrade Safety pre-flight check runs during an upgrade operation. enum: - - Prevent - - IfNoController - None + - Strict type: string - object: - description: |- - object is an optional embedded Kubernetes object to be applied. - - Exactly one of object or ref must be set. + required: + - enforcement + type: object + required: + - crdUpgradeSafety + type: object + x-kubernetes-validations: + - message: at least one of [crdUpgradeSafety] are required when + preflight is specified + rule: has(self.crdUpgradeSafety) + type: object + x-kubernetes-validations: + - message: at least one of [preflight] are required when install is + specified + rule: has(self.preflight) + namespace: + description: |- + namespace specifies a Kubernetes namespace. + This is the namespace where the provided ServiceAccount must exist. + It also designates the default namespace where namespace-scoped resources for the extension are applied to the cluster. + Some extensions may contain namespace-scoped resources to be applied in other namespaces. + This namespace must exist. - This object must be a valid Kubernetes resource with apiVersion, kind, and metadata fields. - type: object - x-kubernetes-embedded-resource: true - x-kubernetes-preserve-unknown-fields: true - ref: - description: |- - ref is an optional reference to a Secret that holds the serialized - object manifest. + The namespace field is required, immutable, and follows the DNS label standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character, + and be no longer than 63 characters. - Exactly one of object or ref must be set. - properties: - key: - description: |- - key is the data key within the referenced Secret containing the - object manifest content. The value at this key must be a - JSON-serialized Kubernetes object manifest. - maxLength: 253 - minLength: 1 - type: string - name: - description: name is the name of the referenced Secret. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - namespace is the namespace of the referenced Secret. - When empty, defaults to the OLM system namespace during ref resolution. - maxLength: 63 - type: string - required: - - key - - name - type: object - type: object - x-kubernetes-validations: - - message: exactly one of object or ref must be set - rule: has(self.object) != has(self.ref) - maxItems: 50 - type: array - required: - - name - - objects - type: object - maxItems: 20 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 63 + type: string x-kubernetes-validations: - - message: phases is immutable - rule: self == oldSelf || oldSelf.size() == 0 + - message: namespace is immutable + rule: self == oldSelf + - message: namespace must be a valid DNS1123 label + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") progressDeadlineMinutes: description: |- progressDeadlineMinutes is an optional field that defines the maximum period @@ -831,983 +748,1104 @@ spec: maximum: 720 minimum: 10 type: integer - progressionProbes: + serviceAccount: description: |- - progressionProbes is an optional field which provides the ability to define custom readiness probes - for objects defined within spec.phases. As documented in that field, most kubernetes-native objects - within the phases already have some kind of readiness check built-in, but this field allows for checks - which are tailored to the objects being rolled out - particularly custom resources. + serviceAccount specifies a ServiceAccount used to perform all interactions with the cluster + that are required to manage the extension. + The ServiceAccount must be configured with the necessary permissions to perform these interactions. + The ServiceAccount must exist in the namespace referenced in the spec. + The serviceAccount field is required. + properties: + name: + description: |- + name is a required, immutable reference to the name of the ServiceAccount used for installation + and management of the content for the package specified in the packageName field. - Probes defined within the progressionProbes list will apply to every phase in the revision. However, the probes will only - execute against phase objects which are a match for the provided selector type. For instance, a probe using a GroupKind selector - for ConfigMaps will automatically be considered to have passed for any non-ConfigMap object, but will halt any phase containing - a ConfigMap if that particular object does not pass the probe check. + This ServiceAccount must exist in the installNamespace. - The maximum number of probes is 20. - items: - description: ProgressionProbe provides a custom probe definition, - consisting of an object selection method and assertions. - properties: - assertions: - description: |- - assertions is a required list of checks which will run against the objects selected by the selector. If - one or more assertions fail then the phase within which the object lives will be not be considered - 'Ready', blocking rollout of all subsequent phases. - items: - description: Assertion is a discriminated union which defines - the probe type and definition used as an assertion. - properties: - conditionEqual: - description: conditionEqual contains the expected condition - type and status. - properties: - status: - description: |- - status sets the expected condition status. + The name field follows the DNS subdomain standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), + start and end with an alphanumeric character, and be no longer than 253 characters. - Allowed values are "True" and "False". - enum: - - "True" - - "False" - type: string - type: - description: type sets the expected condition type, - i.e. "Ready". - maxLength: 200 - minLength: 1 - type: string - required: - - status - - type - type: object - fieldValue: - description: fieldValue contains the expected field path - and value found within. - properties: - fieldPath: - description: |- - fieldPath sets the field path for the field to check, i.e. "status.phase". The probe will fail - if the path does not exist. - maxLength: 200 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must contain a valid field path. valid - fields contain upper or lower-case alphanumeric - characters separated by the "." character. - rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') - value: - description: value sets the expected value found at - fieldPath, i.e. "Bound". - maxLength: 200 - minLength: 1 - type: string - required: - - fieldPath - - value - type: object - fieldsEqual: - description: fieldsEqual contains the two field paths - whose values are expected to match. - properties: - fieldA: - description: |- - fieldA sets the field path for the first field, i.e. "spec.replicas". The probe will fail - if the path does not exist. - maxLength: 200 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must contain a valid field path. valid - fields contain upper or lower-case alphanumeric - characters separated by the "." character. - rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') - fieldB: - description: |- - fieldB sets the field path for the second field, i.e. "status.readyReplicas". The probe will fail - if the path does not exist. - maxLength: 200 - minLength: 1 - type: string - x-kubernetes-validations: - - message: must contain a valid field path. valid - fields contain upper or lower-case alphanumeric - characters separated by the "." character. - rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') - required: - - fieldA - - fieldB - type: object - type: - description: |- - type is a required field which specifies the type of probe to use. + Some examples of valid values are: + - some-serviceaccount + - 123-serviceaccount + - 1-serviceaccount-2 + - someserviceaccount + - some.serviceaccount - The allowed probe types are "ConditionEqual", "FieldsEqual", and "FieldValue". + Some examples of invalid values are: + - -some-serviceaccount + - some-serviceaccount- - When set to "ConditionEqual", the probe checks objects that have reached a condition of specified type and status. - When set to "FieldsEqual", the probe checks that the values found at two provided field paths are matching. - When set to "FieldValue", the probe checks that the value found at the provided field path matches what was specified. - enum: - - ConditionEqual - - FieldsEqual - - FieldValue - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: conditionEqual is required when type is ConditionEqual, - and forbidden otherwise - rule: 'self.type == ''ConditionEqual'' ?has(self.conditionEqual) - : !has(self.conditionEqual)' - - message: fieldsEqual is required when type is FieldsEqual, - and forbidden otherwise - rule: 'self.type == ''FieldsEqual'' ?has(self.fieldsEqual) - : !has(self.fieldsEqual)' - - message: fieldValue is required when type is FieldValue, - and forbidden otherwise - rule: 'self.type == ''FieldValue'' ?has(self.fieldValue) - : !has(self.fieldValue)' - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - selector: - description: |- - selector is a required field which defines the method by which we select objects to apply the below - assertions to. Any object which matches the defined selector will have all the associated assertions - applied against it. + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 253 + type: string + x-kubernetes-validations: + - message: name is immutable + rule: self == oldSelf + - message: name must be a valid DNS1123 subdomain. It must contain + only lowercase alphanumeric characters, hyphens (-) or periods + (.), start and end with an alphanumeric character, and be + no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + required: + - name + type: object + source: + description: |- + source is required and selects the installation source of content for this ClusterExtension. + Set the sourceType field to perform the selection. - If no objects within a phase are selected by the provided selector, then all assertions defined here - are considered to have succeeded. - properties: - groupKind: - description: |- - groupKind specifies the group and kind of objects to select. + Catalog is currently the only implemented sourceType. + Setting sourceType to "Catalog" requires the catalog field to also be defined. - Required when type is "GroupKind". + Below is a minimal example of a source definition (in yaml): - Uses the Kubernetes format specified here: - https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#GroupKind - properties: - group: - type: string - kind: - type: string - required: - - group - - kind - type: object - label: - description: |- - label is the label selector definition. + source: + sourceType: Catalog + catalog: + packageName: example-package + properties: + catalog: + description: |- + catalog configures how information is sourced from a catalog. + It is required when sourceType is "Catalog", and forbidden otherwise. + properties: + channels: + description: |- + channels is optional and specifies a set of channels belonging to the package + specified in the packageName field. - Required when type is "Label". + A channel is a package-author-defined stream of updates for an extension. - A probe using a Label selector will be executed against every object matching the labels or expressions; you must use care - when using this type of selector. For example, if multiple Kind objects are selected via labels then the probe is - likely to fail because the values of different Kind objects rarely share the same schema. + Each channel in the list must follow the DNS subdomain standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), + start and end with an alphanumeric character, and be no longer than 253 characters. + You can specify no more than 256 channels. - The LabelSelector field uses the following Kubernetes format: - https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#LabelSelector - Requires exactly one of matchLabels or matchExpressions. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - x-kubernetes-validations: - - message: exactly one of matchLabels or matchExpressions - must be set - rule: (has(self.matchExpressions) && !has(self.matchLabels)) - || (!has(self.matchExpressions) && has(self.matchLabels)) - type: - description: |- - type is a required field which specifies the type of selector to use. + When specified, it constrains the set of installable bundles and the automated upgrade path. + This constraint is an AND operation with the version field. For example: + - Given channel is set to "foo" + - Given version is set to ">=1.0.0, <1.5.0" + - Only bundles that exist in channel "foo" AND satisfy the version range comparison are considered installable + - Automatic upgrades are constrained to upgrade edges defined by the selected channel - The allowed selector types are "GroupKind" and "Label". + When unspecified, upgrade edges across all channels are used to identify valid automatic upgrade paths. - When set to "GroupKind", all objects which match the specified group and kind will be selected. - When set to "Label", all objects which match the specified labels and/or expressions will be selected. - enum: - - GroupKind - - Label + Some examples of valid values are: + - 1.1.x + - alpha + - stable + - stable-v1 + - v1-stable + - dev-preview + - preview + - community + + Some examples of invalid values are: + - -some-channel + - some-channel- + - thisisareallylongchannelnamethatisgreaterthanthemaximumlength + - original_40 + - --default-channel + + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + items: + maxLength: 253 type: string - required: - - type - type: object - x-kubernetes-validations: - - message: groupKind is required when type is GroupKind, and - forbidden otherwise - rule: 'self.type == ''GroupKind'' ?has(self.groupKind) : !has(self.groupKind)' - - message: label is required when type is Label, and forbidden - otherwise - rule: 'self.type == ''Label'' ?has(self.label) : !has(self.label)' - required: - - assertions - - selector - type: object - maxItems: 20 - minItems: 1 - type: array - x-kubernetes-list-type: atomic - revision: - description: |- - revision is a required, immutable sequence number representing a specific revision - of the parent ClusterExtension. + x-kubernetes-validations: + - message: channels entries must be valid DNS1123 subdomains + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + maxItems: 256 + type: array + packageName: + description: |- + packageName specifies the name of the package to be installed and is used to filter + the content from catalogs. - The revision field must be a positive integer. - Each ClusterExtensionRevision belonging to the same parent ClusterExtension must have a unique revision number. - The revision number must always be the previous revision number plus one, or 1 for the first revision. - format: int64 - minimum: 1 - type: integer - x-kubernetes-validations: - - message: revision is immutable - rule: self == oldSelf - required: - - collisionProtection - - lifecycleState - - revision - type: object - status: - description: status is optional and defines the observed state of the - ClusterExtensionRevision. - properties: - conditions: - description: |- - conditions is an optional list of status conditions describing the state of the - ClusterExtensionRevision. + It is required, immutable, and follows the DNS subdomain standard as defined in [RFC 1123]. + It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), + start and end with an alphanumeric character, and be no longer than 253 characters. - The Progressing condition represents whether the revision is actively rolling out: - - When status is True and reason is RollingOut, the ClusterExtensionRevision rollout is actively making progress and is in transition. - - When status is True and reason is Retrying, the ClusterExtensionRevision has encountered an error that could be resolved on subsequent reconciliation attempts. - - When status is True and reason is Succeeded, the ClusterExtensionRevision has reached the desired state. - - When status is False and reason is Blocked, the ClusterExtensionRevision has encountered an error that requires manual intervention for recovery. - - When status is False and reason is Archived, the ClusterExtensionRevision is archived and not being actively reconciled. + Some examples of valid values are: + - some-package + - 123-package + - 1-package-2 + - somepackage - The Available condition represents whether the revision has been successfully rolled out and is available: - - When status is True and reason is ProbesSucceeded, the ClusterExtensionRevision has been successfully rolled out and all objects pass their readiness probes. - - When status is False and reason is ProbeFailure, one or more objects are failing their readiness probes during rollout. - - When status is Unknown and reason is Reconciling, the ClusterExtensionRevision has encountered an error that prevented it from observing the probes. - - When status is Unknown and reason is Archived, the ClusterExtensionRevision has been archived and its objects have been torn down. - - When status is Unknown and reason is Migrated, the ClusterExtensionRevision was migrated from an existing release and object status probe results have not yet been observed. + Some examples of invalid values are: + - -some-package + - some-package- + - thisisareallylongpackagenamethatisgreaterthanthemaximumlength + - some.package - The Succeeded condition represents whether the revision has successfully completed its rollout: - - When status is True and reason is Succeeded, the ClusterExtensionRevision has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -# Source: olmv1/templates/crds/customresourcedefinition-clusterextensions.olm.operatorframework.io.yml -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.20.1 - olm.operatorframework.io/generator: experimental - name: clusterextensions.olm.operatorframework.io -spec: - group: olm.operatorframework.io - names: - kind: ClusterExtension - listKind: ClusterExtensionList - plural: clusterextensions - singular: clusterextension - scope: Cluster - versions: - - additionalPrinterColumns: - - jsonPath: .status.install.bundle.name - name: Installed Bundle - type: string - - jsonPath: .status.install.bundle.version - name: Version - type: string - - jsonPath: .status.conditions[?(@.type=='Installed')].status - name: Installed - type: string - - jsonPath: .status.conditions[?(@.type=='Progressing')].status - name: Progressing - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: ClusterExtension is the Schema for the clusterextensions API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: spec is an optional field that defines the desired state - of the ClusterExtension. - properties: - config: - description: |- - config is optional and specifies bundle-specific configuration. - Configuration is bundle-specific and a bundle may provide a configuration schema. - When not specified, the default configuration of the resolved bundle is used. + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 253 + type: string + x-kubernetes-validations: + - message: packageName is immutable + rule: self == oldSelf + - message: packageName must be a valid DNS1123 subdomain. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric + character, and be no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + selector: + description: |- + selector is optional and filters the set of ClusterCatalogs used in the bundle selection process. + + When unspecified, all ClusterCatalogs are used in the bundle selection process. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + upgradeConstraintPolicy: + default: CatalogProvided + description: |- + upgradeConstraintPolicy is optional and controls whether the upgrade paths defined in the catalog + are enforced for the package referenced in the packageName field. + + Allowed values are "CatalogProvided", "SelfCertified", or omitted. + + When set to "CatalogProvided", automatic upgrades only occur when upgrade constraints specified by the package + author are met. + + When set to "SelfCertified", the upgrade constraints specified by the package author are ignored. + This allows upgrades and downgrades to any version of the package. + This is considered a dangerous operation as it can lead to unknown and potentially disastrous outcomes, + such as data loss. + Use this option only if you have independently verified the changes. + + When omitted, the default value is "CatalogProvided". + enum: + - CatalogProvided + - SelfCertified + type: string + version: + description: |- + version is an optional semver constraint (a specific version or range of versions). + When unspecified, the latest version available is installed. - config is validated against a configuration schema provided by the resolved bundle. If the bundle does not provide - a configuration schema the bundle is deemed to not be configurable. More information on how - to configure bundles can be found in the OLM documentation associated with your current OLM version. - properties: - configType: + Acceptable version ranges are no longer than 64 characters. + Version ranges are composed of comma- or space-delimited values and one or more comparison operators, + known as comparison strings. + You can add additional comparison strings using the OR operator (||). + + # Range Comparisons + + To specify a version range, you can use a comparison string like ">=3.0, + <3.6". When specifying a range, automatic updates will occur within that + range. The example comparison string means "install any version greater than + or equal to 3.0.0 but less than 3.6.0.". It also states intent that if any + upgrades are available within the version range after initial installation, + those upgrades should be automatically performed. + + # Pinned Versions + + To specify an exact version to install you can use a version range that + "pins" to a specific version. When pinning to a specific version, no + automatic updates will occur. An example of a pinned version range is + "0.6.0", which means "only install version 0.6.0 and never + upgrade from this version". + + # Basic Comparison Operators + + The basic comparison operators and their meanings are: + - "=", equal (not aliased to an operator) + - "!=", not equal + - "<", less than + - ">", greater than + - ">=", greater than OR equal to + - "<=", less than OR equal to + + # Wildcard Comparisons + + You can use the "x", "X", and "*" characters as wildcard characters in all + comparison operations. Some examples of using the wildcard characters: + - "1.2.x", "1.2.X", and "1.2.*" is equivalent to ">=1.2.0, < 1.3.0" + - ">= 1.2.x", ">= 1.2.X", and ">= 1.2.*" is equivalent to ">= 1.2.0" + - "<= 2.x", "<= 2.X", and "<= 2.*" is equivalent to "< 3" + - "x", "X", and "*" is equivalent to ">= 0.0.0" + + # Patch Release Comparisons + + When you want to specify a minor version up to the next major version you + can use the "~" character to perform patch comparisons. Some examples: + - "~1.2.3" is equivalent to ">=1.2.3, <1.3.0" + - "~1" and "~1.x" is equivalent to ">=1, <2" + - "~2.3" is equivalent to ">=2.3, <2.4" + - "~1.2.x" is equivalent to ">=1.2.0, <1.3.0" + + # Major Release Comparisons + + You can use the "^" character to make major release comparisons after a + stable 1.0.0 version is published. If there is no stable version published, // minor versions define the stability level. Some examples: + - "^1.2.3" is equivalent to ">=1.2.3, <2.0.0" + - "^1.2.x" is equivalent to ">=1.2.0, <2.0.0" + - "^2.3" is equivalent to ">=2.3, <3" + - "^2.x" is equivalent to ">=2.0.0, <3" + - "^0.2.3" is equivalent to ">=0.2.3, <0.3.0" + - "^0.2" is equivalent to ">=0.2.0, <0.3.0" + - "^0.0.3" is equvalent to ">=0.0.3, <0.0.4" + - "^0.0" is equivalent to ">=0.0.0, <0.1.0" + - "^0" is equivalent to ">=0.0.0, <1.0.0" + + # OR Comparisons + You can use the "||" character to represent an OR operation in the version + range. Some examples: + - ">=1.2.3, <2.0.0 || >3.0.0" + - "^0 || ^3 || ^5" + + For more information on semver, please see https://semver.org/ + maxLength: 64 + type: string + x-kubernetes-validations: + - message: invalid version expression + rule: self.matches("^(\\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|[x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*]))?(\\.(0|[1-9]\\d*|x|X|\\*))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)((?:\\s+|,\\s*|\\s*\\|\\|\\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*))?(\\.(0|[1-9]\\d*|x|X|\\*]))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)*$") + required: + - packageName + type: object + sourceType: description: |- - configType is required and specifies the type of configuration source. + sourceType is required and specifies the type of install source. - The only allowed value is "Inline". + The only allowed value is "Catalog". - When set to "Inline", the cluster extension configuration is defined inline within the ClusterExtension resource. + When set to "Catalog", information for determining the appropriate bundle of content to install + is fetched from ClusterCatalog resources on the cluster. + When using the Catalog sourceType, the catalog field must also be set. enum: - - Inline + - Catalog type: string - inline: - description: |- - inline contains JSON or YAML values specified directly in the ClusterExtension. - - It is used to specify arbitrary configuration values for the ClusterExtension. - It must be set if configType is 'Inline' and must be a valid JSON/YAML object containing at least one property. - The configuration values are validated at runtime against a JSON schema provided by the bundle. - minProperties: 1 - type: object - x-kubernetes-preserve-unknown-fields: true required: - - configType + - sourceType type: object x-kubernetes-validations: - - message: inline is required when configType is Inline, and forbidden + - message: catalog is required when sourceType is Catalog, and forbidden otherwise - rule: 'has(self.configType) && self.configType == ''Inline'' ?has(self.inline) - : !has(self.inline)' - install: + rule: 'has(self.sourceType) && self.sourceType == ''Catalog'' ? + has(self.catalog) : !has(self.catalog)' + required: + - namespace + - serviceAccount + - source + type: object + status: + description: status is an optional field that defines the observed state + of the ClusterExtension. + properties: + activeRevisions: description: |- - install is optional and configures installation options for the ClusterExtension, - such as the pre-flight check configuration. - properties: - preflight: - description: |- - preflight is optional and configures the checks that run before installation or upgrade - of the content for the package specified in the packageName field. - - When specified, it replaces the default preflight configuration for install/upgrade actions. - When not specified, the default configuration is used. - properties: - crdUpgradeSafety: - description: |- - crdUpgradeSafety configures the CRD Upgrade Safety pre-flight checks that run - before upgrades of installed content. - - The CRD Upgrade Safety pre-flight check safeguards from unintended consequences of upgrading a CRD, - such as data loss. + activeRevisions holds a list of currently active (non-archived) ClusterObjectSets, + including both installed and rolling out revisions. + items: + description: RevisionStatus defines the observed state of a ClusterObjectSet. + properties: + conditions: + description: |- + conditions optionally expose Progressing and Available condition of the revision, + in case when it is not yet marked as successfully installed (condition Succeeded is not set to True). + Given that a ClusterExtension should remain available during upgrades, an observer may use these conditions + to get more insights about reasons for its current state. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. properties: - enforcement: + lastTransitionTime: description: |- - enforcement is required and configures the state of the CRD Upgrade Safety pre-flight check. - - Allowed values are "None" or "Strict". The default value is "Strict". - - When set to "None", the CRD Upgrade Safety pre-flight check is skipped during an upgrade operation. - Use this option with caution as unintended consequences such as data loss can occur. - - When set to "Strict", the CRD Upgrade Safety pre-flight check runs during an upgrade operation. + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. enum: - - None - - Strict + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - - enforcement - type: object - required: - - crdUpgradeSafety - type: object - x-kubernetes-validations: - - message: at least one of [crdUpgradeSafety] are required when - preflight is specified - rule: has(self.crdUpgradeSafety) - type: object - x-kubernetes-validations: - - message: at least one of [preflight] are required when install is - specified - rule: has(self.preflight) - namespace: - description: |- - namespace specifies a Kubernetes namespace. - This is the namespace where the provided ServiceAccount must exist. - It also designates the default namespace where namespace-scoped resources for the extension are applied to the cluster. - Some extensions may contain namespace-scoped resources to be applied in other namespaces. - This namespace must exist. - - The namespace field is required, immutable, and follows the DNS label standard as defined in [RFC 1123]. - It must contain only lowercase alphanumeric characters or hyphens (-), start and end with an alphanumeric character, - and be no longer than 63 characters. - - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 63 - type: string - x-kubernetes-validations: - - message: namespace is immutable - rule: self == oldSelf - - message: namespace must be a valid DNS1123 label - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") - progressDeadlineMinutes: - description: |- - progressDeadlineMinutes is an optional field that defines the maximum period - of time in minutes after which an installation should be considered failed and - require manual intervention. This functionality is disabled when no value - is provided. The minimum period is 10 minutes, and the maximum is 720 minutes (12 hours). - format: int32 - maximum: 720 - minimum: 10 - type: integer - serviceAccount: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + name: + description: name of the ClusterObjectSet resource + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + conditions: description: |- - serviceAccount specifies a ServiceAccount used to perform all interactions with the cluster - that are required to manage the extension. - The ServiceAccount must be configured with the necessary permissions to perform these interactions. - The ServiceAccount must exist in the namespace referenced in the spec. - The serviceAccount field is required. - properties: - name: - description: |- - name is a required, immutable reference to the name of the ServiceAccount used for installation - and management of the content for the package specified in the packageName field. - - This ServiceAccount must exist in the installNamespace. - - The name field follows the DNS subdomain standard as defined in [RFC 1123]. - It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), - start and end with an alphanumeric character, and be no longer than 253 characters. - - Some examples of valid values are: - - some-serviceaccount - - 123-serviceaccount - - 1-serviceaccount-2 - - someserviceaccount - - some.serviceaccount + conditions represents the current state of the ClusterExtension. - Some examples of invalid values are: - - -some-serviceaccount - - some-serviceaccount- + The set of condition types which apply to all spec.source variations are Installed and Progressing. - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 253 - type: string - x-kubernetes-validations: - - message: name is immutable - rule: self == oldSelf - - message: name must be a valid DNS1123 subdomain. It must contain - only lowercase alphanumeric characters, hyphens (-) or periods - (.), start and end with an alphanumeric character, and be - no longer than 253 characters - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") - required: - - name - type: object - source: - description: |- - source is required and selects the installation source of content for this ClusterExtension. - Set the sourceType field to perform the selection. + The Installed condition represents whether the bundle has been installed for this ClusterExtension: + - When Installed is True and the Reason is Succeeded, the bundle has been successfully installed. + - When Installed is False and the Reason is Failed, the bundle has failed to install. - Catalog is currently the only implemented sourceType. - Setting sourceType to "Catalog" requires the catalog field to also be defined. + The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. + When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. + When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. + When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. - Below is a minimal example of a source definition (in yaml): + When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterObjectSets in active roll out. - source: - sourceType: Catalog - catalog: - packageName: example-package + When the ClusterExtension is sourced from a catalog, it surfaces deprecation conditions based on catalog metadata. + These are indications from a package owner to guide users away from a particular package, channel, or bundle: + - BundleDeprecated is True if the installed bundle is marked deprecated, False if not deprecated, or Unknown if no bundle is installed yet or if catalog data is unavailable. + - ChannelDeprecated is True if any requested channel is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable. + - PackageDeprecated is True if the requested package is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable. + - Deprecated is a rollup condition that is True when any deprecation exists, False when none exist, or Unknown when catalog data is unavailable. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + install: + description: install is a representation of the current installation + status for this ClusterExtension. properties: - catalog: + bundle: description: |- - catalog configures how information is sourced from a catalog. - It is required when sourceType is "Catalog", and forbidden otherwise. + bundle is required and represents the identifying attributes of a bundle. + + A "bundle" is a versioned set of content that represents the resources that need to be applied + to a cluster to install a package. properties: - channels: + name: description: |- - channels is optional and specifies a set of channels belonging to the package - specified in the packageName field. - - A channel is a package-author-defined stream of updates for an extension. - - Each channel in the list must follow the DNS subdomain standard as defined in [RFC 1123]. + name is required and follows the DNS subdomain standard as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters. - You can specify no more than 256 channels. - - When specified, it constrains the set of installable bundles and the automated upgrade path. - This constraint is an AND operation with the version field. For example: - - Given channel is set to "foo" - - Given version is set to ">=1.0.0, <1.5.0" - - Only bundles that exist in channel "foo" AND satisfy the version range comparison are considered installable - - Automatic upgrades are constrained to upgrade edges defined by the selected channel - - When unspecified, upgrade edges across all channels are used to identify valid automatic upgrade paths. - - Some examples of valid values are: - - 1.1.x - - alpha - - stable - - stable-v1 - - v1-stable - - dev-preview - - preview - - community - - Some examples of invalid values are: - - -some-channel - - some-channel- - - thisisareallylongchannelnamethatisgreaterthanthemaximumlength - - original_40 - - --default-channel - - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - items: - maxLength: 253 - type: string - x-kubernetes-validations: - - message: channels entries must be valid DNS1123 subdomains - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") - maxItems: 256 - type: array - packageName: + type: string + x-kubernetes-validations: + - message: packageName must be a valid DNS1123 subdomain. + It must contain only lowercase alphanumeric characters, + hyphens (-) or periods (.), start and end with an alphanumeric + character, and be no longer than 253 characters + rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") + version: description: |- - packageName specifies the name of the package to be installed and is used to filter - the content from catalogs. - - It is required, immutable, and follows the DNS subdomain standard as defined in [RFC 1123]. - It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), - start and end with an alphanumeric character, and be no longer than 253 characters. - - Some examples of valid values are: - - some-package - - 123-package - - 1-package-2 - - somepackage + version is required and references the version that this bundle represents. + It follows the semantic versioning standard as defined in https://semver.org/. + type: string + x-kubernetes-validations: + - message: version must be well-formed semver + rule: self.matches("^([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-([-0-9A-Za-z]+(\\.[-0-9A-Za-z]+)*))?(\\+([-0-9A-Za-z]+(-\\.[-0-9A-Za-z]+)*))?") + required: + - name + - version + type: object + required: + - bundle + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +# Source: olmv1/templates/crds/customresourcedefinition-clusterobjectsets.olm.operatorframework.io.yml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + olm.operatorframework.io/generator: experimental + name: clusterobjectsets.olm.operatorframework.io +spec: + group: olm.operatorframework.io + names: + kind: ClusterObjectSet + listKind: ClusterObjectSetList + plural: clusterobjectsets + singular: clusterobjectset + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Available')].status + name: Available + type: string + - jsonPath: .status.conditions[?(@.type=='Progressing')].status + name: Progressing + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + ClusterObjectSet represents an immutable snapshot of Kubernetes objects + for a specific version of a ClusterExtension. Each revision contains objects + organized into phases that roll out sequentially. The same object can only be managed by a single revision + at a time. Ownership of objects is transitioned from one revision to the next as the extension is upgraded + or reconfigured. Once the latest revision has rolled out successfully, previous active revisions are archived for + posterity. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: spec defines the desired state of the ClusterObjectSet. + properties: + collisionProtection: + description: |- + collisionProtection specifies the default collision protection strategy for all objects + in this revision. Individual phases or objects can override this value. - Some examples of invalid values are: - - -some-package - - some-package- - - thisisareallylongpackagenamethatisgreaterthanthemaximumlength - - some.package + When set, this value is used as the default for any phase or object that does not + explicitly specify its own collisionProtection. - [RFC 1123]: https://tools.ietf.org/html/rfc1123 - maxLength: 253 - type: string - x-kubernetes-validations: - - message: packageName is immutable - rule: self == oldSelf - - message: packageName must be a valid DNS1123 subdomain. - It must contain only lowercase alphanumeric characters, - hyphens (-) or periods (.), start and end with an alphanumeric - character, and be no longer than 253 characters - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") - selector: - description: |- - selector is optional and filters the set of ClusterCatalogs used in the bundle selection process. + The resolution order is: object > phase > spec + enum: + - Prevent + - IfNoController + - None + type: string + x-kubernetes-validations: + - message: collisionProtection is immutable + rule: self == oldSelf + lifecycleState: + description: |- + lifecycleState specifies the lifecycle state of the ClusterObjectSet. - When unspecified, all ClusterCatalogs are used in the bundle selection process. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - upgradeConstraintPolicy: - default: CatalogProvided - description: |- - upgradeConstraintPolicy is optional and controls whether the upgrade paths defined in the catalog - are enforced for the package referenced in the packageName field. + When set to "Active", the revision is actively managed and reconciled. + When set to "Archived", the revision is inactive and any resources not managed by a subsequent revision are deleted. + The revision is removed from the owner list of all objects previously under management. + All objects that did not transition to a succeeding revision are deleted. - Allowed values are "CatalogProvided", "SelfCertified", or omitted. + Once a revision is set to "Archived", it cannot be un-archived. - When set to "CatalogProvided", automatic upgrades only occur when upgrade constraints specified by the package - author are met. + It is possible for more than one revision to be "Active" simultaneously. This will occur when + moving from one revision to another. The old revision will not be set to "Archived" until the + new revision has been completely rolled out. + enum: + - Active + - Archived + type: string + x-kubernetes-validations: + - message: cannot un-archive + rule: oldSelf == 'Active' || oldSelf == 'Archived' && oldSelf == + self + phases: + description: |- + phases is an optional, immutable list of phases that group objects to be applied together. - When set to "SelfCertified", the upgrade constraints specified by the package author are ignored. - This allows upgrades and downgrades to any version of the package. - This is considered a dangerous operation as it can lead to unknown and potentially disastrous outcomes, - such as data loss. - Use this option only if you have independently verified the changes. + Objects are organized into phases based on their Group-Kind. Common phases include: + - namespaces: Namespace objects + - policies: ResourceQuota, LimitRange, NetworkPolicy objects + - rbac: ServiceAccount, Role, RoleBinding, ClusterRole, ClusterRoleBinding objects + - crds: CustomResourceDefinition objects + - storage: PersistentVolume, PersistentVolumeClaim, StorageClass objects + - deploy: Deployment, StatefulSet, DaemonSet, Service, ConfigMap, Secret objects + - publish: Ingress, APIService, Route, Webhook objects - When omitted, the default value is "CatalogProvided". - enum: - - CatalogProvided - - SelfCertified - type: string - version: - description: |- - version is an optional semver constraint (a specific version or range of versions). - When unspecified, the latest version available is installed. + All objects in a phase are applied in no particular order. + The revision progresses to the next phase only after all objects in the current phase pass their readiness probes. - Acceptable version ranges are no longer than 64 characters. - Version ranges are composed of comma- or space-delimited values and one or more comparison operators, - known as comparison strings. - You can add additional comparison strings using the OR operator (||). + Once set, even if empty, the phases field is immutable. - # Range Comparisons + Each phase in the list must have a unique name. The maximum number of phases is 20. + items: + description: |- + ClusterObjectSetPhase represents a group of objects that are applied together. The phase is considered + complete only after all objects pass their status probes. + properties: + collisionProtection: + description: |- + collisionProtection specifies the default collision protection strategy for all objects + in this phase. Individual objects can override this value. - To specify a version range, you can use a comparison string like ">=3.0, - <3.6". When specifying a range, automatic updates will occur within that - range. The example comparison string means "install any version greater than - or equal to 3.0.0 but less than 3.6.0.". It also states intent that if any - upgrades are available within the version range after initial installation, - those upgrades should be automatically performed. + When set, this value is used as the default for any object in this phase that does not + explicitly specify its own collisionProtection. - # Pinned Versions + When omitted, we use .spec.collistionProtection as the default for any object in this phase that does not + explicitly specify its own collisionProtection. + enum: + - Prevent + - IfNoController + - None + type: string + name: + description: |- + name is a required identifier for this phase. - To specify an exact version to install you can use a version range that - "pins" to a specific version. When pinning to a specific version, no - automatic updates will occur. An example of a pinned version range is - "0.6.0", which means "only install version 0.6.0 and never - upgrade from this version". + phase names must follow the DNS label standard as defined in [RFC 1123]. + They must contain only lowercase alphanumeric characters or hyphens (-), + start and end with an alphanumeric character, and be no longer than 63 characters. - # Basic Comparison Operators + Common phase names include: namespaces, policies, rbac, crds, storage, deploy, publish. - The basic comparison operators and their meanings are: - - "=", equal (not aliased to an operator) - - "!=", not equal - - "<", less than - - ">", greater than - - ">=", greater than OR equal to - - "<=", less than OR equal to + [RFC 1123]: https://tools.ietf.org/html/rfc1123 + maxLength: 63 + minLength: 1 + type: string + x-kubernetes-validations: + - message: the value must consist of only lowercase alphanumeric + characters and hyphens, and must start with an alphabetic + character and end with an alphanumeric character. + rule: '!format.dns1123Label().validate(self).hasValue()' + objects: + description: |- + objects is a required list of all Kubernetes objects that belong to this phase. - # Wildcard Comparisons + All objects in this list are applied to the cluster in no particular order. The maximum number of objects per phase is 50. + items: + description: |- + ClusterObjectSetObject represents a Kubernetes object to be applied as part + of a phase, along with its collision protection settings. - You can use the "x", "X", and "*" characters as wildcard characters in all - comparison operations. Some examples of using the wildcard characters: - - "1.2.x", "1.2.X", and "1.2.*" is equivalent to ">=1.2.0, < 1.3.0" - - ">= 1.2.x", ">= 1.2.X", and ">= 1.2.*" is equivalent to ">= 1.2.0" - - "<= 2.x", "<= 2.X", and "<= 2.*" is equivalent to "< 3" - - "x", "X", and "*" is equivalent to ">= 0.0.0" + Exactly one of object or ref must be set. + properties: + collisionProtection: + description: |- + collisionProtection controls whether the operator can adopt and modify objects + that already exist on the cluster. - # Patch Release Comparisons + Allowed values are: "Prevent", "IfNoController", and "None". - When you want to specify a minor version up to the next major version you - can use the "~" character to perform patch comparisons. Some examples: - - "~1.2.3" is equivalent to ">=1.2.3, <1.3.0" - - "~1" and "~1.x" is equivalent to ">=1, <2" - - "~2.3" is equivalent to ">=2.3, <2.4" - - "~1.2.x" is equivalent to ">=1.2.0, <1.3.0" + When set to "Prevent", the operator only manages objects it created itself. + This prevents ownership collisions. - # Major Release Comparisons + When set to "IfNoController", the operator can adopt and modify pre-existing objects + that are not owned by another controller. + This is useful for taking over management of manually-created resources. - You can use the "^" character to make major release comparisons after a - stable 1.0.0 version is published. If there is no stable version published, // minor versions define the stability level. Some examples: - - "^1.2.3" is equivalent to ">=1.2.3, <2.0.0" - - "^1.2.x" is equivalent to ">=1.2.0, <2.0.0" - - "^2.3" is equivalent to ">=2.3, <3" - - "^2.x" is equivalent to ">=2.0.0, <3" - - "^0.2.3" is equivalent to ">=0.2.3, <0.3.0" - - "^0.2" is equivalent to ">=0.2.0, <0.3.0" - - "^0.0.3" is equvalent to ">=0.0.3, <0.0.4" - - "^0.0" is equivalent to ">=0.0.0, <0.1.0" - - "^0" is equivalent to ">=0.0.0, <1.0.0" + When set to "None", the operator can adopt and modify any pre-existing object, even if + owned by another controller. + Use this setting with extreme caution as it may cause multiple controllers to fight over + the same resource, resulting in increased load on the API server and etcd. - # OR Comparisons - You can use the "||" character to represent an OR operation in the version - range. Some examples: - - ">=1.2.3, <2.0.0 || >3.0.0" - - "^0 || ^3 || ^5" + When omitted, the value is inherited from the phase, then spec. + enum: + - Prevent + - IfNoController + - None + type: string + object: + description: |- + object is an optional embedded Kubernetes object to be applied. - For more information on semver, please see https://semver.org/ - maxLength: 64 - type: string - x-kubernetes-validations: - - message: invalid version expression - rule: self.matches("^(\\s*(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|[x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*]))?(\\.(0|[1-9]\\d*|x|X|\\*))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)((?:\\s+|,\\s*|\\s*\\|\\|\\s*)(=||!=|>|<|>=|=>|<=|=<|~|~>|\\^)\\s*(v?(0|[1-9]\\d*|x|X|\\*])(\\.(0|[1-9]\\d*|x|X|\\*))?(\\.(0|[1-9]\\d*|x|X|\\*]))?(-([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?(\\+([0-9A-Za-z\\-]+(\\.[0-9A-Za-z\\-]+)*))?)\\s*)*$") - required: - - packageName - type: object - sourceType: - description: |- - sourceType is required and specifies the type of install source. + Exactly one of object or ref must be set. - The only allowed value is "Catalog". + This object must be a valid Kubernetes resource with apiVersion, kind, and metadata fields. + type: object + x-kubernetes-embedded-resource: true + x-kubernetes-preserve-unknown-fields: true + ref: + description: |- + ref is an optional reference to a Secret that holds the serialized + object manifest. - When set to "Catalog", information for determining the appropriate bundle of content to install - is fetched from ClusterCatalog resources on the cluster. - When using the Catalog sourceType, the catalog field must also be set. - enum: - - Catalog - type: string - required: - - sourceType - type: object + Exactly one of object or ref must be set. + properties: + key: + description: |- + key is the data key within the referenced Secret containing the + object manifest content. The value at this key must be a + JSON-serialized Kubernetes object manifest. + maxLength: 253 + minLength: 1 + type: string + name: + description: name is the name of the referenced Secret. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + namespace is the namespace of the referenced Secret. + When empty, defaults to the OLM system namespace during ref resolution. + maxLength: 63 + type: string + required: + - key + - name + type: object + type: object + x-kubernetes-validations: + - message: exactly one of object or ref must be set + rule: has(self.object) != has(self.ref) + maxItems: 50 + type: array + required: + - name + - objects + type: object + maxItems: 20 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map x-kubernetes-validations: - - message: catalog is required when sourceType is Catalog, and forbidden - otherwise - rule: 'has(self.sourceType) && self.sourceType == ''Catalog'' ? - has(self.catalog) : !has(self.catalog)' - required: - - namespace - - serviceAccount - - source - type: object - status: - description: status is an optional field that defines the observed state - of the ClusterExtension. - properties: - activeRevisions: + - message: phases is immutable + rule: self == oldSelf || oldSelf.size() == 0 + progressDeadlineMinutes: description: |- - activeRevisions holds a list of currently active (non-archived) ClusterExtensionRevisions, - including both installed and rolling out revisions. + progressDeadlineMinutes is an optional field that defines the maximum period + of time in minutes after which an installation should be considered failed and + require manual intervention. This functionality is disabled when no value + is provided. The minimum period is 10 minutes, and the maximum is 720 minutes (12 hours). + format: int32 + maximum: 720 + minimum: 10 + type: integer + progressionProbes: + description: |- + progressionProbes is an optional field which provides the ability to define custom readiness probes + for objects defined within spec.phases. As documented in that field, most kubernetes-native objects + within the phases already have some kind of readiness check built-in, but this field allows for checks + which are tailored to the objects being rolled out - particularly custom resources. + + Probes defined within the progressionProbes list will apply to every phase in the revision. However, the probes will only + execute against phase objects which are a match for the provided selector type. For instance, a probe using a GroupKind selector + for ConfigMaps will automatically be considered to have passed for any non-ConfigMap object, but will halt any phase containing + a ConfigMap if that particular object does not pass the probe check. + + The maximum number of probes is 20. items: - description: RevisionStatus defines the observed state of a ClusterExtensionRevision. + description: ProgressionProbe provides a custom probe definition, + consisting of an object selection method and assertions. properties: - conditions: + assertions: description: |- - conditions optionally expose Progressing and Available condition of the revision, - in case when it is not yet marked as successfully installed (condition Succeeded is not set to True). - Given that a ClusterExtension should remain available during upgrades, an observer may use these conditions - to get more insights about reasons for its current state. + assertions is a required list of checks which will run against the objects selected by the selector. If + one or more assertions fail then the phase within which the object lives will be not be considered + 'Ready', blocking rollout of all subsequent phases. items: - description: Condition contains details for one aspect of - the current state of this API Resource. + description: Assertion is a discriminated union which defines + the probe type and definition used as an assertion. properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: + conditionEqual: + description: conditionEqual contains the expected condition + type and status. + properties: + status: + description: |- + status sets the expected condition status. + + Allowed values are "True" and "False". + enum: + - "True" + - "False" + type: string + type: + description: type sets the expected condition type, + i.e. "Ready". + maxLength: 200 + minLength: 1 + type: string + required: + - status + - type + type: object + fieldValue: + description: fieldValue contains the expected field path + and value found within. + properties: + fieldPath: + description: |- + fieldPath sets the field path for the field to check, i.e. "status.phase". The probe will fail + if the path does not exist. + maxLength: 200 + minLength: 1 + type: string + x-kubernetes-validations: + - message: must contain a valid field path. valid + fields contain upper or lower-case alphanumeric + characters separated by the "." character. + rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') + value: + description: value sets the expected value found at + fieldPath, i.e. "Bound". + maxLength: 200 + minLength: 1 + type: string + required: + - fieldPath + - value + type: object + fieldsEqual: + description: fieldsEqual contains the two field paths + whose values are expected to match. + properties: + fieldA: + description: |- + fieldA sets the field path for the first field, i.e. "spec.replicas". The probe will fail + if the path does not exist. + maxLength: 200 + minLength: 1 + type: string + x-kubernetes-validations: + - message: must contain a valid field path. valid + fields contain upper or lower-case alphanumeric + characters separated by the "." character. + rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') + fieldB: + description: |- + fieldB sets the field path for the second field, i.e. "status.readyReplicas". The probe will fail + if the path does not exist. + maxLength: 200 + minLength: 1 + type: string + x-kubernetes-validations: + - message: must contain a valid field path. valid + fields contain upper or lower-case alphanumeric + characters separated by the "." character. + rule: self.matches('^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*$') + required: + - fieldA + - fieldB + type: object + type: description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, - Unknown. + type is a required field which specifies the type of probe to use. + + The allowed probe types are "ConditionEqual", "FieldsEqual", and "FieldValue". + + When set to "ConditionEqual", the probe checks objects that have reached a condition of specified type and status. + When set to "FieldsEqual", the probe checks that the values found at two provided field paths are matching. + When set to "FieldValue", the probe checks that the value found at the provided field path matches what was specified. enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + - ConditionEqual + - FieldsEqual + - FieldValue type: string required: - - lastTransitionTime - - message - - reason - - status - type type: object + x-kubernetes-validations: + - message: conditionEqual is required when type is ConditionEqual, + and forbidden otherwise + rule: 'self.type == ''ConditionEqual'' ?has(self.conditionEqual) + : !has(self.conditionEqual)' + - message: fieldsEqual is required when type is FieldsEqual, + and forbidden otherwise + rule: 'self.type == ''FieldsEqual'' ?has(self.fieldsEqual) + : !has(self.fieldsEqual)' + - message: fieldValue is required when type is FieldValue, + and forbidden otherwise + rule: 'self.type == ''FieldValue'' ?has(self.fieldValue) + : !has(self.fieldValue)' + maxItems: 20 + minItems: 1 type: array - x-kubernetes-list-map-keys: + x-kubernetes-list-type: atomic + selector: + description: |- + selector is a required field which defines the method by which we select objects to apply the below + assertions to. Any object which matches the defined selector will have all the associated assertions + applied against it. + + If no objects within a phase are selected by the provided selector, then all assertions defined here + are considered to have succeeded. + properties: + groupKind: + description: |- + groupKind specifies the group and kind of objects to select. + + Required when type is "GroupKind". + + Uses the Kubernetes format specified here: + https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#GroupKind + properties: + group: + type: string + kind: + type: string + required: + - group + - kind + type: object + label: + description: |- + label is the label selector definition. + + Required when type is "Label". + + A probe using a Label selector will be executed against every object matching the labels or expressions; you must use care + when using this type of selector. For example, if multiple Kind objects are selected via labels then the probe is + likely to fail because the values of different Kind objects rarely share the same schema. + + The LabelSelector field uses the following Kubernetes format: + https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#LabelSelector + Requires exactly one of matchLabels or matchExpressions. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + x-kubernetes-validations: + - message: exactly one of matchLabels or matchExpressions + must be set + rule: (has(self.matchExpressions) && !has(self.matchLabels)) + || (!has(self.matchExpressions) && has(self.matchLabels)) + type: + description: |- + type is a required field which specifies the type of selector to use. + + The allowed selector types are "GroupKind" and "Label". + + When set to "GroupKind", all objects which match the specified group and kind will be selected. + When set to "Label", all objects which match the specified labels and/or expressions will be selected. + enum: + - GroupKind + - Label + type: string + required: - type - x-kubernetes-list-type: map - name: - description: name of the ClusterExtensionRevision resource - type: string + type: object + x-kubernetes-validations: + - message: groupKind is required when type is GroupKind, and + forbidden otherwise + rule: 'self.type == ''GroupKind'' ?has(self.groupKind) : !has(self.groupKind)' + - message: label is required when type is Label, and forbidden + otherwise + rule: 'self.type == ''Label'' ?has(self.label) : !has(self.label)' required: - - name + - assertions + - selector type: object + maxItems: 20 + minItems: 1 type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - conditions: + x-kubernetes-list-type: atomic + revision: description: |- - conditions represents the current state of the ClusterExtension. - - The set of condition types which apply to all spec.source variations are Installed and Progressing. + revision is a required, immutable sequence number representing a specific revision + of the parent ClusterExtension. - The Installed condition represents whether the bundle has been installed for this ClusterExtension: - - When Installed is True and the Reason is Succeeded, the bundle has been successfully installed. - - When Installed is False and the Reason is Failed, the bundle has failed to install. + The revision field must be a positive integer. + Each ClusterObjectSet belonging to the same parent ClusterExtension must have a unique revision number. + The revision number must always be the previous revision number plus one, or 1 for the first revision. + format: int64 + minimum: 1 + type: integer + x-kubernetes-validations: + - message: revision is immutable + rule: self == oldSelf + required: + - collisionProtection + - lifecycleState + - revision + type: object + status: + description: status is optional and defines the observed state of the + ClusterObjectSet. + properties: + conditions: + description: |- + conditions is an optional list of status conditions describing the state of the + ClusterObjectSet. - The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state. - When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state. - When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts. - When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery. + The Progressing condition represents whether the revision is actively rolling out: + - When status is True and reason is RollingOut, the ClusterObjectSet rollout is actively making progress and is in transition. + - When status is True and reason is Retrying, the ClusterObjectSet has encountered an error that could be resolved on subsequent reconciliation attempts. + - When status is True and reason is Succeeded, the ClusterObjectSet has reached the desired state. + - When status is False and reason is Blocked, the ClusterObjectSet has encountered an error that requires manual intervention for recovery. + - When status is False and reason is Archived, the ClusterObjectSet is archived and not being actively reconciled. - When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterExtensionRevisions in active roll out. + The Available condition represents whether the revision has been successfully rolled out and is available: + - When status is True and reason is ProbesSucceeded, the ClusterObjectSet has been successfully rolled out and all objects pass their readiness probes. + - When status is False and reason is ProbeFailure, one or more objects are failing their readiness probes during rollout. + - When status is Unknown and reason is Reconciling, the ClusterObjectSet has encountered an error that prevented it from observing the probes. + - When status is Unknown and reason is Archived, the ClusterObjectSet has been archived and its objects have been torn down. + - When status is Unknown and reason is Migrated, the ClusterObjectSet was migrated from an existing release and object status probe results have not yet been observed. - When the ClusterExtension is sourced from a catalog, it surfaces deprecation conditions based on catalog metadata. - These are indications from a package owner to guide users away from a particular package, channel, or bundle: - - BundleDeprecated is True if the installed bundle is marked deprecated, False if not deprecated, or Unknown if no bundle is installed yet or if catalog data is unavailable. - - ChannelDeprecated is True if any requested channel is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable. - - PackageDeprecated is True if the requested package is marked deprecated, False if not deprecated, or Unknown if catalog data is unavailable. - - Deprecated is a rollup condition that is True when any deprecation exists, False when none exist, or Unknown when catalog data is unavailable. + The Succeeded condition represents whether the revision has successfully completed its rollout: + - When status is True and reason is Succeeded, the ClusterObjectSet has successfully completed its rollout. This condition is set once and persists even if the revision later becomes unavailable. items: description: Condition contains details for one aspect of the current state of this API Resource. @@ -1866,44 +1904,6 @@ spec: x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map - install: - description: install is a representation of the current installation - status for this ClusterExtension. - properties: - bundle: - description: |- - bundle is required and represents the identifying attributes of a bundle. - - A "bundle" is a versioned set of content that represents the resources that need to be applied - to a cluster to install a package. - properties: - name: - description: |- - name is required and follows the DNS subdomain standard as defined in [RFC 1123]. - It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), - start and end with an alphanumeric character, and be no longer than 253 characters. - type: string - x-kubernetes-validations: - - message: packageName must be a valid DNS1123 subdomain. - It must contain only lowercase alphanumeric characters, - hyphens (-) or periods (.), start and end with an alphanumeric - character, and be no longer than 253 characters - rule: self.matches("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$") - version: - description: |- - version is required and references the version that this bundle represents. - It follows the semantic versioning standard as defined in https://semver.org/. - type: string - x-kubernetes-validations: - - message: version must be well-formed semver - rule: self.matches("^([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-([-0-9A-Za-z]+(\\.[-0-9A-Za-z]+)*))?(\\+([-0-9A-Za-z]+(-\\.[-0-9A-Za-z]+)*))?") - required: - - name - - version - type: object - required: - - bundle - type: object type: object type: object served: true @@ -2129,7 +2129,7 @@ rules: - apiGroups: - olm.operatorframework.io resources: - - clusterextensionrevisions + - clusterobjectsets verbs: - create - delete @@ -2141,14 +2141,14 @@ rules: - apiGroups: - olm.operatorframework.io resources: - - clusterextensionrevisions/status + - clusterobjectsets/status verbs: - patch - update - apiGroups: - olm.operatorframework.io resources: - - clusterextensionrevisions/finalizers + - clusterobjectsets/finalizers verbs: - update --- diff --git a/test/e2e/README.md b/test/e2e/README.md index 01dbca7026..a8bd7ba9b6 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -201,7 +201,7 @@ Leverage existing steps for common operations: Use these variables in YAML templates: - `${NAME}`: Scenario-specific ClusterExtension name (e.g., `ce-123`) -- `${CER_NAME}`: Scenario-specific ClusterExtensionRevision name (e.g., `cer-123`; for applying CERs directly) +- `${COS_NAME}`: Scenario-specific ClusterObjectSet name (e.g., `cos-123`; for applying ClusterObjectSets directly) - `${TEST_NAMESPACE}`: Scenario-specific namespace (e.g., `ns-123`) - `${CATALOG_IMG}`: Catalog image reference (defaults to in-cluster registry, overridable via `CATALOG_IMG` env var) diff --git a/test/e2e/features/install.feature b/test/e2e/features/install.feature index 791c51afdd..35aadecae6 100644 --- a/test/e2e/features/install.feature +++ b/test/e2e/features/install.feature @@ -387,7 +387,7 @@ Feature: Install ClusterExtension @ProgressDeadline Scenario: Report ClusterExtension as not progressing if the rollout does not become available within given timeout Given min value for ClusterExtension .spec.progressDeadlineMinutes is set to 1 - And min value for ClusterExtensionRevision .spec.progressDeadlineMinutes is set to 1 + And min value for ClusterObjectSet .spec.progressDeadlineMinutes is set to 1 When ClusterExtension is applied """ apiVersion: olm.operatorframework.io/v1 @@ -409,7 +409,7 @@ Feature: Install ClusterExtension matchLabels: "olm.operatorframework.io/metadata.name": test-catalog """ - Then ClusterExtensionRevision "${NAME}-1" reports Progressing as False with Reason ProgressDeadlineExceeded + Then ClusterObjectSet "${NAME}-1" reports Progressing as False with Reason ProgressDeadlineExceeded And ClusterExtension reports Progressing as False with Reason ProgressDeadlineExceeded and Message: """ Revision has not rolled out for 1 minute(s). @@ -420,7 +420,7 @@ Feature: Install ClusterExtension @ProgressDeadline Scenario: Report ClusterExtension as not progressing if the rollout does not complete within given timeout Given min value for ClusterExtension .spec.progressDeadlineMinutes is set to 1 - And min value for ClusterExtensionRevision .spec.progressDeadlineMinutes is set to 1 + And min value for ClusterObjectSet .spec.progressDeadlineMinutes is set to 1 When ClusterExtension is applied """ apiVersion: olm.operatorframework.io/v1 @@ -441,7 +441,7 @@ Feature: Install ClusterExtension matchLabels: "olm.operatorframework.io/metadata.name": test-catalog """ - Then ClusterExtensionRevision "${NAME}-1" reports Progressing as False with Reason ProgressDeadlineExceeded + Then ClusterObjectSet "${NAME}-1" reports Progressing as False with Reason ProgressDeadlineExceeded And ClusterExtension reports Progressing as False with Reason ProgressDeadlineExceeded and Message: """ Revision has not rolled out for 1 minute(s). @@ -449,7 +449,7 @@ Feature: Install ClusterExtension And ClusterExtension reports Progressing transition between 1 and 2 minutes since its creation @BoxcutterRuntime - Scenario: ClusterExtensionRevision is annotated with bundle properties + Scenario: ClusterObjectSet is annotated with bundle properties When ClusterExtension is applied """ apiVersion: olm.operatorframework.io/v1 @@ -470,13 +470,13 @@ Feature: Install ClusterExtension "olm.operatorframework.io/metadata.name": test-catalog """ # The annotation key and value come from the bundle's metadata/properties.yaml file - Then ClusterExtensionRevision "${NAME}-1" contains annotation "olm.properties" with value + Then ClusterObjectSet "${NAME}-1" contains annotation "olm.properties" with value """ [{"type":"olm.test-property","value":"some-value"}] """ @BoxcutterRuntime - Scenario: ClusterExtensionRevision is labeled with owner information + Scenario: ClusterObjectSet is labeled with owner information When ClusterExtension is applied """ apiVersion: olm.operatorframework.io/v1 @@ -498,11 +498,11 @@ Feature: Install ClusterExtension """ Then ClusterExtension is rolled out And ClusterExtension is available - And ClusterExtensionRevision "${NAME}-1" has label "olm.operatorframework.io/owner-kind" with value "ClusterExtension" - And ClusterExtensionRevision "${NAME}-1" has label "olm.operatorframework.io/owner-name" with value "${NAME}" + And ClusterObjectSet "${NAME}-1" has label "olm.operatorframework.io/owner-kind" with value "ClusterExtension" + And ClusterObjectSet "${NAME}-1" has label "olm.operatorframework.io/owner-name" with value "${NAME}" @BoxcutterRuntime - Scenario: ClusterExtensionRevision objects are externalized to immutable Secrets + Scenario: ClusterObjectSet objects are externalized to immutable Secrets When ClusterExtension is applied """ apiVersion: olm.operatorframework.io/v1 @@ -524,11 +524,11 @@ Feature: Install ClusterExtension """ Then ClusterExtension is rolled out And ClusterExtension is available - And ClusterExtensionRevision "${NAME}-1" phase objects use refs - And ClusterExtensionRevision "${NAME}-1" ref Secrets exist in "olmv1-system" namespace - And ClusterExtensionRevision "${NAME}-1" ref Secrets are immutable - And ClusterExtensionRevision "${NAME}-1" ref Secrets are labeled with revision and owner - And ClusterExtensionRevision "${NAME}-1" ref Secrets have ownerReference to the revision + And ClusterObjectSet "${NAME}-1" phase objects use refs + And ClusterObjectSet "${NAME}-1" ref Secrets exist in "olmv1-system" namespace + And ClusterObjectSet "${NAME}-1" ref Secrets are immutable + And ClusterObjectSet "${NAME}-1" ref Secrets are labeled with revision and owner + And ClusterObjectSet "${NAME}-1" ref Secrets have ownerReference to the revision @DeploymentConfig Scenario: deploymentConfig nodeSelector is applied to the operator deployment diff --git a/test/e2e/features/revision.feature b/test/e2e/features/revision.feature index f2c2c7fa29..d07e3fb198 100644 --- a/test/e2e/features/revision.feature +++ b/test/e2e/features/revision.feature @@ -1,22 +1,22 @@ @BoxcutterRuntime -Feature: Install ClusterExtensionRevision +Feature: Install ClusterObjectSet - As an OLM user I would like to install a cluster extension revision directly, without using the cluster extension API. + As an OLM user I would like to install a ClusterObjectSet directly, without using the cluster extension API. Background: Given OLM is available Scenario: Probe failure for PersistentVolumeClaim halts phase progression Given ServiceAccount "pvc-probe-sa" with needed permissions is available in test namespace - When ClusterExtensionRevision is applied + When ClusterObjectSet is applied """ apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet metadata: annotations: olm.operatorframework.io/service-account-name: pvc-probe-sa olm.operatorframework.io/service-account-namespace: ${TEST_NAMESPACE} - name: ${CER_NAME} + name: ${COS_NAME} spec: lifecycleState: Active collisionProtection: Prevent @@ -63,7 +63,7 @@ Feature: Install ClusterExtensionRevision """ Then resource "persistentvolumeclaim/test-pvc" is installed - And ClusterExtensionRevision "${CER_NAME}" reports Available as False with Reason ProbeFailure and Message: + And ClusterObjectSet "${COS_NAME}" reports Available as False with Reason ProbeFailure and Message: """ Object PersistentVolumeClaim.v1 ${TEST_NAMESPACE}/test-pvc: value at key "status.phase" != "Bound"; expected: "Bound" got: "Pending" """ @@ -71,15 +71,15 @@ Feature: Install ClusterExtensionRevision Scenario: Phases progress when PersistentVolumeClaim becomes "Bound" Given ServiceAccount "pvc-probe-sa" with needed permissions is available in test namespace - When ClusterExtensionRevision is applied + When ClusterObjectSet is applied """ apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet metadata: annotations: olm.operatorframework.io/service-account-name: pvc-probe-sa olm.operatorframework.io/service-account-namespace: ${TEST_NAMESPACE} - name: ${CER_NAME} + name: ${COS_NAME} spec: lifecycleState: Active collisionProtection: Prevent @@ -153,23 +153,23 @@ Feature: Install ClusterExtensionRevision revision: 1 """ - Then ClusterExtensionRevision "${CER_NAME}" reports Progressing as True with Reason Succeeded - And ClusterExtensionRevision "${CER_NAME}" reports Available as True with Reason ProbesSucceeded + Then ClusterObjectSet "${COS_NAME}" reports Progressing as True with Reason Succeeded + And ClusterObjectSet "${COS_NAME}" reports Available as True with Reason ProbesSucceeded And resource "persistentvolume/test-pv" is installed And resource "persistentvolumeclaim/test-pvc" is installed And resource "configmap/test-configmap" is installed Scenario: Phases does not progress when user-provided progressionProbes do not pass Given ServiceAccount "pvc-probe-sa" with needed permissions is available in test namespace - When ClusterExtensionRevision is applied + When ClusterObjectSet is applied """ apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet metadata: annotations: olm.operatorframework.io/service-account-name: pvc-probe-sa olm.operatorframework.io/service-account-namespace: ${TEST_NAMESPACE} - name: ${CER_NAME} + name: ${COS_NAME} spec: lifecycleState: Active collisionProtection: Prevent @@ -214,7 +214,7 @@ Feature: Install ClusterExtensionRevision """ Then resource "configmap/test-configmap-1" is installed - And ClusterExtensionRevision "${CER_NAME}" reports Available as False with Reason ProbeFailure and Message: + And ClusterObjectSet "${COS_NAME}" reports Available as False with Reason ProbeFailure and Message: """ Object ConfigMap.v1 ${TEST_NAMESPACE}/test-configmap-1: value at key "data.foo" != "bar"; expected: "bar" got: "foo" """ @@ -222,15 +222,15 @@ Feature: Install ClusterExtensionRevision Scenario: Phases progresses when user-provided progressionProbes pass Given ServiceAccount "pvc-probe-sa" with needed permissions is available in test namespace - When ClusterExtensionRevision is applied + When ClusterObjectSet is applied """ apiVersion: olm.operatorframework.io/v1 - kind: ClusterExtensionRevision + kind: ClusterObjectSet metadata: annotations: olm.operatorframework.io/service-account-name: pvc-probe-sa olm.operatorframework.io/service-account-namespace: ${TEST_NAMESPACE} - name: ${CER_NAME} + name: ${COS_NAME} spec: lifecycleState: Active collisionProtection: Prevent @@ -337,5 +337,5 @@ Feature: Install ClusterExtensionRevision And resource "serviceaccount/test-serviceaccount" is installed And resource "pod/test-pod" is installed And resource "configmap/test-configmap-3" is installed - And ClusterExtensionRevision "${CER_NAME}" reports Progressing as True with Reason Succeeded - And ClusterExtensionRevision "${CER_NAME}" reports Available as True with Reason ProbesSucceeded \ No newline at end of file + And ClusterObjectSet "${COS_NAME}" reports Progressing as True with Reason Succeeded + And ClusterObjectSet "${COS_NAME}" reports Available as True with Reason ProbesSucceeded \ No newline at end of file diff --git a/test/e2e/features/status.feature b/test/e2e/features/status.feature index 5c8a3141d6..28631d6e3e 100644 --- a/test/e2e/features/status.feature +++ b/test/e2e/features/status.feature @@ -33,13 +33,13 @@ Feature: Report status of the managed ClusterExtension workload Scenario: Report availability change when managed workload is not ready When resource "deployment/test-operator" reports as not ready Then ClusterExtension reports Available as False with Reason ProbeFailure - And ClusterExtensionRevision "${NAME}-1" reports Available as False with Reason ProbeFailure + And ClusterObjectSet "${NAME}-1" reports Available as False with Reason ProbeFailure @BoxcutterRuntime Scenario: Report availability change when managed workload restores its readiness Given resource "deployment/test-operator" reports as not ready And ClusterExtension reports Available as False with Reason ProbeFailure - And ClusterExtensionRevision "${NAME}-1" reports Available as False with Reason ProbeFailure + And ClusterObjectSet "${NAME}-1" reports Available as False with Reason ProbeFailure When resource "deployment/test-operator" reports as ready Then ClusterExtension is available - And ClusterExtensionRevision "${NAME}-1" reports Available as True with Reason ProbesSucceeded \ No newline at end of file + And ClusterObjectSet "${NAME}-1" reports Available as True with Reason ProbesSucceeded \ No newline at end of file diff --git a/test/e2e/features/update.feature b/test/e2e/features/update.feature index ef637fcbff..483fc328e0 100644 --- a/test/e2e/features/update.feature +++ b/test/e2e/features/update.feature @@ -291,10 +291,10 @@ Feature: Update ClusterExtension And ClusterExtension is rolled out And ClusterExtension is available And ClusterExtension reports "${NAME}-2" as active revision - And ClusterExtensionRevision "${NAME}-2" reports Progressing as True with Reason Succeeded - And ClusterExtensionRevision "${NAME}-2" reports Available as True with Reason ProbesSucceeded - And ClusterExtensionRevision "${NAME}-1" is archived - And ClusterExtensionRevision "${NAME}-1" phase objects are not found or not owned by the revision + And ClusterObjectSet "${NAME}-2" reports Progressing as True with Reason Succeeded + And ClusterObjectSet "${NAME}-2" reports Available as True with Reason ProbesSucceeded + And ClusterObjectSet "${NAME}-1" is archived + And ClusterObjectSet "${NAME}-1" phase objects are not found or not owned by the revision @BoxcutterRuntime Scenario: Report all active revisions on ClusterExtension @@ -322,6 +322,6 @@ Feature: Update ClusterExtension And ClusterExtension is available When ClusterExtension is updated to version "1.0.2" Then ClusterExtension reports "${NAME}-1, ${NAME}-2" as active revisions - And ClusterExtensionRevision "${NAME}-2" reports Progressing as True with Reason RollingOut - And ClusterExtensionRevision "${NAME}-2" reports Available as False with Reason ProbeFailure + And ClusterObjectSet "${NAME}-2" reports Progressing as True with Reason RollingOut + And ClusterObjectSet "${NAME}-2" reports Available as False with Reason ProbeFailure diff --git a/test/e2e/steps/hooks.go b/test/e2e/steps/hooks.go index f38559cb2f..ccce95efc5 100644 --- a/test/e2e/steps/hooks.go +++ b/test/e2e/steps/hooks.go @@ -27,22 +27,22 @@ type resource struct { } type scenarioContext struct { - id string - namespace string - clusterExtensionName string - clusterExtensionRevisionName string - clusterCatalogName string - addedResources []resource - removedResources []unstructured.Unstructured - backGroundCmds []*exec.Cmd - metricsResponse map[string]string - leaderPods map[string]string // component name -> leader pod name + id string + namespace string + clusterExtensionName string + clusterObjectSetName string + clusterCatalogName string + addedResources []resource + removedResources []unstructured.Unstructured + backGroundCmds []*exec.Cmd + metricsResponse map[string]string + leaderPods map[string]string // component name -> leader pod name extensionObjects []client.Object } // GatherClusterExtensionObjects collects all resources related to the ClusterExtension container in -// either their Helm release Secret or ClusterExtensionRevision depending on the applier being used +// either their Helm release Secret or ClusterObjectSet depending on the applier being used // and saves them into the context. func (s *scenarioContext) GatherClusterExtensionObjects() error { objs, err := listExtensionResources(s.clusterExtensionName) @@ -153,11 +153,11 @@ func CheckFeatureTags(ctx context.Context, sc *godog.Scenario) (context.Context, func CreateScenarioContext(ctx context.Context, sc *godog.Scenario) (context.Context, error) { scCtx := &scenarioContext{ - id: sc.Id, - namespace: fmt.Sprintf("ns-%s", sc.Id), - clusterExtensionName: fmt.Sprintf("ce-%s", sc.Id), - clusterExtensionRevisionName: fmt.Sprintf("cer-%s", sc.Id), - leaderPods: make(map[string]string), + id: sc.Id, + namespace: fmt.Sprintf("ns-%s", sc.Id), + clusterExtensionName: fmt.Sprintf("ce-%s", sc.Id), + clusterObjectSetName: fmt.Sprintf("cos-%s", sc.Id), + leaderPods: make(map[string]string), } return context.WithValue(ctx, scenarioContextKey, scCtx), nil } @@ -189,8 +189,8 @@ func ScenarioCleanup(ctx context.Context, _ *godog.Scenario, err error) (context if sc.clusterExtensionName != "" { forDeletion = append(forDeletion, resource{name: sc.clusterExtensionName, kind: "clusterextension"}) } - if sc.clusterExtensionRevisionName != "" { - forDeletion = append(forDeletion, resource{name: sc.clusterExtensionRevisionName, kind: "clusterextensionrevision"}) + if sc.clusterObjectSetName != "" { + forDeletion = append(forDeletion, resource{name: sc.clusterObjectSetName, kind: "clusterobjectset"}) } forDeletion = append(forDeletion, resource{name: sc.namespace, kind: "namespace"}) for _, r := range forDeletion { diff --git a/test/e2e/steps/steps.go b/test/e2e/steps/steps.go index d26fc27072..131d5ccfe1 100644 --- a/test/e2e/steps/steps.go +++ b/test/e2e/steps/steps.go @@ -88,19 +88,19 @@ func RegisterSteps(sc *godog.ScenarioContext) { sc.Step(`^(?i)ClusterExtension reports ([[:alnum:]]+) as ([[:alnum:]]+) with Reason ([[:alnum:]]+) and Message includes:$`, ClusterExtensionReportsConditionWithMessageFragment) sc.Step(`^(?i)ClusterExtension reports ([[:alnum:]]+) as ([[:alnum:]]+) with Reason ([[:alnum:]]+)$`, ClusterExtensionReportsConditionWithoutMsg) sc.Step(`^(?i)ClusterExtension reports ([[:alnum:]]+) as ([[:alnum:]]+)$`, ClusterExtensionReportsConditionWithoutReason) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" reports ([[:alnum:]]+) as ([[:alnum:]]+) with Reason ([[:alnum:]]+)$`, ClusterExtensionRevisionReportsConditionWithoutMsg) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" reports ([[:alnum:]]+) as ([[:alnum:]]+) with Reason ([[:alnum:]]+) and Message:$`, ClusterExtensionRevisionReportsConditionWithMsg) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" reports ([[:alnum:]]+) as ([[:alnum:]]+) with Reason ([[:alnum:]]+)$`, ClusterObjectSetReportsConditionWithoutMsg) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" reports ([[:alnum:]]+) as ([[:alnum:]]+) with Reason ([[:alnum:]]+) and Message:$`, ClusterObjectSetReportsConditionWithMsg) sc.Step(`^(?i)ClusterExtension reports ([[:alnum:]]+) transition between (\d+) and (\d+) minutes since its creation$`, ClusterExtensionReportsConditionTransitionTime) - sc.Step(`^(?i)ClusterExtensionRevision is applied(?:\s+.*)?$`, ResourceIsApplied) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" is archived$`, ClusterExtensionRevisionIsArchived) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" contains annotation "([^"]+)" with value$`, ClusterExtensionRevisionHasAnnotationWithValue) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" has label "([^"]+)" with value "([^"]+)"$`, ClusterExtensionRevisionHasLabelWithValue) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" phase objects are not found or not owned by the revision$`, ClusterExtensionRevisionObjectsNotFoundOrNotOwned) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" phase objects use refs$`, ClusterExtensionRevisionPhaseObjectsUseRefs) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" ref Secrets exist in "([^"]+)" namespace$`, ClusterExtensionRevisionRefSecretsExist) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" ref Secrets are immutable$`, ClusterExtensionRevisionRefSecretsAreImmutable) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" ref Secrets are labeled with revision and owner$`, ClusterExtensionRevisionRefSecretsLabeled) - sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" ref Secrets have ownerReference to the revision$`, ClusterExtensionRevisionRefSecretsHaveOwnerRef) + sc.Step(`^(?i)ClusterObjectSet is applied(?:\s+.*)?$`, ResourceIsApplied) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" is archived$`, ClusterObjectSetIsArchived) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" contains annotation "([^"]+)" with value$`, ClusterObjectSetHasAnnotationWithValue) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" has label "([^"]+)" with value "([^"]+)"$`, ClusterObjectSetHasLabelWithValue) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" phase objects are not found or not owned by the revision$`, ClusterObjectSetObjectsNotFoundOrNotOwned) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" phase objects use refs$`, ClusterObjectSetPhaseObjectsUseRefs) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" ref Secrets exist in "([^"]+)" namespace$`, ClusterObjectSetRefSecretsExist) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" ref Secrets are immutable$`, ClusterObjectSetRefSecretsAreImmutable) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" ref Secrets are labeled with revision and owner$`, ClusterObjectSetRefSecretsLabeled) + sc.Step(`^(?i)ClusterObjectSet "([^"]+)" ref Secrets have ownerReference to the revision$`, ClusterObjectSetRefSecretsHaveOwnerRef) sc.Step(`^(?i)resource "([^"]+)" is installed$`, ResourceAvailable) sc.Step(`^(?i)resource "([^"]+)" is available$`, ResourceAvailable) @@ -143,7 +143,7 @@ func RegisterSteps(sc *godog.ScenarioContext) { sc.Step(`^(?i)operator "([^"]+)" target namespace is "([^"]+)"$`, OperatorTargetNamespace) sc.Step(`^(?i)Prometheus metrics are returned in the response$`, PrometheusMetricsAreReturned) - sc.Step(`^(?i)min value for (ClusterExtension|ClusterExtensionRevision) ((?:\.[a-zA-Z]+)+) is set to (\d+)$`, SetCRDFieldMinValue) + sc.Step(`^(?i)min value for (ClusterExtension|ClusterObjectSet) ((?:\.[a-zA-Z]+)+) is set to (\d+)$`, SetCRDFieldMinValue) sc.Step(`^(?i)the current ClusterExtension is tracked for cleanup$`, TrackCurrentClusterExtensionForCleanup) @@ -232,7 +232,7 @@ func substituteScenarioVars(content string, sc *scenarioContext) string { vars := map[string]string{ "TEST_NAMESPACE": sc.namespace, "NAME": sc.clusterExtensionName, - "CER_NAME": sc.clusterExtensionRevisionName, + "COS_NAME": sc.clusterObjectSetName, "CATALOG_IMG": "docker-registry.operator-controller-e2e.svc.cluster.local:5000/e2e/test-catalog:v1", } if v, found := os.LookupEnv("CATALOG_IMG"); found { @@ -293,8 +293,8 @@ func ClusterExtensionVersionUpdate(ctx context.Context, version string) error { return err } -// ResourceIsApplied applies the provided YAML resource to the cluster and in case of ClusterExtension or ClusterExtensionRevision it captures -// its name in the test context so that it can be referred to in later steps with ${NAME} or ${CER_NAME}, respectively +// ResourceIsApplied applies the provided YAML resource to the cluster and in case of ClusterExtension or ClusterObjectSet it captures +// its name in the test context so that it can be referred to in later steps with ${NAME} or ${COS_NAME}, respectively func ResourceIsApplied(ctx context.Context, yamlTemplate *godog.DocString) error { sc := scenarioCtx(ctx) yamlContent := substituteScenarioVars(yamlTemplate.Content, sc) @@ -308,8 +308,8 @@ func ResourceIsApplied(ctx context.Context, yamlTemplate *godog.DocString) error } if res.GetKind() == "ClusterExtension" { sc.clusterExtensionName = res.GetName() - } else if res.GetKind() == "ClusterExtensionRevision" { - sc.clusterExtensionRevisionName = res.GetName() + } else if res.GetKind() == "ClusterObjectSet" { + sc.clusterObjectSetName = res.GetName() } return nil } @@ -575,27 +575,27 @@ func ClusterExtensionReportsActiveRevisions(ctx context.Context, rawRevisionName return nil } -// ClusterExtensionRevisionReportsConditionWithoutMsg waits for the named ClusterExtensionRevision to have a condition +// ClusterObjectSetReportsConditionWithoutMsg waits for the named ClusterObjectSet to have a condition // matching type, status, and reason. Polls with timeout. -func ClusterExtensionRevisionReportsConditionWithoutMsg(ctx context.Context, revisionName, conditionType, conditionStatus, conditionReason string) error { - return waitForCondition(ctx, "clusterextensionrevision", substituteScenarioVars(revisionName, scenarioCtx(ctx)), conditionType, conditionStatus, &conditionReason, nil) +func ClusterObjectSetReportsConditionWithoutMsg(ctx context.Context, revisionName, conditionType, conditionStatus, conditionReason string) error { + return waitForCondition(ctx, "clusterobjectset", substituteScenarioVars(revisionName, scenarioCtx(ctx)), conditionType, conditionStatus, &conditionReason, nil) } -// ClusterExtensionRevisionReportsConditionWithMsg waits for the named ClusterExtensionRevision to have a condition +// ClusterObjectSetReportsConditionWithMsg waits for the named ClusterObjectSet to have a condition // matching type, status, reason, and message. Polls with timeout. -func ClusterExtensionRevisionReportsConditionWithMsg(ctx context.Context, revisionName, conditionType, conditionStatus, conditionReason string, msg *godog.DocString) error { - return waitForCondition(ctx, "clusterextensionrevision", substituteScenarioVars(revisionName, scenarioCtx(ctx)), conditionType, conditionStatus, &conditionReason, messageComparison(ctx, msg)) +func ClusterObjectSetReportsConditionWithMsg(ctx context.Context, revisionName, conditionType, conditionStatus, conditionReason string, msg *godog.DocString) error { + return waitForCondition(ctx, "clusterobjectset", substituteScenarioVars(revisionName, scenarioCtx(ctx)), conditionType, conditionStatus, &conditionReason, messageComparison(ctx, msg)) } -// ClusterExtensionRevisionIsArchived waits for the named ClusterExtensionRevision to have Progressing=False +// ClusterObjectSetIsArchived waits for the named ClusterObjectSet to have Progressing=False // with reason Archived. Polls with timeout. -func ClusterExtensionRevisionIsArchived(ctx context.Context, revisionName string) error { - return waitForCondition(ctx, "clusterextensionrevision", substituteScenarioVars(revisionName, scenarioCtx(ctx)), "Progressing", "False", ptr.To("Archived"), nil) +func ClusterObjectSetIsArchived(ctx context.Context, revisionName string) error { + return waitForCondition(ctx, "clusterobjectset", substituteScenarioVars(revisionName, scenarioCtx(ctx)), "Progressing", "False", ptr.To("Archived"), nil) } -// ClusterExtensionRevisionHasAnnotationWithValue waits for the named ClusterExtensionRevision to have the specified +// ClusterObjectSetHasAnnotationWithValue waits for the named ClusterObjectSet to have the specified // annotation with the expected value. Polls with timeout. -func ClusterExtensionRevisionHasAnnotationWithValue(ctx context.Context, revisionName, annotationKey string, annotationValue *godog.DocString) error { +func ClusterObjectSetHasAnnotationWithValue(ctx context.Context, revisionName, annotationKey string, annotationValue *godog.DocString) error { sc := scenarioCtx(ctx) revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc) expectedValue := "" @@ -603,9 +603,9 @@ func ClusterExtensionRevisionHasAnnotationWithValue(ctx context.Context, revisio expectedValue = annotationValue.Content } waitFor(ctx, func() bool { - obj, err := getResource("clusterextensionrevision", revisionName, "") + obj, err := getResource("clusterobjectset", revisionName, "") if err != nil { - logger.V(1).Error(err, "failed to get clusterextensionrevision", "name", revisionName) + logger.V(1).Error(err, "failed to get clusterobjectset", "name", revisionName) return false } if obj.GetAnnotations() == nil { @@ -616,16 +616,16 @@ func ClusterExtensionRevisionHasAnnotationWithValue(ctx context.Context, revisio return nil } -// ClusterExtensionRevisionHasLabelWithValue waits for the named ClusterExtensionRevision to have the specified label +// ClusterObjectSetHasLabelWithValue waits for the named ClusterObjectSet to have the specified label // with the expected value. Polls with timeout. -func ClusterExtensionRevisionHasLabelWithValue(ctx context.Context, revisionName, labelKey, labelValue string) error { +func ClusterObjectSetHasLabelWithValue(ctx context.Context, revisionName, labelKey, labelValue string) error { sc := scenarioCtx(ctx) revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc) labelValue = substituteScenarioVars(labelValue, sc) waitFor(ctx, func() bool { - obj, err := getResource("clusterextensionrevision", revisionName, "") + obj, err := getResource("clusterobjectset", revisionName, "") if err != nil { - logger.V(1).Error(err, "failed to get clusterextensionrevision", "name", revisionName) + logger.V(1).Error(err, "failed to get clusterobjectset", "name", revisionName) return false } if obj.GetLabels() == nil { @@ -636,17 +636,17 @@ func ClusterExtensionRevisionHasLabelWithValue(ctx context.Context, revisionName return nil } -// ClusterExtensionRevisionObjectsNotFoundOrNotOwned waits for all objects described in the named -// ClusterExtensionRevision's phases to either not exist on the cluster or not contain the revision +// ClusterObjectSetObjectsNotFoundOrNotOwned waits for all objects described in the named +// ClusterObjectSet's phases to either not exist on the cluster or not contain the revision // in their ownerReferences. Polls with timeout. -func ClusterExtensionRevisionObjectsNotFoundOrNotOwned(ctx context.Context, revisionName string) error { +func ClusterObjectSetObjectsNotFoundOrNotOwned(ctx context.Context, revisionName string) error { sc := scenarioCtx(ctx) revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc) - // Get the CER to extract its phase objects - var rev ocv1.ClusterExtensionRevision + // Get the ClusterObjectSet to extract its phase objects + var rev ocv1.ClusterObjectSet waitFor(ctx, func() bool { - out, err := k8sClient("get", "clusterextensionrevision", revisionName, "-o", "json") + out, err := k8sClient("get", "clusterobjectset", revisionName, "-o", "json") if err != nil { return false } @@ -654,7 +654,7 @@ func ClusterExtensionRevisionObjectsNotFoundOrNotOwned(ctx context.Context, revi }) // For each object in each phase, verify it either doesn't exist or - // doesn't have the CER in its ownerReferences + // doesn't have the ClusterObjectSet in its ownerReferences for i, phase := range rev.Spec.Phases { for j, phaseObj := range phase.Objects { var obj *unstructured.Unstructured @@ -668,17 +668,17 @@ func ClusterExtensionRevisionObjectsNotFoundOrNotOwned(ctx context.Context, revi case len(phaseObj.Object.Object) > 0: obj = &phaseObj.Object default: - return fmt.Errorf("clusterextensionrevision %q phase %d object %d has neither ref nor inline object", revisionName, i, j) + return fmt.Errorf("clusterobjectset %q phase %d object %d has neither ref nor inline object", revisionName, i, j) } kind := obj.GetKind() name := obj.GetName() namespace := obj.GetNamespace() if kind == "" { - return fmt.Errorf("clusterextensionrevision %q has a phase object with empty kind", revisionName) + return fmt.Errorf("clusterobjectset %q has a phase object with empty kind", revisionName) } if name == "" { - return fmt.Errorf("clusterextensionrevision %q has a phase object with empty name (kind %q, namespace %q)", revisionName, kind, namespace) + return fmt.Errorf("clusterobjectset %q has a phase object with empty name (kind %q, namespace %q)", revisionName, kind, namespace) } waitFor(ctx, func() bool { @@ -699,9 +699,9 @@ func ClusterExtensionRevisionObjectsNotFoundOrNotOwned(ctx context.Context, revi return false } - // Check that no ownerReference points to this CER + // Check that no ownerReference points to this ClusterObjectSet for _, ref := range clusterObj.GetOwnerReferences() { - if ref.Kind == ocv1.ClusterExtensionRevisionKind && ref.Name == revisionName && ref.UID == rev.UID { + if ref.Kind == ocv1.ClusterObjectSetKind && ref.Name == revisionName && ref.UID == rev.UID { logger.V(1).Info("object still owned by revision", "kind", kind, "name", name, "namespace", namespace, "revision", revisionName) @@ -715,14 +715,14 @@ func ClusterExtensionRevisionObjectsNotFoundOrNotOwned(ctx context.Context, revi return nil } -// ClusterExtensionRevisionPhaseObjectsUseRefs verifies that every object in every phase of the named -// ClusterExtensionRevision uses a ref (not an inline object). Polls with timeout. -func ClusterExtensionRevisionPhaseObjectsUseRefs(ctx context.Context, revisionName string) error { +// ClusterObjectSetPhaseObjectsUseRefs verifies that every object in every phase of the named +// ClusterObjectSet uses a ref (not an inline object). Polls with timeout. +func ClusterObjectSetPhaseObjectsUseRefs(ctx context.Context, revisionName string) error { sc := scenarioCtx(ctx) revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc) waitFor(ctx, func() bool { - obj, err := getResource("clusterextensionrevision", revisionName, "") + obj, err := getResource("clusterobjectset", revisionName, "") if err != nil { return false } @@ -761,9 +761,9 @@ func ClusterExtensionRevisionPhaseObjectsUseRefs(ctx context.Context, revisionNa return nil } -// ClusterExtensionRevisionRefSecretsExist verifies that all Secrets referenced by the named -// ClusterExtensionRevision's phase objects exist in the given namespace. Polls with timeout. -func ClusterExtensionRevisionRefSecretsExist(ctx context.Context, revisionName, namespace string) error { +// ClusterObjectSetRefSecretsExist verifies that all Secrets referenced by the named +// ClusterObjectSet's phase objects exist in the given namespace. Polls with timeout. +func ClusterObjectSetRefSecretsExist(ctx context.Context, revisionName, namespace string) error { sc := scenarioCtx(ctx) revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc) namespace = substituteScenarioVars(strings.TrimSpace(namespace), sc) @@ -782,9 +782,9 @@ func ClusterExtensionRevisionRefSecretsExist(ctx context.Context, revisionName, return nil } -// ClusterExtensionRevisionRefSecretsAreImmutable verifies that all ref Secrets for the named -// ClusterExtensionRevision are immutable. Polls with timeout. -func ClusterExtensionRevisionRefSecretsAreImmutable(ctx context.Context, revisionName string) error { +// ClusterObjectSetRefSecretsAreImmutable verifies that all ref Secrets for the named +// ClusterObjectSet are immutable. Polls with timeout. +func ClusterObjectSetRefSecretsAreImmutable(ctx context.Context, revisionName string) error { sc := scenarioCtx(ctx) revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc) @@ -803,9 +803,9 @@ func ClusterExtensionRevisionRefSecretsAreImmutable(ctx context.Context, revisio return nil } -// ClusterExtensionRevisionRefSecretsLabeled verifies that all ref Secrets for the named -// ClusterExtensionRevision have the expected revision-name and owner-name labels. -func ClusterExtensionRevisionRefSecretsLabeled(ctx context.Context, revisionName string) error { +// ClusterObjectSetRefSecretsLabeled verifies that all ref Secrets for the named +// ClusterObjectSet have the expected revision-name and owner-name labels. +func ClusterObjectSetRefSecretsLabeled(ctx context.Context, revisionName string) error { sc := scenarioCtx(ctx) revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc) @@ -817,12 +817,12 @@ func ClusterExtensionRevisionRefSecretsLabeled(ctx context.Context, revisionName return fmt.Errorf("no ref Secrets found for revision %q", revisionName) } - // Get the owner name from the CER's own labels. - cerObj, err := getResource("clusterextensionrevision", revisionName, "") + // Get the owner name from the ClusterObjectSet's own labels. + cosObj, err := getResource("clusterobjectset", revisionName, "") if err != nil { - return fmt.Errorf("getting CER %q: %w", revisionName, err) + return fmt.Errorf("getting ClusterObjectSet %q: %w", revisionName, err) } - expectedOwner := cerObj.GetLabels()["olm.operatorframework.io/owner-name"] + expectedOwner := cosObj.GetLabels()["olm.operatorframework.io/owner-name"] for _, s := range secrets { revLabel := s.Labels["olm.operatorframework.io/revision-name"] @@ -837,17 +837,17 @@ func ClusterExtensionRevisionRefSecretsLabeled(ctx context.Context, revisionName return nil } -// ClusterExtensionRevisionRefSecretsHaveOwnerRef verifies that all ref Secrets for the named -// ClusterExtensionRevision have an ownerReference pointing to the CER with controller=true. -func ClusterExtensionRevisionRefSecretsHaveOwnerRef(ctx context.Context, revisionName string) error { +// ClusterObjectSetRefSecretsHaveOwnerRef verifies that all ref Secrets for the named +// ClusterObjectSet have an ownerReference pointing to the ClusterObjectSet with controller=true. +func ClusterObjectSetRefSecretsHaveOwnerRef(ctx context.Context, revisionName string) error { sc := scenarioCtx(ctx) revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc) - cerObj, err := getResource("clusterextensionrevision", revisionName, "") + cosObj, err := getResource("clusterobjectset", revisionName, "") if err != nil { - return fmt.Errorf("getting CER %q: %w", revisionName, err) + return fmt.Errorf("getting ClusterObjectSet %q: %w", revisionName, err) } - cerUID := cerObj.GetUID() + cosUID := cosObj.GetUID() secrets, err := listRefSecrets(ctx, revisionName) if err != nil { @@ -860,22 +860,22 @@ func ClusterExtensionRevisionRefSecretsHaveOwnerRef(ctx context.Context, revisio for _, s := range secrets { found := false for _, ref := range s.OwnerReferences { - if ref.Kind == ocv1.ClusterExtensionRevisionKind && ref.Name == revisionName && ref.UID == cerUID { + if ref.Kind == ocv1.ClusterObjectSetKind && ref.Name == revisionName && ref.UID == cosUID { if ref.Controller == nil || !*ref.Controller { - return fmt.Errorf("secret %s/%s has ownerReference to CER but controller is not true", s.Namespace, s.Name) + return fmt.Errorf("secret %s/%s has ownerReference to ClusterObjectSet but controller is not true", s.Namespace, s.Name) } found = true break } } if !found { - return fmt.Errorf("secret %s/%s does not have ownerReference to CER %q (uid %s)", s.Namespace, s.Name, revisionName, cerUID) + return fmt.Errorf("secret %s/%s does not have ownerReference to ClusterObjectSet %q (uid %s)", s.Namespace, s.Name, revisionName, cosUID) } } return nil } -// collectRefSecretNames returns the unique set of Secret names referenced by the CER's phase objects. +// collectRefSecretNames returns the unique set of Secret names referenced by the ClusterObjectSet's phase objects. func collectRefSecretNames(ctx context.Context, revisionName string) ([]string, error) { var names []string seen := sets.New[string]() @@ -883,7 +883,7 @@ func collectRefSecretNames(ctx context.Context, revisionName string) ([]string, var obj *unstructured.Unstructured waitFor(ctx, func() bool { var err error - obj, err = getResource("clusterextensionrevision", revisionName, "") + obj, err = getResource("clusterobjectset", revisionName, "") return err == nil }) @@ -907,7 +907,7 @@ func collectRefSecretNames(ctx context.Context, revisionName string) ([]string, } } if len(names) == 0 { - return nil, fmt.Errorf("no ref Secret names found in CER %q", revisionName) + return nil, fmt.Errorf("no ref Secret names found in ClusterObjectSet %q", revisionName) } return names, nil } @@ -1419,8 +1419,8 @@ func SetCRDFieldMinValue(_ context.Context, resourceType, jsonPath string, minVa switch resourceType { case "ClusterExtension": crdName = "clusterextensions.olm.operatorframework.io" - case "ClusterExtensionRevision": - crdName = "clusterextensionrevisions.olm.operatorframework.io" + case "ClusterObjectSet": + crdName = "clusterobjectsets.olm.operatorframework.io" default: return fmt.Errorf("unsupported resource type: %s", resourceType) } @@ -1650,23 +1650,23 @@ func resolveObjectRef(ref ocv1.ObjectSourceRef) (*unstructured.Unstructured, err } // latestActiveRevisionForExtension returns the latest active revision for the extension called extName -func latestActiveRevisionForExtension(extName string) (*ocv1.ClusterExtensionRevision, error) { - out, err := k8sClient("get", "clusterextensionrevisions", "-l", fmt.Sprintf("olm.operatorframework.io/owner-name=%s", extName), "-o", "json") +func latestActiveRevisionForExtension(extName string) (*ocv1.ClusterObjectSet, error) { + out, err := k8sClient("get", "clusterobjectsets", "-l", fmt.Sprintf("olm.operatorframework.io/owner-name=%s", extName), "-o", "json") if err != nil { return nil, fmt.Errorf("error listing revisions for extension '%s': %w", extName, err) } if strings.TrimSpace(out) == "" { return nil, fmt.Errorf("no revisions found for extension '%s'", extName) } - var revisionList ocv1.ClusterExtensionRevisionList + var revisionList ocv1.ClusterObjectSetList if err := json.Unmarshal([]byte(out), &revisionList); err != nil { return nil, fmt.Errorf("error unmarshalling revisions for extension '%s': %w", extName, err) } - var latest *ocv1.ClusterExtensionRevision + var latest *ocv1.ClusterObjectSet for i := range revisionList.Items { rev := &revisionList.Items[i] - if rev.Spec.LifecycleState != ocv1.ClusterExtensionRevisionLifecycleStateActive { + if rev.Spec.LifecycleState != ocv1.ClusterObjectSetLifecycleStateActive { continue } if latest == nil || rev.Spec.Revision > latest.Spec.Revision { diff --git a/test/e2e/steps/testdata/olm-sa-boxcutter-no-create-rbac-template.yaml b/test/e2e/steps/testdata/olm-sa-boxcutter-no-create-rbac-template.yaml index 8581d69cdc..d94f25a4bb 100644 --- a/test/e2e/steps/testdata/olm-sa-boxcutter-no-create-rbac-template.yaml +++ b/test/e2e/steps/testdata/olm-sa-boxcutter-no-create-rbac-template.yaml @@ -9,9 +9,9 @@ kind: ClusterRole metadata: name: ${TEST_NAMESPACE}-${SERVICEACCOUNT_NAME}-olm-admin-clusterrole rules: - # Allow management of ClusterExtensionRevision finalizers (e.g. by the Boxcutter applier) + # Allow management of ClusterObjectSet finalizers (e.g. by the Boxcutter applier) - apiGroups: [olm.operatorframework.io] - resources: [clusterextensionrevisions/finalizers] + resources: [clusterobjectsets/finalizers] verbs: [update, patch] # OLMv0 compatibility requirement for AllNamespaces install # https://github.com/operator-framework/operator-lifecycle-manager/blob/dfd0b2bea85038d3c0d65348bc812d297f16b8d2/pkg/controller/operators/olm/operatorgroup.go#L530 diff --git a/test/e2e/steps/testdata/olm-sa-boxcutter-rbac-template.yaml b/test/e2e/steps/testdata/olm-sa-boxcutter-rbac-template.yaml index 7c35f997e5..b2a96661d9 100644 --- a/test/e2e/steps/testdata/olm-sa-boxcutter-rbac-template.yaml +++ b/test/e2e/steps/testdata/olm-sa-boxcutter-rbac-template.yaml @@ -9,9 +9,9 @@ kind: ClusterRole metadata: name: ${TEST_NAMESPACE}-${SERVICEACCOUNT_NAME}-olm-admin-clusterrole rules: - # Allow management of ClusterExtensionRevision finalizers (e.g. by the Boxcutter applier) + # Allow management of ClusterObjectSet finalizers (e.g. by the Boxcutter applier) - apiGroups: [olm.operatorframework.io] - resources: [clusterextensionrevisions/finalizers] + resources: [clusterobjectsets/finalizers] verbs: [update, patch] # OLMv0 compatibility requirement for AllNamespaces install # https://github.com/operator-framework/operator-lifecycle-manager/blob/dfd0b2bea85038d3c0d65348bc812d297f16b8d2/pkg/controller/operators/olm/operatorgroup.go#L530 diff --git a/test/e2e/steps/testdata/pvc-probe-sa-boxcutter-rbac-template.yaml b/test/e2e/steps/testdata/pvc-probe-sa-boxcutter-rbac-template.yaml index 526b9845a0..ded711858f 100644 --- a/test/e2e/steps/testdata/pvc-probe-sa-boxcutter-rbac-template.yaml +++ b/test/e2e/steps/testdata/pvc-probe-sa-boxcutter-rbac-template.yaml @@ -4,7 +4,7 @@ metadata: name: ${TEST_NAMESPACE}-${SERVICEACCOUNT_NAME}-clusterrole rules: - apiGroups: [olm.operatorframework.io] - resources: [clusterextensionrevisions/finalizers] + resources: [clusterobjectsets/finalizers] verbs: [update] - apiGroups: [""] resources: [persistentvolumes] diff --git a/test/e2e/steps/testdata/rbac-template-for-all-extensions.yaml b/test/e2e/steps/testdata/rbac-template-for-all-extensions.yaml index fa27689828..67e3444cb4 100644 --- a/test/e2e/steps/testdata/rbac-template-for-all-extensions.yaml +++ b/test/e2e/steps/testdata/rbac-template-for-all-extensions.yaml @@ -12,9 +12,9 @@ rules: - apiGroups: [olm.operatorframework.io] resources: [clusterextensions, clusterextensions/finalizers] verbs: [update] - # Allow ClusterExtensionRevisions to set blockOwnerDeletion ownerReferences + # Allow ClusterObjectSets to set blockOwnerDeletion ownerReferences - apiGroups: [olm.operatorframework.io] - resources: [clusterextensionrevisions, clusterextensionrevisions/finalizers] + resources: [clusterobjectsets, clusterobjectsets/finalizers] verbs: [update, create, list, watch, get, delete, patch] - apiGroups: [apiextensions.k8s.io] resources: [customresourcedefinitions]