Open freegroup opened 3 years ago
it seems that the @kopf.timer
is the problem cause. Adding this annotation adds the finalizer for some reason.
Not documented and not required, or?
Both timers and daemons add finalizers to all served (i.e. matching) resources. This is needed to ensure proper termination of those asyncio tasks running the timers/daemons (timer is essentially a daemon with regular asyncio.sleeps).
To be more specific, from the framework's point of view, timer/daemon cancellation can take a significant time, i.e. it is not guaranteed to be instant because of the timer's/daemon's code (which is outside of Kopf's control). There can be several update cycles before the tasks are finally stopped. As such, the resource must exist to contain the state of this termination. The framework releases the resources only after the tasks are confirmed to be stopped.
And so, finalizers are added when timers/daemons exist — to prevent the sudden disappearance of the resources which would leave orphan (non-stopped) tasks.
It is documented here:
it is not possible to delete the watched resources without removing the finalizer before.
It is unclear why this conclusion is made. Deleting the resource (via CLI or API) only marks it for deletion by setting metadata.deletionTimestamp
. Since this moment, Kopf initiates the deletion handlers (if any) and stops/cancels the daemons and timers (specifically, their asyncio tasks). Once the tasks are stopped, it removes the finalizer and lets the resource actually go (unless there are other finalizers of other operators).
If the operator is not running at the moment, then the resources are indeed "blocked". Force-deletion can help here.
There are cases when the finalizers are removed on deletion without the task being fully stopped: e.g. when cancellation_timeout=0
or any low value (but not None
!) for daemons:
But the finalizers are added anyway. This "instant release" of the resource happens only if the operator is running at the time of deletion.
If this is not the desired behaviour, you can easily simulate timers & daemons with your own threads or tasks: start them in create/resume handlers, store to memo
, stop/cancel in the optional deletion handlers or from the inside — with some risk of getting orphan tasks for deleted resources.
This is essentially what Kopf does, but with more logging and safety measures.
@nolar Is it possible to prevent these finalizers from being set (ie. by setting `settings.persistence.finalizer = None)? I am aware that it's an "at your own risk"-change.
Hey! I stumbled upon this issue while debugging something slightly different. Simplified, we have a Helm chart that has a CRD, one CRD resource and a Kopf operator. There is a timer for the CRD and hence, a finalizer attached to it.
The problem is when helm uninstall
is run and the operator's deployment gets deleted before the CRD resource. The operator shuts down successfully but the finalizer is never removed and the CRD resource with all of its children (that the operator created) are stuck.
Note that this is not necessarily Helm specific, one could observe similar results with a regular yaml template & kubectl apply
and subsequent delete
.
I am not yet sure what the best approach is, but wanted to add a different point of view to this thread. Maybe we could remove the finalizers on operator shutdown?
Long story short
We have some handler for our resources for update/create/timer/startup (without delete). Unfortunately kopf adds a finalizer to the resource. As mention in the documentation (https://kopf.readthedocs.io/en/stable/handlers/#state-changing-handlers) there shouldn't be a finalizer added to the resource.
Kopf version
1.33.0
Kubernetes version
1.20.2
Python version
3.7.4
Code
Logs
No response
Additional information
it is not possible to delete the watched resources without removing the finalizer before. It seems that our code do not provide the required delete endpoint for the finalizer call.