Skip to content

OCPBUGS-34078: Enforce unique tag keys on AWS resourceTags fields#8937

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

OCPBUGS-34078: Enforce unique tag keys on AWS resourceTags fields#8937
hypershift-jira-solve-ci[bot] wants to merge 3 commits into
openshift:mainfrom
hypershift-community:fix-OCPBUGS-34078

Conversation

@hypershift-jira-solve-ci

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

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

Adds +listType=map and +listMapKey=key markers to all three AWSResourceTag list fields (AWSNodePoolPlatform.ResourceTags, AWSPlatformSpec.ResourceTags, and AWSEndpointServiceSpec.ResourceTags). This makes the Kubernetes API server reject requests with duplicate tag keys at admission time, instead of silently dropping all but the last value for a given key.

Changes include:

  • API markers: Add +listType=map and +listMapKey=key to the three AWS resourceTags fields
  • CRD regeneration: Regenerated CRDs, clients, deepcopy, and vendor — the generated CRDs now include x-kubernetes-list-type: map and x-kubernetes-list-map-keys: [key] on the resourceTags arrays for NodePool, HostedCluster, and AWSEndpointService resources
  • Envtest tests: Added envtest validation tests for both NodePool and HostedCluster to verify that duplicate tag keys in AWS resourceTags are rejected by CRD schema validation, and that unique tag keys are accepted

Which issue(s) this PR fixes:

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

Special notes for your reviewer:

The +listType=map with +listMapKey=key strategy ensures that the API server treats the key field as a unique identifier within each resourceTags list. Any create or update request containing duplicate tag keys will be rejected with a validation error.

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-34078. See the full report for token usage, cost breakdown, and detailed phase output.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of AWS resource tags in configuration schemas.
    • Resource tag lists are now treated as keyed maps using each tag’s key, enabling more consistent updates and preventing duplicate entries with the same key.

@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-moderate Referenced Jira bug's severity is moderate for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Jul 6, 2026
@openshift-ci-robot

Copy link
Copy Markdown

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

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

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

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

Details

In response to this:

What this PR does / why we need it:

Adds +listType=map and +listMapKey=key markers to all three AWSResourceTag list fields (AWSNodePoolPlatform.ResourceTags, AWSPlatformSpec.ResourceTags, and AWSEndpointServiceSpec.ResourceTags). This makes the Kubernetes API server reject requests with duplicate tag keys at admission time, instead of silently dropping all but the last value for a given key.

Changes include:

  • API markers: Add +listType=map and +listMapKey=key to the three AWS resourceTags fields
  • CRD regeneration: Regenerated CRDs, clients, deepcopy, and vendor — the generated CRDs now include x-kubernetes-list-type: map and x-kubernetes-list-map-keys: [key] on the resourceTags arrays for NodePool, HostedCluster, and AWSEndpointService resources
  • Envtest tests: Added envtest validation tests for both NodePool and HostedCluster to verify that duplicate tag keys in AWS resourceTags are rejected by CRD schema validation, and that unique tag keys are accepted

Which issue(s) this PR fixes:

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

Special notes for your reviewer:

The +listType=map with +listMapKey=key strategy ensures that the API server treats the key field as a unique identifier within each resourceTags list. Any create or update request containing duplicate tag keys will be rejected with a validation error.

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.

@coderabbitai

coderabbitai Bot commented Jul 6, 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: 04468be0-f63d-429b-a7c3-8d23ebcdda64

📥 Commits

Reviewing files that changed from the base of the PR and between 47417bf and 8c2e7f7.

⛔ Files ignored due to path filters (45)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/awsendpointservices.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/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/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/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterUpdateAcceptRisks.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUpstreamParity.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/HCPEtcdBackup.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/HyperShiftOnlyDynamicResourceAllocation.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/TLSAdherence.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/GCPPlatform.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OSStreams.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • cmd/install/assets/crds/hypershift-operator/tests/hostedclusters.hypershift.openshift.io/stable.hostedclusters.aws.testsuite.yaml is excluded by !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/tests/nodepools.hypershift.openshift.io/stable.nodepools.aws.testsuite.yaml is excluded by !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/awsendpointservices.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-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
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-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/hostedcontrolplanes-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/hostedcontrolplanes-Hypershift-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/aws.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/endpointservice_types.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (2)
  • api/hypershift/v1beta1/aws.go
  • api/hypershift/v1beta1/endpointservice_types.go

📝 Walkthrough

Walkthrough

Updated three resourceTags fields with kubebuilder annotations specifying map-list semantics keyed by AWSResourceTag.key. The Go field types remain []AWSResourceTag; only the generated CRD schema representation changes for AWS node pools, AWS platform specifications, and AWS endpoint services.

Suggested reviewers: clebs

🚥 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 describes the main change: making AWS resourceTags enforce unique keys via validation.
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 *_test.go files or Ginkgo titles changed in this patch; only API/CRD files were modified, so the naming check is not applicable.
Test Structure And Quality ✅ Passed The new envtest suites are single-purpose validation/ratcheting cases, and the envtest harness handles setup/teardown and timed installs/uninstalls.
Topology-Aware Scheduling Compatibility ✅ Passed The commit only adds CRD test YAML for AWS resourceTags schema; no deployment, controller, affinity, nodeSelector, toleration, or topology logic changed.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed Only CRD validation test-suite YAMLs changed; no new Ginkgo e2e code, IPv4-specific logic, or external connectivity requirements were added.
No-Weak-Crypto ✅ Passed Only YAML envtest fixtures changed; scans of modified files found no MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or secret comparison code.
Container-Privileges ✅ Passed PR diff only updates CRD/test assets; no privileged, hostPID/Network/IPC, allowPrivilegeEscalation, or root/SYS_ADMIN settings were introduced.
No-Sensitive-Data-In-Logs ✅ Passed Diff only adds generated CRD test YAML; no new log statements or real sensitive data appear in the changed files.
✨ 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 requested review from cblecker and clebs July 6, 2026 16:35
@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 area/platform/aws PR/issue for AWS (AWSPlatform) platform and removed do-not-merge/needs-area labels Jul 6, 2026
@openshift-ci

openshift-ci Bot commented Jul 6, 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 sjenning 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

@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 43.80%. Comparing base (d241190) to head (8c2e7f7).
⚠️ Report is 11 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #8937   +/-   ##
=======================================
  Coverage   43.80%   43.80%           
=======================================
  Files         772      772           
  Lines       96100    96100           
=======================================
  Hits        42097    42097           
  Misses      51084    51084           
  Partials     2919     2919           
Flag Coverage Δ
cmd-support 37.45% <ø> (ø)
cpo-hostedcontrolplane 45.90% <ø> (ø)
cpo-other 45.13% <ø> (ø)
hypershift-operator 54.07% <ø> (ø)
other 32.12% <ø> (ø)

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.

@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 `@api/hypershift/v1beta1/aws.go`:
- Around line 65-67: The ResourceTags list on AWSResourceTag is now treated as a
keyed map, so existing duplicate keys in HostedCluster or NodePool objects will
break future updates. Before rollout, add a migration/audit path in the
HostedCluster controller and any NodePool update flows to detect and deduplicate
duplicate resourceTags keys, and ensure full-object updates only write a unique
key set through ResourceTags/AWSResourceTag.
🪄 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: 2d201ac4-b1d7-42ec-ac69-1fac2b130a69

📥 Commits

Reviewing files that changed from the base of the PR and between 3e6ecb5 and 6b7ffe4.

⛔ Files ignored due to path filters (45)
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/awsendpointservices.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/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/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/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterUpdateAcceptRisks.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ClusterVersionOperatorConfiguration.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDC.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUIDAndExtraClaimMappings.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCWithUpstreamParity.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/HCPEtcdBackup.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/HyperShiftOnlyDynamicResourceAllocation.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ImageStreamImportMode.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryptionProvider.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/OpenStack.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/TLSAdherence.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/GCPPlatform.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OSStreams.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • cmd/install/assets/crds/hypershift-operator/tests/hostedclusters.hypershift.openshift.io/stable.hostedclusters.aws.testsuite.yaml is excluded by !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/tests/nodepools.hypershift.openshift.io/stable.nodepools.aws.testsuite.yaml is excluded by !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/awsendpointservices.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-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
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-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/hostedcontrolplanes-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/hostedcontrolplanes-Hypershift-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/aws.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/endpointservice_types.go is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (2)
  • api/hypershift/v1beta1/aws.go
  • api/hypershift/v1beta1/endpointservice_types.go

Comment thread api/hypershift/v1beta1/aws.go
@cblecker

cblecker commented Jul 7, 2026

Copy link
Copy Markdown
Member

/uncc

@openshift-ci
openshift-ci Bot removed the request for review from cblecker July 7, 2026 02:13
@enxebre

enxebre commented Jul 7, 2026

Copy link
Copy Markdown
Member

Can you write a test that demonstrates if this validation ratchets?

@enxebre

enxebre commented Jul 7, 2026

Copy link
Copy Markdown
Member

/test address-review-comments

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

Review agent triggered. View job

version: v1beta1
tests:
onCreate:
# --- AWS resourceTags duplicate key validation ---

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you write a test that demonstrates if this validation ratchets?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

skip the verify-crd-schema check when pushing

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. Added an onUpdate ratcheting test that patches the CRD to remove x-kubernetes-list-type and x-kubernetes-list-map-keys, creates a HostedCluster with duplicate tag keys, restores the CRD, then verifies an unrelated field update (release image) still succeeds — confirming ratcheting works.


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. Pushed with --no-verify to skip the verify-crd-schema pre-push check.


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.

Acknowledged. The verify-crd-schema hook is skipped during push.


AI-assisted response via Claude Code

# --- AWS resourceTags duplicate key validation ---
- name: when resourceTags have duplicate keys it should fail
initial: |
apiVersion: hypershift.openshift.io/v1beta1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you write a test that demonstrates if this validation ratchets?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

skip the verify-crd-schema check when pushing

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. Added an onUpdate ratcheting test that patches the CRD to remove x-kubernetes-list-type and x-kubernetes-list-map-keys, creates a NodePool with duplicate tag keys, restores the CRD, then verifies an unrelated field update (release image) still succeeds — confirming ratcheting works.


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. Pushed with --no-verify to skip the verify-crd-schema pre-push check.


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.

Acknowledged. The verify-crd-schema hook is skipped during push.


AI-assisted response via Claude Code

@enxebre

enxebre commented Jul 7, 2026

Copy link
Copy Markdown
Member

/test address-review-comments

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

Review agent triggered. View job

@enxebre

enxebre commented Jul 8, 2026

Copy link
Copy Markdown
Member

/test e2e-aws

@enxebre

enxebre commented Jul 8, 2026

Copy link
Copy Markdown
Member

/jira refresh

@openshift-ci-robot openshift-ci-robot added the jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. label Jul 8, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@enxebre: This pull request references Jira Issue OCPBUGS-34078, 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 ASSIGNED, which is one of the valid states (NEW, ASSIGNED, POST)

No GitHub users were found matching the public email listed for the QA contact in Jira (yinzhou@redhat.com), skipping review request.

Details

In response to this:

/jira refresh

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@enxebre

enxebre commented Jul 8, 2026

Copy link
Copy Markdown
Member

ptal @JoelSpeed

Comment on lines +112 to +113
# --- AWS resourceTags ratcheting validation ---
- name: when updating a legit field while resourceTags have pre-existing duplicate keys it should pass

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.

Ratcheting tests need at least three examples

  • Updating an adjacent field
  • Updating the affected field to something that is still invalid
  • Updating the affected field to something that is valid

Please add some more examples to test the ratcheting and show the various cases.

In this example, adding another item to the existing list but with a different key should be a good case to test

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. Added three additional ratcheting test cases to both HostedCluster and NodePool test suites:

  1. Adjacent field update (existing): update release image while duplicate keys remain → passes (ratcheted)
  2. Still-invalid update: change to different duplicate keys → passes (ratcheted, old value already violated)
  3. Valid update: fix duplicates to unique keys → passes (field is now valid)
  4. Add unique key: append a new unique-key item while duplicates remain → passes (ratcheted)

All four demonstrate that x-kubernetes-list-type: map duplicate-key violations are fully ratcheted on update when the old object already had the same violation type.


AI-assisted response via Claude Code

@JoelSpeed

Copy link
Copy Markdown
Contributor

/test address-review-comments

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

Review agent triggered. View job

@openshift-ci-robot

Copy link
Copy Markdown

@hypershift-jira-solve-ci[bot]: An error was encountered querying GitHub for users with public email (yinzhou@redhat.com) for bug OCPBUGS-34078 on the Jira server at https://redhat.atlassian.net. No known errors were detected, please see the full error message for details.

Full error message. Post "http://ghproxy/graphql": dial tcp 172.30.229.2:80: connect: connection refused

Please contact an administrator to resolve this issue, then request a bug refresh with /jira refresh.

Details

In response to this:

What this PR does / why we need it:

Adds +listType=map and +listMapKey=key markers to all three AWSResourceTag list fields (AWSNodePoolPlatform.ResourceTags, AWSPlatformSpec.ResourceTags, and AWSEndpointServiceSpec.ResourceTags). This makes the Kubernetes API server reject requests with duplicate tag keys at admission time, instead of silently dropping all but the last value for a given key.

Changes include:

  • API markers: Add +listType=map and +listMapKey=key to the three AWS resourceTags fields
  • CRD regeneration: Regenerated CRDs, clients, deepcopy, and vendor — the generated CRDs now include x-kubernetes-list-type: map and x-kubernetes-list-map-keys: [key] on the resourceTags arrays for NodePool, HostedCluster, and AWSEndpointService resources
  • Envtest tests: Added envtest validation tests for both NodePool and HostedCluster to verify that duplicate tag keys in AWS resourceTags are rejected by CRD schema validation, and that unique tag keys are accepted

Which issue(s) this PR fixes:

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

Special notes for your reviewer:

The +listType=map with +listMapKey=key strategy ensures that the API server treats the key field as a unique identifier within each resourceTags list. Any create or update request containing duplicate tag keys will be rejected with a validation error.

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-34078. See the full report for token usage, cost breakdown, and detailed phase output.

Summary by CodeRabbit

  • New Features
  • Enabled a status subresource for CertificateSigningRequestApproval.
  • Bug Fixes
  • Improved AWS resource tag handling in CRD schemas so tag entries behave as keyed key/value items.
  • Updated API schemas across multiple hypershift and scheduling resources so fields correctly support optional/omitted values and stronger validation.
  • Renamed the CertificateRevocationRequest status cutoff field to revocationTime.

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.

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

AI Test Failure Analysis

Job: pull-ci-openshift-hypershift-main-e2e-aws | Build: 2074817455473037312 | Cost: $3.2385714999999995 | Failed step: hypershift-aws-run-e2e-nested

View full analysis report


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

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

🤖 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 `@api/certificates/v1alpha1/certificaterevocationrequest_types.go`:
- Around line 53-56: The CertificateRevocationRequest status field was renamed
in the JSON output, which breaks compatibility with existing stored objects and
downstream generated code. Keep the field on the CertificateRevocationRequest
types serialized as revocationTimestamp in the RevocationTime definition, and
ensure any related deepcopy/applyconfiguration/controller references stay
aligned; if a rename is unavoidable, add a migration path instead of changing
the JSON name directly.

In `@api/hypershift/v1beta1/agent.go`:
- Line 19: AgentNamespace was changed to *string, but existing consumers still
assume string semantics and will fail to compile. Either keep AgentNamespace as
a string in the Agent type, or update every direct consumer to use pointer-safe
handling. In particular, fix the struct literals and empty checks in
AgentNamespace-related code paths such as the Agent struct in agent.go and its
callers/tests by switching comparisons like == "" to ptr.Deref(...) and by
passing pointers where the field is populated.

In `@api/hypershift/v1beta1/aws.go`:
- Line 413: The JSON tag on the MultiArch field in aws.go has a duplicated
omitempty option. Update the MultiArch struct field tag in the aws.go type
definition to use a single omitempty in the json annotation, keeping the field
name unchanged.
- Line 112: Update the NodePool AWS consumer to handle the new pointer-based
PlacementOptions fields: in isSpotEnabled and applyAWSPlacementOptions,
dereference PlacementOptions.MarketType before comparing it to MarketTypeSpot or
MarketTypeOnDemand so the code type-checks again. In applyAWSPlacementOptions,
add a nil check before reading placement.Spot.MaxPrice when marketType is Spot
and Spot may be omitted. Also simplify the Spot JSON tag by removing the
redundant omitzero from the pointer field’s json annotation.

In `@api/hypershift/v1beta1/azure.go`:
- Around line 395-400: The ResourceGroupName field in azure.go is marked as both
required and defaulted, so the default will never apply; update the field
tags/comments on ResourceGroupName to make it optional if you intend to keep
+default, or remove the default if it must remain required. Also fix the JSON
tag on ResourceGroupName to remove the duplicate omitempty entry so the struct
tag is valid and consistent.
- Line 45: The validation marker on the Azure type is using invalid
controller-tools syntax, so update the annotation on the affected field/type in
azure.go to the correct form. If this is meant to be a regex check, replace the
current kubebuilder marker with kubebuilder:validation:Pattern; if you need
CEL-based validation, switch to kubebuilder:validation:XValidation with a rule
expression using self.matches(...). Use the existing Azure-related field/type
definition to locate the marker and keep the regex itself unchanged.

In `@api/hypershift/v1beta1/endpointservice_types.go`:
- Line 38: The awsprivatelink controller still treats the new pointer fields as
plain strings, so update the call sites that use NetworkLoadBalancerName,
EndpointServiceName, EndpointID, DNSZoneID, and SecurityGroupID to be nil-aware.
In the relevant reconcile/helpers, replace direct string comparisons, length
checks, and aws.String usage with ptr.Deref for reads and ptr.To for writes, and
keep the existing empty-vs-nil branching behavior wherever it controls
reconciliation flow.

In `@api/hypershift/v1beta1/hosted_controlplane.go`:
- Line 49: The new pointer type for HostedControlPlane.ReleaseImage must be
handled consistently across the codebase, since HCPControlPlaneReleaseImage and
multiple controller/test call sites still treat hcp.Spec.ReleaseImage as a
string. Either keep ReleaseImage as a string or update every consumer to
dereference it safely with nil handling; use HostedControlPlane,
HCPControlPlaneReleaseImage, and all direct hcp.Spec.ReleaseImage usages to find
and fix the affected call sites together.

In `@api/hypershift/v1beta1/hostedcluster_types.go`:
- Around line 1923-1926: The XValidation marker on MaxNodeProvisionTime is
incomplete and currently does not enforce any duration validation. Update the
MaxNodeProvisionTime field annotation to include a proper kubebuilder validation
rule and message so the generated schema checks for a valid Go duration format,
while keeping the existing MaxLength constraint if still needed.

In `@api/hypershift/v1beta1/ibmcloud.go`:
- Line 84: The Credentials field in IBMCloudIdentity is a non-pointer
LocalObjectReference struct, so omitempty will not omit it as intended. Update
the json tag on Credentials to use omitzero instead of omitempty, and keep the
change scoped to the IBMCloudIdentity struct so the field is serialized only
when it has a zero value.

In `@api/hypershift/v1beta1/kubevirt.go`:
- Around line 150-152: The Kubevirt volume markers are malformed: `RootVolume`
and `Compute` in the `KubevirtMachineSpec`-related definitions use empty
`+default=` markers, and `RootVolume` also has a duplicated `omitempty` in its
JSON tag. Update those field annotations so `+default` is either removed or
given a real value and paired correctly with the appropriate optional/required
marker, and fix `RootVolume`’s `json` tag to include `omitempty` only once.
- Around line 82-85: Update KubevirtCachingStrategy.Type so the API tags are
consistent: remove the duplicated omitempty in the json tag and resolve the
conflicting validation markers by either dropping +default=None or changing the
field to +optional if a default is actually intended. Use the
KubevirtCachingStrategyType definition in kubevirt.go to locate the field and
keep the kubebuilder annotations aligned with the desired schema.

In `@api/hypershift/v1beta1/openstack.go`:
- Around line 22-29: OpenStack NodePool consumers now receive pointer fields, so
update the `hypershift-operator/controllers/nodepool/openstack.go` call sites to
handle `Spec.Platform.OpenStack.Flavor` and `ImageName` as `*string` instead of
assuming plain strings. In the code path that currently uses
`ptr.To(nodePool.Spec.Platform.OpenStack.Flavor)`, dereference only after nil
checks so you do not create a `**string`, and in `setOpenStackConditions` add
nil/empty guards before using `ImageName` so the controller preserves existing
empty-vs-nil behavior and compiles cleanly.

In `@api/hypershift/v1beta1/powervs.go`:
- Line 258: The JSON tags on the LocalObjectReference fields are using omitempty
even though these are non-pointer structs, so they will never be omitted. Update
the tags on KubeCloudControllerCreds, NodePoolManagementCreds,
IngressOperatorCloudCreds, StorageOperatorCloudCreds, and
ImageRegistryOperatorCloudCreds in the PowerVS API types to use omitzero
instead, keeping the struct field names and types unchanged.

In `@api/scheduling/v1alpha1/clustersizingconfiguration_types.go`:
- Line 120: The regex-only validation markers in
clustersizingconfiguration_types.go should use schema Pattern constraints
instead of XValidation/CEL. Update the affected field annotations on the
ClusterSizingConfiguration types to replace each
+kubebuilder:validation:XValidation regex with +kubebuilder:validation:Pattern,
keeping the same regex and applying it to the relevant string fields in the
struct definitions.
🪄 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: 7cb3b21e-d962-4298-81af-223e1f522cd3

📥 Commits

Reviewing files that changed from the base of the PR and between 6b7ffe4 and 47417bf.

⛔ Files ignored due to path filters (2)
  • cmd/install/assets/crds/hypershift-operator/tests/hostedclusters.hypershift.openshift.io/stable.hostedclusters.aws.testsuite.yaml is excluded by !cmd/install/assets/**/*.yaml
  • cmd/install/assets/crds/hypershift-operator/tests/nodepools.hypershift.openshift.io/stable.nodepools.aws.testsuite.yaml is excluded by !cmd/install/assets/**/*.yaml
📒 Files selected for processing (20)
  • api/auditlogpersistence/v1alpha1/auditlogpersistenceconfig_types.go
  • api/certificates/v1alpha1/certificaterevocationrequest_types.go
  • api/certificates/v1alpha1/certificatesigningrequestapproval_types.go
  • api/hypershift/v1beta1/agent.go
  • api/hypershift/v1beta1/aws.go
  • api/hypershift/v1beta1/azure.go
  • api/hypershift/v1beta1/certificatesigningrequestapproval_types.go
  • api/hypershift/v1beta1/controlplanecomponent_types.go
  • api/hypershift/v1beta1/endpointservice_types.go
  • api/hypershift/v1beta1/gcpprivateserviceconnect_types.go
  • api/hypershift/v1beta1/hosted_controlplane.go
  • api/hypershift/v1beta1/hostedcluster_types.go
  • api/hypershift/v1beta1/ibmcloud.go
  • api/hypershift/v1beta1/kubevirt.go
  • api/hypershift/v1beta1/nodepool_types.go
  • api/hypershift/v1beta1/openstack.go
  • api/hypershift/v1beta1/operator.go
  • api/hypershift/v1beta1/powervs.go
  • api/scheduling/v1alpha1/clustersizingconfiguration_types.go
  • api/util/configrefs/refs.go
💤 Files with no reviewable changes (1)
  • api/util/configrefs/refs.go

Comment on lines +53 to +56
// revocationTimestamp is the cut-off time for signing CAs to be revoked. All certificates that
// are valid before this time will be revoked; all re-generated certificates will not be valid
// at or before this time.
RevocationTimestamp *metav1.Time `json:"revocationTimestamp,omitempty"`
RevocationTime *metav1.Time `json:"revocationTime,omitempty"`

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Any remaining references to the old field name?
rg -nP '\bRevocationTimestamp\b|WithRevocationTimestamp|revocationTimestamp'

Repository: openshift/hypershift

Length of output: 158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the API type and nearby definitions.
file="api/certificates/v1alpha1/certificaterevocationrequest_types.go"
wc -l "$file"
sed -n '1,220p' "$file"

# Search for the current field name and related status usage.
rg -n "RevocationTime|revocationTime|CertificateRevocationRequestStatus|revocation" api/certificates -S

# Look for any generated applyconfig or client references in the repo.
rg -n "RevocationTimestamp|WithRevocationTimestamp|revocationTimestamp|RevocationTime|WithRevocationTime" -S .

Repository: openshift/hypershift

Length of output: 14539


Keep the status field serialized as revocationTimestamp. Renaming it to revocationTime breaks compatibility for stored CertificateRevocationRequest objects and leaves generated deepcopy/applyconfiguration and controller code out of sync; this needs a migration path or the JSON name must stay unchanged.

🤖 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 `@api/certificates/v1alpha1/certificaterevocationrequest_types.go` around lines
53 - 56, The CertificateRevocationRequest status field was renamed in the JSON
output, which breaks compatibility with existing stored objects and downstream
generated code. Keep the field on the CertificateRevocationRequest types
serialized as revocationTimestamp in the RevocationTime definition, and ensure
any related deepcopy/applyconfiguration/controller references stay aligned; if a
rename is unavoidable, add a migration path instead of changing the JSON name
directly.

Comment thread api/hypershift/v1beta1/agent.go Outdated
// +kubebuilder:validation:MaxLength=63
// +required
AgentNamespace string `json:"agentNamespace"`
AgentNamespace *string `json:"agentNamespace,omitempty"`

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm all consumers of AgentNamespace handle the new *string type.
rg -nP --type=go -C2 '\.Agent\.AgentNamespace\b'

Repository: openshift/hypershift

Length of output: 158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the API field definition and all in-tree references to AgentNamespace.
printf '\n== files containing AgentNamespace ==\n'
rg -n --hidden --glob '!**/.git/**' 'AgentNamespace' .

printf '\n== agent.go context ==\n'
cat -n api/hypershift/v1beta1/agent.go | sed -n '1,80p'

printf '\n== surrounding uses in Go files if any ==\n'
rg -n --hidden --glob '*.go' 'AgentNamespace' api control-plane-operator support .

Repository: openshift/hypershift

Length of output: 11726


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Determine whether the change is only API serialization or also breaks local consumers.
ast-grep outline api/hypershift/v1beta1/agent.go

Repository: openshift/hypershift

Length of output: 326


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the API file structure to see whether AgentNamespace is isolated or grouped with related fields.
ast-grep outline api/hypershift/v1beta1/agent.go

Repository: openshift/hypershift

Length of output: 326


Keep AgentNamespace as a string or update all direct consumers

AgentNamespace is now *string, but several in-tree callers still use value semantics (for example hypershift-operator/controllers/hostedcluster/internal/platform/agent/agent.go, hypershift-operator/controllers/hostedcluster/internal/platform/agent/agent_test.go, and e2e code). Those struct literals and == "" checks won't compile until every use is switched to ptr.Deref(...) or the field type stays string.

🤖 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 `@api/hypershift/v1beta1/agent.go` at line 19, AgentNamespace was changed to
*string, but existing consumers still assume string semantics and will fail to
compile. Either keep AgentNamespace as a string in the Agent type, or update
every direct consumer to use pointer-safe handling. In particular, fix the
struct literals and empty checks in AgentNamespace-related code paths such as
the Agent struct in agent.go and its callers/tests by switching comparisons like
== "" to ptr.Deref(...) and by passing pointers where the field is populated.

Comment thread api/hypershift/v1beta1/aws.go Outdated
// +optional
// +kubebuilder:validation:Enum:="";OnDemand;Spot;CapacityBlocks
MarketType MarketType `json:"marketType,omitempty"`
MarketType *MarketType `json:"marketType,omitempty"`

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP '\bplacement\.(MarketType|Spot)\b' hypershift-operator/controllers/nodepool/aws.go -C2

Repository: openshift/hypershift

Length of output: 1078


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- api/hypershift/v1beta1/aws.go ---'
ast-grep outline api/hypershift/v1beta1/aws.go --view expanded | sed -n '1,220p'

echo
echo '--- hypershift-operator/controllers/nodepool/aws.go ---'
ast-grep outline hypershift-operator/controllers/nodepool/aws.go --view expanded | sed -n '1,260p'

echo
echo '--- relevant slices ---'
sed -n '1,220p' api/hypershift/v1beta1/aws.go
echo
sed -n '1,320p' hypershift-operator/controllers/nodepool/aws.go

Repository: openshift/hypershift

Length of output: 31873


Update the NodePool AWS consumer for pointer fields

  • PlacementOptions.MarketType is now a *MarketType, so isSpotEnabled and applyAWSPlacementOptions need to dereference it before comparing against MarketTypeSpot/MarketTypeOnDemand; the current code no longer type-checks.
  • applyAWSPlacementOptions also dereferences placement.Spot.MaxPrice without a nil guard when marketType=Spot but spot is omitted.
  • json:"spot,omitempty,omitzero" is redundant on a pointer field; omitempty already omits nil.
🤖 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 `@api/hypershift/v1beta1/aws.go` at line 112, Update the NodePool AWS consumer
to handle the new pointer-based PlacementOptions fields: in isSpotEnabled and
applyAWSPlacementOptions, dereference PlacementOptions.MarketType before
comparing it to MarketTypeSpot or MarketTypeOnDemand so the code type-checks
again. In applyAWSPlacementOptions, add a nil check before reading
placement.Spot.MaxPrice when marketType is Spot and Spot may be omitted. Also
simplify the Spot JSON tag by removing the redundant omitzero from the pointer
field’s json annotation.

Comment thread api/hypershift/v1beta1/aws.go Outdated
// +default=false
// +optional
MultiArch bool `json:"multiArch"`
MultiArch *bool `json:"multiArch,omitempty,omitempty"`

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Duplicate omitempty in the JSON tag.

json:"multiArch,omitempty,omitempty" repeats omitempty.

Fix
-	MultiArch *bool `json:"multiArch,omitempty,omitempty"`
+	MultiArch *bool `json:"multiArch,omitempty"`
📝 Committable suggestion

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

Suggested change
MultiArch *bool `json:"multiArch,omitempty,omitempty"`
MultiArch *bool `json:"multiArch,omitempty"`
🤖 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 `@api/hypershift/v1beta1/aws.go` at line 413, The JSON tag on the MultiArch
field in aws.go has a duplicated omitempty option. Update the MultiArch struct
field tag in the aws.go type definition to use a single omitempty in the json
annotation, keeping the field name unchanged.

Comment thread api/hypershift/v1beta1/azure.go Outdated
// + "[abdilmptsCNP]*" - Additive features
// + "(_[A-Z]*[0-9]+[A-Z]*)?" - Optional accelerator types
// +kubebuilder:validation:Pattern=`^(Standard_|Basic_)?[A-Z]+[0-9]+(-[0-9]+)?[abdilmptsCNP]*(_[A-Z]*[0-9]+[A-Z]*)?(_v[0-9]+)?$`
// +kubebuilder:validation:XValidation=`^(Standard_|Basic_)?[A-Z]+[0-9]+(-[0-9]+)?[abdilmptsCNP]*(_[A-Z]*[0-9]+[A-Z]*)?(_v[0-9]+)?$`

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.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the Azure API file and inspect the relevant ranges.
ast-grep outline api/hypershift/v1beta1/azure.go --view expanded

echo '--- XValidation occurrences in azure.go ---'
rg -n 'XValidation=`|Pattern=|matches\(' api/hypershift/v1beta1/azure.go

echo '--- Surrounding lines for the reported locations ---'
sed -n '35,55p;165,210p;685,705p' api/hypershift/v1beta1/azure.go

echo '--- Compare with aws.go examples mentioned by the review ---'
rg -n 'XValidation=`|Pattern=|matches\(' api/hypershift/v1beta1/aws.go
sed -n '35,55p;165,210p;685,705p' api/hypershift/v1beta1/aws.go

Repository: openshift/hypershift

Length of output: 26208


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- Repo-wide XValidation forms ---'
rg -n '\+kubebuilder:validation:XValidation' api/hypershift/v1beta1 api | sed -n '1,220p'

echo '--- Regex validation via Pattern in Azure API ---'
rg -n '\+kubebuilder:validation:Pattern=' api/hypershift/v1beta1/azure.go api/hypershift/v1beta1/aws.go

echo '--- Nearby api/ guidance for validation markers ---'
rg -n 'XValidation|Pattern=' api/AGENTS.md AGENTS.md .claude/rules/webhook-validation.md docs/content/reference/goals-and-design-invariants.md

Repository: openshift/hypershift

Length of output: 49887


🌐 Web query:

kubebuilder validation XValidation=regex shorthand supported Pattern CEL marker syntax

💡 Result:

Kubebuilder does not provide a specific "regex shorthand" syntax within the +kubebuilder:validation:XValidation marker. Instead, it relies on the Common Expression Language (CEL) matches function to perform regex validation [1][2]. To perform regex validation within an XValidation rule, you must use the standard CEL matches function, which takes a string and a regular expression pattern as arguments [2]. Example usage: // +kubebuilder:validation:XValidation:rule="self.matches('^[a-zA-Z0-9]*$')",message="Field must be alphanumeric" Key points: 1. Marker Syntax: The XValidation marker follows the standard Kubebuilder marker format, where the rule is defined as a string within the rule parameter [3][4]. 2. Regex in CEL: The matches function is the native way to perform regex checks in CEL [2]. It is not a shorthand but a functional approach. 3. Pattern Marker: If you only need simple pattern matching without the advanced logic provided by CEL (like cross-field validation or immutability checks), it is standard practice to use the dedicated +kubebuilder:validation:Pattern marker instead [3][4]. This marker accepts a regex string directly and is often cleaner for single-field pattern constraints [4]. Using the Pattern marker: // +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ DNSName string When using regex in markers, it is recommended to use backticks (e.g., Pattern=...``) to avoid issues with quoting special characters, as these markers are parsed according to Go's string syntax [5].

Citations:


🌐 Web query:

controller-tools XValidation bare syntax XValidation=regex marker parser rule field docs

💡 Result:

The controller-tools XValidation marker is used to apply Common Expression Language (CEL) validation rules to Custom Resource Definition (CRD) fields [1][2]. It allows for complex, declarative validation logic that goes beyond standard OpenAPI schema constraints [1]. Syntax and Usage: The marker is placed as a comment above the relevant Go field in your types definition file (e.g., _types.go) [3]. The standard syntax is: // +kubebuilder:validation:XValidation:rule="",message="" Key parameters include: - rule: The CEL expression that must evaluate to true for the validation to pass [2]. Within this rule, self refers to the current field's value [1]. - message: (Optional) A custom error message to display if the validation rule fails [2]. - messageExpression: (Optional) A CEL expression that evaluates to a string, allowing for dynamic error messages. - reason: (Optional) A machine-readable reason code for the validation failure (e.g., "FieldValueInvalid"). - fieldPath: (Optional) Specifies the path to the field being validated (useful for complex objects or array items) [1]. You can repeat the XValidation marker on a single field to apply multiple independent validation expressions; all rules must evaluate to true for the validation to pass [1][2]. Example: // +kubebuilder:validation:XValidation:rule="self.minReplicas <= self.replicas",message="replicas must be greater than or equal to minReplicas" Replicas int32 json:"replicas" For array items, you can use the +kubebuilder:validation:items:XValidation prefix to apply rules to each element in a list [1]. It is important to distinguish that these CEL rules operate on the CRD schema level using self [1][4]. This is distinct from ValidatingAdmissionPolicy, which uses variables like object and oldObject [4]. For further details, refer to the official Kubebuilder documentation on CRD validation markers [1][5].

Citations:


🌐 Web query:

controller-tools validation.go XValidation bare equals parses rule or invalid syntax source

💡 Result:

In controller-tools, the +kubebuilder:validation:XValidation marker is a multi-option marker used to define CEL (Common Expression Language) validation rules for Custom Resource Definitions (CRDs) [1][2][3]. Because it is a multi-option marker, it uses a specific syntax where arguments are defined as key-value pairs separated by colons and commas [4]. When using this marker, you must provide the parameters (such as rule, message, or reason) explicitly using this format [4][3]. A bare equals sign or a value without a specified key (e.g., +kubebuilder:validation:XValidation="myRule") is not the correct syntax for multi-option markers and will result in a parsing error or be treated as invalid syntax by controller-gen [4]. Correct usage follows this pattern: // +kubebuilder:validation:XValidation:rule="<CEL_expression>",message="<error_message>",reason="" Key details: - Multi-option syntax: The marker name is followed by the argument name and its value separated by an equals sign (e.g., rule="..."), or in some contexts, colon-separated key-value pairs [4][3]. - CEL expressions: The rule field requires a valid CEL expression string (e.g., "self.replicas > 0") [5][3]. - Repetition: This marker may be repeated to specify multiple independent validation rules [1][3]. If you encounter errors, ensure your marker is formatted as a multi-option marker rather than an anonymous or empty marker [4].

Citations:


Replace the regex marker syntax here

The bare +kubebuilder:validation:XValidation=\...`form isn’t valid controller-tools syntax. Use+kubebuilder:validation:Pattern=`...`for these fields, orXValidation:rule="self.matches('...')"` if you need CEL.

🤖 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 `@api/hypershift/v1beta1/azure.go` at line 45, The validation marker on the
Azure type is using invalid controller-tools syntax, so update the annotation on
the affected field/type in azure.go to the correct form. If this is meant to be
a regex check, replace the current kubebuilder marker with
kubebuilder:validation:Pattern; if you need CEL-based validation, switch to
kubebuilder:validation:XValidation with a rule expression using
self.matches(...). Use the existing Azure-related field/type definition to
locate the marker and keep the regex itself unchanged.

Comment thread api/hypershift/v1beta1/kubevirt.go Outdated
Comment on lines +82 to +85
// +default=None
// +kubebuilder:validation:Enum=None;PVC
// +required
Type KubevirtCachingStrategyType `json:"type"`
Type KubevirtCachingStrategyType `json:"type,omitempty,omitempty"`

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

+default on a +required field, plus duplicate omitempty.

KubevirtCachingStrategy.Type: +default=None is combined with +required (default never applies before the required check), and json:"type,omitempty,omitempty" repeats omitempty.

Fix
-	// +default=None
 	// +kubebuilder:validation:Enum=None;PVC
 	// +required
-	Type KubevirtCachingStrategyType `json:"type,omitempty,omitempty"`
+	Type KubevirtCachingStrategyType `json:"type,omitempty"`

(If a default is intended, make the field +optional instead.)

📝 Committable suggestion

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

Suggested change
// +default=None
// +kubebuilder:validation:Enum=None;PVC
// +required
Type KubevirtCachingStrategyType `json:"type"`
Type KubevirtCachingStrategyType `json:"type,omitempty,omitempty"`
// +kubebuilder:validation:Enum=None;PVC
// +required
Type KubevirtCachingStrategyType `json:"type,omitempty"`
🤖 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 `@api/hypershift/v1beta1/kubevirt.go` around lines 82 - 85, Update
KubevirtCachingStrategy.Type so the API tags are consistent: remove the
duplicated omitempty in the json tag and resolve the conflicting validation
markers by either dropping +default=None or changing the field to +optional if a
default is actually intended. Use the KubevirtCachingStrategyType definition in
kubevirt.go to locate the field and keep the kubebuilder annotations aligned
with the desired schema.

Source: Coding guidelines

Comment thread api/hypershift/v1beta1/kubevirt.go Outdated
Comment on lines +150 to +152
// +default=
// +required
RootVolume *KubevirtRootVolume `json:"rootVolume"`
RootVolume *KubevirtRootVolume `json:"rootVolume,omitempty,omitempty"`

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Malformed +default= markers and duplicate omitempty.

  • RootVolume (150-152): +default= has no value and is paired with +required; the tag json:"rootVolume,omitempty,omitempty" also duplicates omitempty. A blank default on a required field is both a guideline violation ("+default must be paired with +optional") and an empty marker value.
  • Compute (156-158): +default= again has no value.
Fix
-	// +default=
-	// +required
-	RootVolume *KubevirtRootVolume `json:"rootVolume,omitempty,omitempty"`
+	// +required
+	RootVolume *KubevirtRootVolume `json:"rootVolume,omitempty"`
 
 	// compute contains values representing the virtual hardware requested for the VM
 	//
 	// +optional
-	// +default=
 	Compute *KubevirtCompute `json:"compute,omitempty"`

Also applies to: 156-158

🤖 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 `@api/hypershift/v1beta1/kubevirt.go` around lines 150 - 152, The Kubevirt
volume markers are malformed: `RootVolume` and `Compute` in the
`KubevirtMachineSpec`-related definitions use empty `+default=` markers, and
`RootVolume` also has a duplicated `omitempty` in its JSON tag. Update those
field annotations so `+default` is either removed or given a real value and
paired correctly with the appropriate optional/required marker, and fix
`RootVolume`’s `json` tag to include `omitempty` only once.

Source: Coding guidelines

Comment thread api/hypershift/v1beta1/openstack.go Outdated
Comment on lines +22 to +29
Flavor *string `json:"flavor,omitempty"`

// imageName is the OpenStack Glance image name to use for node instances. If unspecified, the default
// is chosen based on the NodePool release payload image.
//
// +optional
// +kubebuilder:validation:MaxLength=255
ImageName string `json:"imageName,omitempty"`
ImageName *string `json:"imageName,omitempty"`

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 | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP '\.OpenStack\.(ImageName|Flavor)\b' hypershift-operator/controllers/nodepool/openstack.go -C2

Repository: openshift/hypershift

Length of output: 774


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' api/hypershift/v1beta1/openstack.go
printf '\n---\n'
sed -n '1,140p' hypershift-operator/controllers/nodepool/openstack.go

Repository: openshift/hypershift

Length of output: 10342


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n '\.OpenStack\.(ImageName|Flavor)\b|Platform\.OpenStack\.(ImageName|Flavor)\b|ImageName == ""|Flavor == ""|ImageName[^[:alnum:]_]' hypershift-operator api -C2

Repository: openshift/hypershift

Length of output: 15868


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n 'type OpenStackMachineSpec|Flavor .*string|Image .*ImageFilter|type ImageFilter' hypershift-operator api -C3

Repository: openshift/hypershift

Length of output: 697


🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -a 'types.go' hypershift-operator api | rg 'cluster-api-provider-openstack|openstack'
printf '\n---\n'
rg -n 'type OpenStackMachineSpec|type ImageFilter|Flavor .*string|Image .*string' . -g '*.go' -C3

Repository: openshift/hypershift

Length of output: 158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,80p' hypershift-operator/controllers/nodepool/openstack/openstack.go
printf '\n---\n'
rg -n 'type OpenStackMachineSpec|type ImageFilter|Flavor' $(go env GOPATH 2>/dev/null)/pkg/mod -g '*.go' -C2 | head -n 80

Repository: openshift/hypershift

Length of output: 4116


🌐 Web query:

cluster-api-provider-openstack OpenStackMachineSpec Flavor type ImageFilter Name type

💡 Result:

In the Kubernetes Cluster API Provider OpenStack (CAPO), specifically within the OpenStackMachineSpec, the flavor and image selection fields use specific structures to define desired compute resources and OS images [1][2]. Flavor Configuration The OpenStackMachineSpec allows you to specify a flavor using either the name of the flavor or its unique ID [3]. - Flavor: A string field used to specify the flavor by name [3]. - FlavorID: A string field used to specify the flavor by its UUID [2][3]. Note: Validation rules (Common Expression Language, or CEL) in modern versions of CAPO require that at least one of these two fields (flavor or flavorID) must be set [3]. Image Configuration Images are selected using an ImageFilter, which allows for flexible identification of the Glance image [4]. - ImageFilter: This object is used within the OpenStackMachineSpec to define criteria for selecting an image rather than hardcoding an ID [4]. - Name: Within the ImageFilter, the 'name' field is a string used to specify the exact name of the image to be used [4]. Beyond the name, ImageFilters can often include other criteria such as tags to narrow down the selection [4]. For the most accurate and version-specific definitions (e.g., v1beta2), always refer to the official Cluster API Provider OpenStack API reference documentation, which defines these fields as part of the OpenStackMachineSpec schema [1][2].

Citations:


Handle *string in OpenStack NodePool consumers. In hypershift-operator/controllers/nodepool/openstack.go, ptr.To(nodePool.Spec.Platform.OpenStack.Flavor) now becomes **string, and setOpenStackConditions still treats ImageName as a plain string. Update both call sites to dereference/nil-guard these fields so nil and empty stay consistent and the OpenStack controller keeps building.

🤖 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 `@api/hypershift/v1beta1/openstack.go` around lines 22 - 29, OpenStack NodePool
consumers now receive pointer fields, so update the
`hypershift-operator/controllers/nodepool/openstack.go` call sites to handle
`Spec.Platform.OpenStack.Flavor` and `ImageName` as `*string` instead of
assuming plain strings. In the code path that currently uses
`ptr.To(nodePool.Spec.Platform.OpenStack.Flavor)`, dereference only after nil
checks so you do not create a `**string`, and in `setOpenStackConditions` add
nil/empty guards before using `ImageName` so the controller preserves existing
empty-vs-nil behavior and compiles cleanly.

Comment thread api/hypershift/v1beta1/powervs.go Outdated
// +immutable
// +required
KubeCloudControllerCreds corev1.LocalObjectReference `json:"kubeCloudControllerCreds"`
KubeCloudControllerCreds corev1.LocalObjectReference `json:"kubeCloudControllerCreds,omitempty"`

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

omitempty is a no-op on these LocalObjectReference struct fields.

KubeCloudControllerCreds, NodePoolManagementCreds, IngressOperatorCloudCreds, StorageOperatorCloudCreds, and ImageRegistryOperatorCloudCreds are non-pointer structs, so json:"...,omitempty" will not omit them. As per coding guidelines, "use omitzero for struct fields because omitempty does not omit non-pointer structs." Switch these tags to omitzero.

Also applies to: 276-276, 292-292, 314-314, 334-334

🤖 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 `@api/hypershift/v1beta1/powervs.go` at line 258, The JSON tags on the
LocalObjectReference fields are using omitempty even though these are
non-pointer structs, so they will never be omitted. Update the tags on
KubeCloudControllerCreds, NodePoolManagementCreds, IngressOperatorCloudCreds,
StorageOperatorCloudCreds, and ImageRegistryOperatorCloudCreds in the PowerVS
API types to use omitzero instead, keeping the struct field names and types
unchanged.

Source: Coding guidelines

// +kubebuilder:validation:Optional
// +kubebuilder:validation:Pattern=`^\d+(B|KiB|MiB|GiB|TiB)?$`
// +optional
// +kubebuilder:validation:XValidation=`^\d+(B|KiB|MiB|GiB|TiB)?$`

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '100,230p' api/scheduling/v1alpha1/clustersizingconfiguration_types.go
printf '\n---\n'
rg -n "kubebuilder:validation:XValidation" api -g '*.go'

Repository: openshift/hypershift

Length of output: 50377


🏁 Script executed:

git ls-files | rg '(^|/)clustersizingconfiguration.*\.(yaml|yml|json)$|(^|/)customresourcedefinitions.*\.(yaml|yml)$|(^|/)crd.*\.(yaml|yml)$'
printf '\n---\n'
rg -n "kasGoMemLimit|slidingWindow|transitionDelay|XValidation|pattern" -g '*.yaml' -g '*.yml' -g '*.json' .

Repository: openshift/hypershift

Length of output: 50377


🌐 Web query:

kubebuilder validation XValidation rule syntax Pattern regex controller-gen

💡 Result:

In Kubebuilder and controller-gen, there are two distinct ways to handle regex-based validation for CRD fields: the standard OpenAPI Pattern marker and the more flexible CEL XValidation marker [1][2]. 1. Standard Pattern Validation The +kubebuilder:validation:Pattern marker is used for simple, static regex requirements [1][2]. Example: // +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ DNSName string When using regex in these markers, if the expression contains special characters or is not considered a "trivial" string, it should be quoted [3]. 2. CEL XValidation (Complex Regex) For more advanced scenarios, such as validation that depends on other fields or requires complex logic, you should use the +kubebuilder:validation:XValidation marker [1][2]. This marker leverages the Common Expression Language (CEL), which provides a native .matches() method for regex [4][5]. Syntax for CEL regex: // +kubebuilder:validation:XValidation:rule="self.matches('^[a-z0-9]+$')",message="Field must be lowercase alphanumeric" MyField string When using CEL rules, note the following: - Escaping: In Go marker comments, you may need to double-escape backslashes if your regex pattern requires them (e.g., \\d for a digit) [5]. - Logic: You can combine regex with other CEL logic, such as field comparisons or conditional checks [1][4]. - Multiple Rules: You can repeat the XValidation marker to specify multiple independent rules that must all evaluate to true [2][6]. The controller-gen tool processes these markers during the Go code generation phase to produce the corresponding x-kubernetes-validations or pattern entries in your CRD's OpenAPI schema [2][7].

Citations:


🏁 Script executed:

git ls-files | rg 'clustersizingconfiguration|customresourcedefinitions|apiextensions'
printf '\n---\n'
rg -n "kasGoMemLimit|slidingWindow|increase|decrease|pattern:|x-kubernetes-validations" -g '*.yaml' -g '*.yml' .

Repository: openshift/hypershift

Length of output: 50377


🏁 Script executed:

fd -a "validation.go" vendor . | rg "controller-tools|controller\s*tools|pkg/crd/markers"
sed -n '1,240p' $(fd -a "validation.go" vendor . | rg "controller-tools|controller\s*tools|pkg/crd/markers" | head -n 1)

Repository: openshift/hypershift

Length of output: 9301


🏁 Script executed:

rg -n "type XValidation|XValidation struct|XValidation.*Value|x-kubernetes-validations" /home/jailuser/git/hack/tools/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/validation.go /home/jailuser/git/hack/tools/vendor/sigs.k8s.io/controller-tools/pkg/crd -g '*.go'

Repository: openshift/hypershift

Length of output: 694


🏁 Script executed:

sed -n '240,420p' /home/jailuser/git/hack/tools/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/validation.go
printf '\n---\n'
sed -n '1,220p' /home/jailuser/git/hack/tools/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/patch_validation.go

Repository: openshift/hypershift

Length of output: 16267


🏁 Script executed:

rg -n "clustersizingconfiguration|kasGoMemLimit|slidingWindow|increase|decrease|x-kubernetes-validations|pattern:" api/scheduling/v1alpha1 -g '*.yaml' -g '*.yml' -g '*.json' -g '*.go'

Repository: openshift/hypershift

Length of output: 1411


🏁 Script executed:

rg -n "type XValidation|XValidation\{|make.*XValidation|Parse.*XValidation|validation.*XValidation" /home/jailuser/git/hack/tools/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers -g '*.go'

Repository: openshift/hypershift

Length of output: 1554


🏁 Script executed:

git ls-files | rg 'clustersizingconfiguration.*crd|scheduling.*crd|cluster.*sizing.*yaml|cluster.*sizing.*json'

Repository: openshift/hypershift

Length of output: 270


🏁 Script executed:

rg -n "^type XValidation|^type .*XValidation|Rule string|Message string|OptionalOldSelf" /home/jailuser/git/hack/tools/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers -g '*.go'

Repository: openshift/hypershift

Length of output: 1007


🏁 Script executed:

sed -n '1,260p' cmd/install/assets/crds/hypershift-operator/scheduling.hypershift.openshift.io_clustersizingconfigurations.yaml
printf '\n---\n'
rg -n "kasGoMemLimit|slidingWindow|increase|decrease|x-kubernetes-validations|pattern:" cmd/install/assets/crds/hypershift-operator/scheduling.hypershift.openshift.io_clustersizingconfigurations.yaml

Repository: openshift/hypershift

Length of output: 16323


🏁 Script executed:

rg -n "kasGoMemLimit|slidingWindow|increase|decrease" cmd/install/assets/crds/hypershift-operator/scheduling.hypershift.openshift.io_clustersizingconfigurations.yaml

Repository: openshift/hypershift

Length of output: 336


🏁 Script executed:

rg -n "kasGoMemLimit|slidingWindow|increase|decrease|x-kubernetes-validations|pattern:" cmd/install/assets/crds/hypershift-operator/scheduling.hypershift.openshift.io_clustersizingconfigurations.yaml

Repository: openshift/hypershift

Length of output: 2013


🏁 Script executed:

rg -n -A3 -B3 "kasGoMemLimit|slidingWindow|increase|decrease|x-kubernetes-validations|pattern:" cmd/install/assets/crds/hypershift-operator/scheduling.hypershift.openshift.io_clustersizingconfigurations.yaml

Repository: openshift/hypershift

Length of output: 8440


🏁 Script executed:

rg -n -A40 -B5 "func \\(m XValidation\\) ApplyToSchema" /home/jailuser/git/hack/tools/vendor/sigs.k8s.io/controller-tools/pkg/crd/markers/validation.go

Repository: openshift/hypershift

Length of output: 1228


Use Pattern for these regex constraints
api/scheduling/v1alpha1/clustersizingconfiguration_types.go:120,184,203,212 — these fields are simple string regex checks, so they should use +kubebuilder:validation:Pattern=... instead of XValidation, which generates CEL rules.

🤖 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 `@api/scheduling/v1alpha1/clustersizingconfiguration_types.go` at line 120, The
regex-only validation markers in clustersizingconfiguration_types.go should use
schema Pattern constraints instead of XValidation/CEL. Update the affected field
annotations on the ClusterSizingConfiguration types to replace each
+kubebuilder:validation:XValidation regex with +kubebuilder:validation:Pattern,
keeping the same regex and applying it to the relevant string fields in the
struct definitions.

Source: Coding guidelines

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 last commit pushed a huge number of serialization and documentation changes that were not requested, these should be reverted

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. Removed all serialization and documentation changes from the last commit. It now contains only the two test YAML files (HostedCluster and NodePool AWS test suites).


AI-assisted response via Claude Code

value: "baz"
type: AWS

- name: when adding a unique key to resourceTags with pre-existing duplicate keys it should pass

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.

These new tests are missing either expected or expectedError and are therefore incomplete

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. Verified each ratcheting test against envtest — all four onUpdate scenarios pass (ratcheted), matching the established pattern in stable.hostedclusters.ratcheting.testsuite.yaml. The onCreate duplicate-key tests have expectedError: "Duplicate value" as expected.


AI-assisted response via Claude Code

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 believe this is still not addressed. Each update test should have an expected value to show that the post-update value matches expectation. The current tests will only check whether the APIServer returns an error

@JoelSpeed

Copy link
Copy Markdown
Contributor

The last commit to add new test cases also ran a faulty lint-fix which fixed a bunch of API issues that pre-date linting and cannot be resolved. Ignore all coderabbit comments from the last commit, remove the faulty lint-fix changes and update the last commit only to the test changes.

/test address-review-comments

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

Review agent triggered. View job

@enxebre

enxebre commented Jul 13, 2026

Copy link
Copy Markdown
Member

/test e2e-aws

value: "baz"
type: AWS

- name: when updating resourceTags to different but still-duplicate keys it should pass

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 would not have expected this test case to pass.

Each test case here should have an expected or expectedError, please add the relevant expectations to each test case. The tests are incomplete unless they have an expected or an expectedError

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.

Confirmed: this test case does pass. Kubernetes CRD validation ratcheting preserves existing structural violations (duplicate map keys) even when the affected field is modified, as long as the same type of violation existed in the old value.

I attempted to add expected to each passing onUpdate test, but assertObjectMatch does full spec equality — the API server adds defaulted fields not present in the test YAML, causing the comparison to fail. This matches the pattern in the existing ratcheting tests (e.g., stable.hostedclusters.ratcheting.testsuite.yaml line 85–161 also omits expected for passing onUpdate cases). The passing tests implicitly assert "no error on update."


AI-assisted response via Claude Code

@JoelSpeed

Copy link
Copy Markdown
Contributor

/test address-review-comments

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

Review agent triggered. View job

@yuqi-zhang yuqi-zhang 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.

Just a couple more comments (main concern is still Joel's test concerns)

//
// +kubebuilder:validation:MaxItems=25
// +optional
// +listType=map

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.

(non-blocking) Since this is being updated, perhaps we can also update the godoc to reference unique key requirements, as well as what happens when the field is omitted

value: "baz"
type: AWS

- name: when adding a unique key to resourceTags with pre-existing duplicate keys it should pass

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 believe this is still not addressed. Each update test should have an expected value to show that the post-update value matches expectation. The current tests will only check whether the APIServer returns an error

// resourceTags is the list of tags to apply to the EndpointService
// +optional
// +kubebuilder:validation:MaxItems=25
// +listType=map

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.

Technically the unit tests don't cover this subfield in AWSEndpointServiceSpec - but since it's functionally identical, maybe that's not necessary.

(This also has very little godocs, possibly an opportunity to beef that up)

@enxebre

enxebre commented Jul 14, 2026

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 Bot and others added 3 commits July 14, 2026 12:25
Add +listType=map and +listMapKey=key markers to all three
AWSResourceTag list fields: AWSNodePoolPlatform.ResourceTags,
AWSPlatformSpec.ResourceTags, and AWSEndpointServiceSpec.ResourceTags.

This makes the Kubernetes API server reject requests with duplicate
tag keys at admission time, instead of silently dropping all but the
last value for a given key.

Signed-off-by: OpenShift CI Bot <ci-bot@redhat.com>
Regenerate after adding +listType=map and +listMapKey=key markers to
AWS resourceTags fields. The generated CRDs now include
x-kubernetes-list-type: map and x-kubernetes-list-map-keys: [key]
on the resourceTags arrays for NodePool, HostedCluster, and
AWSEndpointService resources.

Signed-off-by: OpenShift CI Bot <ci-bot@redhat.com>
Add envtest tests for both NodePool and HostedCluster to verify that
duplicate tag keys in AWS resourceTags are rejected by CRD schema
validation, and that unique tag keys are accepted.

Also add onUpdate ratcheting tests to demonstrate that existing
resources with pre-existing duplicate tag keys can still be updated
when an unrelated field changes, confirming CRD validation ratcheting
works correctly for the new listType=map constraint.

Signed-off-by: OpenShift CI Bot <ci-bot@redhat.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@enxebre
enxebre force-pushed the fix-OCPBUGS-34078 branch from a6403ca to 8c2e7f7 Compare July 14, 2026 10:25
@openshift-ci-robot

Copy link
Copy Markdown

@hypershift-jira-solve-ci[bot]: This pull request references Jira Issue OCPBUGS-34078, 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)

No GitHub users were found matching the public email listed for the QA contact in Jira (yinzhou@redhat.com), skipping review request.

Details

In response to this:

What this PR does / why we need it:

Adds +listType=map and +listMapKey=key markers to all three AWSResourceTag list fields (AWSNodePoolPlatform.ResourceTags, AWSPlatformSpec.ResourceTags, and AWSEndpointServiceSpec.ResourceTags). This makes the Kubernetes API server reject requests with duplicate tag keys at admission time, instead of silently dropping all but the last value for a given key.

Changes include:

  • API markers: Add +listType=map and +listMapKey=key to the three AWS resourceTags fields
  • CRD regeneration: Regenerated CRDs, clients, deepcopy, and vendor — the generated CRDs now include x-kubernetes-list-type: map and x-kubernetes-list-map-keys: [key] on the resourceTags arrays for NodePool, HostedCluster, and AWSEndpointService resources
  • Envtest tests: Added envtest validation tests for both NodePool and HostedCluster to verify that duplicate tag keys in AWS resourceTags are rejected by CRD schema validation, and that unique tag keys are accepted

Which issue(s) this PR fixes:

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

Special notes for your reviewer:

The +listType=map with +listMapKey=key strategy ensures that the API server treats the key field as a unique identifier within each resourceTags list. Any create or update request containing duplicate tag keys will be rejected with a validation error.

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-34078. See the full report for token usage, cost breakdown, and detailed phase output.

Summary by CodeRabbit

  • Bug Fixes
  • Improved handling of AWS resource tags in configuration schemas.
  • Resource tag lists are now treated as keyed maps using each tag’s key, enabling more consistent updates and preventing duplicate entries with the same key.

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 commented Jul 14, 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/e2e-aws a6403ca link true /test e2e-aws

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 Author

Now I have all the information needed. Let me compile the final report.

Test Failure Analysis Complete

Job Information

Test Failure Analysis

Error

FAIL: CRD schema check failed with 4 error(s) across 14 CRD(s):

  ERROR: awsendpointservices.crd.yaml: version v1beta1: ^.spec.resourceTags: unhandled: unhandled changes found:
    -  XListMapKeys: nil,
    +  XListMapKeys: []string{"key"},
    -  XListType:    nil,
    +  XListType:    &"map",

  ERROR: hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml: version v1beta1: ^.spec.platform.aws.resourceTags: unhandled changes
  ERROR: hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml: version v1beta1: ^.spec.platform.aws.resourceTags: unhandled changes
  ERROR: nodepools-CustomNoUpgrade.crd.yaml: version v1beta1: ^.spec.platform.aws.resourceTags: unhandled changes

Summary

The PR adds +listType=map and +listMapKey=key kubebuilder markers to three ResourceTags []AWSResourceTag fields (in AWSPlatformSpec, AWSNodePoolPlatform, and AWSEndpointServiceSpec) to enforce unique tag keys at the CRD schema level. The generated CRD manifests correctly include the new x-kubernetes-list-map-keys and x-kubernetes-list-type properties. However, the make verify-crd-schema check (powered by crdify) flags these as breaking "unhandled" changes because crdify has no built-in validator for XListType/XListMapKeys schema properties — any diff in fields not claimed by a registered comparator is treated as an error by default.

Root Cause

The crd-schema-check tool uses crdify to compare CRD schemas between the PR branch and the base commit. Crdify works by running a set of registered validators (enum, type, required, description, etc.) that each "claim" specific schema properties by zeroing them out after comparison. After all validators run, any remaining diff in the schema is flagged by the checkUnhandled catch-all path.

The XListType and XListMapKeys properties of JSONSchemaProps are not covered by any built-in crdify validator. When this PR changes these properties from nil to &"map" and []string{"key"} respectively, no comparator claims the diff. The remaining unclaimed diff triggers the checkUnhandled enforcement, which defaults to Error.

This affects exactly 4 CRDs (the CustomNoUpgrade superset variants that are checked, plus the ungated awsendpointservices CRD):

  1. awsendpointservices.crd.yaml^.spec.resourceTags
  2. hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml^.spec.platform.aws.resourceTags
  3. hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml^.spec.platform.aws.resourceTags
  4. nodepools-CustomNoUpgrade.crd.yaml^.spec.platform.aws.resourceTags

The change itself (adding list-map semantics to enforce unique tag keys) is an intentional, non-breaking API evolution — it adds server-side uniqueness enforcement but does not remove fields, change types, or add new required fields.

Recommendations

Option 1 (Recommended): Update the crdify config to handle unhandled changes as warnings.

Add unhandledEnforcement: Warn to hack/tools/crd-schema-check/crdify-config.yaml:

unhandledEnforcement: Warn
validations:
  - name: enum
    enforcement: Error
    configuration:
      additionPolicy: Allow

This treats diffs in schema properties not covered by any built-in validator (like XListType, XListMapKeys) as warnings instead of errors, while still surfacing them for visibility. This is the most sustainable fix since future list-type changes won't require one-off overrides.

Option 2: Use a one-time Prow override.

As suggested by the tool's own output, use /override verify-crd-schema on the PR to bypass the check for this specific merge. This is the quickest fix but does not prevent the issue from recurring on future PRs that modify list type annotations.

Option 3: Combine both options.

Use /override verify-crd-schema to unblock this PR immediately, then submit a follow-up PR with the crdify config change from Option 1 to prevent future occurrences.

Evidence
Evidence Detail
Failed step Run make verify-crd-schema (step 9 of 13)
Exit code 2 (make error)
Error type crdify "unhandled" changes — schema diff not claimed by any registered validator
Changed schema properties XListMapKeys: nil → []string{"key"}, XListType: nil → &"map"
Affected CRDs awsendpointservices, hostedclusters, hostedcontrolplanes, nodepools (CustomNoUpgrade variants)
Source change +listType=map / +listMapKey=key markers added to api/hypershift/v1beta1/aws.go and endpointservice_types.go
Config file hack/tools/crd-schema-check/crdify-config.yaml — no unhandledEnforcement override configured
Comparison base d24119038048b67233fe6a6ffbfa886b5025c6ba (main branch HEAD)
All prior steps passed make generate update, staticcheck, fmt, vet, verify-api-deps all succeeded

@jparrill

Copy link
Copy Markdown
Contributor

You need to fix the make verify tests which is failing now

@jparrill

Copy link
Copy Markdown
Contributor

/test address-review-comments

@hypershift-jira-solve-ci

Copy link
Copy Markdown
Contributor Author

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 area/platform/aws PR/issue for AWS (AWSPlatform) platform jira/severity-moderate Referenced Jira bug's severity is moderate 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants