Skip to content

Commit c880295

Browse files
authored
never delete dependents that is not owned by current component (#219)
1 parent 1ab02f0 commit c880295

File tree

3 files changed

+17
-8
lines changed

3 files changed

+17
-8
lines changed

clm/cmd/delete.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ func newDeleteCmd() *cobra.Command {
5454

5555
releaseClient := release.NewClient(fullName, clnt)
5656

57+
ownerId := fullName + "/" + namespace + "/" + name
58+
5759
release, err := releaseClient.Get(context.TODO(), namespace, name)
5860
if err != nil {
5961
return err
@@ -94,7 +96,7 @@ func newDeleteCmd() *cobra.Command {
9496

9597
for {
9698
release.State = component.StateDeleting
97-
ok, err := reconciler.Delete(context.TODO(), &release.Inventory)
99+
ok, err := reconciler.Delete(context.TODO(), &release.Inventory, ownerId)
98100
if err != nil {
99101
if !isEphmeralError(err) || errCount >= maxErrCount {
100102
return err

pkg/component/target.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,10 @@ func (t *reconcileTarget[T]) Apply(ctx context.Context, component T) (bool, stri
7474

7575
func (t *reconcileTarget[T]) Delete(ctx context.Context, component T) (bool, error) {
7676
// log := log.FromContext(ctx)
77+
ownerId := t.reconcilerId + "/" + component.GetNamespace() + "/" + component.GetName()
7778
status := component.GetStatus()
7879

79-
return t.reconciler.Delete(ctx, &status.Inventory)
80+
return t.reconciler.Delete(ctx, &status.Inventory, ownerId)
8081
}
8182

8283
func (t *reconcileTarget[T]) IsDeletionAllowed(ctx context.Context, component T) (bool, string, error) {

pkg/reconciler/reconciler.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ func (r *Reconciler) Apply(ctx context.Context, inventory *[]*InventoryItem, obj
577577

578578
switch item.Phase {
579579
case PhaseScheduledForCompletion:
580-
if err := r.deleteObject(ctx, item, existingObject); err != nil {
580+
if err := r.deleteObject(ctx, item, existingObject, hashedOwnerId); err != nil {
581581
return false, errors.Wrapf(err, "error deleting object %s", item)
582582
}
583583
item.Phase = PhaseCompleting
@@ -662,7 +662,7 @@ func (r *Reconciler) Apply(ctx context.Context, inventory *[]*InventoryItem, obj
662662
switch updatePolicy {
663663
case UpdatePolicyRecreate:
664664
// TODO: perform an additional owner id check
665-
if err := r.deleteObject(ctx, object, existingObject); err != nil {
665+
if err := r.deleteObject(ctx, object, existingObject, hashedOwnerId); err != nil {
666666
return false, errors.Wrapf(err, "error deleting (while recreating) object %s", item)
667667
}
668668
default:
@@ -772,7 +772,7 @@ func (r *Reconciler) Apply(ctx context.Context, inventory *[]*InventoryItem, obj
772772
// note: here is a theoretical risk that we delete an existing foreign object, because informers are not yet synced
773773
// however not sending the delete request is also not an option, because this might lead to orphaned own dependents
774774
// TODO: perform an additional owner id check
775-
if err := r.deleteObject(ctx, item, existingObject); err != nil {
775+
if err := r.deleteObject(ctx, item, existingObject, hashedOwnerId); err != nil {
776776
return false, errors.Wrapf(err, "error deleting object %s", item)
777777
}
778778
item.Phase = PhaseDeleting
@@ -824,9 +824,11 @@ func (r *Reconciler) Apply(ctx context.Context, inventory *[]*InventoryItem, obj
824824
// This method will change the passed inventory (remove elements, change elements). If Delete() returns true, then all objects are gone; otherwise,
825825
// if it returns false, the caller should recall it timely, until it returns true. In any case, the passed inventory should match the state of the
826826
// inventory after the previous invocation of Delete(); usually, the caller saves the inventory after calling Delete(), and loads it before calling Delete().
827-
func (r *Reconciler) Delete(ctx context.Context, inventory *[]*InventoryItem) (bool, error) {
827+
func (r *Reconciler) Delete(ctx context.Context, inventory *[]*InventoryItem, ownerId string) (bool, error) {
828828
log := log.FromContext(ctx)
829829

830+
hashedOwnerId := sha256base32([]byte(ownerId))
831+
830832
// delete objects and maintain inventory;
831833
// objects are deleted in waves according to their delete order;
832834
// that means, only if all objects of a wave are gone, the next wave will be processed;
@@ -877,7 +879,7 @@ func (r *Reconciler) Delete(ctx context.Context, inventory *[]*InventoryItem) (b
877879
// note: here is a theoretical risk that we delete an existing (foreign) object, because informers are not yet synced
878880
// however not sending the delete request is also not an option, because this might lead to orphaned own dependents
879881
// TODO: perform an additional owner id check
880-
if err := r.deleteObject(ctx, item, existingObject); err != nil {
882+
if err := r.deleteObject(ctx, item, existingObject, hashedOwnerId); err != nil {
881883
return false, errors.Wrapf(err, "error deleting object %s", item)
882884
}
883885
item.Phase = PhaseDeleting
@@ -1111,7 +1113,7 @@ func (r *Reconciler) updateObject(ctx context.Context, object client.Object, exi
11111113
// then an error will be returned after issuing the delete call; otherwise, if the crd or api service is not in use, then our
11121114
// finalizer (i.e. the finalizer equal to the reconciler name) will be cleared, such that the object can be physically
11131115
// removed (unless other finalizers prevent this)
1114-
func (r *Reconciler) deleteObject(ctx context.Context, key types.ObjectKey, existingObject *unstructured.Unstructured) (err error) {
1116+
func (r *Reconciler) deleteObject(ctx context.Context, key types.ObjectKey, existingObject *unstructured.Unstructured, ownerId string) (err error) {
11151117
if counter := r.metrics.DeleteCounter; counter != nil {
11161118
counter.Inc()
11171119
}
@@ -1131,6 +1133,10 @@ func (r *Reconciler) deleteObject(ctx context.Context, key types.ObjectKey, exis
11311133

11321134
// TODO: validate (by panic) that existingObject (if present) fits to key
11331135

1136+
if existingObject != nil && existingObject.GetLabels()[r.labelKeyOwnerId] != ownerId {
1137+
return fmt.Errorf("owner conflict; object %s has no or different owner", types.ObjectKeyToString(key))
1138+
}
1139+
11341140
object := &unstructured.Unstructured{}
11351141
object.SetGroupVersionKind(key.GetObjectKind().GroupVersionKind())
11361142
object.SetNamespace(key.GetNamespace())

0 commit comments

Comments
 (0)