Skip to content

controllers: rework status writes#4083

Merged
openshift-merge-bot[bot] merged 3 commits into
mainfrom
handle-status-update-with-patch
May 19, 2026
Merged

controllers: rework status writes#4083
openshift-merge-bot[bot] merged 3 commits into
mainfrom
handle-status-update-with-patch

Conversation

@ffromani

Copy link
Copy Markdown
Member

we bundle two changes about how controllers write back their status to the apiserver:

  • switch from Update() to Patch(): we can reuse more controller-runtime/client-go primitives and shed some equivalent but worse application code
  • make sure to not lose status updates: if we fail to update the status subresource, we need to requeue the operation. Supercedes controllers: requeue on status update failure #3992

@openshift-ci openshift-ci Bot requested review from shajmakh and swatisehgal May 19, 2026 08:55
@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 19, 2026
@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Rate limit exceeded

@ffromani has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 55 minutes and 31 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 125a889b-2fb8-47f1-9cd6-3ab2f2af9b2e

📥 Commits

Reviewing files that changed from the base of the PR and between ec43514 and e72e14a.

📒 Files selected for processing (2)
  • internal/controller/numaresourcesoperator_controller.go
  • internal/controller/numaresourcesscheduler_controller.go
📝 Walkthrough

Walkthrough

Both NUMAResourcesOperator and NUMAResourcesScheduler controller reconcilers are refactored to capture a deep copy of the CR at reconciliation start and use it as the merge base for all status patches, replacing a previous status-update helper and centralizing patch logic into degradeStatus.

Changes

Status Patching Strategy Refactor

Layer / File(s) Summary
NUMAResourcesOperator status patching and error handling
internal/controller/numaresourcesoperator_controller.go
Reconcile creates instanceBase := instance.DeepCopy() early, then routes incorrect resource name, validation, and tree-discovery failures through degradeStatus(ctx, instanceBase, ...) which patches status directly using client.MergeFrom(instanceBase). Normal-path status updates also use the same merge-based patch with instanceBase.
NUMAResourcesScheduler status patching and helper removal
internal/controller/numaresourcesscheduler_controller.go
Reconcile creates instanceBase := instance.DeepCopy() early and routes incorrect resource name checks directly to refactored degradeStatus, which now returns (ctrl.Result, error) and performs status patching itself. After reconcile work, status is patched using client.MergeFrom(instanceBase). The prior updateStatus helper method is removed.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'controllers: rework status writes' directly and clearly summarizes the main change across both modified controller files: refactoring how they write status to the API server.
Description check ✅ Passed The description explains the two key changes: switching from Update() to Patch() for status writes and ensuring status-write failures trigger requeuing, both of which are reflected in the file-level changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch handle-status-update-with-patch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

ffromani added 2 commits May 19, 2026 10:59
move from Update() to Patch() to set Status in the
CR owned by controllers. This move enables us to
remove the NeedsUpdate functions and use controller-runtime
or client-go primitives, which is simpler and more robust.

The controllers own the resources anyway, so we don't
expect conflicts, and the status writes are always
authoritative.

Signed-off-by: Francesco Romani <fromani@redhat.com>
Should a status write fail, just return error and let the
controller-runtime handle with exponential backoff.
This is safe and simple to do once we migrated to use Patch()
to send status writes, because in this flow now status write
failures are (ven) more likely to be transient errors,
so the default backoff curve offers a friendlier time
progression.

Signed-off-by: Francesco Romani <fromani@redhat.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/controller/numaresourcesoperator_controller.go`:
- Line 159: degradeStatus currently swallows the original reconcile error by
returning only the status-patch error; change its contract so it returns only
the error from the status patch (patchErr) and do not discard the original
reconcile error in callers. Update callers (e.g., the places that call
degradeStatus from getTreesByNodeGroup and the other listed call sites) to
capture patchErr := r.degradeStatus(ctx, instanceBase, instance,
status.ConditionTypeIncorrectNUMAResourcesOperatorResourceName, err) and then
return patchErr if patchErr != nil else return the original err so transient
reconcile failures still trigger retries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 2e769bad-c73f-4430-9933-98b2abf75ed9

📥 Commits

Reviewing files that changed from the base of the PR and between 2863214 and ec43514.

📒 Files selected for processing (2)
  • internal/controller/numaresourcesoperator_controller.go
  • internal/controller/numaresourcesscheduler_controller.go

if req.Name != objectnames.DefaultNUMAResourcesOperatorCrName {
err := fmt.Errorf("incorrect NUMAResourcesOperator resource name: %s", instance.Name)
return r.degradeStatus(ctx, initialStatus, instance, status.ConditionTypeIncorrectNUMAResourcesOperatorResourceName, err)
return r.degradeStatus(ctx, instanceBase, instance, status.ConditionTypeIncorrectNUMAResourcesOperatorResourceName, err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t let degradeStatus swallow transient reconcile failures.

This helper now returns only the status-patch error. That is fine for terminal validation/name problems, but it also turns transient degraded paths into nil returns once the status patch succeeds. In particular, the getTreesByNodeGroup() path can fail on the MCP List() call, and after Line 173 patches status the reconcile stops retrying until some unrelated event fires.

Consider making degradeStatus report only whether the status patch failed, and let each caller decide whether to return the original reconcile error.

Suggested shape
-func (r *NUMAResourcesOperatorReconciler) degradeStatus(ctx context.Context, instanceBase, instance *nropv1.NUMAResourcesOperator, reason string, stErr error) (ctrl.Result, error) {
+func (r *NUMAResourcesOperatorReconciler) degradeStatus(ctx context.Context, instanceBase, instance *nropv1.NUMAResourcesOperator, reason string, stErr error) error {
 	info := conditioninfo.DegradedFromError(stErr)
 	if reason != "" { // intentionally overwrite
 		info.Reason = reason
 	}

 	instance.Status.Conditions, _ = status.ComputeConditions(instance.Status.Conditions, info.ToMetav1Condition(instance.Generation), time.Now())

 	err := r.Client.Status().Patch(ctx, instance, client.MergeFrom(instanceBase))
 	if err != nil {
 		klog.InfoS("Failed to update numaresourcesoperator status", "error", err)
 	}

-	return ctrl.Result{}, err
+	return err
 }
 if err := validation.NodeGroups(instance.Spec.NodeGroups, r.Platform); err != nil {
-	return r.degradeStatus(ctx, instanceBase, instance, validation.NodeGroupsError, err)
+	if patchErr := r.degradeStatus(ctx, instanceBase, instance, validation.NodeGroupsError, err); patchErr != nil {
+		return ctrl.Result{}, patchErr
+	}
+	return ctrl.Result{}, nil
 }

 trees, err := getTreesByNodeGroup(ctx, r.Client, instance.Spec.NodeGroups, r.Platform)
 if err != nil {
-	return r.degradeStatus(ctx, instanceBase, instance, validation.NodeGroupsError, err)
+	if patchErr := r.degradeStatus(ctx, instanceBase, instance, validation.NodeGroupsError, err); patchErr != nil {
+		return ctrl.Result{}, patchErr
+	}
+	return ctrl.Result{}, err
 }

Also applies to: 168-168, 173-173, 178-178, 190-190, 201-214

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/numaresourcesoperator_controller.go` at line 159,
degradeStatus currently swallows the original reconcile error by returning only
the status-patch error; change its contract so it returns only the error from
the status patch (patchErr) and do not discard the original reconcile error in
callers. Update callers (e.g., the places that call degradeStatus from
getTreesByNodeGroup and the other listed call sites) to capture patchErr :=
r.degradeStatus(ctx, instanceBase, instance,
status.ConditionTypeIncorrectNUMAResourcesOperatorResourceName, err) and then
return patchErr if patchErr != nil else return the original err so transient
reconcile failures still trigger retries.

make the scheduler controller code shape more like
the operator/rte controller for better code clarity.

Includes the status write requeue fix using the
builtin exponential backoff.
Other than this, no intended changes in behavior.

Signed-off-by: Francesco Romani <fromani@redhat.com>
@ffromani ffromani force-pushed the handle-status-update-with-patch branch from ec43514 to e72e14a Compare May 19, 2026 09:00

@Tal-or Tal-or left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

/approve
/lgtm

Looks really nice. we may consider (in future PR ofc) to switch to Patch() also for the resources themselves and not only the status.

Following up #1502 which intended to minimize API server load and CPU churn

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label May 19, 2026
@openshift-ci

openshift-ci Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ffromani, Tal-or

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

@openshift-merge-bot openshift-merge-bot Bot merged commit 1bf3817 into main May 19, 2026
20 of 21 checks passed
@openshift-merge-bot openshift-merge-bot Bot deleted the handle-status-update-with-patch branch May 19, 2026 11:24
@openshift-ci

openshift-ci Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@ffromani: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/security e72e14a link false /test security
ci/prow/ci-index-tested-numaresources-operator-bundle e72e14a link unknown /test ci-index-tested-numaresources-operator-bundle
ci/prow/ci-must-gather-e2e e72e14a link unknown /test ci-must-gather-e2e
ci/prow/ci-install-e2e e72e14a link unknown /test ci-install-e2e

Full PR test history. Your PR dashboard.

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. I understand the commands that are listed here.

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. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants