-
Notifications
You must be signed in to change notification settings - Fork 159
Protect ProviderConfigs while managed resources terminate #1113
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 1 commit
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 |
|---|---|---|
|
|
@@ -67,6 +67,7 @@ const ( | |
| errReconcileUpdate = "update failed" | ||
| errReconcileDelete = "delete failed" | ||
| errRecordChangeLog = "cannot record change log entry" | ||
| errUntrackProviderConfig = "cannot release ProviderConfigUsage" | ||
|
|
||
| errExternalResourceNotExist = "external resource does not exist" | ||
|
|
||
|
|
@@ -615,6 +616,7 @@ type mrManaged struct { | |
| Initializer | ||
| ReferenceResolver | ||
| LocalConnectionPublisher | ||
| resource.ProviderConfigUsageCleaner | ||
| } | ||
|
|
||
| func defaultMRManaged(m manager.Manager) mrManaged { | ||
|
|
@@ -876,12 +878,18 @@ func (r *Reconciler) effectivePollInterval(o metav1.Object) time.Duration { | |
|
|
||
| // NewReconciler returns a Reconciler that reconciles managed resources of the | ||
| // supplied ManagedKind with resources in an external system such as a cloud | ||
| // provider API. It panics if asked to reconcile a managed resource kind that is | ||
| // not registered with the supplied manager's runtime.Scheme. The returned | ||
| // Reconciler reconciles with a dummy, no-op 'external system' by default; | ||
| // callers should supply an ExternalConnector that returns an ExternalClient | ||
| // capable of managing resources in a real system. | ||
| func NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOption) *Reconciler { | ||
| // provider API. The usage cleaner should be the tracker used to track the | ||
| // managed resource's ProviderConfigUsage. Managed resources that do not use a | ||
| // ProviderConfigUsage should pass resource.NewNopProviderConfigUsageCleaner(). | ||
| // NewReconciler panics if the usage cleaner is nil or the managed resource kind | ||
| // is not registered with the supplied manager's runtime.Scheme. The returned | ||
| // Reconciler uses a no-op external system by default; callers should supply an | ||
| // ExternalConnector that can manage resources in a real system. | ||
| func NewReconciler(m manager.Manager, of resource.ManagedKind, usage resource.ProviderConfigUsageCleaner, o ...ReconcilerOption) *Reconciler { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about making We would then remove the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| if usage == nil { | ||
| panic("managed reconciler requires a ProviderConfigUsageCleaner") | ||
| } | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| nm := func() resource.Managed { | ||
| //nolint:forcetypeassert // If this isn't an MR it's a programming error and we want to panic. | ||
| return resource.MustCreateObject(schema.GroupVersionKind(of), m.GetScheme()).(resource.Managed) | ||
|
|
@@ -907,6 +915,7 @@ func NewReconciler(m manager.Manager, of resource.ManagedKind, o ...ReconcilerOp | |
| change: newNopChangeLogger(), | ||
| conditions: new(conditions.ObservedGenerationPropagationManager), | ||
| } | ||
| r.managed.ProviderConfigUsageCleaner = usage | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We may consider moving this to a
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, |
||
|
|
||
| for _, ro := range o { | ||
| ro(r) | ||
|
|
@@ -1070,6 +1079,18 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu | |
| return reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus) | ||
| } | ||
|
|
||
| // Release the usage after removing the managed resource finalizer so the | ||
| // ProviderConfig remains available throughout deletion. | ||
| if err := r.managed.Untrack(ctx, managed); err != nil { | ||
| log.Debug("Cannot release ProviderConfigUsage", "error", err) | ||
|
ezgidemirel marked this conversation as resolved.
|
||
| uerr := errors.Wrap(err, errUntrackProviderConfig) | ||
| status.MarkConditions(xpv2.Deleting(), xpv2.ReconcileError(uerr)) | ||
| if err := updateStatus(); err != nil { | ||
| return reconcile.Result{Requeue: true}, errors.Wrap(err, errUpdateManagedStatus) | ||
| } | ||
| return reconcile.Result{Requeue: true}, uerr | ||
| } | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| // We've successfully unpublished our managed resource's connection | ||
| // details and removed our finalizer. If we assume we were the only | ||
| // controller that added a finalizer to this resource then it should no | ||
|
|
@@ -1231,7 +1252,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu | |
| if meta.WasDeleted(managed) { | ||
| log = log.WithValues("deletion-timestamp", managed.GetDeletionTimestamp()) | ||
|
|
||
| if len(managed.GetFinalizers()) > 1 { | ||
| if numControllerFinalizers(managed) > 1 { | ||
| // There are other controllers monitoring this resource so preserve the external instance | ||
| // until all other finalizers have been removed | ||
| log.Debug("Delay external deletion until all finalizers have been removed") | ||
|
|
@@ -1311,6 +1332,18 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu | |
| return reconcile.Result{Requeue: true}, errors.Wrap(updateStatus(), errUpdateManagedStatus) | ||
| } | ||
|
|
||
| // Release the usage after removing the managed resource finalizer so the | ||
| // ProviderConfig remains available throughout deletion. | ||
| if err := r.managed.Untrack(ctx, managed); err != nil { | ||
| log.Debug("Cannot release ProviderConfigUsage", "error", err) | ||
| uerr := errors.Wrap(err, errUntrackProviderConfig) | ||
| status.MarkConditions(xpv2.Deleting(), xpv2.ReconcileError(uerr)) | ||
| if err := updateStatus(); err != nil { | ||
| return reconcile.Result{Requeue: true}, errors.Wrap(err, errUpdateManagedStatus) | ||
| } | ||
| return reconcile.Result{Requeue: true}, uerr | ||
| } | ||
|
|
||
| // We've successfully deleted our external resource (if necessary) and | ||
| // removed our finalizer. If we assume we were the only controller that | ||
| // added a finalizer to this resource then it should no longer exist and | ||
|
|
@@ -1576,3 +1609,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (resu | |
|
|
||
| return reconcile.Result{RequeueAfter: reconcileAfter}, errors.Wrap(updateStatus(), errUpdateManagedStatus) | ||
| } | ||
|
|
||
| // numControllerFinalizers returns the number of finalizers not managed by the | ||
| // Kubernetes garbage collector. | ||
| func numControllerFinalizers(mg resource.Managed) int { | ||
|
ezgidemirel marked this conversation as resolved.
Outdated
|
||
| return len(meta.FinalizersExcludingPropagation(mg)) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.