Skip to content

feat(nodepool): trigger rollout on trust bundle ConfigMap content changes#9007

Open
alokstech wants to merge 2 commits into
openshift:mainfrom
alokstech:feat/rfe-8751-trust-bundle-rollout
Open

feat(nodepool): trigger rollout on trust bundle ConfigMap content changes#9007
alokstech wants to merge 2 commits into
openshift:mainfrom
alokstech:feat/rfe-8751-trust-bundle-rollout

Conversation

@alokstech

@alokstech alokstech commented Jul 14, 2026

Copy link
Copy Markdown

Summary

Fixes RFE-8751 (Phase 1): NodePool rollouts now react to in-place changes of ca-bundle.crt content in ConfigMaps referenced by HostedCluster.spec.additionalTrustBundle and HostedCluster.spec.configuration.proxy.trustedCA, not only reference name changes.

  • Hash trust-bundle ConfigMap content in the NodePool rollout config hash
  • Watch HC-referenced ConfigMaps and enqueue NodePools on in-place updates
  • Update rollout documentation
  • Add unit tests

Pull secret behavior is unchanged: in-place pull secret edits still do not trigger a NodePool rollout.

One-time migration on HyperShift Operator upgrade

Important operational note for reviewers and fleet operators:

This change replaces the rollout hash input for trust bundles from ConfigMap name to ConfigMap content hash. That means:

Scenario Node rollout?
HO upgrade on clusters without additionalTrustBundle / proxy.trustedCA No — hash inputs remain empty
First HO upgrade on clusters with trust-bundle references configured (CA content unchanged) Yes — one-time migration rollout
Subsequent HO image upgrades (same content-hash logic, CA content unchanged) No
In-place edit of ca-bundle.crt after migration Yes (intended RFE-8751 behavior)

The one-time migration rollout happens because the stored config hash was computed with the old formula (name) and the new operator computes a different hash (content) even when the PEM bytes are identical. After that rollout completes, later HO upgrades should not replace nodes unless trust-bundle content or another rollout input actually changes.

This is the same class of change called out in project guidance for NodePool config hash changes. e2e-aws-upgrade-hypershift-operator validates that HO upgrade does not spuriously roll nodes on default test clusters (no trust bundle configured); it does not exercise the one-time migration path for fleets that already reference trust bundles.

Manual testing

Validated on AWS management cluster with custom HO image:

  • Installed HO: quay.io/rhn-support-alosingh/hypershift-operator:feat-rfe-8751-trust-bundle-rollout
  • Created HostedCluster rfe8751-test
  • Applied additionalTrustBundle ConfigMap with real self-signed CA
  • In-place edit of ca-bundle.crt triggered UpdatingConfig=True and node replacement without renaming the ConfigMap or HostedCluster reference

Test plan

  • make verify (unit tests + lint)
  • Manual AWS test: in-place additionalTrustBundle ConfigMap edit triggers NodePool rollout
  • pull-ci-openshift-hypershift-main-e2e-aws-upgrade-hypershift-operator (CI presubmit)
  • Pull secret in-place edit regression (should not roll nodes)

Made with Cursor

Summary by CodeRabbit

  • New Features
    • NodePool rollouts now respond to changes in the content of referenced trust-bundle ConfigMaps (including proxy trusted CAs), not only ConfigMap name changes.
    • NodePool reconciliation is triggered when a HostedCluster’s referenced trust-bundle ConfigMap is updated.
  • Documentation
    • Updated rollout trigger documentation and the summary table to reflect “ConfigMap name or content change” behavior.
  • Tests
    • Added/updated test coverage for trust-bundle reference detection, rollout hash behavior, and reconcile triggering.

…nges

Hash additionalTrustBundle and proxy trustedCA ConfigMap contents in the
NodePool config hash and watch those ConfigMaps so in-place CA updates
roll out workers without renaming the reference.

Refs: RFE-8751

Signed-off-by: alokstech <alok@redhat.com>
Commit-Message-Assisted-by: Claude (via Claude Code)
Co-authored-by: Cursor <cursoragent@cursor.com>
@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 openshift-ci Bot added do-not-merge/needs-area needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Jul 14, 2026
@openshift-ci
openshift-ci Bot requested review from Nirshal and devguyio July 14, 2026 15:39
@openshift-ci openshift-ci Bot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. area/documentation Indicates the PR includes changes for documentation labels Jul 14, 2026
@openshift-ci

openshift-ci Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Hi @alokstech. Thanks for your PR.

I'm waiting for a openshift 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.

Tip

We noticed you've done this a few times! Consider joining the org to skip this step and gain /lgtm and other bot rights. We recommend asking approvers on your previous PRs to sponsor you.

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.

@openshift-ci openshift-ci Bot added the area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release label Jul 14, 2026
@openshift-ci

