-
Notifications
You must be signed in to change notification settings - Fork 73
🐛 OCPBUGS-78455: fix(Boxcutter-Collision): Fix collision detection bypass after ClusterExtension upgrade #2578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -142,7 +142,7 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer | |
| return c.delete(ctx, cer) | ||
| } | ||
|
|
||
| phases, opts, err := c.buildBoxcutterPhases(ctx, cer) | ||
| phases, opts, siblingRevisionNames, err := c.buildBoxcutterPhases(ctx, cer) | ||
| if err != nil { | ||
| setRetryingConditions(cer, err.Error()) | ||
| return ctrl.Result{}, fmt.Errorf("converting to boxcutter revision: %v", err) | ||
|
|
@@ -210,6 +210,11 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, cer | |
| if ores.Action() == machinery.ActionCollision { | ||
| collidingObjs = append(collidingObjs, ores.String()) | ||
| } | ||
| if ores.Action() == machinery.ActionProgressed && siblingRevisionNames != nil { | ||
| if controllerRef := getForeignRevisionController(ores.Object(), siblingRevisionNames); controllerRef != nil { | ||
| collidingObjs = append(collidingObjs, ores.String()+fmt.Sprintf("\nConflicting Owner: %s", controllerRef.String())) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if len(collidingObjs) > 0 { | ||
|
|
@@ -460,21 +465,39 @@ 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) | ||
| if err != nil { | ||
| return nil, nil, fmt.Errorf("listing previous revisions: %w", err) | ||
| } | ||
| func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Context, cer *ocv1.ClusterExtensionRevision) ([]boxcutter.Phase, []boxcutter.RevisionReconcileOption, sets.Set[string], error) { | ||
| var siblingRevisionNames sets.Set[string] | ||
| previousObjs := make([]client.Object, 0) | ||
|
|
||
| if ownerLabel, ok := cer.Labels[labels.OwnerNameKey]; ok { | ||
| revList := &ocv1.ClusterExtensionRevisionList{} | ||
| if err := c.TrackingCache.List(ctx, revList, client.MatchingLabels{ | ||
| labels.OwnerNameKey: ownerLabel, | ||
| }); err != nil { | ||
| return nil, nil, nil, fmt.Errorf("listing revisions: %w", err) | ||
| } | ||
|
|
||
| // Convert to []client.Object for boxcutter | ||
| previousObjs := make([]client.Object, len(previous)) | ||
| for i, rev := range previous { | ||
| previousObjs[i] = rev | ||
| siblingRevisionNames = sets.New[string](cer.Name) | ||
| for i := range revList.Items { | ||
| r := &revList.Items[i] | ||
| siblingRevisionNames.Insert(r.Name) | ||
| if r.Name == cer.Name { | ||
| continue | ||
| } | ||
| if r.Spec.LifecycleState == ocv1.ClusterExtensionRevisionLifecycleStateArchived || | ||
| !r.DeletionTimestamp.IsZero() { | ||
| continue | ||
| } | ||
| if r.Spec.Revision >= cer.Spec.Revision { | ||
| continue | ||
| } | ||
| previousObjs = append(previousObjs, r) | ||
| } | ||
|
Comment on lines
+468
to
+495
|
||
| } | ||
|
|
||
| progressionProbes, err := buildProgressionProbes(cer.Spec.ProgressionProbes) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| return nil, nil, nil, err | ||
| } | ||
|
|
||
| opts := []boxcutter.RevisionReconcileOption{ | ||
|
|
@@ -507,7 +530,7 @@ func (c *ClusterExtensionRevisionReconciler) buildBoxcutterPhases(ctx context.Co | |
| } | ||
| phases = append(phases, boxcutter.NewPhase(specPhase.Name, objs)) | ||
| } | ||
| return phases, opts, nil | ||
| return phases, opts, siblingRevisionNames, nil | ||
| } | ||
|
|
||
| // EffectiveCollisionProtection resolves the collision protection value using | ||
|
|
@@ -522,6 +545,23 @@ func EffectiveCollisionProtection(cp ...ocv1.CollisionProtection) ocv1.Collision | |
| return ecp | ||
| } | ||
|
|
||
| // getForeignRevisionController returns the controller OwnerReference if the object is | ||
| // controlled by a ClusterExtensionRevision from a different ClusterExtension. It returns | ||
| // nil when the controller is a sibling revision (same ClusterExtension) or not a CER at all. | ||
| func getForeignRevisionController(obj metav1.Object, siblingRevisionNames 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].APIVersion == ocv1.GroupVersion.String() { | ||
| if !siblingRevisionNames.Has(refs[i].Name) { | ||
| return &refs[i] | ||
| } | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // buildProgressionProbes creates a set of boxcutter probes from the fields provided in the CER's spec.progressionProbes. | ||
| // Returns nil and an error if encountered while attempting to build the probes. | ||
| func buildProgressionProbes(progressionProbes []ocv1.ProgressionProbe) (probing.And, error) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
labels.OwnerNameKeyis missing on the CER, this function returnssiblingRevisionNamescontaining onlycer.Nameand an emptypreviousObjs. With the newActionProgressedforeign-controller detection, that incomplete sibling set can cause false-positive collisions (any CER controller ref other thancer.Namewill look “foreign”). Consider returning an explicithasOwnerLabelflag (or otherwise ensuring the sibling set is complete) so callers can safely decide whether to enforce the foreign-owner collision check.