Skip to content

GCP-391: Add ImageRegistry GSA field and IAM binding for Image Registry Operator#7828

Open
cblecker wants to merge 3 commits intoopenshift:mainfrom
cblecker:feat/gcp-391-image-registry-gsa
Open

GCP-391: Add ImageRegistry GSA field and IAM binding for Image Registry Operator#7828
cblecker wants to merge 3 commits intoopenshift:mainfrom
cblecker:feat/gcp-391-image-registry-gsa

Conversation

@cblecker
Copy link
Member

@cblecker cblecker commented Feb 28, 2026

What this PR does / why we need it:

Adds the ImageRegistry field to GCPServiceAccountsEmails and the corresponding IAM binding, enabling the Image Registry Operator on GCP Hosted Control Planes. This is the foundational API change (GCP-391) and IAM binding (GCP-408) that all downstream image registry stories depend on.

The GCPServiceAccountsEmails struct already has 4 GSA fields (NodePool, ControlPlane, CloudController, Storage). This PR adds a 5th: ImageRegistry. The IAM bindings JSON already has 4 entries — this adds a 5th. Both changes follow the exact established patterns.

Changes:

  • API Extension (api/hypershift/v1beta1/gcp.go): Add ImageRegistry field to GCPServiceAccountsEmails with validation, immutability CEL rule, and project-scoping CEL validation on GCPPlatformSpec
  • IAM Bindings (cmd/infra/gcp/iam-bindings.json): Add image-registry service account with roles/storage.admin for GCS bucket operations, targeting openshift-image-registry/cluster-image-registry-operator
  • Credential Reconciliation (hypershift-operator/.../gcp/gcp.go): Add ImageRegistryCredsSecret(), extend ReconcileCredentials() credential map and validateWorkloadIdentityConfiguration() validation
  • Tests: Update all test helpers and add missing image registry validation test case
  • Generated CRDs: Regenerated with the new imageRegistry field present in all CRD variants

Reference: /add-gcp-service-account skill — this PR covers Steps 1-2 (hypershift repo). Steps 3-5 are tracked by GCP-409.

Which issue(s) this PR fixes:

Fixes GCP-391, GCP-408

Special notes for your reviewer:

This follows the exact pattern used for the 4 existing GSA fields. No Go code changes needed for IAM bindings — the JSON is loaded via //go:embed and iterated generically by CreateServiceAccounts() and DeleteServiceAccounts().

Checklist:

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

Summary by CodeRabbit

  • New Features

    • Added support for configuring an Image Registry service account for GCP deployments, including validation, immutability, and required-entry enforcement.
  • Documentation

    • Updated API reference and CRD docs to describe the imageRegistry field, format rules, length constraints, and IAM role guidance.
  • Tests

    • Extended unit tests and fixtures to cover the new imageRegistry service account and validation cases.
  • Chores

    • Added IAM binding and infra manifest entries to provision the Image Registry service account.

cblecker and others added 2 commits February 27, 2026 16:09
…stry Operator

Add the ImageRegistry field to GCPServiceAccountsEmails struct to support
the Image Registry Operator on GCP Hosted Control Planes. This is the 5th
GSA field, following the established pattern of NodePool, ControlPlane,
CloudController, and Storage.

Changes:
- Add ImageRegistry field to GCPServiceAccountsEmails with validation,
  immutability CEL rule, and project-scoping CEL validation on GCPPlatformSpec
- Add image-registry service account definition in iam-bindings.json with
  roles/storage.admin for GCS bucket operations
- Add ImageRegistryCredsSecret() and extend ReconcileCredentials() credential
  map and validateWorkloadIdentityConfiguration() validation
- Update tests for new field across gcp_test.go and create_iam_test.go

Signed-off-by: Christoph Blecker <cblecker@redhat.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Regenerated after rebuilding codegen tool for Go 1.26 compatibility.

Signed-off-by: Christoph Blecker <cblecker@redhat.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@openshift-ci-robot
Copy link

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

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

This repository is configured in: LGTM mode

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Feb 28, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 28, 2026

@cblecker: This pull request references GCP-391 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:

Adds the ImageRegistry field to GCPServiceAccountsEmails and the corresponding IAM binding, enabling the Image Registry Operator on GCP Hosted Control Planes. This is the foundational API change (GCP-391) and IAM binding (GCP-408) that all downstream image registry stories depend on.

The GCPServiceAccountsEmails struct already has 4 GSA fields (NodePool, ControlPlane, CloudController, Storage). This PR adds a 5th: ImageRegistry. The IAM bindings JSON already has 4 entries — this adds a 5th. Both changes follow the exact established patterns.

Changes:

  • API Extension (api/hypershift/v1beta1/gcp.go): Add ImageRegistry field to GCPServiceAccountsEmails with validation, immutability CEL rule, and project-scoping CEL validation on GCPPlatformSpec
  • IAM Bindings (cmd/infra/gcp/iam-bindings.json): Add image-registry service account with roles/storage.admin for GCS bucket operations, targeting openshift-image-registry/cluster-image-registry-operator
  • Credential Reconciliation (hypershift-operator/.../gcp/gcp.go): Add ImageRegistryCredsSecret(), extend ReconcileCredentials() credential map and validateWorkloadIdentityConfiguration() validation
  • Tests: Update all test helpers and add missing image registry validation test case
  • Generated CRDs: Regenerated with the new imageRegistry field present in all CRD variants