openshift-ci Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: alokstech
Once this PR has been reviewed and has the lgtm label, please assign enxebre for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found 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

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 352d7f76-48de-4347-aad8-ff7c2bdce565

📥 Commits

Reviewing files that changed from the base of the PR and between 51b6ae9 and b182fab.

⛔ Files ignored due to path filters (1)
  • docs/content/reference/aggregated-docs.md is excluded by !docs/content/reference/aggregated-docs.md
📒 Files selected for processing (5)
  • hypershift-operator/controllers/nodepool/config.go
  • hypershift-operator/controllers/nodepool/config_test.go
  • hypershift-operator/controllers/nodepool/nodepool_controller.go
  • hypershift-operator/controllers/nodepool/token.go
  • hypershift-operator/controllers/nodepool/token_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • hypershift-operator/controllers/nodepool/token.go
  • hypershift-operator/controllers/nodepool/token_test.go
  • hypershift-operator/controllers/nodepool/nodepool_controller.go
  • hypershift-operator/controllers/nodepool/config.go
  • hypershift-operator/controllers/nodepool/config_test.go

📝 Walkthrough

Walkthrough

NodePool rollout configuration now hashes ca-bundle.crt content from referenced additional-trust-bundle and proxy trusted-CA ConfigMaps. These hashes are included in rollout calculations. ConfigMap event handling identifies HostedClusters referencing changed ConfigMaps and enqueues their NodePools. Tests cover hashing, reference detection, reconciliation, and updated inputs. Documentation now describes both name and content changes as rollout triggers.

Sequence Diagram(s)

sequenceDiagram
  participant ConfigMap
  participant NodePoolController
  participant HostedCluster
  participant ConfigGenerator
  participant NodePool
  ConfigMap->>NodePoolController: update referenced trust bundle
  NodePoolController->>HostedCluster: inspect trust-bundle references
  HostedCluster-->>NodePoolController: identify matching NodePools
  NodePoolController->>NodePool: enqueue reconciliation
  NodePool->>ConfigGenerator: generate rollout configuration
  ConfigGenerator->>ConfigMap: read ca-bundle.crt
  ConfigMap-->>ConfigGenerator: return bundle content
  ConfigGenerator->>NodePool: calculate rollout hash
Loading

Possibly related PRs

Suggested reviewers: devguyio, nirshal

🚥 Pre-merge checks | ✅ 11
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: NodePool rollouts now react to trust bundle ConfigMap content 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.
Stable And Deterministic Test Names ✅ Passed No Ginkgo titles were added; the changed tests use static t.Run strings, with no dynamic data in test names.
Test Structure And Quality ✅ Passed The touched tests are plain table-driven unit tests, not Ginkgo; each case is focused, uses fake clients, and adds no waits or cleanup risks.
Topology-Aware Scheduling Compatibility ✅ Passed Touched code only hashes trust-bundle ConfigMaps and enqueues NodePools; no pod affinity, nodeSelector, toleration, or replica-topology logic was added.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed PASS: The new Ginkgo e2e test only queries cluster CRs and gates on GCP platform; it contains no hardcoded IPv4s, IP parsing, or direct public-host/network calls.
No-Weak-Crypto ✅ Passed No MD5/SHA1/DES/RC4/etc or secret comparisons were added; the new hashing uses supportutil.HashSimple (FNV-1a), not a banned crypto primitive.
Container-Privileges ✅ Passed Diff scan found no added privileged/hostNetwork/hostPID/hostIPC/SYS_ADMIN/runAsUser:0/allowPrivilegeEscalation lines in changed manifests; edits are TLS/hash related.
No-Sensitive-Data-In-Logs ✅ Passed No new logging of sensitive data was added; touched paths only have existing status/error logs, with no secrets, tokens, or hostnames emitted.
✨ 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.

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor

I now have a complete picture. Here is the analysis:

Test Failure Analysis Complete

Job Information

Test Failure Analysis

Error

tide: Not mergeable. PR has a merge conflict.
mergeable: CONFLICTING | mergeable_state: dirty | rebaseable: false

Summary

No CI tests ran. The PR branch (feat/rfe-8751-trust-bundle-rollout, last committed 2026-07-12) has git merge conflicts with the current main branch, caused by two commits that landed on main on 2026-07-09 and 2026-07-13 modifying the same ConfigGenerator.Hash() / HashWithoutVersion() functions in config.go. Tide correctly blocks the merge and all CI test jobs remain in "pending — Waiting for pipeline condition to trigger" because the branch cannot be cleanly merged. All 10 CI presubmit jobs (e2e-aks, e2e-aws, e2e-v2-aws, etc.) are gated behind tide and will not start until the conflicts are resolved.

Root Cause

The root cause is git merge conflicts in two files, not a product bug or test failure:

  1. hypershift-operator/controllers/nodepool/config.go — 2 conflict regions:

    • Hash() function (line ~177): The PR replaces cg.additionalTrustBundleName with cg.additionalTrustBundleHash + cg.proxyTrustedCAHash (to hash ConfigMap content instead of names). Meanwhile, commit ef295ce77f on main (2026-07-13, by Salvatore Dario Minonne) added + cg.rhelStream to the same hash expression. Both changes modify the same line.
    • HashWithoutVersion() function (line ~189): Same conflict pattern — the PR adds trust bundle hash fields and new helper functions (rolloutTrustBundleHashes, configMapCABundleHash, hostedClusterReferencesConfigMap), while main added cg.rhelStream.
  2. hypershift-operator/controllers/nodepool/config_test.go — 1 conflict region:

    • Test case expected hashes (line ~402): The "When additionalTrustBundle is specified" test case has different expected hash values because the hash inputs changed differently on each branch (PR: "1ec69ac9"/"7b5702f4" vs main: "632801f8"/"71375893").

Conflicting commits on main:

  • b1b0e9acdd (2026-07-09): "feat(nodepool): integrate osImageStream into NodePool controller" — added osImageStream concept to config generation
  • ef295ce77f (2026-07-13): "feat(nodepool): thread streamName and wire usesRunc detection into RHEL stream resolution" — added cg.rhelStream to the config hash

The PR branch was last committed on 2026-07-12, so it missed commit ef295ce77f which landed on 2026-07-13 — one day before the PR was opened (2026-07-14).

Recommendations
  1. Rebase the PR branch onto current main to resolve the merge conflicts:

    git fetch upstream main
    git rebase upstream/main
  2. Resolve the conflicts in config.go by combining both changes — the Hash() and HashWithoutVersion() functions need to include both the new cg.rhelStream field (from main) and the cg.additionalTrustBundleHash + cg.proxyTrustedCAHash fields (from the PR), replacing cg.additionalTrustBundleName. The merged Hash() should look like:

    return supportutil.HashSimple(cg.mcoRawConfig + cg.releaseImage.Version() + cg.pullSecretName + cg.additionalTrustBundleHash + cg.proxyTrustedCAHash + cg.globalConfig + cg.rhelStream)
  3. Update test expected hashes in config_test.go — after merging the hash inputs, the expected hash values will change. Recompute them by running the tests locally after resolving conflicts.

  4. Force-push the rebased branch and wait for CI to trigger the full presubmit suite.

Evidence
Evidence Detail
Tide status Not mergeable. PR has a merge conflict.
GitHub merge state mergeable: false, mergeable_state: dirty, rebaseable: false
Conflicting file 1 hypershift-operator/controllers/nodepool/config.go — 2 conflict regions at lines ~177 and ~189 (Hash() and HashWithoutVersion() functions)
Conflicting file 2 hypershift-operator/controllers/nodepool/config_test.go — 1 conflict region at line ~402 (expected hash values)
Conflicting commit (main) ef295ce77f (2026-07-13, Salvatore Dario Minonne): "feat(nodepool): thread streamName and wire usesRunc detection into RHEL stream resolution" — added cg.rhelStream to hash
Earlier related commit (main) b1b0e9acdd (2026-07-09): "feat(nodepool): integrate osImageStream into NodePool controller"
PR branch last commit 51b6ae94 (2026-07-12) — one day before the conflicting commit landed on main
CI presubmit jobs All 10 jobs (e2e-aks, e2e-aws, e2e-v2-aws, etc.) stuck in pending — Waiting for pipeline condition to trigger
Nature of conflict Both branches modify the same hash expression in ConfigGenerator.Hash() — PR adds trust bundle content hashing, main adds RHEL stream to the hash

Combine trust-bundle content hashing with upstream rhelStream hash changes
from main and update unit test expectations.

Co-authored-by: Cursor <cursoragent@cursor.com>
@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 14, 2026

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
hypershift-operator/controllers/nodepool/token.go (1)

57-62: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Clarify the scope of these hashes These values are written to the token Secret for change detection, not to the NodePool rollout hash. pullSecretHash still tracks pull-secret content separately from the rollout path, so this wording implies rollout behavior that the code does not have.

🤖 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 `@hypershift-operator/controllers/nodepool/token.go` around lines 57 - 62,
Update the comment above pullSecretHash, additionalTrustBundleHash, and
globalConfigHash to state that these hashes are written to the token Secret for
change detection, not included in the NodePool rollout config hash. Clarify that
pullSecretHash tracks pull-secret content separately, and remove the inaccurate
claim that these values trigger NodePool rollouts.
🤖 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 `@hypershift-operator/controllers/nodepool/config.go`:
- Around line 137-163: Update rolloutTrustBundleHashes and configMapCABundleHash
so missing or malformed trust-bundle ConfigMaps are represented as a degraded
HostedCluster condition rather than propagated as a hard NodePool reconciliation
error. Preserve successful hash calculation, and ensure NewConfigGenerator or
its caller records the degraded state while allowing reconciliation to continue
through transient delete/recreate windows.

---

Outside diff comments:
In `@hypershift-operator/controllers/nodepool/token.go`:
- Around line 57-62: Update the comment above pullSecretHash,
additionalTrustBundleHash, and globalConfigHash to state that these hashes are
written to the token Secret for change detection, not included in the NodePool
rollout config hash. Clarify that pullSecretHash tracks pull-secret content
separately, and remove the inaccurate claim that these values trigger NodePool
rollouts.
🪄 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: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: b8ad4e64-1086-4660-99e7-3eb0dff885ce

📥 Commits

Reviewing files that changed from the base of the PR and between a84834f and 51b6ae9.

⛔ Files ignored due to path filters (1)
  • docs/content/reference/aggregated-docs.md is excluded by !docs/content/reference/aggregated-docs.md
📒 Files selected for processing (7)
  • docs/content/reference/nodepool-rollouts.md
  • hypershift-operator/controllers/nodepool/config.go
  • hypershift-operator/controllers/nodepool/config_test.go
  • hypershift-operator/controllers/nodepool/config_trustbundle_test.go
  • hypershift-operator/controllers/nodepool/nodepool_controller.go
  • hypershift-operator/controllers/nodepool/token.go
  • hypershift-operator/controllers/nodepool/token_test.go

Comment on lines +137 to +163
func rolloutTrustBundleHashes(ctx context.Context, c client.Client, hc *hyperv1.HostedCluster) (additionalTrustBundleHash, proxyTrustedCAHash string, err error) {
if hc.Spec.AdditionalTrustBundle != nil {
additionalTrustBundleHash, err = configMapCABundleHash(ctx, c, hc.Namespace, hc.Spec.AdditionalTrustBundle.Name)
if err != nil {
return "", "", err
}
}
if hc.Spec.Configuration != nil && hc.Spec.Configuration.Proxy != nil && hc.Spec.Configuration.Proxy.TrustedCA.Name != "" {
proxyTrustedCAHash, err = configMapCABundleHash(ctx, c, hc.Namespace, hc.Spec.Configuration.Proxy.TrustedCA.Name)
if err != nil {
return "", "", err
}
}
return additionalTrustBundleHash, proxyTrustedCAHash, nil
}

func configMapCABundleHash(ctx context.Context, c client.Client, namespace, name string) (string, error) {
cm := &corev1.ConfigMap{}
if err := c.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, cm); err != nil {
return "", fmt.Errorf("cannot get ConfigMap %s/%s: %w", namespace, name, err)
}
data, ok := cm.Data[certs.UserCABundleMapKey]
if !ok {
return "", fmt.Errorf("ConfigMap %s/%s missing %q key", namespace, name, certs.UserCABundleMapKey)
}
return supportutil.HashSimple(data), 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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and locate the call chain / tests.
ast-grep outline hypershift-operator/controllers/nodepool/config.go
rg -n "NewConfigGenerator|rolloutTrustBundleHashes|configMapCABundleHash|AdditionalTrustBundle|TrustedCA|config_trustbundle_test" hypershift-operator/controllers/nodepool -S

# Read the relevant section of the implementation.
sed -n '1,260p' hypershift-operator/controllers/nodepool/config.go

# Read the trust-bundle tests, if present.
sed -n '1,260p' hypershift-operator/controllers/nodepool/config_trustbundle_test.go

Repository: openshift/hypershift

Length of output: 34581


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '320,520p' hypershift-operator/controllers/nodepool/nodepool_controller.go
sed -n '1088,1125p' hypershift-operator/controllers/nodepool/nodepool_controller.go
sed -n '340,410p' hypershift-operator/controllers/nodepool/conditions.go

Repository: openshift/hypershift

Length of output: 13672


Trust-bundle lookup now blocks all NodePool reconciliation for the HostedCluster

In rolloutTrustBundleHashes/configMapCABundleHash, a missing or malformed trust-bundle ConfigMap becomes a hard error, and NewConfigGenerator propagates that on every NodePool reconcile path. If transient delete/recreate windows are expected, surface this as a degraded condition instead of failing the whole reconcile loop.

🤖 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 `@hypershift-operator/controllers/nodepool/config.go` around lines 137 - 163,
Update rolloutTrustBundleHashes and configMapCABundleHash so missing or
malformed trust-bundle ConfigMaps are represented as a degraded HostedCluster
condition rather than propagated as a hard NodePool reconciliation error.
Preserve successful hash calculation, and ensure NewConfigGenerator or its
caller records the degraded state while allowing reconciliation to continue
through transient delete/recreate windows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/documentation Indicates the PR includes changes for documentation area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant