Skip to content

OCPBUGS-86949: Guard HCCO KubeletConfig CM deletion against transient source absence - #8672

Merged
openshift-merge-bot[bot] merged 1 commit into
openshift:mainfrom
vsolanki12:fix-OCPBUGS-86949
Jun 23, 2026
Merged

OCPBUGS-86949: Guard HCCO KubeletConfig CM deletion against transient source absence#8672
openshift-merge-bot[bot] merged 1 commit into
openshift:mainfrom
vsolanki12:fix-OCPBUGS-86949

Conversation

@vsolanki12

@vsolanki12 vsolanki12 commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

HCCO's reconcileKubeletConfig deletes guest-side ConfigMaps whose source is absent from the HCP namespace. During the immutable-to-mutable migration (OCPBUGS-85778) or any transient API error, the source CM can be briefly absent, causing HCCO to delete the guest copy. NTO then regenerates MachineConfigs without it, triggering an MCO node rollout.

This PR:

  • Guards deletion of mirrored CMs: Skips deletion of guest-side CMs carrying NTOMirroredConfigLabel, since their source is expected to reappear on the next reconcile cycle
  • Adds ownership guard to deleteImmutableConfigMapIfNeeded: Refactors to use DeleteIfNeededWithPredicate with a KubeletConfigConfigMapLabel ownership check, preventing accidental deletion of unrelated immutable ConfigMaps
  • Reinitializes the object after predicate-based delete: DeleteIfNeededWithPredicate populates the passed object via its internal Get, leaking server-side fields (ResourceVersion, Immutable, etc.). The object is reinitialized as a fresh ConfigMap with only Name/Namespace to avoid stale field leakage into the subsequent CreateOrUpdate

Which issue(s) this PR fixes:

Fixes OCPBUGS-86949

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

    • Prevented stale server-populated fields during KubeletConfig ConfigMap reconciliation by reinitializing before updates.
    • Preserved mirrored guest-side kubelet-config ConfigMaps during cleanup when upstream sources are temporarily missing.
    • Deleted non-mirrored guest-side kubelet-config ConfigMaps when upstream sources are absent.
    • Improved immutable kubelet-config ConfigMap handling to delete and recreate as mutable only when ownership is indicated.
  • Tests

    • Expanded reconciliation tests for mirroring behavior, transient absence, preservation of unrelated immutables, and immutable-to-mutable recreation.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci-robot openshift-ci-robot added jira/severity-low Referenced Jira bug's severity is low for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. labels Jun 4, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@vsolanki12: This pull request references Jira Issue OCPBUGS-86949, which is valid. The bug has been moved to the POST state.

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

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:

HCCO's reconcileKubeletConfig deletes guest-side ConfigMaps whose source is absent from the HCP namespace. During the immutable-to-mutable migration (OCPBUGS-85778) or any transient API error, the source CM can be briefly absent, causing HCCO to delete the guest copy. NTO then regenerates MachineConfigs without it, triggering an MCO node rollout.

This PR:

  • Guards deletion of mirrored CMs: Skips deletion of guest-side CMs carrying NTOMirroredConfigLabel, since their source is expected to reappear on the next reconcile cycle
  • Adds ownership guard to deleteImmutableConfigMapIfNeeded: Refactors to use DeleteIfNeededWithPredicate with a KubeletConfigConfigMapLabel ownership check, preventing accidental deletion of unrelated immutable ConfigMaps
  • Clears stale fields after predicate-based delete: Resets ResourceVersion and Immutable after DeleteIfNeededWithPredicate to avoid stale-resourceVersion errors and immutable leakage on the subsequent CreateOrUpdate

Which issue(s) this PR fixes:

Fixes OCPBUGS-86949

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.

@openshift-ci openshift-ci Bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. do-not-merge/needs-area labels Jun 4, 2026
@openshift-ci

openshift-ci Bot commented Jun 4, 2026

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

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR modifies kubelet-config reconciliation to delete immutable hosted-cluster kubelet-config ConfigMaps before recreating them and reinitializes the in-memory hostedClusterCM to avoid stale server-populated fields. Cleanup now skips ConfigMaps labeled as NTO-mirrored when the upstream source is absent. The immutable-delete helper was refactored to use a predicate-based delete that only targets immutable KubeletConfig-owned ConfigMaps and logs deletions. Tests were extended with cases for mirrored preservation, non-mirrored deletion, immutable recreation, and asserting reconciled ConfigMaps are mutable.

Sequence Diagram(s)

sequenceDiagram
  participant reconcileKubeletConfig
  participant deleteImmutableConfigMapIfNeeded
  participant kubeAPIServer
  reconcileKubeletConfig->>deleteImmutableConfigMapIfNeeded: invoke deleteImmutableConfigMapIfNeeded
  deleteImmutableConfigMapIfNeeded->>kubeAPIServer: Delete immutable CM if KubeletConfig-owned
  kubeAPIServer-->>deleteImmutableConfigMapIfNeeded: deletion result
  deleteImmutableConfigMapIfNeeded-->>reconcileKubeletConfig: return (log deletion)
  reconcileKubeletConfig->>reconcileKubeletConfig: reinitialize hostedClusterCM object
  reconcileKubeletConfig->>kubeAPIServer: CreateOrUpdate with clean state
  kubeAPIServer-->>reconcileKubeletConfig: create/update result
Loading

Possibly related PRs

  • openshift/hypershift#8543: Both PRs change ConfigMap reconciliation to handle immutable kubelet/config-mirrored ConfigMaps by deleting immutable ones before CreateOrUpdate and ensuring recreated ConfigMaps are mutable, including updated handling for relevant delete/immutable logic and tests.

Suggested reviewers

  • jparrill
  • sjenning
🚥 Pre-merge checks | ✅ 10 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Test Structure And Quality ⚠️ Warning Test has a format string bug: line 1743 has incomplete format placeholder "want=%" without format specifier, breaking assertion message reliability. Fix line 1743 format string from "got=%d want=%" to "got=%d want=%d" to properly format both integers in the assertion message.
✅ Passed checks (10 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: guarding KubeletConfig ConfigMap deletion against transient source absence, which directly addresses the PR's core objective of preventing premature deletion of guest-side ConfigMaps.
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.
Stable And Deterministic Test Names ✅ Passed The PR modifies resources_test.go with table-driven test cases for TestReconcileKubeletConfig. All test case names are static and deterministic: "copy kubelet config from control plane NS", "so...
Topology-Aware Scheduling Compatibility ✅ Passed PR changes only ConfigMap reconciliation logic and deletion guards; no scheduling constraints, pod deployments, affinity rules, or topology-aware changes are introduced.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed PR adds only standard Go unit tests in resources_test.go (29 testing.T functions), not Ginkgo e2e tests. The custom check applies specifically to Ginkgo e2e tests (Describe, It, Context, When patte...
No-Weak-Crypto ✅ Passed No weak cryptography detected in PR changes. The PR modifies KubeletConfig ConfigMap management functions with no weak crypto (MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB) usage, custom crypto impleme...
Container-Privileges ✅ Passed PR modifies only Go source code for KubeletConfig reconciliation logic. No container manifests or privilege configurations (privileged: true, hostPID, hostNetwork, hostIPC, SYS_ADMIN, allowPrivileg...
No-Sensitive-Data-In-Logs ✅ Passed All logging statements only log ObjectKeys (namespace/name) and operation results, not sensitive ConfigMap data, credentials, or PII.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@openshift-ci-robot

Copy link
Copy Markdown

@vsolanki12: This pull request references Jira Issue OCPBUGS-86949, which is valid.

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

In response to this:

What this PR does / why we need it:

HCCO's reconcileKubeletConfig deletes guest-side ConfigMaps whose source is absent from the HCP namespace. During the immutable-to-mutable migration (OCPBUGS-85778) or any transient API error, the source CM can be briefly absent, causing HCCO to delete the guest copy. NTO then regenerates MachineConfigs without it, triggering an MCO node rollout.

This PR:

  • Guards deletion of mirrored CMs: Skips deletion of guest-side CMs carrying NTOMirroredConfigLabel, since their source is expected to reappear on the next reconcile cycle
  • Adds ownership guard to deleteImmutableConfigMapIfNeeded: Refactors to use DeleteIfNeededWithPredicate with a KubeletConfigConfigMapLabel ownership check, preventing accidental deletion of unrelated immutable ConfigMaps
  • Clears stale fields after predicate-based delete: Resets ResourceVersion and Immutable after DeleteIfNeededWithPredicate to avoid stale-resourceVersion errors and immutable leakage on the subsequent CreateOrUpdate

Which issue(s) this PR fixes:

Fixes OCPBUGS-86949

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

  • KubeletConfig ConfigMaps are now properly converted to mutable state during reconciliation.

  • Mirrored ConfigMaps are preserved during cleanup when their source is transiently unavailable.

  • Immutable KubeletConfig ConfigMaps are properly deleted and recreated as mutable.

  • Tests

  • Extended test coverage for KubeletConfig ConfigMap reconciliation scenarios.

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.

@openshift-ci openshift-ci Bot added area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release and removed do-not-merge/needs-area labels Jun 4, 2026
@codecov

codecov Bot commented Jun 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.47368% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 41.85%. Comparing base (e4a1ba2) to head (1e0b1ba).
⚠️ Report is 52 commits behind head on main.

Files with missing lines Patch % Lines
...rconfigoperator/controllers/resources/resources.go 89.47% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #8672   +/-   ##
=======================================
  Coverage   41.84%   41.85%           
=======================================
  Files         759      759           
  Lines       94073    94083   +10     
=======================================
+ Hits        39361    39374   +13     
+ Misses      51956    51953    -3     
  Partials     2756     2756           
Files with missing lines Coverage Δ
...rconfigoperator/controllers/resources/resources.go 56.95% <89.47%> (+0.24%) ⬆️
Flag Coverage Δ
cmd-support 35.13% <ø> (ø)
cpo-hostedcontrolplane 44.10% <ø> (ø)
cpo-other 43.52% <89.47%> (+0.07%) ⬆️
hypershift-operator 52.02% <ø> (ø)
other 31.56% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@vsolanki12
vsolanki12 marked this pull request as ready for review June 4, 2026 15:57
@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 Jun 4, 2026
@openshift-ci
openshift-ci Bot requested review from cblecker and sdminonne June 4, 2026 15:58
@cblecker

cblecker commented Jun 4, 2026

Copy link
Copy Markdown
Member

/uncc

@openshift-ci
openshift-ci Bot removed the request for review from cblecker June 4, 2026 16:08
@vsolanki12

vsolanki12 commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

I have tried to reproduce and tested the fix in my test cluster as below:

Environment: KubeVirt HCP cluster vsolankihcp, version 5.0.0-ec.1, 2-node NodePool

Custom CPO image deployed:

  $ oc get pod hosted-cluster-config-operator-7df85759cc-k25pl -n clusters-vsolankihcp \
      -o jsonpath='{.spec.containers[*].image}'
  quay.io/vsolanki/control-plane-operator:OCPBUGS-86949

Test scenario: Deleted source KubeletConfig CM from HCP namespace to simulate transient absence during immutable-to-mutable migration (OCPBUGS-85778).

Before fix: guest-side CM immediately removed:

  $ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
      --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig
  Error from server (NotFound): configmaps "kubelet-config-maxpods-vsolankihcp" not found

After fix: guest side CM preserved:

  $ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
      --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig
  NAME                                  DATA   AGE
  kubelet-config-maxpods-vsolankihcp    1      2m29s

HCCO logs guard active:

  {"level":"info","ts":"2026-06-04T15:17:18Z",
   "msg":"skipping deletion of mirrored ConfigMap with transiently absent source",
   "controller":"resources",
   "configMap":"openshift-config-managed/kubelet-config-maxpods-vsolankihcp"}

@vsolanki12

Copy link
Copy Markdown
Contributor Author

/cc @jparrill

@openshift-ci
openshift-ci Bot requested a review from jparrill June 4, 2026 16:22

@sdminonne sdminonne 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.

I realy like this. I just need a couple of explanation and some words for reviewers and people after us 😃
Mind follow-up?
TY!

return nil
}

// deleteImmutableConfigMapIfNeeded checks if a ConfigMap exists and is immutable,

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.

Any reason to delete this comment?

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.

Thanks for pointing out. The old comment described the previous implementation which didn't have the ownership guard. Removed it during the refactor but missed adding the updated one. Will add it back with the updated description.

if err := r.client.Get(ctx, client.ObjectKeyFromObject(cm), existingCM); err != nil {
if apierrors.IsNotFound(err) {
return nil
_, err := k8sutil.DeleteIfNeededWithPredicate(ctx, r.client, cm, func(existing *corev1.ConfigMap) bool {

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.

The code being replaced is getting existingCM a fresh object from the server.
The new version is deleting directly the hostedClusterCM. It's true that it's resetting immutable and resourceVersion but what about other fields (if any)?

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.

Thank you for the suggestion, Instead of clearing individual fields, I now reinitialize hostedClusterCM as a fresh object after deleteImmutableConfigMapIfNeeded. DeleteIfNeededWithPredicate calls Get() which populates the passed object with all server side fields. Reinitializing discards all of them, so no stale fields can leak into the subsequent CreateOrUpdate.

continue
}
if cm.Labels[nodepool.NTOMirroredConfigLabel] == "true" {
log.Info("skipping deletion of mirrored ConfigMap with transiently absent source",

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.

I know that it''s unlikely it will happen but this is preventing deletion of ANY mirrored CM even the ones potentially orphaned.
May you add a comment to explain the deletion path in case it happens?

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.

Thanks your, Added a comment explaining the trade off. Mirrored CMs have a source in the HCP namespace managed by the NodePool controller. During delete+recreate migrations or transient API errors, the source can be briefly absent. Deleting the guest copy would cause NTO to regenerate MachineConfigs without it, triggering MCO node rollouts. If the source is permanently removed (e.g. NodePool deletion), the orphaned guest CM is harmless and will be cleaned up when the HostedCluster is deleted.

return err
}
hostedClusterCM.SetResourceVersion("")
hostedClusterCM.Immutable = 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.

Posterity needs some words to explain this. 😄
May you add a comment?

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.

Addressed together with #2. The reinitialize approach replaces the two field clears, and the inline comment explains why:
DeleteIfNeededWithPredicate populates the object via Get with all server-side fields, so we reinitialize to avoid leaking stale fields into CreateOrUpdate.

@jparrill

jparrill commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

/retest-required

@jparrill jparrill 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.

Dropped some comments. Thanks!

"configMap", client.ObjectKeyFromObject(cm).String())
continue
}
log.Info("delete mirror config ConfigMap", "config", client.ObjectKeyFromObject(cm).String())

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.

This log message says "transiently absent source" but mutateKubeletConfig always sets NTOMirroredConfigLabel: "true" on every guest CM it creates — so this guard matches all KubeletConfig CMs, including permanently orphaned ones after a NodePool is deleted. The message will appear in logs for CMs whose source is never coming back, which could mislead operators.

Suggestion: "skipping deletion of mirrored ConfigMap; source may be transiently absent or permanently removed after NodePool deletion"

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 the log message to "skipping deletion of mirrored ConfigMap; source may be transiently absent or permanently removed after NodePool deletion" to accurately reflect both scenarios.

log.Info("skipping deletion of mirrored ConfigMap with transiently absent source",
"configMap", client.ObjectKeyFromObject(cm).String())
continue
}

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.

Since mutateKubeletConfig at line 3056 always sets NTOMirroredConfigLabel: "true", this guard effectively disables orphan cleanup for all KubeletConfig CMs — not just ones with a transiently absent source. The trade-off is correct (stale CM < spurious MCO rollout), but worth a TODO for a future improvement: check whether the owning NodePool (via hyperv1.NodePoolLabel) still exists before unconditionally skipping.

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.

Added a TODO to check whether the owning NodePool (via NodePoolLabel) still exists before skipping, to allow cleanup of truly orphaned CMs in a future improvement.

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.

If I'm understanding properly once this PR get merged (if we merge as it is), orphaned KubeletConfig CMs will persist in the guest cluster until the HostedCluster is deleted. Right?
Unsure what is meant with TODO here. Please follow-up

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.

Yes, that's correct. Orphaned KubeletConfig CMs will persist in the guest cluster until the HostedCluster is deleted. The trade-off is: a stale CM in openshift-config-managed is harmless (NTO ignores CMs that don't match any active MachineConfigPool), while deleting it during a transient source absence triggers an MCO node rollout.
The TODO proposes a future improvement: before skipping deletion, check whether the owning NodePool (tracked via NodePoolLabel) still exists. If the NodePool is gone, the CM is truly orphaned and safe to delete. This requires a cross-namespace lookup so I'll raise a follow-up Jira to track it

hostedControlPlaneObjects: []client.Object{},
existHostedControlPlaneObjects: []client.Object{
makeKubeletConfigConfigMap(netutil.ShortenName("bar", npName1, validation.LabelValueMaxLength), hcNamespace, kubeletConfig1),
},

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.

nit: This test exercises a path that can't happen in production — mutateKubeletConfig always sets both KubeletConfigConfigMapLabel and NTOMirroredConfigLabel. Worth a one-line comment like // Defensive: this path is only reachable for CMs created before NTOMirroredConfigLabel was introduced so future readers know why it exists.

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.

Added a comment: // Defensive: this path is only reachable for CMs created before NTOMirroredConfigLabel was introduced.

existingCM := &corev1.ConfigMap{}
if err := r.client.Get(ctx, client.ObjectKeyFromObject(cm), existingCM); err != nil {
if apierrors.IsNotFound(err) {
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.

The refactor to DeleteIfNeededWithPredicate + ownership guard is clean. One test gap: there's no case for an immutable CM without KubeletConfigConfigMapLabel — i.e., the return false at line 3054. Could you add a test case with an immutable CM that lacks the label and verify it is NOT deleted?

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.

Added test case "When guest CM is immutable but not a KubeletConfig, it should not be deleted" covers the return false path when KubeletConfigConfigMapLabel is absent on an immutable CM.

@jparrill

jparrill commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jun 8, 2026
@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor

AI Test Failure Analysis

Job: pull-ci-openshift-hypershift-main-e2e-aks | Build: 2067541887249551360 | Cost: $2.8857957999999995 | Failed step: hypershift-azure-run-e2e

View full analysis report


Generated by hypershift-analyze-e2e-failure post-step using Claude claude-opus-4-6

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

/retest-required

Remaining retests: 0 against base HEAD e4a1ba2 and 1 for PR HEAD 439580d in total

@openshift-ci-robot openshift-ci-robot removed the verified Signifies that the PR passed pre-merge verification criteria label Jun 18, 2026
@openshift-ci openshift-ci Bot added area/testing Indicates the PR includes changes for e2e testing and removed lgtm Indicates that a PR is ready to be merged. labels Jun 18, 2026
…bsence

HCCO's reconcileKubeletConfig deletes guest-side ConfigMaps whose
source is absent from the HCP namespace. During the immutable-to-mutable
migration (OCPBUGS-85778) or any transient API error, the source CM can
be briefly absent, causing HCCO to delete the guest copy. NTO then
regenerates MachineConfigs without it, triggering an MCO node rollout.

Skip deletion of guest-side CMs that carry the NTOMirroredConfigLabel,
since their source is expected to reappear on the next reconcile.

Also refactor deleteImmutableConfigMapIfNeeded to use
DeleteIfNeededWithPredicate with a KubeletConfigConfigMapLabel ownership
guard, and clear ResourceVersion after the predicate-based delete to
avoid stale-resourceVersion errors on the subsequent CreateOrUpdate.

Signed-off-by: Vimal Solanki <vsolanki@redhat.com>
@sdminonne

Copy link
Copy Markdown
Contributor

/lgtm

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

Copy link
Copy Markdown
Contributor

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aks-4-22
/test e2e-aws-4-22
/test e2e-aks
/test e2e-aws
/test e2e-aws-upgrade-hypershift-operator
/test e2e-azure-v2-self-managed
/test e2e-kubevirt-aws-ovn-reduced
/test e2e-v2-aws
/test e2e-v2-gke

@vsolanki12

Copy link
Copy Markdown
Contributor Author

/retest

@openshift-ci

openshift-ci Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

@vsolanki12: The following test 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/e2e-azure-self-managed cd14bae link true /test e2e-azure-self-managed

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.

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor

I now have all the evidence needed. The failure is completely clear — this is a CI infrastructure issue, not a code issue. Let me output the report.

Test Failure Analysis Complete

Job Information

Test Failure Analysis

Error

failed to get CLI image: unable to find the 'cli' image in the provided release image:
pod pending for more than 1h0m0s: pod has not been scheduled in 1h0m0.00061293s:
0/39 nodes are available: 12 node(s) didn't match Pod's node affinity/selector,
18 node(s) had untolerated taint(s), 2 node(s) didn't match pod anti-affinity rules,
7 node(s) were unschedulable.

Reporting job state 'failed' with reason 'executing_graph:step_failed:importing_release:pod_pending'

Summary

This failure is a CI infrastructure issue — not related to the PR code changes. The job failed because the CI build cluster (build01) could not schedule two release image import pods (release-images-initial-422-cli and release-images-latest-422-cli) for over 1 hour. The pods were stuck in Pending state due to cluster-wide resource exhaustion: across 39–56 nodes, all were blocked by a combination of node affinity/selector mismatches, untolerated taints, pod anti-affinity rules, and nodes marked unschedulable. The e2e test step (e2e-aws-4-22) never ran — the job timed out during the release payload import phase, before any test code was executed. The PR's changes (guarding HCCO KubeletConfig CM deletion) were never exercised.

Root Cause

The root cause is CI build cluster (build01) resource exhaustion during the release payload import phase. Specifically:

  1. Two pods could not be scheduled: release-images-initial-422-cli and release-images-latest-422-cli — both needed to extract the cli image from OCP 4.22 release payloads.

  2. All cluster nodes were unavailable for the entire 1-hour timeout period. The scheduling events show a consistent pattern across 96 recorded scheduling attempts for each pod:

    • Node affinity/selector mismatches: 7–21 nodes didn't match the pod's node affinity/selector requirements
    • Untolerated taints: 7–21 nodes had taints the pods couldn't tolerate
    • Pod anti-affinity rules: 1–2 nodes were excluded by pod anti-affinity constraints
    • Unschedulable nodes: 7–20 nodes were cordoned/marked unschedulable
  3. No preemption was possible: The scheduler explicitly reported "No preemption victims found for incoming pod" and "Preemption is not helpful for scheduling" on every attempt.

  4. The node count fluctuated (from 38 to 56 nodes over the hour), indicating cluster autoscaling was active but new nodes were also subject to taints/affinity constraints that prevented scheduling.

  5. The job never reached the e2e test phase: The ci-operator step graph shows all image builds succeeded (src, hypershift, hypershift-operator, hypershift-tests, hypershift-cli), and the latest release image was created successfully. Only the initial-422 and latest-422 release payload imports failed — blocking all downstream steps including the actual e2e-aws-4-22 test.

This is a transient CI infrastructure problem unrelated to PR #8672's code changes.

Recommendations
  1. Retest the PR — Run /retest or /test e2e-aws-4-22 to trigger a new run. This failure is entirely due to CI cluster resource pressure and is not reproducible by the PR's changes.

  2. No code changes needed — The PR's changes (guarding HCCO KubeletConfig CM deletion against transient source absence) were never exercised during this run. The failure occurred in CI infrastructure before any test code ran.

  3. If the failure persists — Check the build01 cluster health. The pattern of high unschedulable node counts and taint pressure suggests the cluster was under heavy load at the time (2026-06-22 ~01:53–02:53 UTC). Persistent failures may indicate a cluster-wide issue that needs platform team attention.

Evidence
Evidence Detail
Failure reason executing_graph:step_failed:importing_release:pod_pending
Failed steps Import the release payload "initial-422" and Import the release payload "latest-422"
Pending pods release-images-initial-422-cli, release-images-latest-422-cli
Pending duration >1 hour (01:53:06Z to 02:53:06Z) — hit the 1h scheduling timeout
Build cluster build01
Scheduling events 96 FailedScheduling events per pod, all showing 0 available nodes
Node constraints Nodes blocked by: affinity/selector (7–21), taints (7–21), anti-affinity (1–2), unschedulable (7–20)
Preemption "No preemption victims found" on all attempts
Image builds All 5 image builds succeeded (src, hypershift, hypershift-operator, hypershift-tests, hypershift-cli)
Test execution e2e-aws-4-22 step never started — blocked by release import failure
JUnit failures 2 of 21 testcases failed (both release payload imports)

@vsolanki12

Copy link
Copy Markdown
Contributor Author

/retest

@vsolanki12

Copy link
Copy Markdown
Contributor Author

/verified by @vsolanki12

Before Fix:

1. Created a KubeletConfig ConfigMap attached to the NodePool. Verified it was mirrored from HCP namespace to guest cluster's openshift-config-managed.
Artifact: Source CM in HCP namespace
$ oc get configmaps -n clusters-vsolankihcp -l hypershift.openshift.io/kubeletconfig-config=true
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      5m
Artifact: Mirrored CM in guest cluster with labels

$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
    --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig -o jsonpath='{.metadata.labels}'

{"hypershift.openshift.io/kubeletconfig-config":"true",
 "hypershift.openshift.io/managed":"true",
 "hypershift.openshift.io/mirrored-config":"true",
 "hypershift.openshift.io/nodePool":"vsolankihcp"}

2. Trigger — Delete source CM from HCP namespace
$ oc delete configmap kubelet-config-maxpods-vsolankihcp -n clusters-vsolankihcp
configmap "kubelet-config-maxpods-vsolankihcp" deleted

3. Result — Guest-side CM deleted by HCCO
Artifact: Guest CM gone immediately
$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
    --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig
Error from server (NotFound): configmaps "kubelet-config-maxpods-vsolankihcp" not found

After Fix:

1. HCCO running custom image
Artifact: HCCO pod image
$ oc get pod hosted-cluster-config-operator-7df85759cc-k25pl -n clusters-vsolankihcp \
    -o jsonpath='{.spec.containers[*].image}'

quay.io/vsolanki/control-plane-operator:OCPBUGS-86949

2. Setup — Source CM present and mirrored
Artifact: Source CM in HCP namespace
$ oc get configmaps -n clusters-vsolankihcp -l hypershift.openshift.io/kubeletconfig-config=true
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      96s
Artifact: Guest-side CM with mirrored-config label
$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
    --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig -o jsonpath='{.metadata.labels}'

{"hypershift.openshift.io/kubeletconfig-config":"true",
 "hypershift.openshift.io/managed":"true",
 "hypershift.openshift.io/mirrored-config":"true",
 "hypershift.openshift.io/nodePool":"vsolankihcp"}

3. Trigger — Delete source CM from HCP namespace
$ oc delete configmap kubelet-config-maxpods-vsolankihcp -n clusters-vsolankihcp
configmap "kubelet-config-maxpods-vsolankihcp" deleted

4. Result — Guest-side CM preserved
Artifact: Guest CM still exists after 10s
$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
    --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      2m2s
Artifact: Guest CM still exists after 30s
$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
    --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      2m29s
Artifact: HCCO logs showing skip guard active
$ oc logs hosted-cluster-config-operator-7df85759cc-k25pl -n clusters-vsolankihcp | grep "skipping"

{"level":"info","ts":"2026-06-04T15:17:18Z",
 "msg":"skipping deletion of mirrored ConfigMap with transiently absent source",
 "controller":"resources",
 "configMap":"openshift-config-managed/kubelet-config-maxpods-vsolankihcp"}

{"level":"info","ts":"2026-06-04T15:17:23Z",
 "msg":"skipping deletion of mirrored ConfigMap with transiently absent source",
 "controller":"resources",
 "configMap":"openshift-config-managed/kubelet-config-maxpods-vsolankihcp"}

{"level":"info","ts":"2026-06-04T15:17:23Z",
 "msg":"skipping deletion of mirrored ConfigMap with transiently absent source",
 "controller":"resources",
 "configMap":"openshift-config-managed/kubelet-config-maxpods-vsolankihcp"}

5. Recovery — NodePool reconciler recreated source
Artifact: Source CM recreated automatically
$ oc get configmaps -n clusters-vsolankihcp -l hypershift.openshift.io/kubeletconfig-config=true
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      56s

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

Copy link
Copy Markdown

@vsolanki12: This PR has been marked as verified by @vsolanki12.

Details

In response to this:

/verified by @vsolanki12

Before Fix:

1. Created a KubeletConfig ConfigMap attached to the NodePool. Verified it was mirrored from HCP namespace to guest cluster's openshift-config-managed.
Artifact: Source CM in HCP namespace
$ oc get configmaps -n clusters-vsolankihcp -l hypershift.openshift.io/kubeletconfig-config=true
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      5m
Artifact: Mirrored CM in guest cluster with labels

$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
   --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig -o jsonpath='{.metadata.labels}'

{"hypershift.openshift.io/kubeletconfig-config":"true",
"hypershift.openshift.io/managed":"true",
"hypershift.openshift.io/mirrored-config":"true",
"hypershift.openshift.io/nodePool":"vsolankihcp"}

2. Trigger — Delete source CM from HCP namespace
$ oc delete configmap kubelet-config-maxpods-vsolankihcp -n clusters-vsolankihcp
configmap "kubelet-config-maxpods-vsolankihcp" deleted

3. Result — Guest-side CM deleted by HCCO
Artifact: Guest CM gone immediately
$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
   --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig
Error from server (NotFound): configmaps "kubelet-config-maxpods-vsolankihcp" not found

After Fix:

1. HCCO running custom image
Artifact: HCCO pod image
$ oc get pod hosted-cluster-config-operator-7df85759cc-k25pl -n clusters-vsolankihcp \
   -o jsonpath='{.spec.containers[*].image}'

quay.io/vsolanki/control-plane-operator:OCPBUGS-86949

2. Setup — Source CM present and mirrored
Artifact: Source CM in HCP namespace
$ oc get configmaps -n clusters-vsolankihcp -l hypershift.openshift.io/kubeletconfig-config=true
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      96s
Artifact: Guest-side CM with mirrored-config label
$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
   --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig -o jsonpath='{.metadata.labels}'

{"hypershift.openshift.io/kubeletconfig-config":"true",
"hypershift.openshift.io/managed":"true",
"hypershift.openshift.io/mirrored-config":"true",
"hypershift.openshift.io/nodePool":"vsolankihcp"}

3. Trigger — Delete source CM from HCP namespace
$ oc delete configmap kubelet-config-maxpods-vsolankihcp -n clusters-vsolankihcp
configmap "kubelet-config-maxpods-vsolankihcp" deleted

4. Result — Guest-side CM preserved
Artifact: Guest CM still exists after 10s
$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
   --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      2m2s
Artifact: Guest CM still exists after 30s
$ oc get configmap kubelet-config-maxpods-vsolankihcp -n openshift-config-managed \
   --kubeconfig=/tmp/vsolankihcp-guest.kubeconfig
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      2m29s
Artifact: HCCO logs showing skip guard active
$ oc logs hosted-cluster-config-operator-7df85759cc-k25pl -n clusters-vsolankihcp | grep "skipping"

{"level":"info","ts":"2026-06-04T15:17:18Z",
"msg":"skipping deletion of mirrored ConfigMap with transiently absent source",
"controller":"resources",
"configMap":"openshift-config-managed/kubelet-config-maxpods-vsolankihcp"}

{"level":"info","ts":"2026-06-04T15:17:23Z",
"msg":"skipping deletion of mirrored ConfigMap with transiently absent source",
"controller":"resources",
"configMap":"openshift-config-managed/kubelet-config-maxpods-vsolankihcp"}

{"level":"info","ts":"2026-06-04T15:17:23Z",
"msg":"skipping deletion of mirrored ConfigMap with transiently absent source",
"controller":"resources",
"configMap":"openshift-config-managed/kubelet-config-maxpods-vsolankihcp"}

5. Recovery — NodePool reconciler recreated source
Artifact: Source CM recreated automatically
$ oc get configmaps -n clusters-vsolankihcp -l hypershift.openshift.io/kubeletconfig-config=true
NAME                                  DATA   AGE
kubelet-config-maxpods-vsolankihcp    1      56s

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.

@openshift-merge-bot
openshift-merge-bot Bot merged commit ca7fdff into openshift:main Jun 23, 2026
43 checks passed
@openshift-ci-robot

Copy link
Copy Markdown

@vsolanki12: Jira Issue Verification Checks: Jira Issue OCPBUGS-86949
✔️ This pull request was pre-merge verified.
✔️ All associated pull requests have merged.
✔️ All associated, merged pull requests were pre-merge verified.

Jira Issue OCPBUGS-86949 has been moved to the MODIFIED state and will move to the VERIFIED state when the change is available in an accepted nightly payload. 🕓

Details

In response to this:

What this PR does / why we need it:

HCCO's reconcileKubeletConfig deletes guest-side ConfigMaps whose source is absent from the HCP namespace. During the immutable-to-mutable migration (OCPBUGS-85778) or any transient API error, the source CM can be briefly absent, causing HCCO to delete the guest copy. NTO then regenerates MachineConfigs without it, triggering an MCO node rollout.

This PR:

  • Guards deletion of mirrored CMs: Skips deletion of guest-side CMs carrying NTOMirroredConfigLabel, since their source is expected to reappear on the next reconcile cycle
  • Adds ownership guard to deleteImmutableConfigMapIfNeeded: Refactors to use DeleteIfNeededWithPredicate with a KubeletConfigConfigMapLabel ownership check, preventing accidental deletion of unrelated immutable ConfigMaps
  • Reinitializes the object after predicate-based delete: DeleteIfNeededWithPredicate populates the passed object via its internal Get, leaking server-side fields (ResourceVersion, Immutable, etc.). The object is reinitialized as a fresh ConfigMap with only Name/Namespace to avoid stale field leakage into the subsequent CreateOrUpdate

Which issue(s) this PR fixes:

Fixes OCPBUGS-86949

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

  • Prevented stale server-populated fields during KubeletConfig ConfigMap reconciliation by reinitializing before updates.

  • Preserved mirrored guest-side kubelet-config ConfigMaps during cleanup when upstream sources are temporarily missing.

  • Deleted non-mirrored guest-side kubelet-config ConfigMaps when upstream sources are absent.

  • Improved immutable kubelet-config ConfigMap handling to delete and recreate as mutable only when ownership is indicated.

  • Tests

  • Expanded reconciliation tests for mirroring behavior, transient absence, preservation of unrelated immutables, and immutable-to-mutable recreation.

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.

@openshift-merge-robot

Copy link
Copy Markdown
Contributor

Fix included in release 5.0.0-0.nightly-2026-06-23-223621

vsolanki12 added a commit to vsolanki12/hypershift that referenced this pull request Jul 2, 2026
…eletion

The guard added in PR openshift#8672 unconditionally skips deletion of guest-side
ConfigMaps with NTOMirroredConfigLabel, preventing spurious MCO rollouts
when the source CM is transiently absent. However, this also preserves
CMs whose owning NodePool has been permanently deleted.

Derive NodePool existence from the wantCMList already fetched from the
HCP namespace: when a NodePool is deleted, its finalizer removes all its
CMs, so zero CMs for a given NodePool means it has been deleted. Build
an activeNodePools set and only skip deletion when the owning NodePool
is still active.

Signed-off-by: Vimal Solanki <vsolanki@redhat.com>
vsolanki12 added a commit to vsolanki12/hypershift that referenced this pull request Jul 4, 2026
…eletion

The guard added in PR openshift#8672 unconditionally skips deletion of guest-side
ConfigMaps with NTOMirroredConfigLabel, preventing spurious MCO rollouts
when the source CM is transiently absent. However, this also preserves
CMs whose owning NodePool has been permanently deleted.

Derive NodePool existence from the wantCMList already fetched from the
HCP namespace: when a NodePool is deleted, its finalizer removes all its
CMs, so zero CMs for a given NodePool means it has been deleted. Build
an activeNodePools set and only skip deletion when the owning NodePool
is still active.

Signed-off-by: Vimal Solanki <vsolanki@redhat.com>
vsolanki12 added a commit to vsolanki12/hypershift that referenced this pull request Jul 6, 2026
…eletion

The guard added in PR openshift#8672 unconditionally skips deletion of guest-side
ConfigMaps with NTOMirroredConfigLabel, preventing spurious MCO rollouts
when the source CM is transiently absent. However, this also preserves
CMs whose owning NodePool has been permanently deleted.

Derive NodePool existence from the wantCMList already fetched from the
HCP namespace: when a NodePool is deleted, its finalizer removes all its
CMs, so zero CMs for a given NodePool means it has been deleted. Build
an activeNodePools set and only skip deletion when the owning NodePool
is still active.

Signed-off-by: Vimal Solanki <vsolanki@redhat.com>
vsolanki12 added a commit to vsolanki12/hypershift that referenced this pull request Jul 14, 2026
…eletion

The guard added in PR openshift#8672 unconditionally skips deletion of guest-side
ConfigMaps with NTOMirroredConfigLabel, preventing spurious MCO rollouts
when the source CM is transiently absent. However, this also preserves
CMs whose owning NodePool has been permanently deleted.

Derive NodePool existence from the wantCMList already fetched from the
HCP namespace: when a NodePool is deleted, its finalizer removes all its
CMs, so zero CMs for a given NodePool means it has been deleted. Build
an activeNodePools set and only skip deletion when the owning NodePool
is still active.

Signed-off-by: Vimal Solanki <vsolanki@redhat.com>
vsolanki12 added a commit to vsolanki12/hypershift that referenced this pull request Aug 25, 2026
…eletion

The guard added in PR openshift#8672 unconditionally skips deletion of guest-side
ConfigMaps with NTOMirroredConfigLabel, preventing spurious MCO rollouts
when the source CM is transiently absent. However, this also preserves
CMs whose owning NodePool has been permanently deleted.

Derive NodePool existence from the wantCMList already fetched from the
HCP namespace: when a NodePool is deleted, its finalizer removes all its
CMs, so zero CMs for a given NodePool means it has been deleted. Build
an activeNodePools set and only skip deletion when the owning NodePool
is still active.

Signed-off-by: Vimal Solanki <vsolanki@redhat.com>
rutvik23 pushed a commit to rutvik23/hypershift that referenced this pull request Aug 26, 2026
…eletion

The guard added in PR openshift#8672 unconditionally skips deletion of guest-side
ConfigMaps with NTOMirroredConfigLabel, preventing spurious MCO rollouts
when the source CM is transiently absent. However, this also preserves
CMs whose owning NodePool has been permanently deleted.

Derive NodePool existence from the wantCMList already fetched from the
HCP namespace: when a NodePool is deleted, its finalizer removes all its
CMs, so zero CMs for a given NodePool means it has been deleted. Build
an activeNodePools set and only skip deletion when the owning NodePool
is still active.

Signed-off-by: Vimal Solanki <vsolanki@redhat.com>
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/testing Indicates the PR includes changes for e2e testing jira/severity-low Referenced Jira bug's severity is low 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. lgtm Indicates that a PR is ready to be merged. verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants