Skip to content

OCPBUGS-65687: fix(controlplane-component): prevent informer creation for unused platform resources - #7417

Closed
muraee wants to merge 1 commit into
openshift:mainfrom
muraee:fix-OCPBUGS-65687
Closed

OCPBUGS-65687: fix(controlplane-component): prevent informer creation for unused platform resources#7417
muraee wants to merge 1 commit into
openshift:mainfrom
muraee:fix-OCPBUGS-65687

Conversation

@muraee

@muraee muraee commented Dec 22, 2025

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

When components use .WithPredicate() (e.g., azure-cloud-controller-manager), non-matching platforms trigger cleanup mode which calls Client.Get() to check for existing resources before deletion. For platform-specific resource types like SecretProviderClass, this causes controller-runtime to create informers that continuously LIST/WATCH resources that will never exist on non-Azure platforms.

Track whether resources have been successfully applied and skip delete operations entirely for resources that were never created, avoiding the Client.Get() call that triggers unnecessary informer creation.

Which issue(s) this PR fixes:

Fixes

Special notes for your reviewer:

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Summary by CodeRabbit

  • Bug Fixes
    • Improved component state tracking to prevent deletion attempts on resources that haven't been successfully applied.
    • Enhanced deletion logic to safely handle unapplied resources and avoid deletion errors.

…tform resources

When components use .WithPredicate() (e.g., azure-cloud-controller-manager),
non-matching platforms trigger cleanup mode which calls Client.Get() to check
for existing resources before deletion. For platform-specific resource types
like SecretProviderClass, this causes controller-runtime to create informers
that continuously LIST/WATCH resources that will never exist on non-Azure
platforms.

Track whether resources have been successfully applied and skip delete
operations entirely for resources that were never created, avoiding the
Client.Get() call that triggers unnecessary informer creation.
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Dec 22, 2025
@openshift-ci-robot openshift-ci-robot added the jira/severity-critical Referenced Jira bug's severity is critical for the branch this PR is targeting. label Dec 22, 2025
@openshift-ci

openshift-ci Bot commented Dec 22, 2025

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci-robot openshift-ci-robot added jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Dec 22, 2025
@openshift-ci-robot

Copy link
Copy Markdown

@muraee: This pull request references Jira Issue OCPBUGS-65687, which is invalid:

  • expected the bug to target either version "4.22." or "openshift-4.22.", but it targets "4.21.0" instead

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

What this PR does / why we need it:

When components use .WithPredicate() (e.g., azure-cloud-controller-manager), non-matching platforms trigger cleanup mode which calls Client.Get() to check for existing resources before deletion. For platform-specific resource types like SecretProviderClass, this causes controller-runtime to create informers that continuously LIST/WATCH resources that will never exist on non-Azure platforms.

Track whether resources have been successfully applied and skip delete operations entirely for resources that were never created, avoiding the Client.Get() call that triggers unnecessary informer creation.

Which issue(s) this PR fixes:

Fixes

Special notes for your reviewer:

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

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 openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai

coderabbitai Bot commented Dec 22, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Both control plane components now track an internal hasBeenApplied state flag to guard deletion operations. The flag is set to true after successful reconciliation, preventing delete operations on resources that haven't been created yet. The flag is reset to false when reconciliation fails or resources are not observed.

Changes

Cohort / File(s) Summary
State Tracking for Deletion Guards
support/controlplane-component/controlplane-component.go, support/controlplane-component/generic-adapter.go
Added hasBeenApplied field to control deletion logic. Flag is set to true after successful resource application/reconciliation, and checked before deletion to prevent operations on non-existent resources. Flag is reset to false on reconciliation failure or when resources are not observed.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Test Structure And Quality ⚠️ Warning The test file lacks comprehensive coverage for new hasBeenApplied logic and violates single responsibility principle with a monolithic TestReconcile function testing 10+ unrelated behaviors. Break down TestReconcile into focused tests (TestReconcileDeploymentLabels, TestReconcileAffinity, etc.) and add dedicated tests for hasBeenApplied state management logic.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: preventing informer creation for unused platform resources by tracking application state, which aligns with the core functionality added across both files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Stable And Deterministic Test Names ✅ Passed All test names in controlplane-component test files are stable and deterministic, using descriptive static strings with no dynamic information.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

@openshift-ci

openshift-ci Bot commented Dec 22, 2025

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: muraee

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-ci openshift-ci Bot added approved Indicates a PR has been approved by an approver from all required OWNERS files. area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release and removed do-not-merge/needs-area labels Dec 22, 2025
@muraee

muraee commented Dec 22, 2025

Copy link
Copy Markdown
Contributor Author

/jira refresh

@openshift-ci-robot openshift-ci-robot added jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. and removed jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Dec 22, 2025
@openshift-ci-robot

Copy link
Copy Markdown

@muraee: This pull request references Jira Issue OCPBUGS-65687, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (4.22.0) matches configured target version for branch (4.22.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @xiuwang

Details

In response to this:

/jira refresh

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 openshift-eng/jira-lifecycle-plugin repository.

@muraee

muraee commented Dec 22, 2025

Copy link
Copy Markdown
Contributor Author

/test unit
/test verify

@openshift-ci
openshift-ci Bot requested a review from xiuwang December 22, 2025 15:58
@openshift-ci

openshift-ci Bot commented Jan 12, 2026

Copy link
Copy Markdown
Contributor

@muraee: you cannot LGTM your own PR.

Details

In response to this:

/lgtm
/verified by @zhouying7780

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.

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Jan 12, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@muraee: This PR has been marked as verified by @zhouying7780.

Details

In response to this:

/lgtm
/verified by @zhouying7780

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 openshift-eng/jira-lifecycle-plugin repository.

@jhjaggars
jhjaggars marked this pull request as ready for review February 23, 2026 21:33
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 23, 2026
@openshift-ci
openshift-ci Bot requested review from csrwng and devguyio February 23, 2026 21:33
@openshift-ci-robot

Copy link
Copy Markdown

@muraee: This pull request references Jira Issue OCPBUGS-65687, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (4.22.0) matches configured target version for branch (4.22.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @xiuwang

Details

In response to this:

What this PR does / why we need it:

When components use .WithPredicate() (e.g., azure-cloud-controller-manager), non-matching platforms trigger cleanup mode which calls Client.Get() to check for existing resources before deletion. For platform-specific resource types like SecretProviderClass, this causes controller-runtime to create informers that continuously LIST/WATCH resources that will never exist on non-Azure platforms.

Track whether resources have been successfully applied and skip delete operations entirely for resources that were never created, avoiding the Client.Get() call that triggers unnecessary informer creation.

Which issue(s) this PR fixes:

Fixes

Special notes for your reviewer:

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Summary by CodeRabbit

  • Bug Fixes
  • Improved component state tracking to prevent deletion attempts on resources that haven't been successfully applied.
  • Enhanced deletion logic to safely handle unapplied resources and avoid deletion errors.

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 openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
support/controlplane-component/generic-adapter.go (1)

16-23: ⚠️ Potential issue | 🔴 Critical

Fix lost state mutation: store genericAdapter pointers or write back to map after reconcile().

genericAdapter is stored by value in manifestsAdapters map. When adapter.reconcile() is called (line 269 of controlplane-component.go), the pointer receiver implicitly takes the address of the temporary copy retrieved from the map. Mutations like ga.hasBeenApplied = true (line 96 of generic-adapter.go) are written to that temporary and discarded; the map entry is never updated. On the next reconcile, hasBeenApplied remains false, causing the cleanup guard at line 54 to never trigger correctly.

Recommended fix: either store *genericAdapter in the map, or reassign the adapter back to the map after reconcile completes.

Example fix (write-back approach)
adapter, exist := c.manifestsAdapters[manifestName]
if exist {
-    return adapter.reconcile(cpContext, obj)
+    if err := adapter.reconcile(cpContext, obj); err != nil {
+        return err
+    }
+    c.manifestsAdapters[manifestName] = adapter
+    return nil
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@support/controlplane-component/generic-adapter.go` around lines 16 - 23, The
map manifestsAdapters stores genericAdapter values so pointer-receiver mutations
inside genericAdapter.reconcile() (which sets genericAdapter.hasBeenApplied) are
lost; fix by either changing the map to store *genericAdapter pointers or by
writing the mutated adapter back into manifestsAdapters after calling
reconcile(). Locate the manifestsAdapters map usage (where reconcile() is
invoked) and implement one of: 1) change its value type to *genericAdapter and
update creation sites to take addresses, or 2) after calling
adapter.reconcile(...), assign the possibly-modified adapter back into
manifestsAdapters (e.g., manifestsAdapters[key] = adapter) so hasBeenApplied
mutations persist.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@support/controlplane-component/controlplane-component.go`:
- Around line 197-201: The delete method on controlPlaneWorkload[T] currently
early-returns when the in-memory flag hasBeenApplied is false, which prevents
cleanup after process restarts; change the logic in
controlPlaneWorkload[T].delete to derive persisted applied state (e.g., read
hyperv1.ControlPlaneComponent status or annotation via the
ControlPlaneContext/k8s client) or rehydrate hasBeenApplied at startup so delete
does not rely solely on the volatile hasBeenApplied boolean; specifically, add a
lookup (using ControlPlaneContext) for the corresponding
hyperv1.ControlPlaneComponent status/annotation to decide whether resources
exist and only skip deletion when that persisted marker indicates not applied,
and update any rehydration initialization path so hasBeenApplied reflects
persisted state.
- Around line 176-180: The code sets c.hasBeenApplied = true unconditionally
after calling c.update(cpContext), which can mark the component applied even if
c.update returned an error; change this so reconcilationError =
c.update(cpContext) is evaluated first and only set c.hasBeenApplied = true when
reconcilationError == nil (i.e., update succeeded), leaving the flag unchanged
on error; locate the block that checks unavailableDependencies and adjust the
order/guard around c.update, c.hasBeenApplied and reconcilationError
accordingly.

---

Outside diff comments:
In `@support/controlplane-component/generic-adapter.go`:
- Around line 16-23: The map manifestsAdapters stores genericAdapter values so
pointer-receiver mutations inside genericAdapter.reconcile() (which sets
genericAdapter.hasBeenApplied) are lost; fix by either changing the map to store
*genericAdapter pointers or by writing the mutated adapter back into
manifestsAdapters after calling reconcile(). Locate the manifestsAdapters map
usage (where reconcile() is invoked) and implement one of: 1) change its value
type to *genericAdapter and update creation sites to take addresses, or 2) after
calling adapter.reconcile(...), assign the possibly-modified adapter back into
manifestsAdapters (e.g., manifestsAdapters[key] = adapter) so hasBeenApplied
mutations persist.

ℹ️ Review info

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 351f6ae and 394c01f.

📒 Files selected for processing (2)
  • support/controlplane-component/controlplane-component.go
  • support/controlplane-component/generic-adapter.go

Comment on lines 176 to 180
if len(unavailableDependencies) == 0 {
// reconcile only when all dependencies are available, and don't return error immediately so it can be included in the status condition first.
reconcilationError = c.update(cpContext)
c.hasBeenApplied = true
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Set hasBeenApplied only after a successful update.

c.hasBeenApplied = true runs even when c.update(...) returns an error. That can mark components as applied even when nothing was created, undermining the “skip delete if never applied” guard. Gate this on reconcilationError == nil.

✅ Suggested fix
- reconcilationError = c.update(cpContext)
- c.hasBeenApplied = true
+ reconcilationError = c.update(cpContext)
+ if reconcilationError == nil {
+     c.hasBeenApplied = true
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if len(unavailableDependencies) == 0 {
// reconcile only when all dependencies are available, and don't return error immediately so it can be included in the status condition first.
reconcilationError = c.update(cpContext)
c.hasBeenApplied = true
}
if len(unavailableDependencies) == 0 {
// reconcile only when all dependencies are available, and don't return error immediately so it can be included in the status condition first.
reconcilationError = c.update(cpContext)
if reconcilationError == nil {
c.hasBeenApplied = true
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@support/controlplane-component/controlplane-component.go` around lines 176 -
180, The code sets c.hasBeenApplied = true unconditionally after calling
c.update(cpContext), which can mark the component applied even if c.update
returned an error; change this so reconcilationError = c.update(cpContext) is
evaluated first and only set c.hasBeenApplied = true when reconcilationError ==
nil (i.e., update succeeded), leaving the flag unchanged on error; locate the
block that checks unavailableDependencies and adjust the order/guard around
c.update, c.hasBeenApplied and reconcilationError accordingly.

Comment on lines 197 to +201
func (c *controlPlaneWorkload[T]) delete(cpContext ControlPlaneContext) error {
if !c.hasBeenApplied {
// if the component has not been applied, it doesn't exist, so there's nothing to delete.
return nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard can skip cleanup after operator restart.

hasBeenApplied is in-memory only. After a restart it resets to false, so if the component is disabled at startup, delete becomes a no-op even when resources exist from prior runs. Consider persisting applied state (e.g., via hyperv1.ControlPlaneComponent status/annotation) or rehydrating it once per process to allow cleanup after restarts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@support/controlplane-component/controlplane-component.go` around lines 197 -
201, The delete method on controlPlaneWorkload[T] currently early-returns when
the in-memory flag hasBeenApplied is false, which prevents cleanup after process
restarts; change the logic in controlPlaneWorkload[T].delete to derive persisted
applied state (e.g., read hyperv1.ControlPlaneComponent status or annotation via
the ControlPlaneContext/k8s client) or rehydrate hasBeenApplied at startup so
delete does not rely solely on the volatile hasBeenApplied boolean;
specifically, add a lookup (using ControlPlaneContext) for the corresponding
hyperv1.ControlPlaneComponent status/annotation to decide whether resources
exist and only skip deletion when that persisted marker indicates not applied,
and update any rehydration initialization path so hasBeenApplied reflects
persisted state.

@openshift-ci

openshift-ci Bot commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

@muraee: all tests passed!

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.

@muraee

muraee commented Feb 27, 2026

Copy link
Copy Markdown
Contributor Author

superseded by #7819

@muraee muraee closed this Feb 27, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@muraee: This pull request references Jira Issue OCPBUGS-65687. The bug has been updated to no longer refer to the pull request using the external bug tracker.

Details

In response to this:

What this PR does / why we need it:

When components use .WithPredicate() (e.g., azure-cloud-controller-manager), non-matching platforms trigger cleanup mode which calls Client.Get() to check for existing resources before deletion. For platform-specific resource types like SecretProviderClass, this causes controller-runtime to create informers that continuously LIST/WATCH resources that will never exist on non-Azure platforms.

Track whether resources have been successfully applied and skip delete operations entirely for resources that were never created, avoiding the Client.Get() call that triggers unnecessary informer creation.

Which issue(s) this PR fixes:

Fixes

Special notes for your reviewer:

Checklist:

  • Subject and description added to both, commit and PR.
  • Relevant issues have been referenced.
  • This change includes docs.
  • This change includes unit tests.

Summary by CodeRabbit

  • Bug Fixes
  • Improved component state tracking to prevent deletion attempts on resources that haven't been successfully applied.
  • Enhanced deletion logic to safely handle unapplied resources and avoid deletion errors.

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 openshift-eng/jira-lifecycle-plugin repository.

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. area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release jira/severity-critical Referenced Jira bug's severity is critical for the branch this PR is targeting. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants