-
Notifications
You must be signed in to change notification settings - Fork 98
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
Managed resource reconciler refactoring #65
Conversation
I think I need some strong reasons to introduce Something like this;
|
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.
Thanks for digging into these issues right away! I know they look different but also underlying issues are pretty similar, but I think we can still separate this PRs into two or three different ones. Maybe one for references+comments and one for initializer thing?
@@ -138,6 +138,20 @@ type ManagedFinalizer interface { | |||
Finalize(ctx context.Context, cm Managed) error | |||
} | |||
|
|||
// A FinalizerChain chains multiple managed finalizers. | |||
type FinalizerChain []ManagedFinalizer |
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.
Noticed it the first time in this PR but why do we call r.managed.Finalize
in claim reconciler? Shouldn't claim reconciler just send the deletion signal and checks whether object is deleted, i.e (NotFound)
and then calls its claim's finalizer? Pretty much like how managed reconciler treats its external resource.
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.
In both of these reconcilers the "Finalize" interface is not intended explicitly to mean "remove a finalizer" in the metadata sense, but rather "finalize the lifecycle of this object". I can see how this is confusing, but struggled to find a better name for the interface.
The managed resource reconciler finalizes its work on a managed resource by removing its finalizer. The claim binding reconclise finalizes its work on a managed resource by removing its claim ref and setting it to unbound.
pkg/resource/managed_reconciler.go
Outdated
@@ -339,7 +405,7 @@ func NewManagedReconciler(m manager.Manager, of ManagedKind, o ...ManagedReconci | |||
|
|||
// Reconcile a managed resource with an external resource. | |||
func (r *ManagedReconciler) Reconcile(req reconcile.Request) (reconcile.Result, error) { // nolint:gocyclo | |||
// NOTE(negz): This method is a little over our cyclomatic complexity goal. | |||
// NOTE(negz): This method is a well over our cyclomatic complexity goal. |
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.
Sad but true.
I disagree with this. You're right that in practice pretty much no-one is going to disable adding and removing finalizers, but I think we should keep them abstracted in something like an
I'd like to keep the responsibility of updating finalizer metadata contained within a separate type that we can stub out. However per our discussion this morning I think we could probably make this less obtuse by:
|
I've broken out two PRs from this one: I'll break some more out once the above are merged, since much of my updates assume the new testing style. |
This seems like a good path forward to me as discussed. Having the About the naming of the |
This commit refactors ResolveReferencers to allow the code that finds types within a struct that satisfy AttributeReferencer to be swapped out. It also updates the default AttributeReferencerFinder to avoid checking struct tags. Previously errors were returned when: 1. A struct field tagged as a referencer did not satisfy AttributeReferencer 2. A struct field not tagged as a referencer satisfied AttributeReferencer If either of these scenarios occurred, ResolveReferences would panic with the returned error the first time it encountered an incorrectly written API type. My feeling is that both of these conditions are testing for programmer errors that would be better caught at build time than at runtime. Signed-off-by: Nic Cope <negz@rk0n.org>
Signed-off-by: Nic Cope <negz@rk0n.org>
Signed-off-by: Nic Cope <negz@rk0n.org>
the established convention is for the managed resource reconciler to requeue after a short wait (typically 30 seconds) when it knows it is waiting for an operation. Signed-off-by: Nic Cope <negz@rk0n.org>
Signed-off-by: Nic Cope <negz@rk0n.org>
Reference resolution is now a no-op if nothing changes, so we run it on every reconcile. We also run it after delete has been handled, so unresolved references will only block creates and updates. This commit means we'll make more get calls to the cache (or API) in order to resolve our references each reconcile, and also risk potentially changing the values of 'immutable' fields automatically if and when our references resources change. I believe we should address this by having referencers be no-ops when the field value they would set is already set. Signed-off-by: Nic Cope <negz@rk0n.org>
The mock binding status was identical to the real one, while the mock conditioned status set only the most recent condition, leading to a few slightly broken managed resource reconciler tests. Signed-off-by: Nic Cope <negz@rk0n.org>
* By the time we get here we know our Observe call worked. If (for example) our cloud provider credentials were completely wrong, we'd never proceed far enough to add the finalizer. * If Observe works but Create fails (for example because we had RO cloud provider credentials) we would already have added the finalizer, but... * When the managed resource was deleted we'd be able to Observe that the external resource does not exist (because we were never able to Create it) and thus would not call Delete on the external resource and go straight to unpublishing credentials and removing the finalizer. Signed-off-by: Nic Cope <negz@rk0n.org>
The initialization process sets the finalizer, while the annotation process sets the managed resource's external name. We now set the finalizer later in the reconciliation process, but still want to set the annotations early in the reconcile process. Signed-off-by: Nic Cope <negz@rk0n.org>
It turns out certain managed resources use part of their spec to observe their external resource, and that part of the spec can depend on a reference. GCP connections are an example of this; there's no "get connections" call so instead we list all connections within a network, which is a spec field that could be set by a reference. Signed-off-by: Nic Cope <negz@rk0n.org>
We no longer need CanReference types to satisfy the metav1.Object interface. It was used only to determine the namespace of the referencing object before all such objects became cluster scoped. Signed-off-by: Nic Cope <negz@rk0n.org>
Description of your changes
Opening this as a draft for now, because it's fairly wide ranging and should probably be more than one PR. In the meantime everything is broken down by commit fairly cleanly.
The PR:
attributereferencer
struct tag - see rationale in that commit message.Fixes #63
Fixes #62
Fixes #53
Fixes #51
I've tested this all out by creating a GKE cluster and CloudSQL database using the
examples/workload
manifests.Checklist
I have:
make reviewable
to ensure this PR is ready for review.clusterrole.yaml
to include any new types.