Skip to content

OCPBUGS-98065: Prevent infraID and clusterID removal via parent-level CEL rules - #9102

Open
hypershift-jira-solve-ci[bot] wants to merge 3 commits into
openshift:mainfrom
hypershift-community:fix-OCPBUGS-98065
Open

OCPBUGS-98065: Prevent infraID and clusterID removal via parent-level CEL rules#9102
hypershift-jira-solve-ci[bot] wants to merge 3 commits into
openshift:mainfrom
hypershift-community:fix-OCPBUGS-98065

Conversation

@hypershift-jira-solve-ci

@hypershift-jira-solve-ci hypershift-jira-solve-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

Field-level CEL rules (oldSelf == "" || self == oldSelf) on infraID and clusterID only fire when the field is present in both old and new objects. Since both fields are +optional with omitempty, a JSON merge patch setting the field to null removes the key entirely, bypassing validation. The controller then auto-generates a new infraID, causing catastrophic infrastructure re-provisioning.

This PR adds parent-level XValidation rules on HostedClusterSpec following the existing secretEncryption pattern (!has(oldSelf.infraID) || has(self.infraID)) and the equivalent for clusterID. These rules fire at the parent struct level where both old and new objects are always present.

Changes:

  • Add parent-level CEL rules preventing infraID and clusterID removal on HostedClusterSpec
  • Regenerate CRD manifests and vendor the updated API module
  • Add envtest cases asserting removal prevention and value immutability for both fields
  • Add positive envtest cases verifying initial set and unchanged-value update paths
  • Fix test-changed Makefile target to exclude the api/ module (separate Go module)

Which issue(s) this PR fixes:

Fixes https://redhat.atlassian.net/browse/OCPBUGS-98065

Special notes for your reviewer:

The parent-level CEL rules follow the same pattern already used for secretEncryption immutability in the codebase. The field-level rules are preserved for value-change protection; the new parent-level rules specifically guard against field removal.

Checklist:

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

Always review AI generated responses prior to use.
Generated with Claude Code via openshift-developer plugin


Note: This PR was auto-generated by the jira-agent periodic CI job in response to OCPBUGS-98065. See the full report for token usage, cost breakdown, and detailed phase output.

Summary by CodeRabbit

  • New Features

    • Added validation to prevent spec.infraID and spec.clusterID from being removed after they have been set.
    • This helps preserve essential cluster identity settings during configuration updates.
  • Bug Fixes

    • Improved targeted test selection by excluding API package paths, reducing unrelated test execution during focused testing.

@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 the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jul 24, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@hypershift-jira-solve-ci[bot]: This pull request references Jira Issue OCPBUGS-98065, which is invalid:

  • expected the bug to target the "5.0.0" version, but no target version was set

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:

Field-level CEL rules (oldSelf == "" || self == oldSelf) on infraID and clusterID only fire when the field is present in both old and new objects. Since both fields are +optional with omitempty, a JSON merge patch setting the field to null removes the key entirely, bypassing validation. The controller then auto-generates a new infraID, causing catastrophic infrastructure re-provisioning.

This PR adds parent-level XValidation rules on HostedClusterSpec following the existing secretEncryption pattern (!has(oldSelf.infraID) || has(self.infraID)) and the equivalent for clusterID. These rules fire at the parent struct level where both old and new objects are always present.

Changes:

  • Add parent-level CEL rules preventing infraID and clusterID removal on HostedClusterSpec
  • Regenerate CRD manifests and vendor the updated API module
  • Add envtest cases asserting removal prevention and value immutability for both fields
  • Add positive envtest cases verifying initial set and unchanged-value update paths
  • Fix test-changed Makefile target to exclude the api/ module (separate Go module)

Which issue(s) this PR fixes:

Fixes https://redhat.atlassian.net/browse/OCPBUGS-98065

Special notes for your reviewer:

The parent-level CEL rules follow the same pattern already used for secretEncryption immutability in the codebase. The field-level rules are preserved for value-change protection; the new parent-level rules specifically guard against field removal.

Checklist:

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

Always review AI generated responses prior to use.
Generated with Claude Code via openshift-developer plugin

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-robot openshift-ci-robot added the jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. label Jul 24, 2026
@coderabbitai

coderabbitai Bot commented Jul 24, 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

The test-changed Makefile target now excludes packages under ./api/ from its changed-package test list. HostedClusterSpec validation now prevents spec.infraID and spec.clusterID from being removed after they have been set.

Suggested reviewers: cblecker, nirshal

🚥 Pre-merge checks | ✅ 10 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Test Structure And Quality ⚠️ Warning The added table cases use the shared envtest assertions, but expected-error paths call Expect(err).To(HaveOccurred()) and ContainSubstring without diagnostic messages (generator.go:501-502). Add meaningful messages to the expected-error assertions, including the table entry name or operation, and review other unlabelled cluster assertions in the generator.
✅ 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 clearly describes the main change: preventing infraID and clusterID removal through parent-level CEL validation rules.
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 All 27 Ginkgo test names in the validation test suite are static and deterministic. Test names contain no dynamic information, format strings, timestamps, UUIDs, or resource identifiers that would...
Topology-Aware Scheduling Compatibility ✅ Passed The PR changes CEL validation, CRD/test fixtures, vendoring, and Makefile logic. It adds no deployments, controllers, replicas, affinity, selectors, tolerations, topology spread, or PDB constraints.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The PR adds one declarative envtest YAML suite, with no test/e2e paths or Ginkgo declarations; the harness uses only the local envtest API.
No-Weak-Crypto ✅ Passed The PR adds only CEL presence checks, generated schema/test data, and a Makefile filter; added lines contain no MD5, SHA1, DES, RC4, Blowfish, ECB, crypto APIs, or secret comparisons.
Container-Privileges ✅ Passed No container privilege escalation settings (privileged, hostPID, hostNetwork, hostIPC, SYS_ADMIN, allowPrivilegeEscalation) found in PR changes. Changes are limited to API validation rules, CRD man...
No-Sensitive-Data-In-Logs ✅ Passed The PR adds CEL validation rules and Makefile changes only. No logging that exposes passwords, tokens, API keys, PII, session IDs, hostnames, or customer data is present.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@openshift-ci

openshift-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: hypershift-jira-solve-ci[bot]
Once this PR has been reviewed and has the lgtm label, please assign csrwng 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

@openshift-ci
openshift-ci Bot requested review from Nirshal and cblecker July 24, 2026 09:55
@openshift-ci openshift-ci Bot added area/api Indicates the PR includes changes for the API area/cli Indicates the PR includes changes for CLI and removed do-not-merge/needs-area labels Jul 24, 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

🤖 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 `@Makefile`:
- Line 395: Update the package filter in the Makefile command to exclude both
the exact ./api path and paths beneath it, using an end-of-string-aware pattern
such as the suggested ./api(/|$) form while preserving the existing vendor and
hack/tools exclusions.
🪄 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: 993c044d-2888-45ab-a8d9-45eba670c5b8

📥 Commits

Reviewing files that changed from the base of the PR and between 144cca9 and c1d9614.

⛔ Files ignored due to path filters (19)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterUpdateAcceptRisks.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/EtcdSharding.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDC.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCWithUpstreamParity.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/GCPPlatform.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/HCPEtcdBackup.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/HyperShiftOnlyDynamicResourceAllocation.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ImageStreamImportMode.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryptionProvider.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/OpenStack.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/TLSAdherence.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • cmd/install/assets/crds/hypershift-operator/tests/hostedclusters.hypershift.openshift.io/stable.hostedclusters.validation.testsuite.yaml is excluded by !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-Default.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/hostedcluster_types.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (2)
  • Makefile
  • api/hypershift/v1beta1/hostedcluster_types.go

Comment thread Makefile Outdated
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 45.09%. Comparing base (4009f0f) to head (290045f).
⚠️ Report is 68 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9102      +/-   ##
==========================================
+ Coverage   44.95%   45.09%   +0.14%     
==========================================
  Files         778      780       +2     
  Lines       97434    97658     +224     
==========================================
+ Hits        43797    44043     +246     
+ Misses      50615    50571      -44     
- Partials     3022     3044      +22     

see 20 files with indirect coverage changes

Flag Coverage Δ
cmd-support 38.77% <ø> (+0.15%) ⬆️
cpo-hostedcontrolplane 47.56% <ø> (+0.27%) ⬆️
cpo-other 45.89% <ø> (+0.22%) ⬆️
hypershift-operator 54.99% <ø> (+0.03%) ⬆️
other 34.38% <ø> (+0.07%) ⬆️

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.

Comment on lines 538 to 540
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.infraID) || has(self.infraID)",message="infraID cannot be removed once set"
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.clusterID) || has(self.clusterID)",message="clusterID cannot be removed once set"
type HostedClusterSpec struct {

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.

While this does work if spec cannot be emptied, I took a look and it looks like the HostedClusterSpec type is used in an optional spec field for the HostedCluster resource:

type HostedCluster struct {
metav1.TypeMeta `json:",inline"`
// metadata is the metadata for the HostedCluster.
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// spec is the desired behavior of the HostedCluster.
// +optional
Spec HostedClusterSpec `json:"spec,omitempty"`
// status is the latest observed status of the HostedCluster.
// +optional
Status HostedClusterStatus `json:"status,omitempty"`
}

In order for this to be truly immutable this rule must be enforced at the nearest required parent - which in this case would be the root HostedCluster type.

Without enforcing it on the nearest required parent, someone could unset the spec and then re-set the spec with new values for these fields.

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 comment has still not been addressed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. Moved the CEL rules from HostedClusterSpec to the root HostedCluster type to prevent the two-step bypass via spec removal. The rules now check through self.spec: !has(oldSelf.spec) || !has(oldSelf.spec.infraID) || (has(self.spec) && has(self.spec.infraID)).


AI-assisted response via Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. Addressed in the latest push — the rules are now on the root HostedCluster type.


AI-assisted response via Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. The CEL rules have been moved from HostedClusterSpec to the root HostedCluster type (lines 2918-2919 in hostedcluster_types.go), preventing the two-step bypass via spec removal. CRDs regenerated and validation test cases added in commit e63a2c0.


AI-assisted response via Claude Code

@bryan-cox

Copy link
Copy Markdown
Member

/test address-review-comments

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

Review agent triggered. View job

@openshift-ci openshift-ci Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 27, 2026
@bryan-cox

Copy link
Copy Markdown
Member

/rebase

@github-actions

Copy link
Copy Markdown

🤖 Rebasing PR onto main: workflow run

@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 27, 2026
@bryan-cox

Copy link
Copy Markdown
Member

/test address-review-comments

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

Review agent triggered. View job

@openshift-ci openshift-ci Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 28, 2026
@bryan-cox

Copy link
Copy Markdown
Member

/rebase

@github-actions

Copy link
Copy Markdown

🤖 Rebasing PR onto main: workflow run

@openshift-ci openshift-ci Bot added needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. labels Jul 28, 2026
@bryan-cox

Copy link
Copy Markdown
Member

/rebase

@github-actions

Copy link
Copy Markdown

🤖 Rebasing PR onto main: workflow run

@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 31, 2026
@bryan-cox

Copy link
Copy Markdown
Member

/rebase

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 Rebasing PR onto main: workflow run

@bryan-cox

Copy link
Copy Markdown
Member

/restructure-commits

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

🤖 Restructuring commits: workflow run

@cblecker

cblecker commented Aug 4, 2026

Copy link
Copy Markdown
Member

/uncc

@openshift-ci
openshift-ci Bot removed the request for review from cblecker August 4, 2026 18:11
@bryan-cox

Copy link
Copy Markdown
Member

/restructure-commits

@github-actions

Copy link
Copy Markdown

🤖 Restructuring commits: workflow run

@bryan-cox

Copy link
Copy Markdown
Member

/test address-review-comments

OpenShift CI Bot added 3 commits August 10, 2026 13:17
… removal

Field-level CEL rules on infraID and clusterID only fire when the field
is present in both old and new objects. Since both fields are +optional
with omitempty, a JSON merge patch setting the field to null removes the
key entirely, bypassing validation. The controller then auto-generates a
new infraID, causing catastrophic infrastructure re-provisioning.

Add parent-level XValidation rules on HostedClusterSpec following the
existing secretEncryption pattern:
- !has(oldSelf.infraID) || has(self.infraID)
- !has(oldSelf.clusterID) || has(self.clusterID)

These rules fire at the parent struct level where both old and new
objects are always present, preventing removal via null-patch.

Signed-off-by: OpenShift CI Bot <ci-bot@redhat.com>
Commit-Message-Assisted-by: Claude (via Claude Code)
Regenerate CRD manifests and vendor the updated API module to pick up the
new parent-level CEL rules for infraID and clusterID immutability.

- Update hostedclusters CRD manifests (Default, CustomNoUpgrade,
  TechPreviewNoUpgrade) with the new XValidation rules
- Vendor the updated hostedcluster_types.go
- Add CRD validation test cases for infraID and clusterID removal
  prevention and value change prevention

Signed-off-by: OpenShift CI Bot <ci-bot@redhat.com>
Commit-Message-Assisted-by: Claude (via Claude Code)
The api/ directory is a separate Go module (github.com/openshift/hypershift/api)
and cannot be tested via go test from the root module. The test-changed
Makefile target already excludes vendor/ and hack/tools/ but was missing
api/, causing failures when commits modify API types.

Signed-off-by: OpenShift CI Bot <ci-bot@redhat.com>
Commit-Message-Assisted-by: Claude (via Claude Code)
@openshift-ci

openshift-ci Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@hypershift-jira-solve-ci[bot]: 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/address-review-comments e9fed3b link false /test address-review-comments

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.

@jira-solve-bot

Copy link
Copy Markdown

Review agent triggered. View job

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

Labels

area/api Indicates the PR includes changes for the API area/cli Indicates the PR includes changes for CLI jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants