Skip to content

Add support for Server-Side Apply finalizer management#3275

Merged
knative-prow[bot] merged 1 commit intoknative:mainfrom
enarha:support-ssa-finalizers
Nov 4, 2025
Merged

Add support for Server-Side Apply finalizer management#3275
knative-prow[bot] merged 1 commit intoknative:mainfrom
enarha:support-ssa-finalizers

Conversation

@enarha
Copy link
Copy Markdown
Contributor

@enarha enarha commented Oct 18, 2025

This improves performance and load on the k8s API by decreasing the number of re-tries, especially on objects managed by multiple controllers. The feature is opt-in and the current use of merge patch is still the default behavior.
We introduce three controller options:

  • UseServerSideApplyForFinalizers used to enable that functionality.
  • FinalizerFieldManager to specify finalizer field manager, required when SSA is enabled.
  • ForceApplyFinalizers to set the force option on the apply patch (default disabled).

Changes

  • 🎁 Add support for Server-Side Apply finalizer management
    This improves performance and load on the k8s API by decreasing the number of re-tries, especially on objects managed by multiple controllers. The feature is opt-in and the current use of merge patch is still the default behavior.
    We introduce three controller options:
  • UseServerSideApplyForFinalizers used to enable that functionality.
  • FinalizerFieldManager to specify finalizer field manager, required when SSA is enabled.
  • ForceApplyFinalizers to set the force option on the apply patch (default disabled).

/kind api-change

Fixes #2128

Release Note

Added support Server-Side Apply finalizer management. The feature is opt-in and the current use of merge patch is still the default behavior. Here is an example of controller options which enable and configure that functionality:

controller.Options{
    FinalizerName: "my-finalizer",
    UseServerSideApplyForFinalizers: true,
    FinalizerFieldManager: "my-finalizer-manager"
}

`UseServerSideApplyForFinalizers` enables the use Server-Side Apply for the finalizer management. If enabled, `FinalizerFieldManager` must also be provided. Use unique finalizer name and finalizer field manager to avoid conflicts. If you need to force the finalizer update, you can also add the `ForceApplyFinalizers` option and set it to `true`. That's usually not a good idea and different controllers should use different finalizer name and field manager.

IMPORTANT: 
If your controller is managing other fields using Server-Side Apply as part of its reconciliation loop (e.g. annotations), make sure you are using different filed manager for each (e.g. "my-controller/finalizers", "my-controller/annotations") to avoid accidental removal.
When switching from Merge Patch strategy to Server-Side Apply, objects caught in flight (existing on the cluster during transition) will need extra attention. Finalizer set using Merge Patch can't be removed using Server-Side Apply because filed manager (used with Server-Side Apply) does not own that field. In that case the finalizer must be removed manually.

Docs


@knative-prow knative-prow Bot added the kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API label Oct 18, 2025
@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented Oct 18, 2025

CLA Signed

The committers listed above are authorized under a signed CLA.

  • ✅ login: enarha / name: Emil Natan (0e36a17)

@knative-prow
Copy link
Copy Markdown

knative-prow Bot commented Oct 18, 2025

Welcome @enarha! It looks like this is your first PR to knative/pkg 🎉

@knative-prow
Copy link
Copy Markdown

knative-prow Bot commented Oct 18, 2025

Hi @enarha. Thanks for your PR.

I'm waiting for a github.com member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@knative-prow knative-prow Bot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Oct 18, 2025
@knative-prow knative-prow Bot requested review from Leo6Leo and skonto October 18, 2025 20:55
@cardil
Copy link
Copy Markdown
Contributor

cardil commented Oct 21, 2025

/assign @dprotaso

@dsimansk
Copy link
Copy Markdown
Contributor

/ok-to-test

@knative-prow knative-prow Bot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Oct 21, 2025
@codecov
Copy link
Copy Markdown

codecov Bot commented Oct 21, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.58%. Comparing base (aadc6f6) to head (0e36a17).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3275      +/-   ##
==========================================
- Coverage   74.93%   74.58%   -0.36%     
==========================================
  Files         188      188              
  Lines       10263     8184    -2079     
==========================================
- Hits         7691     6104    -1587     
+ Misses       2332     1840     -492     
  Partials      240      240              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@enarha enarha force-pushed the support-ssa-finalizers branch from 5dc1f6b to 363a01d Compare October 21, 2025 08:58
@dprotaso
Copy link
Copy Markdown
Member

/hold

pkg release-1.20 branch was cut today. Let's revisit this after the 1.20 release next week

@knative-prow knative-prow Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Oct 21, 2025
@enarha
Copy link
Copy Markdown
Contributor Author

enarha commented Oct 29, 2025

Can this PR get some attention please. I'll be happy to elaborate if you have any questions. Thank you.

Copy link
Copy Markdown
Member

@nader-ziada nader-ziada left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added some comments in the updateFinalizersFilteredServerSideApply func which is repeated in a bunch of places as far as I can see

updated, err := patcher.Patch(ctx, resource.Name, types.ApplyPatchType, patch, patchOpts)
if err != nil {
r.Recorder.Eventf(resource, corev1.EventTypeWarning, "FinalizerUpdateFailed",
"Failed to remove finalizer for %q via server-side apply: %v", resource.Name, err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use %w for error wrapping

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we do not return the error, just print it, so the simpler %v makes more sense and it is also consistent with the original code (merge patch). I updated the case where we return the error to use %w which allows unwrapping. If you still think I should update this one, I'll do.

// This effectively removes our finalizer while preserving others.
gvks, _, err := scheme.Scheme.ObjectKinds(resource)
if err != nil || len(gvks) == 0 {
return resource, fmt.Errorf("failed to determine GVK for resource: %v", err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use %w for error wrapping

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return resource, nil
}
// Apply configuration with only our finalizer to add it.
gvks, _, err := scheme.Scheme.ObjectKinds(resource)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This GVK lookup logic is duplicated below in the else condition

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it has. It could be called before the if clause, but then it should be called on each reconciliation cycle. Instead we call it just twice in the lifetime of the object, once to set it and once to remove it, while the reconciliation loop is called 10-20 or even more times, at least on the controllers I work on. That's why I think less calls is worth the duplication, but I'll be happy to change it you think otherwise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, I moved it after the else block.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

// Apply configuration with only our finalizer to add it.
gvks, _, err := scheme.Scheme.ObjectKinds(resource)
if err != nil || len(gvks) == 0 {
return resource, fmt.Errorf("failed to determine GVK for resource: %v", err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use %w for error wrapping

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, that makes sense as we return error.

},
}

patch, err := json.Marshal(applyConfig)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch operation logic is duplicated below as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. It could be moved after the else clause with the only penalty of having less accurate log message "failed to add/remove finalizer" and "Added/removed finalizer" compared to "failed to update finalizer" and "updated finalizer". Personally I prefer the former, but I'll update so it's also inline with the merge patch method.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Much cleaner now.


patchOpts := metav1.PatchOptions{
FieldManager: r.finalizerFieldManager,
Force: &r.forceApplyFinalizers,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is force necessary here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default it's not set as the value is set to false. We expose the ForceApplyFinalizers controller option though, so the controller implementation can set that to true. Force apply is often discussed in the context of server-side apply, so I decided to provide that flexibility to the controller implementation.

@dprotaso
Copy link
Copy Markdown
Member

dprotaso commented Nov 3, 2025

/hold cancel

@knative-prow knative-prow Bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Nov 3, 2025
@dprotaso
Copy link
Copy Markdown
Member

dprotaso commented Nov 3, 2025

Testing this downstream with - knative/serving#16217

@dprotaso
Copy link
Copy Markdown
Member

dprotaso commented Nov 3, 2025

Downstream test ran successfully. I'm good to merge once Nader's feedback is addressed.

This improves performance and load on the k8s API by decreasing the
number of re-tries, especially on objects managed by multiple
controllers. The feature is opt-in and the current use of merge patch is
still the default behavior.
We introduce three controller options:
- UseServerSideApplyForFinalizers used to enable that functionality.
- FinalizerFieldManager to specify finalizer field manager, required
  when SSA is enabled.
- ForceApplyFinalizers to set the force option on the apply patch
  (default disabled).
@enarha enarha force-pushed the support-ssa-finalizers branch from 363a01d to 0e36a17 Compare November 4, 2025 12:57
@nader-ziada
Copy link
Copy Markdown
Member

/lgtm

@knative-prow knative-prow Bot added the lgtm Indicates that a PR is ready to be merged. label Nov 4, 2025
@nader-ziada
Copy link
Copy Markdown
Member

thanks for the updates @enarha

@dprotaso good from my side, you will need to approve it if you are good

@dprotaso
Copy link
Copy Markdown
Member

dprotaso commented Nov 4, 2025

/approve

@knative-prow
Copy link
Copy Markdown

knative-prow Bot commented Nov 4, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dprotaso, enarha

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prow knative-prow Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Nov 4, 2025
@knative-prow knative-prow Bot merged commit 04fdd0b into knative:main Nov 4, 2025
36 checks passed
enarha added a commit to enarha/chains that referenced this pull request Apr 16, 2026
tekton-robot pushed a commit to tektoncd/chains that referenced this pull request Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API lgtm Indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate server-side apply to allow cooperating controllers

5 participants