Patch the Task finalizer instead of updating the whole Task
What type of PR is this?
/kind bug
What this PR does / why we need it:
The Task controller adds and removes kelos.dev/finalizer with Update, which sends the whole Task back to the API server. TaskSpec is immutable (+kubebuilder:validation:XValidation:rule="self == oldSelf"). So when a stored spec no longer round-trips byte for byte through the current Go types, the API server rejects the Update:
Task.kelos.dev "<name>" is invalid: spec: Invalid value: Task spec is immutable after creation
This happens, for example, to Tasks created before a schema change. The deletion path then returns that error on every reconcile ("unable to remove finalizer"), and the Task never finishes deleting. On one cluster this left 144 deleted Tasks stuck for up to four weeks. Each had already had its Job removed, but kept its finalizer.
This PR switches both the add and the remove to a metadata-only merge patch (client.MergeFrom(original)), so the spec is never written back.
Tests: TestReconcile_DeletionRemovesFinalizerWithoutUpdatingSpec and TestReconcile_AddsFinalizerWithoutUpdatingSpec use a fake client whose interceptor refuses every Task Update with the same Invalid error the API server returns. Without the change, both fail with that error. With it, the deleted Task is removed and the new Task gets its finalizer. go test ./internal/controller/ and go vet pass.
Which issue(s) this PR is related to:
N/A
Special notes for your reviewer:
Only the Task controller is changed. Other controllers that Update objects with an immutable spec could be affected in the same way. I kept this PR focused, but can follow up.
Does this PR introduce a user-facing change?
Fix deleted Tasks that could never finish deleting because removing the finalizer was rejected with "Task spec is immutable after creation".