@@ -16,7 +16,6 @@ import (
1616 "k8s.io/apimachinery/pkg/api/equality"
1717 "k8s.io/apimachinery/pkg/api/meta"
1818 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19- "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2019 "k8s.io/apimachinery/pkg/runtime/schema"
2120 "k8s.io/apimachinery/pkg/types"
2221 "k8s.io/apimachinery/pkg/util/sets"
@@ -116,7 +115,17 @@ func checkForUnexpectedClusterExtensionRevisionFieldChange(a, b ocv1.ClusterExte
116115func (c * ClusterExtensionRevisionReconciler ) reconcile (ctx context.Context , rev * ocv1.ClusterExtensionRevision ) (ctrl.Result , error ) {
117116 l := log .FromContext (ctx )
118117
119- revision , opts , previous := toBoxcutterRevision (rev )
118+ revision , opts , err := c .toBoxcutterRevision (ctx , rev )
119+ if err != nil {
120+ meta .SetStatusCondition (& rev .Status .Conditions , metav1.Condition {
121+ Type : ocv1 .ClusterExtensionRevisionTypeAvailable ,
122+ Status : metav1 .ConditionFalse ,
123+ Reason : ocv1 .ClusterExtensionRevisionReasonReconcileFailure ,
124+ Message : err .Error (),
125+ ObservedGeneration : rev .Generation ,
126+ })
127+ return ctrl.Result {}, fmt .Errorf ("converting to boxcutter revision: %v" , err )
128+ }
120129
121130 if ! rev .DeletionTimestamp .IsZero () || rev .Spec .LifecycleState == ocv1 .ClusterExtensionRevisionLifecycleStateArchived {
122131 return c .teardown (ctx , rev , revision )
@@ -212,7 +221,11 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
212221
213222 //nolint:nestif
214223 if rres .IsComplete () {
215- // Archive other revisions.
224+ // Archive previous revisions
225+ previous , err := c .ListPreviousRevisions (ctx , rev )
226+ if err != nil {
227+ return ctrl.Result {}, fmt .Errorf ("listing previous revisions: %v" , err )
228+ }
216229 for _ , a := range previous {
217230 patch := []byte (`{"spec":{"lifecycleState":"Archived"}}` )
218231 if err := c .Client .Patch (ctx , a , client .RawPatch (types .MergePatchType , patch )); err != nil {
@@ -428,14 +441,41 @@ func (c *ClusterExtensionRevisionReconciler) removeFinalizer(ctx context.Context
428441 return nil
429442}
430443
431- func toBoxcutterRevision (rev * ocv1.ClusterExtensionRevision ) (* boxcutter.Revision , []boxcutter.RevisionReconcileOption , []client.Object ) {
432- previous := make ([]client.Object , 0 , len (rev .Spec .Previous ))
433- for _ , specPrevious := range rev .Spec .Previous {
434- prev := & unstructured.Unstructured {}
435- prev .SetName (specPrevious .Name )
436- prev .SetUID (specPrevious .UID )
437- prev .SetGroupVersionKind (ocv1 .GroupVersion .WithKind (ocv1 .ClusterExtensionRevisionKind ))
438- previous = append (previous , prev )
444+ // ListPreviousRevisions returns active revisions with the same owner, excluding self and archived.
445+ // Used by boxcutter for collision detection and to mark old revisions as archived on successful rollout.
446+ func (c * ClusterExtensionRevisionReconciler ) ListPreviousRevisions (ctx context.Context , rev * ocv1.ClusterExtensionRevision ) ([]client.Object , error ) {
447+ ownerLabel , ok := rev .Labels [ClusterExtensionRevisionOwnerLabel ]
448+ if ! ok {
449+ return nil , nil
450+ }
451+
452+ revList := & ocv1.ClusterExtensionRevisionList {}
453+ if err := c .Client .List (ctx , revList , client.MatchingLabels {
454+ ClusterExtensionRevisionOwnerLabel : ownerLabel ,
455+ }); err != nil {
456+ return nil , fmt .Errorf ("listing revisions: %w" , err )
457+ }
458+
459+ previous := make ([]client.Object , 0 , len (revList .Items ))
460+ for i := range revList .Items {
461+ r := & revList .Items [i ]
462+ if r .Name == rev .Name {
463+ continue
464+ }
465+ if r .Spec .LifecycleState == ocv1 .ClusterExtensionRevisionLifecycleStateArchived ||
466+ ! r .DeletionTimestamp .IsZero () {
467+ continue
468+ }
469+ previous = append (previous , r )
470+ }
471+
472+ return previous , nil
473+ }
474+
475+ func (c * ClusterExtensionRevisionReconciler ) toBoxcutterRevision (ctx context.Context , rev * ocv1.ClusterExtensionRevision ) (* boxcutter.Revision , []boxcutter.RevisionReconcileOption , error ) {
476+ previous , err := c .ListPreviousRevisions (ctx , rev )
477+ if err != nil {
478+ return nil , nil , fmt .Errorf ("listing previous revisions: %w" , err )
439479 }
440480
441481 opts := []boxcutter.RevisionReconcileOption {
@@ -476,7 +516,7 @@ func toBoxcutterRevision(rev *ocv1.ClusterExtensionRevision) (*boxcutter.Revisio
476516 if rev .Spec .LifecycleState == ocv1 .ClusterExtensionRevisionLifecycleStatePaused {
477517 opts = append (opts , boxcutter.WithPaused {})
478518 }
479- return r , opts , previous
519+ return r , opts , nil
480520}
481521
482522var (
0 commit comments