Reference: /add-gcp-service-account skill — this PR covers Steps 1-2 (hypershift repo). Steps 3-5 are tracked by GCP-409.

Which issue(s) this PR fixes:

Fixes GCP-391, GCP-408

Special notes for your reviewer:

This follows the exact pattern used for the 4 existing GSA fields. No Go code changes needed for IAM bindings — the JSON is loaded via //go:embed and iterated generically by CreateServiceAccounts() and DeleteServiceAccounts().

Checklist:

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

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

@cblecker cblecker marked this pull request as ready for review February 28, 2026 00:24
@openshift-ci openshift-ci bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. do-not-merge/needs-area labels Feb 28, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 28, 2026

@cblecker: This pull request references GCP-391 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:

Adds the ImageRegistry field to GCPServiceAccountsEmails and the corresponding IAM binding, enabling the Image Registry Operator on GCP Hosted Control Planes. This is the foundational API change (GCP-391) and IAM binding (GCP-408) that all downstream image registry stories depend on.

The GCPServiceAccountsEmails struct already has 4 GSA fields (NodePool, ControlPlane, CloudController, Storage). This PR adds a 5th: ImageRegistry. The IAM bindings JSON already has 4 entries — this adds a 5th. Both changes follow the exact established patterns.

Changes:

  • API Extension (api/hypershift/v1beta1/gcp.go): Add ImageRegistry field to GCPServiceAccountsEmails with validation, immutability CEL rule, and project-scoping CEL validation on GCPPlatformSpec
  • IAM Bindings (cmd/infra/gcp/iam-bindings.json): Add image-registry service account with roles/storage.admin for GCS bucket operations, targeting openshift-image-registry/cluster-image-registry-operator
  • Credential Reconciliation (hypershift-operator/.../gcp/gcp.go): Add ImageRegistryCredsSecret(), extend ReconcileCredentials() credential map and validateWorkloadIdentityConfiguration() validation
  • Tests: Update all test helpers and add missing image registry validation test case
  • Generated CRDs: Regenerated with the new imageRegistry field present in all CRD variants

Reference: /add-gcp-service-account skill — this PR covers Steps 1-2 (hypershift repo). Steps 3-5 are tracked by GCP-409.

Which issue(s) this PR fixes:

Fixes GCP-391, GCP-408

Special notes for your reviewer:

This follows the exact pattern used for the 4 existing GSA fields. No Go code changes needed for IAM bindings — the JSON is loaded via //go:embed and iterated generically by CreateServiceAccounts() and DeleteServiceAccounts().

Checklist:

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

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

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 28, 2026

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

Walkthrough

Adds an ImageRegistry GCP service account field to workload identity across API types, CRD manifests, client apply configs, docs, IAM bindings, tests, and controller logic; adjusts several GCP service-account minLength validations from 38→37 and adds immutability and same-project validation.

Changes

Cohort / File(s) Summary
Core API Schema
api/hypershift/v1beta1/gcp.go
Added ImageRegistry string to GCPServiceAccountsEmails with pattern, minLength 37, maxLength 100, immutability marker; added XValidation on GCPPlatformSpec to require imageRegistry service account belong to the same project.
HostedCluster CRD Manifests
cmd/install/assets/.../hostedclusters-*.crd.yaml, api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/GCPPlatform.yaml
Added imageRegistry under spec.workloadIdentity.serviceAccountsEmails with pattern, minLength 37, maxLength 100, immutability, and made it required; adjusted other GSA minLength constraints 38→37.
HostedControlPlane CRD Manifests
cmd/install/assets/.../hostedcontrolplanes-*.crd.yaml, api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml
Added imageRegistry under spec.workloadIdentity.serviceAccountsEmails and spec.serviceAccountsEmails with same validations and immutability; added cross-field validation enforcing same-project ownership; adjusted other GSA minLength constraints 38→37.
Client Apply Configuration
client/applyconfiguration/hypershift/v1beta1/gcpserviceaccountsemails.go
Added ImageRegistry *string to apply-configuration struct and WithImageRegistry() builder method.
GCP Infrastructure & Bindings
cmd/infra/gcp/iam-bindings.json, cmd/infra/gcp/create_iam_test.go
Added image-registry service account entry (roles/storage.admin) and updated test expected output to include image-registry email.
Controller Implementation & Tests
hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp.go, .../gcp_test.go
Added ImageRegistryCredsSecret(controlPlaneNamespace) function; wired into ReconcileCredentials secret map; extended workload identity validation to require ImageRegistry email; updated tests and fixtures.
Documentation
docs/content/reference/api.md, docs/content/reference/aggregated-docs.md
Documented imageRegistry field in GCP service-accounts emails / ManagedIdentity with purpose, IAM roles (storage), format, and typical origin (hypershift infra output).

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Controller as HostedCluster Controller
participant GCP as GCP (Service Account / IAM)
participant K8s as Kubernetes API (Secret)
participant ImageReg as Image Registry Operator
Controller->>GCP: Read configured imageRegistry GSA email & metadata
Controller->>K8s: Create/Update image-registry-creds Secret (ImageRegistryCredsSecret)
K8s-->>Controller: Secret stored
ImageReg->>K8s: Mount/use image-registry-creds Secret to access GCS
Note over Controller,GCP: Validations enforce immutability and same-project ownership

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ⚠️ Warning Test assertions lack meaningful failure messages as second parameters; 0 of 17 assertions include explicit diagnostic messages. Add explicit failure messages to assertions (e.g., g.Expect(err).To(BeNil(), "message")) and consolidate setup patterns into helper functions.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding an ImageRegistry Google Service Account field and its IAM binding for the Image Registry Operator on GCP platforms.
Stable And Deterministic Test Names ✅ Passed All test names in modified test files use static, descriptive strings with no dynamic values, timestamps, UUIDs, or random suffixes.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Feb 28, 2026
@openshift-ci openshift-ci bot requested review from apahim and devguyio February 28, 2026 00:30
@openshift-ci openshift-ci bot added the area/api Indicates the PR includes changes for the API label Feb 28, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 28, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added area/cli Indicates the PR includes changes for CLI area/documentation Indicates the PR includes changes for documentation area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release area/platform/gcp PR/issue for GCP (GCPPlatform) platform and removed do-not-merge/needs-area labels Feb 28, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 28, 2026

@cblecker: This pull request references GCP-391 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:

Adds the ImageRegistry field to GCPServiceAccountsEmails and the corresponding IAM binding, enabling the Image Registry Operator on GCP Hosted Control Planes. This is the foundational API change (GCP-391) and IAM binding (GCP-408) that all downstream image registry stories depend on.

The GCPServiceAccountsEmails struct already has 4 GSA fields (NodePool, ControlPlane, CloudController, Storage). This PR adds a 5th: ImageRegistry. The IAM bindings JSON already has 4 entries — this adds a 5th. Both changes follow the exact established patterns.

Changes:

  • API Extension (api/hypershift/v1beta1/gcp.go): Add ImageRegistry field to GCPServiceAccountsEmails with validation, immutability CEL rule, and project-scoping CEL validation on GCPPlatformSpec
  • IAM Bindings (cmd/infra/gcp/iam-bindings.json): Add image-registry service account with roles/storage.admin for GCS bucket operations, targeting openshift-image-registry/cluster-image-registry-operator
  • Credential Reconciliation (hypershift-operator/.../gcp/gcp.go): Add ImageRegistryCredsSecret(), extend ReconcileCredentials() credential map and validateWorkloadIdentityConfiguration() validation
  • Tests: Update all test helpers and add missing image registry validation test case
  • Generated CRDs: Regenerated with the new imageRegistry field present in all CRD variants

Reference: /add-gcp-service-account skill — this PR covers Steps 1-2 (hypershift repo). Steps 3-5 are tracked by GCP-409.

Which issue(s) this PR fixes:

Fixes GCP-391, GCP-408

Special notes for your reviewer:

This follows the exact pattern used for the 4 existing GSA fields. No Go code changes needed for IAM bindings — the JSON is loaded via //go:embed and iterated generically by CreateServiceAccounts() and DeleteServiceAccounts().

Checklist:

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

Summary by CodeRabbit

Release Notes

  • New Features

  • Added support for specifying an Image Registry service account for GCP-based deployments. The new field enables configuration of a dedicated Google Service Account for the Image Registry Operator with required storage permissions.

  • Documentation

  • Updated API reference and configuration documentation to document the new Image Registry service account field, including format requirements and IAM role guidance.

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
cmd/infra/gcp/iam-bindings.json (1)

61-65: Consider restricting Image Registry Operator GCS permissions to bucket scope.

Line 64 assigns roles/storage.admin, a project-level role with broad storage management permissions. The cluster-image-registry-operator requires only object-level operations (list, read, write, delete, update). If buckets are pre-created, use bucket-scoped roles instead: roles/storage.objectUser paired with roles/storage.legacyBucketReader (or roles/storage.objectAdmin alone) bound to the specific registry bucket to reduce blast radius. Verify the cluster provisioning workflow does not require project-level bucket management before narrowing scope.

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

In `@cmd/infra/gcp/iam-bindings.json` around lines 61 - 65, The current service
account entry "image-registry" assigns the project-level role
"roles/storage.admin"; replace this with bucket-scoped roles appropriate for
object operations (e.g., use "roles/storage.objectAdmin" on the specific
registry bucket, or combine "roles/storage.objectUser" +
"roles/storage.legacyBucketReader" on that bucket) and ensure the binding is
applied to the specific registry bucket rather than the project; update the
"roles" array for the "image-registry" entry and confirm the cluster
provisioning workflow does not require project-level bucket creation before
narrowing the scope.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml`:
- Around line 5077-5079: The new imageRegistry field is marked +required and
uses an immutability validation rule (self == oldSelf), which prevents adding it
to existing HostedControlPlane objects; either relax the schema or make the
validation transition-safe: remove the +required marker or change the validation
rule for imageRegistry (and the other four service account fields nodePool,
controlPlane, cloudController, storage) to allow initial setting on updates
(e.g., permit when oldSelf is null) or implement a backfill/reconciliation that
populates missing values before enforcing immutability; locate the field
definitions and their x-kubernetes-validations in the generated manifest and
update the required/validation logic accordingly.

In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml`:
- Around line 6190-6194: The CRD currently lists imageRegistry as required under
the HostedCluster spec which is incompatible with the immutable workloadIdentity
field; remove imageRegistry from the required array (the block that enumerates
cloudController, controlPlane, imageRegistry, nodePool) so imageRegistry remains
optional while workloadIdentity stays immutable, and ensure you also remove any
duplicate mentions of imageRegistry being required at the other occurrences
noted (the other required arrays around the workloadIdentity definition) so
existing HostedCluster objects can be updated without breaking status writes.

In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml`:
- Around line 6082-6087: The CRD currently makes the spec.imageRegistry field
required while spec.workloadIdentity is immutable, which prevents safe
backfilling of existing HostedControlPlane objects; to fix this, remove
"imageRegistry" from the required: list in the HostedControlPlane CRD (the
zz_generated.crd-manifests entry where required includes cloudController,
controlPlane, imageRegistry, nodePool, storage) so the field remains optional
for this release, or alternatively implement a migration/defaulting path (e.g.,
webhook/defaulting logic tied to the same CRD) before making spec.imageRegistry
required in a follow-up release; do not change the immutability of
spec.workloadIdentity in this change.
- Around line 6032-6034: The CRD field currently sets minLength: 38 for GSA
email validation which incorrectly rejects the shortest valid addresses; update
the schema entry that pairs minLength with the regex pattern
^[a-z][a-z0-9-]{4,28}[a-z0-9]@[a-z][a-z0-9-]{4,28}[a-z0-9]\.iam\.gserviceaccount\.com$
by changing minLength from 38 to 37 so the minimal valid service account email
is accepted.

---

Nitpick comments:
In `@cmd/infra/gcp/iam-bindings.json`:
- Around line 61-65: The current service account entry "image-registry" assigns
the project-level role "roles/storage.admin"; replace this with bucket-scoped
roles appropriate for object operations (e.g., use "roles/storage.objectAdmin"
on the specific registry bucket, or combine "roles/storage.objectUser" +
"roles/storage.legacyBucketReader" on that bucket) and ensure the binding is
applied to the specific registry bucket rather than the project; update the
"roles" array for the "image-registry" entry and confirm the cluster
provisioning workflow does not require project-level bucket creation before
narrowing the scope.

ℹ️ Review info

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

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 40d6470 and 5982742.

⛔ Files ignored due to path filters (1)
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/gcp.go is excluded by !**/vendor/**, !vendor/**
📒 Files selected for processing (14)
  • api/hypershift/v1beta1/gcp.go
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/GCPPlatform.yaml
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml
  • client/applyconfiguration/hypershift/v1beta1/gcpserviceaccountsemails.go
  • cmd/infra/gcp/create_iam_test.go
  • cmd/infra/gcp/iam-bindings.json
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-TechPreviewNoUpgrade.crd.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-TechPreviewNoUpgrade.crd.yaml
  • docs/content/reference/aggregated-docs.md
  • docs/content/reference/api.md
  • hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp.go
  • hypershift-operator/controllers/hostedcluster/internal/platform/gcp/gcp_test.go

Comment on lines +5077 to +5079
x-kubernetes-validations:
- message: ImageRegistry is immutable
rule: self == oldSelf
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 28, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, verify the exact content at the reported lines
echo "=== Checking GCPPlatform.yaml at reported lines ==="
sed -n '5075,5082p' api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml

echo
echo "=== Checking required fields section around line 5126 ==="
sed -n '5120,5135p' api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml

echo
echo "=== Search for imageRegistry definition in entire file ==="
rg -n "imageRegistry" api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml | head -20

Repository: openshift/hypershift

Length of output: 3097


🏁 Script executed:

# Check the source API type for defaulting/conversion markers
echo "=== Search for imageRegistry in API type definitions ==="
find api/hypershift -name "*.go" -type f | xargs rg -l "imageRegistry|ImageRegistry" | head -10

echo
echo "=== Check for webhooks or conversion strategies ==="
find . -path "./vendor" -prune -o -name "*webhook*" -type f -print | head -10
find . -path "./vendor" -prune -o -name "*conversion*" -type f -print | head -10

Repository: openshift/hypershift

Length of output: 2109


🏁 Script executed:

# Check similar immutable + required patterns in CRD
echo "=== Look for other immutable required fields ==="
rg -B5 "is immutable" api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/ | grep -E "(required:|message:|rule:)" | head -30

Repository: openshift/hypershift

Length of output: 5533


🏁 Script executed:

echo "=== Search for imageRegistry field definition in gcp.go ==="
rg -B5 -A10 "imageRegistry.*string|ImageRegistry" api/hypershift/v1beta1/gcp.go | head -100

echo
echo "=== Check for +kubebuilder markers on imageRegistry ==="
rg -B10 "imageRegistry" api/hypershift/v1beta1/gcp.go | grep -E "^\+" | head -20

echo
echo "=== Check if imageRegistry has default value or omitempty tag ==="
rg -B3 -A3 'imageRegistry.*string' api/hypershift/v1beta1/gcp.go | head -50

Repository: openshift/hypershift

Length of output: 1038


🏁 Script executed:

echo "=== Check GCPServiceAccountsEmails struct definition ==="
ast-grep --pattern 'type GCPServiceAccountsEmails struct {
  $$$
}'

echo
echo "=== Look for SetDefaults or defaulting webhooks ==="
rg -n "SetDefaults|Default.*GCP|webhook" api/hypershift/v1beta1/gcp.go | head -20

Repository: openshift/hypershift

Length of output: 23726


🏁 Script executed:

echo "=== Check if imageRegistry was recently added or if field is conditionally required ==="
git log --oneline --all -- api/hypershift/v1beta1/gcp.go | head -10
git log --oneline --all -- api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml | head -10

Repository: openshift/hypershift

Length of output: 1544


🏁 Script executed:

echo "=== Check if imageRegistry is auto-populated by reconciliation or webhooks ==="
rg -n "imageRegistry|ImageRegistry" api/hypershift/v1beta1/ --glob="*.go" | grep -iE "default|set|populate|webhook|reconcil" | head -20

echo
echo "=== Verify the contradiction: field is required but omitempty ==="
grep -A2 "ImageRegistry string" api/hypershift/v1beta1/gcp.go | head -20

Repository: openshift/hypershift

Length of output: 267


🏁 Script executed:

echo "=== Check if this breaks existing GCP platform objects without imageRegistry ==="
rg -n "GCPPlatform" api/hypershift/v1beta1/ --glob="*.go" -A5 | grep -E "required|imageRegistry" | head -30

echo
echo "=== See if other newly added fields follow the same unsafe pattern ==="
git show 598274204 --stat

Repository: openshift/hypershift

Length of output: 50378


imageRegistry field is both required and immutable, blocking updates to pre-existing HostedControlPlane objects.

This field was just added (commit 5982742) with the marker +required and immutability rule self == oldSelf. Pre-existing HostedControlPlane objects created before this change will fail validation during the next update because they lack this newly required field. The immutability rule self == oldSelf prevents adding the field after creation—when oldSelf is absent and self is set, they never match.

Transition-safe fix
                                imageRegistry:
                                  x-kubernetes-validations:
-                                - message: ImageRegistry is immutable
-                                  rule: self == oldSelf
+                                - message: ImageRegistry is immutable once set
+                                  rule: '!has(oldSelf) || self == oldSelf'
                                required:
                                - cloudController
                                - controlPlane
-                               - imageRegistry
+                               # Remove imageRegistry from required if it cannot be auto-populated

Note: The same pattern affects all five service account fields (nodePool, controlPlane, cloudController, storage, imageRegistry). Verify whether there is a reconciliation mechanism to backfill missing imageRegistry on existing objects, or whether this field should not be required.

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

In
`@api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml`
around lines 5077 - 5079, The new imageRegistry field is marked +required and
uses an immutability validation rule (self == oldSelf), which prevents adding it
to existing HostedControlPlane objects; either relax the schema or make the
validation transition-safe: remove the +required marker or change the validation
rule for imageRegistry (and the other four service account fields nodePool,
controlPlane, cloudController, storage) to allow initial setting on updates
(e.g., permit when oldSelf is null) or implement a backfill/reconciliation that
populates missing values before enforcing immutability; locate the field
definitions and their x-kubernetes-validations in the generated manifest and
update the required/validation logic accordingly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not applicable. GCP HCP is pre-GA (behind the GCPPlatform feature gate) with no long-lived clusters in existence. All 5 GSA fields follow the same +required + +immutable pattern established when the struct was first introduced. There are no existing objects to break.

Copy link
Contributor

Choose a reason for hiding this comment

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

@cblecker Thanks for the context. Acknowledged—since GCP HCP is pre-GA behind the GCPPlatform feature gate and there are no long-lived clusters, the required + immutable pattern for all five GSA fields (including imageRegistry) is intentional and safe. I’m withdrawing the earlier “migration risk” as non-blocking and won’t suggest schema changes here.

If you’d like, I can tune future review heuristics to avoid flagging “required + immutable” in this feature-gated GCPPlatform area for this repo.

(=^-^=)っ∩? Thanks for the clarification!


📝 Learnings were identified and not saved (knowledge base disabled). Enable

Comment on lines 6190 to 6194
required:
- cloudController
- controlPlane
- imageRegistry
- nodePool
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 28, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid making imageRegistry required inside an immutable workloadIdentity.

At Line 6193, imageRegistry becomes required, while workloadIdentity is immutable at Line 6204. Existing HostedClusters created before this field existed cannot add it later, which can cause update/status write failures on those objects. Keep this field optional for compatibility (at least for one transition release), and gate enforcement elsewhere.

💡 Proposed CRD-side transition fix
                             required:
                             - cloudController
                             - controlPlane
-                            - imageRegistry
                             - nodePool
                             - storage
                             type: object
@@
-                    - message: imageRegistry service account must belong to the same
-                        project
-                      rule: self.workloadIdentity.serviceAccountsEmails.imageRegistry.contains('@')
-                        && self.workloadIdentity.serviceAccountsEmails.imageRegistry.endsWith('@'
-                        + self.project + '.iam.gserviceaccount.com')
+                    - message: if set, imageRegistry service account must belong
+                        to the same project
+                      rule: '!has(self.workloadIdentity.serviceAccountsEmails.imageRegistry)
+                        || (self.workloadIdentity.serviceAccountsEmails.imageRegistry.contains(''@'')
+                        && self.workloadIdentity.serviceAccountsEmails.imageRegistry.endsWith(''@''
+                        + self.project + ''.iam.gserviceaccount.com''))'

Also applies to: 6204-6205, 6231-6235

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

In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml`
around lines 6190 - 6194, The CRD currently lists imageRegistry as required
under the HostedCluster spec which is incompatible with the immutable
workloadIdentity field; remove imageRegistry from the required array (the block
that enumerates cloudController, controlPlane, imageRegistry, nodePool) so
imageRegistry remains optional while workloadIdentity stays immutable, and
ensure you also remove any duplicate mentions of imageRegistry being required at
the other occurrences noted (the other required arrays around the
workloadIdentity definition) so existing HostedCluster objects can be updated
without breaking status writes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not applicable. GCP HCP is pre-GA (behind the GCPPlatform feature gate) with no long-lived clusters in existence. All 5 GSA fields follow the same +required + +immutable pattern established when the struct was first introduced. There are no existing objects to break.

Copy link
Contributor

Choose a reason for hiding this comment

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

@cblecker

Thanks for the clarification. Given GCP HCP is pre-GA and gated behind GCPPlatform, and there are no long-lived HostedClusters, keeping imageRegistry required and maintaining the existing required+immutable pattern across all five GSAs is acceptable. I’m retracting the earlier suggestion about making imageRegistry optional. The validations around project scoping (e.g., Lines 6231–6235) also remain appropriate under this context.

As a minor follow-up, I’ll just note for release notes/docs that imageRegistry is now part of the required GSA set when the GCPPlatform gate is enabled.


📝 Learnings were identified and not saved (knowledge base disabled). Enable

Comment on lines 6082 to 6087
required:
- cloudController
- controlPlane
- imageRegistry
- nodePool
- storage
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 28, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Required + immutable combination is migration-hostile for existing objects.

Line 6085 makes imageRegistry mandatory, but workloadIdentity is immutable (Line 6096 context). Existing HCPs missing this field cannot be backfilled safely after CRD rollout, which risks breaking updates/reconciliation.

A safer rollout pattern is to keep this field optional for one release (or introduce a migration/defaulting path), then enforce requiredness once legacy objects are migrated.

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

In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml`
around lines 6082 - 6087, The CRD currently makes the spec.imageRegistry field
required while spec.workloadIdentity is immutable, which prevents safe
backfilling of existing HostedControlPlane objects; to fix this, remove
"imageRegistry" from the required: list in the HostedControlPlane CRD (the
zz_generated.crd-manifests entry where required includes cloudController,
controlPlane, imageRegistry, nodePool, storage) so the field remains optional
for this release, or alternatively implement a migration/defaulting path (e.g.,
webhook/defaulting logic tied to the same CRD) before making spec.imageRegistry
required in a follow-up release; do not change the immutability of
spec.workloadIdentity in this change.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not applicable. GCP HCP is pre-GA (behind the GCPPlatform feature gate) with no long-lived clusters in existence. All 5 GSA fields follow the same +required + +immutable pattern established when the struct was first introduced. There are no existing objects to break.

Copy link
Contributor

Choose a reason for hiding this comment

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

@cblecker Thanks, acknowledged. I verified this CRD carries the merged-by-featuregates annotation and the imageRegistry field is required alongside the other GSAs. Given GCP HCP is pre-GA behind the GCPPlatform feature gate with no long‑lived clusters, I agree the required+immutable combo is acceptable here and consistent with the existing pattern. No changes requested; I’ll just keep a note to re-check requiredness before/when the gate is lifted/GA.


📝 Learnings were identified and not saved (knowledge base disabled). Enable

@cblecker
Copy link
Member Author

/pipeline required

@openshift-ci-robot
Copy link

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

The minimum length for a GCP service account email
(a@b.iam.gserviceaccount.com) is 37 characters per GCP documentation,
not 38. Update all 5 GCPServiceAccountsEmails fields and regenerate
CRDs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cblecker
Copy link
Member Author

/test e2e-aws-techpreview
/pipeline required

@openshift-ci-robot
Copy link

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

@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 28, 2026

@cblecker: This pull request references GCP-391 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

What this PR does / why we need it:

Adds the ImageRegistry field to GCPServiceAccountsEmails and the corresponding IAM binding, enabling the Image Registry Operator on GCP Hosted Control Planes. This is the foundational API change (GCP-391) and IAM binding (GCP-408) that all downstream image registry stories depend on.

The GCPServiceAccountsEmails struct already has 4 GSA fields (NodePool, ControlPlane, CloudController, Storage). This PR adds a 5th: ImageRegistry. The IAM bindings JSON already has 4 entries — this adds a 5th. Both changes follow the exact established patterns.

Changes:

  • API Extension (api/hypershift/v1beta1/gcp.go): Add ImageRegistry field to GCPServiceAccountsEmails with validation, immutability CEL rule, and project-scoping CEL validation on GCPPlatformSpec
  • IAM Bindings (cmd/infra/gcp/iam-bindings.json): Add image-registry service account with roles/storage.admin for GCS bucket operations, targeting openshift-image-registry/cluster-image-registry-operator
  • Credential Reconciliation (hypershift-operator/.../gcp/gcp.go): Add ImageRegistryCredsSecret(), extend ReconcileCredentials() credential map and validateWorkloadIdentityConfiguration() validation
  • Tests: Update all test helpers and add missing image registry validation test case
  • Generated CRDs: Regenerated with the new imageRegistry field present in all CRD variants

Reference: /add-gcp-service-account skill — this PR covers Steps 1-2 (hypershift repo). Steps 3-5 are tracked by GCP-409.

Which issue(s) this PR fixes:

Fixes GCP-391, GCP-408

Special notes for your reviewer:

This follows the exact pattern used for the 4 existing GSA fields. No Go code changes needed for IAM bindings — the JSON is loaded via //go:embed and iterated generically by CreateServiceAccounts() and DeleteServiceAccounts().

Checklist:

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

Summary by CodeRabbit

  • New Features

  • Added support for configuring an Image Registry service account for GCP deployments, including validation, immutability, and required-entry enforcement.

  • Documentation

  • Updated API reference and CRD docs to describe the imageRegistry field, format rules, length constraints, and IAM role guidance.

  • Tests

  • Extended unit tests and fixtures to cover the new imageRegistry service account and validation cases.

  • Chores

  • Added IAM binding and infra manifest entries to provision the Image Registry service account.

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml (1)

6082-6087: ⚠️ Potential issue | 🟠 Major

Required imageRegistry + immutable workloadIdentity can block updates for pre-existing objects.

At Line 6085, imageRegistry is newly required, while Line 6096 keeps workloadIdentity immutable. Existing HostedControlPlanes created before this field may fail future updates because they cannot be backfilled.

Suggested mitigation
                             required:
                             - cloudController
                             - controlPlane
-                            - imageRegistry
                             - nodePool
                             - storage

Also applies to: 6096-6097

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

In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml`
around lines 6082 - 6087, Newly making spec.imageRegistry required while
spec.workloadIdentity remains immutable will block updates for pre-existing
HostedControlPlane objects; change the CRD so imageRegistry is not added to the
required list (remove "imageRegistry" from the required array in the
HostedControlPlane schema) and/or remove the immutability constraint on
workloadIdentity (clear the immutable flag on spec.workloadIdentity) so existing
resources can be backfilled during upgrade; if you must keep imageRegistry
required, instead implement a migration/admission path to populate defaults
before enforcing the requirement. Ensure you update the schema entries named
imageRegistry and workloadIdentity in the HostedControlPlane CRD.
api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml (1)

5077-5079: ⚠️ Potential issue | 🔴 Critical

Required + immutable imageRegistry remains migration-breaking for existing objects.

Line 5126 makes imageRegistry mandatory, while Line 5079 enforces strict immutability. Existing HostedControlPlanes that predate this field and lack it cannot be updated to add it later.

Suggested transition-safe direction
-                                - message: ImageRegistry is immutable
-                                  rule: self == oldSelf
+                                - message: ImageRegistry is immutable once set
+                                  rule: '!has(oldSelf) || self == oldSelf'
...
-                            - imageRegistry
+                            # keep optional unless backfilled before enforcement

Please apply this in the source API markers (not directly in generated YAML) and regenerate CRDs.

Also applies to: 5123-5127

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

In
`@api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml`
around lines 5077 - 5079, The imageRegistry field is currently marked required
and immutable in the generated CRD which blocks upgrades for existing
HostedControlPlane objects that lack the field; update the source API markers on
the HostedControlPlane spec (the ImageRegistry field in the API Go type) to
remove the required validation and make the field optional (add/ensure the
kubebuilder Optional marker or remove the +kubebuilder:validation:Required
marker), and remove or relax the immutability x-kubernetes-validations marker
(the self == oldSelf rule) so that existing objects can be populated during
migration; after changing the ImageRegistry field markers in the API type,
regenerate the CRDs.
cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml (1)

6190-6195: ⚠️ Potential issue | 🟠 Major

Keep imageRegistry optional while workloadIdentity is immutable (compat risk).

At Line 6193, imageRegistry is required, but workloadIdentity is immutable at Line 6204-Line 6205. Existing HostedClusters created before this field existed can fail validation on update/status paths. Also, the Line 6231-Line 6235 CEL rule should be optional-aware if this field is transitional/optional.

Proposed transition-safe CRD diff
                             required:
                             - cloudController
                             - controlPlane
-                            - imageRegistry
                             - nodePool
                             - storage
                             type: object
@@
-                    - message: imageRegistry service account must belong to the same
-                        project
-                      rule: self.workloadIdentity.serviceAccountsEmails.imageRegistry.contains('@')
-                        && self.workloadIdentity.serviceAccountsEmails.imageRegistry.endsWith('@'
-                        + self.project + '.iam.gserviceaccount.com')
+                    - message: if set, imageRegistry service account must belong to the same
+                        project
+                      rule: '!has(self.workloadIdentity.serviceAccountsEmails.imageRegistry)
+                        || (self.workloadIdentity.serviceAccountsEmails.imageRegistry.contains(''@'')
+                        && self.workloadIdentity.serviceAccountsEmails.imageRegistry.endsWith(''@''
+                        + self.project + ''.iam.gserviceaccount.com''))'

Also applies to: 6204-6205, 6231-6235

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

In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml`
around lines 6190 - 6195, The CRD currently lists imageRegistry as required
while workloadIdentity is immutable, which can break updates for existing
HostedClusters; remove imageRegistry from the required array (so imageRegistry
becomes optional) and update the CEL validation (the rule referenced around the
CEL block at lines ~6231-6235) to be presence-aware—i.e., guard the CEL
expression with a has(self.spec.imageRegistry) check (or equivalent) so the rule
only runs when imageRegistry is set; ensure no change makes workloadIdentity
(the immutable field) required or subject to this CEL check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-TechPreviewNoUpgrade.crd.yaml`:
- Around line 5939-5957: The new required + immutable imageRegistry field will
block updates for existing HostedClusters; make the transition
backward-compatible by (1) removing the required constraint for imageRegistry
(so pre-existing resources without the field aren’t rejected) and (2) relax the
x-kubernetes-validations rule on imageRegistry to allow one-time initialization
by changing the rule from "self == oldSelf" to "oldSelf == null || self ==
oldSelf" (refer to the imageRegistry field and its x-kubernetes-validations
entry in the CRD).

---

Duplicate comments:
In
`@api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml`:
- Around line 5077-5079: The imageRegistry field is currently marked required
and immutable in the generated CRD which blocks upgrades for existing
HostedControlPlane objects that lack the field; update the source API markers on
the HostedControlPlane spec (the ImageRegistry field in the API Go type) to
remove the required validation and make the field optional (add/ensure the
kubebuilder Optional marker or remove the +kubebuilder:validation:Required
marker), and remove or relax the immutability x-kubernetes-validations marker
(the self == oldSelf rule) so that existing objects can be populated during
migration; after changing the ImageRegistry field markers in the API type,
regenerate the CRDs.

In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml`:
- Around line 6190-6195: The CRD currently lists imageRegistry as required while
workloadIdentity is immutable, which can break updates for existing
HostedClusters; remove imageRegistry from the required array (so imageRegistry
becomes optional) and update the CEL validation (the rule referenced around the
CEL block at lines ~6231-6235) to be presence-aware—i.e., guard the CEL
expression with a has(self.spec.imageRegistry) check (or equivalent) so the rule
only runs when imageRegistry is set; ensure no change makes workloadIdentity
(the immutable field) required or subject to this CEL check.

In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml`:
- Around line 6082-6087: Newly making spec.imageRegistry required while
spec.workloadIdentity remains immutable will block updates for pre-existing
HostedControlPlane objects; change the CRD so imageRegistry is not added to the
required list (remove "imageRegistry" from the required array in the
HostedControlPlane schema) and/or remove the immutability constraint on
workloadIdentity (clear the immutable flag on spec.workloadIdentity) so existing
resources can be backfilled during upgrade; if you must keep imageRegistry
required, instead implement a migration/admission path to populate defaults
before enforcing the requirement. Ensure you update the schema entries named
imageRegistry and workloadIdentity in the HostedControlPlane CRD.

ℹ️ Review info

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

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5982742 and 16b2bc9.

⛔ Files ignored due to path filters (1)
  • vendor/github.com/openshift/hypershift/api/hypershift/v1beta1/gcp.go is excluded by !**/vendor/**, !vendor/**
📒 Files selected for processing (7)
  • api/hypershift/v1beta1/gcp.go
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/GCPPlatform.yaml
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/GCPPlatform.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-TechPreviewNoUpgrade.crd.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml
  • cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-TechPreviewNoUpgrade.crd.yaml

Comment on lines +5939 to +5957
imageRegistry:
description: |-
imageRegistry is the Google Service Account email for the Image Registry Operator
that manages GCS storage for the internal container image registry.
This GSA requires the following IAM roles:
- roles/storage.admin (Storage Admin - for creating and managing GCS buckets and objects)
See cmd/infra/gcp/iam-bindings.json for the authoritative role definitions.
Format: service-account-name@project-id.iam.gserviceaccount.com

This is a user-provided value referencing a pre-created Google Service Account.
Typically obtained from the output of `hypershift infra create gcp` which creates
the required service accounts with appropriate IAM roles and WIF bindings.
maxLength: 100
minLength: 37
pattern: ^[a-z][a-z0-9-]{4,28}[a-z0-9]@[a-z][a-z0-9-]{4,28}[a-z0-9]\.iam\.gserviceaccount\.com$
type: string
x-kubernetes-validations:
- message: ImageRegistry is immutable
rule: self == oldSelf
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Required + immutable imageRegistry may block updates for pre-existing HostedClusters.

Adding imageRegistry as required while also enforcing strict immutability can break updates for existing resources created before this field existed. Please add a migration/backfill path or allow one-time initialization before immutability is enforced.

🔧 Backward-compatible transition example
                               imageRegistry:
                                 ...
                                 x-kubernetes-validations:
-                                - message: ImageRegistry is immutable
-                                  rule: self == oldSelf
+                                - message: ImageRegistry is immutable after initial set
+                                  rule: oldSelf == null || self == oldSelf
                             required:
                             - cloudController
                             - controlPlane
-                            - imageRegistry
                             - nodePool
                             - storage

Also applies to: 6001-6005

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

In
`@cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-TechPreviewNoUpgrade.crd.yaml`
around lines 5939 - 5957, The new required + immutable imageRegistry field will
block updates for existing HostedClusters; make the transition
backward-compatible by (1) removing the required constraint for imageRegistry
(so pre-existing resources without the field aren’t rejected) and (2) relax the
x-kubernetes-validations rule on imageRegistry to allow one-time initialization
by changing the rule from "self == oldSelf" to "oldSelf == null || self ==
oldSelf" (refer to the imageRegistry field and its x-kubernetes-validations
entry in the CRD).

@cwbotbot
Copy link

cwbotbot commented Feb 28, 2026

Test Results

e2e-aks

e2e-aws

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 28, 2026

@cblecker: The following tests 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-4-21 16b2bc9 link true /test e2e-aws-4-21
ci/prow/e2e-aws-techpreview 16b2bc9 link false /test e2e-aws-techpreview
ci/prow/e2e-aks-4-21 16b2bc9 link true /test e2e-aks-4-21

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.

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/documentation Indicates the PR includes changes for documentation area/hypershift-operator Indicates the PR includes changes for the hypershift operator and API - outside an OCP release area/platform/gcp PR/issue for GCP (GCPPlatform) platform 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.

3 participants