Skip to content

OCPBUGS-105193: extract HCCO webhook validation into a dedicated controller - #9238

Closed
bryan-cox wants to merge 2 commits into
openshift:mainfrom
bryan-cox:OCPBUGS-105193
Closed

OCPBUGS-105193: extract HCCO webhook validation into a dedicated controller#9238
bryan-cox wants to merge 2 commits into
openshift:mainfrom
bryan-cox:OCPBUGS-105193

Conversation

@bryan-cox

@bryan-cox bryan-cox commented Aug 6, 2026

Copy link
Copy Markdown
Member

What this PR does / why we need it:

Extracts ensureGuestAdmissionWebhooksAreValid() from the monolithic HCCO resources controller into a dedicated webhook-validation controller. The [Feature:WebhookValidation] e2e test has a 47% pass rate on e2e-v2-azure-self-managed because webhook validation runs at the tail of a 15+ sub-reconciler chain, causing 60+ second delays between webhook creation and deletion.

The new controller watches ValidatingWebhookConfiguration and MutatingWebhookConfiguration directly, so reconciliation triggers immediately on webhook events. It encodes the webhook type in the request namespace field so Reconcile() targets only the type that fired, avoiding a redundant Get for the other kind.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/OCPBUGS-105193

Special notes for your reviewer:

  • The HCCO manager cache already includes VWC and MWC with labels.Everything() (no label filter) — see operator/config.go:129-130 — so the resources controller retains its watches for ensureResourceCreationIsBlocked
  • Two commits: (1) the extraction refactor, (2) e2e timeout bump (separate rationale)
  • 13 unit tests: 7 table-driven reconcile cases + 2 error-path tests + 4 URL validation tests

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

Summary by CodeRabbit

  • New Features

    • Added automated validation for admission webhooks targeting restricted control-plane services.
    • Preserves webhooks explicitly allowed for guest use and external or service-reference configurations.
  • Bug Fixes

    • Improves reliability of webhook cleanup by handling missing resources and retry-safe deletion.
  • Tests

    • Expanded coverage for allowed, disallowed, mixed, and error scenarios.
    • Extended the end-to-end validation wait time to improve test stability.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

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

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

This repository is configured in: LGTM mode

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 6, 2026
@openshift-ci

openshift-ci Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

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

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

Copy link
Copy Markdown

@bryan-cox: This pull request references Jira Issue OCPBUGS-105193, 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)

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:

Extracts ensureGuestAdmissionWebhooksAreValid() from the monolithic HCCO resources controller into a dedicated webhook-validation controller. The [Feature:WebhookValidation] e2e test has a 47% pass rate on e2e-v2-azure-self-managed because webhook validation runs at the tail of a 15+ sub-reconciler chain, causing 60+ second delays between webhook creation and deletion.

The new controller watches ValidatingWebhookConfiguration and MutatingWebhookConfiguration directly, so reconciliation triggers immediately on webhook events. It encodes the webhook type in the request namespace field so Reconcile() targets only the type that fired, avoiding a redundant Get for the other kind.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/OCPBUGS-105193

Special notes for your reviewer:

  • The HCCO manager cache already includes VWC and MWC with labels.Everything() (no label filter) — see operator/config.go:129-130 — so the resources controller retains its watches for ensureResourceCreationIsBlocked
  • Two commits: (1) the extraction refactor, (2) e2e timeout bump (separate rationale)
  • 13 unit tests: 7 table-driven reconcile cases + 2 error-path tests + 4 URL validation tests

Checklist:

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

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

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

@openshift-ci-robot openshift-ci-robot added the jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. label Aug 6, 2026
@openshift-ci openshift-ci Bot added do-not-merge/needs-area area/api Indicates the PR includes changes for the API area/ci-tooling Indicates the PR includes changes for CI or tooling area/cli Indicates the PR includes changes for CLI area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release 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/aws PR/issue for AWS (AWSPlatform) platform and removed do-not-merge/needs-area labels Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: d68a52f5-6c28-4032-9bbb-eb03b27ac250

📥 Commits

Reviewing files that changed from the base of the PR and between 1d48188 and 81a7168.

📒 Files selected for processing (7)
  • control-plane-operator/hostedclusterconfigoperator/cmd.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/setup.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation_test.go
  • test/e2e/v2/tests/hosted_cluster_security_test.go

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting


📝 Walkthrough

Walkthrough

The PR adds a dedicated webhook validation controller. The controller watches validating and mutating webhook configurations, identifies configurations that target disallowed control-plane service URLs, and deletes them. The operator registers the controller during startup. The previous inline cleanup logic is removed from the resources reconciler. Unit tests cover URL matching, reconciliation, error handling, and resource preservation. The end-to-end cleanup timeout increases to three minutes.

Sequence Diagram(s)

sequenceDiagram
  participant WebhookConfiguration
  participant WebhookValidationController
  participant ControlPlaneClient
  participant GuestClient
  WebhookConfiguration->>WebhookValidationController: webhook event
  WebhookValidationController->>ControlPlaneClient: list control-plane services
  ControlPlaneClient-->>WebhookValidationController: service URLs
  WebhookValidationController->>GuestClient: get webhook configuration
  GuestClient-->>WebhookValidationController: webhook configuration
  WebhookValidationController->>GuestClient: delete disallowed configuration
Loading

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error webhookvalidation.go:64 logs the complete disallowed webhook URL; the URL can contain internal service hostnames and arbitrary embedded credentials or tokens. Do not log the URL. Log only a fixed reason and, if needed, a non-sensitive webhook identifier; redact URL userinfo, query, and fragment data.
Test Structure And Quality ⚠️ Warning The webhook Ginkgo test has cleanup and a 3-minute Eventually timeout, but its cluster-scoped Create assertion has no diagnostic failure message. Add a message such as "failed to create test-malicious-webhook" to the hcClient.Create assertion; keep the existing DeferCleanup and timeout.
✅ Passed checks (9 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: extracting HCCO webhook validation into a dedicated controller.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All changed Ginkgo titles are static literals; the e2e change only increases a timeout, and new controller tests use fixed table-case names without runtime values.
Topology-Aware Scheduling Compatibility ✅ Passed The PR adds no scheduling constraints. The new HCCO controller only watches and deletes webhook configurations; added-line scans found no affinity, spread, selector, toleration, replica, or PDB set...
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The PR adds no new Ginkgo e2e test; it only changes an existing webhook test timeout. Its URL uses cluster-internal DNS, and existing IP handling uses net.JoinHostPort.
No-Weak-Crypto ✅ Passed The PR adds no MD5, SHA-1, DES, 3DES, RC4, Blowfish, or ECB code; its only crypto use is existing SHA-256 naming, and comparisons target webhook URLs.
Container-Privileges ✅ Passed The PR changes no container or Kubernetes manifest. New code only registers a webhook controller; no privileged, hostPID, hostNetwork, hostIPC, SYS_ADMIN, or allowPrivilegeEscalation setting was ad...
✨ 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 added area/platform/azure PR/issue for Azure (AzurePlatform) platform area/platform/gcp PR/issue for GCP (GCPPlatform) platform area/platform/kubevirt PR/issue for KubeVirt (KubevirtPlatform) platform area/platform/none PR/issue for None (NonePlatform) platform - user-supplied infrastructure area/platform/openstack PR/issue for OpenStack (OpenStackPlatform) platform area/platform/powervs PR/issue for PowerVS (PowerVSPlatform) platform area/testing Indicates the PR includes changes for e2e testing labels Aug 6, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@bryan-cox: This pull request references Jira Issue OCPBUGS-105193, which is valid.

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

In response to this:

What this PR does / why we need it:

Extracts ensureGuestAdmissionWebhooksAreValid() from the monolithic HCCO resources controller into a dedicated webhook-validation controller. The [Feature:WebhookValidation] e2e test has a 47% pass rate on e2e-v2-azure-self-managed because webhook validation runs at the tail of a 15+ sub-reconciler chain, causing 60+ second delays between webhook creation and deletion.

The new controller watches ValidatingWebhookConfiguration and MutatingWebhookConfiguration directly, so reconciliation triggers immediately on webhook events. It encodes the webhook type in the request namespace field so Reconcile() targets only the type that fired, avoiding a redundant Get for the other kind.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/OCPBUGS-105193

Special notes for your reviewer:

  • The HCCO manager cache already includes VWC and MWC with labels.Everything() (no label filter) — see operator/config.go:129-130 — so the resources controller retains its watches for ensureResourceCreationIsBlocked
  • Two commits: (1) the extraction refactor, (2) e2e timeout bump (separate rationale)
  • 13 unit tests: 7 table-driven reconcile cases + 2 error-path tests + 4 URL validation tests

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

Summary by CodeRabbit

  • New Features

  • Private clusters now use internal API server routes correctly, including when no hostname is configured.

  • Added DNS resolution checks for Azure external ignition endpoints.

  • Improved global pull-secret synchronization with readiness checks and rolling updates.

  • Added lifecycle manifests and informing-test reports for clearer end-to-end test results.

  • Expanded AWS storage capabilities for snapshot restores, volume status, copying, and snapshot locking.

  • Bug Fixes

  • Improved Azure private DNS cleanup and legacy resource detection.

  • Corrected limited-support metric reporting and route status handling.

  • Documentation

  • Added comprehensive end-to-end test-flow guidance and a July 2026 progress report.

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 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: 18

🧹 Nitpick comments (3)
control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation.go (1)

74-90: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider making the type assertions safe.

Both accessors use unchecked type assertions. Reconcile pairs the object and the accessor positionally at Lines 42-46. If a future edit swaps a pair, the controller panics instead of returning an error.

A comma-ok assertion converts that failure into a reconcile error.

♻️ Optional: return an error instead of panicking
-func validatingWebhookURLs(obj client.Object) []*string {
-	vwc := obj.(*admissionregistrationv1.ValidatingWebhookConfiguration)
+func validatingWebhookURLs(obj client.Object) ([]*string, error) {
+	vwc, ok := obj.(*admissionregistrationv1.ValidatingWebhookConfiguration)
+	if !ok {
+		return nil, fmt.Errorf("expected *admissionregistrationv1.ValidatingWebhookConfiguration, got %T", obj)
+	}
 	urls := make([]*string, 0, len(vwc.Webhooks))
 	for i := range vwc.Webhooks {
 		urls = append(urls, vwc.Webhooks[i].ClientConfig.URL)
 	}
-	return urls
+	return urls, nil
 }

Based on the coding guideline "Avoid panics except in truly unrecoverable cases."

🤖 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
`@control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation.go`
around lines 74 - 90, Update validatingWebhookURLs and mutatingWebhookURLs to
use comma-ok type assertions and return an error alongside the URL slice when
the object has an unexpected type. Propagate that error through the positional
accessor calls in Reconcile so mismatched object/accessor pairs produce a
reconcile error instead of panicking.

Source: Coding guidelines

control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation_test.go (1)

86-94: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a case for an unrecognized webhook type.

The default branch of the switch in Reconcile returns no error and takes no action. No test covers it. A case with an unexpected req.Namespace would pin that contract.

Consider also replacing expectWebhookGone and expectWebhookAlive with one field. Two independent booleans allow a case that asserts nothing when an author sets neither.

💚 Suggested extra table case
 		{
 			name:        "When webhook config does not exist, it should return without error",
 			webhookType: webhookTypeValidating,
@@
 			guestObjects:  []client.Object{},
 			reconcileName: "nonexistent-webhook",
 		},
+		{
+			name:               "When the webhook type is unrecognized, it should preserve the webhook",
+			webhookType:        "unknown",
+			cpServices:         []corev1.Service{},
+			guestObjects:       []client.Object{},
+			reconcileName:      "any-webhook",
+		},

The unknown-type case needs an assertion path that does not depend on assertWebhookExists, because that helper also switches on the webhook type.

Also applies to: 255-268

🤖 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
`@control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation_test.go`
around lines 86 - 94, Extend the Reconcile table tests with an unrecognized
webhook type, such as an unexpected req.Namespace, and assert it returns no
error without changing resources; use an assertion path that does not call
assertWebhookExists. Replace the independent expectWebhookGone and
expectWebhookAlive fields with a single expectation field or equivalent so every
case must explicitly define its webhook outcome, updating the table cases and
assertions accordingly.
test/e2e/v2/lifecycle/manifest_test.go (1)

16-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use the required test-case description format.

Start each table test name with When. Include it should after the condition. Apply this format to all cases in this file.

As per coding guidelines, unit-test descriptions must use When ... it should ....

Also applies to: 67-117, 155-156

🤖 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 `@test/e2e/v2/lifecycle/manifest_test.go` around lines 16 - 29, Update every
table-test name in the manifest tests, including the cases near the shown
single- and multiple-cluster entries and the other referenced ranges, to start
with “When” and include “it should” between the condition and expected behavior.
Preserve each test’s existing scenario meaning while applying the required “When
... it should ...” format consistently throughout the file.

Source: Coding guidelines

🤖 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 @.github/workflows/reusable-claude-on-pr.yaml:
- Around line 141-149: Update the token-selection flow around PUSH_TOKEN and
steps.app-token.outputs.token so every supported PR_REPO receives a
write-capable installation token, including external forks. Reject unsupported
PR_REPO values before checkout and Claude execution rather than falling back to
github.token, and preserve the existing push flow for validated repositories.

In `@cmd/infra/aws/iam.go`:
- Around line 118-124: Update awsEBSCSIPermPolicy to define a CSI ownership tag
and restrict ec2:LockSnapshot to snapshot resources carrying that tag. Apply the
same ownership/resource constraints to ec2:CreateTags and ec2:DeleteTags so the
controller cannot retag unrelated snapshots to bypass the lock condition, while
preserving unrelated EC2 permissions.

In `@cmd/infra/azure/networking.go`:
- Line 185: The private DNS zone provisioning and cleanup paths must preserve
exact zone identity across the naming transition. In
cmd/infra/azure/networking.go lines 185-185, update the flow around
BeginCreateOrUpdate to first look up and reuse the current or legacy DNS zone
before creating a current-format zone. In cmd/infra/azure/destroy.go lines
225-229, update the destroy matching logic to require the exact private DNS zone
identity using the base domain or an immutable ownership tag, and avoid deleting
unrelated resource types by name prefix.

In `@control-plane-operator/controllers/hostedcontrolplane/infra/infra.go`:
- Around line 255-277: Wrap each of the three DeleteIfNeeded errors in the
external route reconciliation branches with contextual errors that identify the
deletion action and the affected route name. Update the handlers for
externalPrivateRoute and externalPublicRoute deletions while preserving the
existing error propagation behavior.

In `@control-plane-operator/controllers/hostedcontrolplane/kas/service.go`:
- Around line 180-187: Add unit tests in service_test.go covering
ReconcileServiceStatus when strategy.Route.Hostname is empty, verifying it falls
back first to svc.Status.LoadBalancer.Ingress[0].Hostname and then to the
ingress IP when no hostname is present. Preserve the existing Route hostname
precedence and assert the resulting status for each fallback case.

In
`@control-plane-operator/hostedclusterconfigoperator/controllers/globalps/globalps_test.go`:
- Around line 520-521: Update the scheme setup in the test to assert the errors
returned by corev1.AddToScheme and appsv1.AddToScheme instead of discarding
them, so registration failures are reported at the source.

In
`@control-plane-operator/hostedclusterconfigoperator/controllers/globalps/globalps.go`:
- Around line 181-188: The reconciliation logic in globalps.go must build the
desired DaemonSet specification before deciding to skip, compare the existing
DaemonSet’s relevant fields—including readiness probe and rolling-update
strategy—in addition to the config seed and volumes, and return non-NotFound
errors from c.Get instead of ignoring them. In globalps_test.go, update the
referenced test to create a matching-seed DaemonSet missing the new fields and
assert reconciliation performs an update.

In
`@control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation.go`:
- Around line 92-118: Update isAllowedWebhookURL to parse each webhook URL and
compare its Hostname() exactly against the disallowed service DNS names
generated by buildDisallowedURLs, replacing substring matching and handling
parse errors safely. Add a regression test covering a service name such as api
not matching a different host such as api.example.com.

In `@docs/content/blog/2026-07-progress-report.md`:
- Line 44: Correct the line-reduction metric in the paragraph around the
architecture changes: the stated net reduction must reconcile with replacing
1,399 lines by 210 lines, or explicitly account for any additional removed lines
before retaining 1,647. Keep the release references and architectural
description unchanged.
- Around line 206-263: The contributor table has 56 rows while the report claims
57 contributors. Reconcile the contributor total by either adding the missing
contributor row to the table or updating the report’s stated total to 56,
ensuring the summary and contributor table agree.

In `@docs/content/how-to/ci/v2-testing/test-flow.md`:
- Line 275: Standardize the process-table wording from “pre step” to “pre-step”
in both affected locations: docs/content/how-to/ci/v2-testing/test-flow.md lines
275-275 and docs/content/reference/e2e-v2-test-flow.md lines 324-324. No other
changes are needed.
- Around line 336-364: Update the flow descriptions in
docs/content/how-to/ci/v2-testing/test-flow.md (lines 336-364) and
docs/content/reference/e2e-v2-test-flow.md (lines 383-413) to document
SHARED_DIR/cluster-manifests.json as the manifest written by create-guests
before provisioning. State that run-tests resolves cluster names, namespaces,
and variants from the manifest, and destroy-guests destroys its entries; remove
the per-variant cluster-name file and PROW_JOB_ID/SHA256 re-derivation claims.

In `@docs/content/reference/e2e-v2-test-flow.md`:
- Line 68: Specify the language on the ASCII flow code block by changing its
opening fence to use the text language, satisfying markdownlint MD040.
- Around line 1-5: Add docs/content/reference/e2e-v2-test-flow.md to the
Reference navigation entries in mkdocs.yml, preserving the existing navigation
structure and ordering.

In `@go.mod`:
- Line 66: Update the Go module dependencies in go.mod and corresponding go.sum
entries to fixed versions that eliminate the reported OSV findings across the
module graphs, including golang.org/x/crypto, golang.org/x/net,
golang.org/x/text, Docker, etcd, OpenTelemetry, and AWS modules. If any finding
cannot be upgraded, document its approved exception; then rerun SCA for ., api,
and hack/tools and verify the findings are resolved.

In `@hack/tools/go.mod`:
- Line 3: Update the Go version declared by the hack/tools module and the
corresponding CI workflow and Dockerfile.github-actions-runner configuration to
Go 1.26.5 or a later patched Go 1.26.x release. Ensure all three toolchain
references stay aligned so actions/setup-go does not install unpatched Go
1.26.0.

In `@test/e2e/v2/lifecycle/platform.go`:
- Around line 53-63: Update the resolve closure to track each variant as visited
before calling manifest.LookupCluster, so repeated missing variants are skipped
as well as successfully resolved ones. Preserve the existing missing append and
resolved assignment behavior for the first lookup.

In `@test/e2e/v2/tests/nodepool_osimagestream_test.go`:
- Around line 424-426: Update the test setup around
NodePoolOSImageStreamDefaultStatusTest to use an isolated NodePool fixture whose
lifecycle teardown deletes it, rather than mutating the shared default NodePool.
Preserve the version-derived default validation while ensuring
spec.osImageStream changes cannot persist and cause later default-resolution
checks to be skipped.

---

Nitpick comments:
In
`@control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation_test.go`:
- Around line 86-94: Extend the Reconcile table tests with an unrecognized
webhook type, such as an unexpected req.Namespace, and assert it returns no
error without changing resources; use an assertion path that does not call
assertWebhookExists. Replace the independent expectWebhookGone and
expectWebhookAlive fields with a single expectation field or equivalent so every
case must explicitly define its webhook outcome, updating the table cases and
assertions accordingly.

In
`@control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation.go`:
- Around line 74-90: Update validatingWebhookURLs and mutatingWebhookURLs to use
comma-ok type assertions and return an error alongside the URL slice when the
object has an unexpected type. Propagate that error through the positional
accessor calls in Reconcile so mismatched object/accessor pairs produce a
reconcile error instead of panicking.

In `@test/e2e/v2/lifecycle/manifest_test.go`:
- Around line 16-29: Update every table-test name in the manifest tests,
including the cases near the shown single- and multiple-cluster entries and the
other referenced ranges, to start with “When” and include “it should” between
the condition and expected behavior. Preserve each test’s existing scenario
meaning while applying the required “When ... it should ...” format consistently
throughout the file.
🪄 Autofix

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: 74754443-d0cd-43bc-81ae-5d93974d7357

📥 Commits

Reviewing files that changed from the base of the PR and between 1d48188 and 3e3e6f3.

⛔ Files ignored due to path filters (273)
  • api/go.sum is excluded by !**/*.sum
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCExternalClaimsSourcing.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/IngressComponentRouteLabels.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryption.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCExternalClaimsSourcing.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/IngressComponentRouteLabels.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryption.yaml is excluded by !**/zz_generated.featuregated-crd-manifests/**
  • api/vendor/github.com/openshift/api/config/v1/types_authentication.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/types_infrastructure.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/types_ingress.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/types_kmsencryption.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/config/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !**/zz_generated*
  • api/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • api/vendor/github.com/openshift/api/operator/v1/types.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/operator/v1/types_kmsencryption.go is excluded by !**/vendor/**
  • api/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • api/vendor/github.com/openshift/api/operator/v1/zz_generated.model_name.go is excluded by !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • api/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • api/vendor/modules.txt is excluded by !**/vendor/**
  • cmd/cluster/azure/testdata/zz_fixture_TestCreateCluster_When_endpoint_access_is_Private_with_endpoint_access_private_flags_it_should_render_HostedCluster_with_Private_endpoint_access.yaml is excluded by !**/testdata/**
  • cmd/infra/aws/delegating_client.go is excluded by !cmd/infra/aws/delegating_client.go
  • 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/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yaml is excluded by !**/zz_generated.crd-manifests/**, !cmd/install/assets/**/*.yaml
  • control-plane-operator/controllers/hostedcontrolplane/infra/testdata/zz_fixture_TestReconcileInfrastructure_When_Azure_Private_cluster_has_Route_strategy_without_hostname__it_should_only_need_an_internal_router.yaml is excluded by !**/testdata/**
  • docs/content/reference/aggregated-docs.md is excluded by !docs/content/reference/aggregated-docs.md
  • go.sum is excluded by !**/*.sum
  • vendor/github.com/fxamacker/cbor/v2/.golangci.yml is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/cache.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/decode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/decode_map_utils.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/diagnose.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/encode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/simplevalue.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/stream.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/structfields.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/tag.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/fxamacker/cbor/v2/valid.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/.cliff.toml is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/.gitignore is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/.golangci.yml is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/NOTICE is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/SECURITY.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/errors.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/ifaces.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/options.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonpointer/pointer.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonreference/.cliff.toml is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonreference/.gitignore is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonreference/.golangci.yml is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonreference/NOTICE is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonreference/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonreference/SECURITY.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/jsonreference/reference.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/.gitignore is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/CONTRIBUTORS.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/SECURITY.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/go.work is excluded by !**/*.work, !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/jsonname/go_name_provider.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/jsonname/ifaces.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/jsonname/name_provider.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/loading/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/loading/loading.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/loading/options.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/go-openapi/swag/mangling/BENCHMARK.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/AUTHORS is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/CONTRIBUTORS is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/gogoproto/Makefile is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/gogoproto/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.golden is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/gogoproto/gogo.proto is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/gogoproto/helper.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/Makefile is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/clone.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/custom_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/decode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/deprecated.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/discard.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/duration.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/duration_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/encode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/encode_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/equal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/extensions.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/extensions_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/lib.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/lib_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/message_set.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/pointer_reflect.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/pointer_reflect_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/pointer_unsafe.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/properties.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/properties_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/skip_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/table_marshal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/table_marshal_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/table_merge.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/table_unmarshal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/table_unmarshal_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/text.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/text_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/text_parser.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/timestamp.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/timestamp_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/wrappers.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/proto/wrappers_gogo.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/Makefile is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/helper.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/googleapis/gax-go/v2/.release-please-manifest.json is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/googleapis/gax-go/v2/CHANGES.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/googleapis/gax-go/v2/internal/version.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/googleapis/gax-go/v2/release-please-config.json is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/googleapis/gax-go/v2/telemetry.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_authentication.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_infrastructure.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_ingress.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/types_kmsencryption.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/api/config/v1alpha1/types_cluster_monitoring.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/config/v1alpha1/zz_generated.deepcopy.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • vendor/github.com/openshift/api/config/v1alpha1/zz_generated.model_name.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • vendor/github.com/openshift/api/config/v1alpha1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/api/envtest-releases.yaml is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/features.md is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/machineconfiguration/v1/types.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/machineconfiguration/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machineconfiguration/v1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/api/operator/v1/types.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/operator/v1/types_kmsencryption.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • vendor/github.com/openshift/api/operator/v1/zz_generated.model_name.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated.swagger_doc_generated.go, !**/zz_generated*
  • vendor/github.com/openshift/api/route/v1/generated.proto is excluded by !vendor/**, !**/vendor/**, !**/generated.proto
  • vendor/github.com/openshift/api/route/v1/types.go is excluded by !vendor/**, !**/vendor/**
  • vendor/github.com/openshift/api/route/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*
  • vendor/go.etcd.io/etcd/api/v3/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/authpb/auth.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/go.etcd.io/etcd/api/v3/authpb/auth.proto is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/authpb/deprecated.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/etcdserverpb/etcdserver.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/go.etcd.io/etcd/api/v3/etcdserverpb/etcdserver.proto is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/etcdserverpb/raft_internal.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/go.etcd.io/etcd/api/v3/etcdserverpb/raft_internal.proto is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/etcdserverpb/raft_internal_stringer.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.proto is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc_grpc.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/go.etcd.io/etcd/api/v3/etcdserverpb/util.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/membershippb/membership.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/go.etcd.io/etcd/api/v3/membershippb/membership.proto is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/mvccpb/deprecated.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/mvccpb/extension.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/mvccpb/kv.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/go.etcd.io/etcd/api/v3/mvccpb/kv.proto is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/version/version.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/api/v3/versionpb/version.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/go.etcd.io/etcd/api/v3/versionpb/version.proto is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/pkg/v3/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_opts.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_tls.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_transport.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/pkg/v3/types/set.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/pkg/v3/types/urls.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/pkg/v3/verify/verify.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/.gomodguard.yaml is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/auth.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/block_logger.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/compare.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/config.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/kv.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/lease.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/logger.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/maintenance.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/op.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/retry.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/retry_interceptor.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/txn.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/client/v3/watch.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/pkg/v3/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/pkg/v3/cpuutil/endian.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/pkg/v3/netutil/host_normalize.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/pkg/v3/netutil/netutil.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/server/v3/LICENSE is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/server/v3/etcdserver/api/membership/cluster.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/server/v3/etcdserver/api/membership/store.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/server/v3/etcdserver/api/membership/storev2.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/etcd/server/v3/etcdserver/errors/errors.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/.go-version is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/.golangci.yaml is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/README.md is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/bootstrap.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/code-of-conduct.md is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/confchange/confchange.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/confchange/restore.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/doc.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/log.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/log_unstable.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/node.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/quorum/majority.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/raft.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/raftpb/alias.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/raftpb/confchange.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/raftpb/confstate.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/raftpb/raft.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/go.etcd.io/raft/v3/raftpb/raft.proto is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/raftpb/util.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/rawnode.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/read_only.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/state_trace.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/state_trace_nop.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/status.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/storage.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/tracker/tracker.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/types.go is excluded by !vendor/**, !**/vendor/**
  • vendor/go.etcd.io/raft/v3/util.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/compute/v1/compute-api.json is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/compute/v1/compute-gen.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/compute/v1/compute2-gen.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/compute/v1/compute3-gen.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/dns/v1/dns-api.json is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/iam/v1/iam-api.json is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/iam/v1/iam-gen.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/internal/version.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/storage/v1/storage-api.json is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/api/storage/v1/storage-gen.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/balancer/balancer.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/dialoptions.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/encoding/encoding.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/encoding/gzip/gzip.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/experimental/balancer/weight/weight.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/google.golang.org/grpc/internal/envconfig/envconfig.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/envconfig/xds.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/resolver/config_selector.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/stats/labels.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/transport/client_stream.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/transport/controlbuf.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/transport/flowcontrol.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/transport/handler_server.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/transport/http2_client.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/transport/http2_server.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/transport/internal/internal.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/internal/transport/transport.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go is excluded by !**/*.pb.go, !vendor/**, !**/vendor/**, !**/*.pb.go
  • vendor/google.golang.org/grpc/rpc_util.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/server.go is excluded by !vendor/**, !**/vendor/**
  • vendor/google.golang.org/grpc/version.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/helpers.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.go is excluded by !vendor/**, !**/vendor/**
  • vendor/k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.go is excluded by !vendor/**, !**/vendor/**, !**/zz_generated*.go, !**/zz_generated*
  • vendor/modules.txt is excluded by !vendor/**, !**/vendor/**
📒 Files selected for processing (72)
  • .github/workflows/rebase.yaml
  • .github/workflows/restructure-commits.yaml
  • .github/workflows/reusable-claude-on-pr.yaml
  • OWNERS_ALIASES
  • api/go.mod
  • cmd/cluster/aws/create.go
  • cmd/cluster/azure/create.go
  • cmd/cluster/core/create.go
  • cmd/cluster/core/create_test.go
  • cmd/cluster/gcp/create.go
  • cmd/cluster/kubevirt/create.go
  • cmd/cluster/none/create.go
  • cmd/cluster/openstack/create.go
  • cmd/cluster/powervs/create.go
  • cmd/infra/aws/iam.go
  • cmd/infra/azure/destroy.go
  • cmd/infra/azure/networking.go
  • contrib/konflux/cpo_4_14_stream.yaml
  • control-plane-operator/controllers/hostedcontrolplane/infra/infra.go
  • control-plane-operator/controllers/hostedcontrolplane/infra/infra_test.go
  • control-plane-operator/controllers/hostedcontrolplane/kas/service.go
  • control-plane-operator/hostedclusterconfigoperator/cmd.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/globalps/globalps.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/globalps/globalps_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/globalps/setup.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/globalps/setup_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/setup.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation_test.go
  • docs/content/blog/2026-07-progress-report.md
  • docs/content/blog/index.md
  • docs/content/how-to/ci/triage/presubmit-failures.md
  • docs/content/how-to/ci/v2-testing/ci-pipeline.md
  • docs/content/how-to/ci/v2-testing/debugging.md
  • docs/content/how-to/ci/v2-testing/index.md
  • docs/content/how-to/ci/v2-testing/test-flow.md
  • docs/content/reference/e2e-v2-test-flow.md
  • docs/mkdocs.yml
  • go.mod
  • hack/tools/go.mod
  • hypershift-operator/controllers/hostedcluster/hostedcluster_controller.go
  • hypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.go
  • hypershift-operator/controllers/hostedcluster/hostedcluster_webhook.go
  • hypershift-operator/controllers/hostedcluster/hostedcluster_webhook_test.go
  • hypershift-operator/controllers/hostedcluster/metrics/metrics.go
  • hypershift-operator/controllers/hostedcluster/metrics/metrics_test.go
  • hypershift-operator/controllers/hostedcluster/reconcile_legacy.go
  • hypershift-operator/controllers/nodepool/conditions.go
  • hypershift-operator/controllers/nodepool/conditions_test.go
  • hypershift-operator/controllers/nodepool/nodepool_controller.go
  • support/awsapi/ec2.go
  • support/globalconfig/dns.go
  • support/globalconfig/dns_test.go
  • test/e2e/OWNERS
  • test/e2e/v2/AGENTS.md
  • test/e2e/v2/cmd/create-guests/main.go
  • test/e2e/v2/cmd/destroy-guests/main.go
  • test/e2e/v2/cmd/dump-guests/main.go
  • test/e2e/v2/cmd/run-tests/main.go
  • test/e2e/v2/internal/fail_handler.go
  • test/e2e/v2/internal/junit.go
  • test/e2e/v2/internal/junit_test.go
  • test/e2e/v2/lifecycle/aws.go
  • test/e2e/v2/lifecycle/azure.go
  • test/e2e/v2/lifecycle/manifest.go
  • test/e2e/v2/lifecycle/manifest_test.go
  • test/e2e/v2/lifecycle/platform.go
  • test/e2e/v2/tests/hosted_cluster_security_test.go
  • test/e2e/v2/tests/nodepool_osimagestream_test.go
  • test/e2e/v2/tests/suite_test.go
💤 Files with no reviewable changes (2)
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.go
  • control-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.go

Comment on lines +141 to +149
GH_TOKEN: ${{ github.token }}
PUSH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
PR_REPO: ${{ steps.pr.outputs.repo }}
PR_BRANCH: ${{ steps.pr.outputs.branch }}
PR_NUMBER: ${{ github.event.issue.number }}
REPO: ${{ github.repository }}
run: |
git remote set-url origin "https://x-access-token:${PUSH_TOKEN}@github.com/${PR_REPO}.git"
if ! git -c core.hooksPath=/dev/null push --force-with-lease origin "HEAD:${PR_BRANCH}" 2>&1; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

file=".github/workflows/reusable-claude-on-pr.yaml"
printf '%s\n' "--- workflow excerpt ---"
sed -n '1,220p' "$file"

printf '%s\n' "--- related token, checkout, push, and fork logic ---"
rg -n -C 4 'app-token|PUSH_TOKEN|PR_REPO|PR_BRANCH|checkout|pull_request|issue|fork|permissions|push --force-with-lease|hypershift-community/hypershift' "$file" .github/workflows Makefile 2>/dev/null || true

Repository: openshift/hypershift

Length of output: 50376


🏁 Script executed:

# Awaiting the repository inspection results.

Repository: openshift/hypershift

Length of output: 158


🌐 Web query:

GitHub Actions GITHUB_TOKEN push to pull request fork branch "Allow edits from maintainers"

💡 Result:

When a pull request is submitted from a forked repository, the GITHUB_TOKEN is automatically set to read-only mode by GitHub for security reasons, regardless of any workflow permissions settings [1][2][3]. This prevents unauthorized modifications to your repository from potentially malicious code in a fork [1][2]. If you need to perform write operations (such as pushing commits or adding comments) on a pull request from a fork, you have two primary options: 1. Allow Edits from Maintainers: If the pull request author enables "Allow edits from maintainers" (or "Allow edits and access to secrets by maintainers" if Actions are present) on their pull request, repository maintainers with push access can manually push changes to that branch [4][5]. Note that this grants maintainers access to the forked branch, not the GITHUB_TOKEN itself [4][5]. 2. Use the pull_request_target event: If you need a workflow to run with write permissions and access to secrets triggered by a pull request from a fork, you must use the pull_request_target event [1][6][7]. Unlike pull_request, which runs using the code from the pull request's head, pull_request_target runs in the context of the base repository (the "target") and uses the workflow file from the base branch [1][2]. Security Warning: Using pull_request_target is inherently risky if the workflow checks out or executes code from the untrusted fork, as it grants that code access to the base repository's secrets and read/write tokens [1][2][7]. It should only be used for metadata-based operations (like labeling or commenting) where you do not check out or execute the PR's source code [2][7]. If you need to build and test code from a fork, consider a two-workflow pattern where a pull_request workflow builds the code and uploads an artifact, and a separate workflow_run trigger executes the privileged action using that artifact [2].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' "--- callers of reusable workflow ---"
rg -n -C 8 'reusable-claude-on-pr\.yaml|uses:.*claude-on-pr|secrets: inherit|permissions:' .github/workflows --glob '*.yaml' --glob '*.yml' | head -300

printf '%s\n' "--- exact workflow metadata and token-related declarations ---"
awk 'NR <= 165 { printf "%4d %s\n", NR, $0 }' .github/workflows/reusable-claude-on-pr.yaml
printf '%s\n' "--- permissions in relevant caller workflows ---"
for f in .github/workflows/*.yaml .github/workflows/*.yml; do
  [ -f "$f" ] || continue
  if rg -q 'reusable-claude-on-pr|claude-on-pr' "$f"; then
    printf '\nFILE %s\n' "$f"
    sed -n '1,120p' "$f"
  fi
done

Repository: openshift/hypershift

Length of output: 27819


🏁 Script executed:

# Awaiting the focused workflow and documentation results.

Repository: openshift/hypershift

Length of output: 158


Provide a write-capable token for every supported PR repository.

For PRs from external forks other than hypershift-community/hypershift, PUSH_TOKEN falls back to github.token. That token is scoped to openshift/hypershift and cannot push to the external fork. “Allow edits from maintainers” does not grant this token access to the fork. Generate an installation token for each supported fork, or reject unsupported PR_REPO values before checkout and Claude execution. Otherwise, rebase and restructure operations fail at the push step.

🤖 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 @.github/workflows/reusable-claude-on-pr.yaml around lines 141 - 149, Update
the token-selection flow around PUSH_TOKEN and steps.app-token.outputs.token so
every supported PR_REPO receives a write-capable installation token, including
external forks. Reject unsupported PR_REPO values before checkout and Claude
execution rather than falling back to github.token, and preserve the existing
push flow for validated repositories.

Source: MCP tools

Comment thread cmd/infra/aws/iam.go
Comment on lines +118 to +124
"ec2:ModifyVolume",
"ec2:DescribeAvailabilityZones",
"ec2:EnableFastSnapshotRestores",
"ec2:DescribeInstanceTypes",
"ec2:DescribeVolumeStatus",
"ec2:CopyVolumes",
"ec2:LockSnapshot"

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the CSI snapshot ownership-tag convention and all affected IAM bindings.
rg -n -C 4 'LockSnapshot|CreateSnapshot|CSIVolumeSnapshotName|VolumeSnapshotName|ebs\.csi\.aws\.com' .
rg -n -C 5 'awsEBSCSIPermPolicy|rosaManagedPolicyARN|CreateOIDCRole|CreateSharedOIDCRole' cmd/infra/aws

Repository: openshift/hypershift

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- IAM policy ---'
sed -n '1,180p' cmd/infra/aws/iam.go

printf '%s\n' '--- non-vendor snapshot and tag references ---'
rg -n -C 3 --glob '!vendor/**' --glob '!**/testdata/**' \
  'LockSnapshot|CreateSnapshot|CSIVolumeSnapshotName|VolumeSnapshotName|ebs\.csi\.aws\.com|kubernetes\.io/created-for' .

printf '%s\n' '--- related policy definitions ---'
rg -n -C 8 --glob '!vendor/**' \
  'awsEBSCSIPermPolicy|awsEBSCSIPolicy|EBSCSI|snapshot' cmd/infra/aws

Repository: openshift/hypershift

Length of output: 50377


🌐 Web query:

AWS EC2 LockSnapshot IAM resource-level permissions condition keys snapshot tags

💡 Result:

To implement IAM resource-level permissions for the ec2:LockSnapshot action, you can use resource-level constraints and specific condition keys to control how snapshots are locked [1]. Resource-Level Permissions The ec2:LockSnapshot action supports resource-level permissions [2]. You can restrict the policy to specific snapshots by using their Amazon Resource Name (ARN) in the Resource element of your IAM policy [1][2]. { "Effect": "Allow", "Action": "ec2:LockSnapshot", "Resource": "arn:aws:ec2:region:account-id:snapshot/snap-example" } Restricting with Condition Keys In addition to resource-level constraints, you can use EC2-specific condition keys to govern the locking behavior [1]: 1. ec2:SnapshotLockDuration: Restricts users to specific lock durations [1]. Example: Restricting the duration to between 10 and 50 days: "Condition": { "NumericGreaterThan": { "ec2:SnapshotLockDuration": 10 }, "NumericLessThan": { "ec2:SnapshotLockDuration": 50 } } 2. ec2:CoolOffPeriod: Restricts the cooling-off period when locking snapshots in compliance mode [1]. Example: Requiring a cooling-off period greater than 48 hours: "Condition": { "NumericGreaterThan": { "ec2:CoolOffPeriod": 48 } } Using Snapshot Tags You can also control access based on tags attached to the snapshot using the aws:ResourceTag/tag-key condition key [3][4]. This allows for attribute-based access control (ABAC), ensuring that only users with the appropriate permissions can lock snapshots with specific tags [4]. { "Effect": "Allow", "Action": "ec2:LockSnapshot", "Resource": "*", "Condition": { "StringEquals": { "aws:ResourceTag/Project": "SecretProject" } } } Security Best Practice: If you implement tag-based access control for snapshot locks, it is strongly recommended that you explicitly deny the ability to modify those same tags (ec2:CreateTags, ec2:DeleteTags) for those resources to prevent users from bypassing your restrictions by changing the tags [5][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- changed policy statement ---'
sed -n '95,135p' cmd/infra/aws/iam.go

printf '%s\n' '--- snapshot and tag identifiers outside generated/vendor files ---'
rg -n -C 2 \
  --glob '!vendor/**' \
  --glob '!api/**' \
  --glob '!**/testdata/**' \
  'CSIVolumeSnapshotName|VolumeSnapshotName|snapshot.*tag|tag.*snapshot|kubernetes\.io/created-for|ebs\.csi\.aws\.com|CreateSnapshot|DeleteSnapshot|CreateTags|DeleteTags|LockSnapshot' \
  cmd hack hypershift control-plane-operator 2>/dev/null || true

printf '%s\n' '--- all relevant IAM actions in this policy ---'
python3 - <<'PY'
from pathlib import Path
text = Path("cmd/infra/aws/iam.go").read_text()
start = text.index("awsEBSCSIPermPolicy")
end = text.find("]", start)
print(text[start:end + 1])
PY

Repository: openshift/hypershift

Length of output: 50376


🌐 Web query:

site:docs.aws.amazon.com/ebs/latest/userguide/snapshot-lock-iam.html ec2:LockSnapshot aws:ResourceTag CreateTags DeleteTags

💡 Result:

The ec2:LockSnapshot permission is an IAM action used to control access to locking Amazon EBS snapshots [1]. Users require this specific permission, along with others like ec2:UnlockSnapshot and ec2:DescribeLockedSnapshots, to manage snapshot locks [1]. There is no documented direct functional link or mandatory requirement between ec2:LockSnapshot and the permissions ec2:CreateTags or ec2:DeleteTags, nor is there documented support for using aws:ResourceTag condition keys specifically with the ec2:LockSnapshot action in the provided source material [1]. Access control for snapshot locking is primarily managed through specific IAM policy actions and condition keys such as ec2:SnapshotLockDuration and ec2:CoolOffPeriod [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

url='https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html'
html="$(curl -fsSL "$url")"

python3 - "$html" <<'PY'
import re
import sys
from html import unescape

text = unescape(sys.argv[1])
for match in re.finditer(r'LockSnapshot', text, re.IGNORECASE):
    start = max(0, match.start() - 1500)
    end = min(len(text), match.end() + 2500)
    print(re.sub(r'<[^>]+>', ' ', text[start:end]))
    print('---')
PY

Repository: openshift/hypershift

Length of output: 158


🌐 Web query:

site:docs.aws.amazon.com/service-authorization/latest/reference/list_amazonec2.html "LockSnapshot" "Resource types" "Condition keys"

💡 Result:

LockSnapshot in Amazon EC2 IAM:

  • Description: Grants permission to lock an Amazon EBS snapshot in governance or compliance mode.
  • Access level: Write
  • Resource type: snapshot* (required)
  • Condition keys: aws:ResourceTag/${TagKey}, ec2:Encrypted, ec2:Owner, ec2:ParentVolume, ec2:ResourceTag/${TagKey}, ec2:SnapshotCoolOffPeriod, ec2:SnapshotID, ec2:SnapshotLockDuration, ec2:SnapshotTime, ec2:VolumeSize, and ec2:Region. [1] (docs.aws.amazon.com)

Citations:


Scope ec2:LockSnapshot to CSI-owned snapshots.

awsEBSCSIPermPolicy grants ec2:LockSnapshot with "Resource": "*", so the controller role can lock any EBS snapshot. AWS supports snapshot resources and resource-tag conditions for this action. This repository does not define an existing CSI ownership tag. Define one, restrict ec2:LockSnapshot to it, and scope ec2:CreateTags and ec2:DeleteTags so the role cannot bypass the condition by retagging arbitrary snapshots.

🤖 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 `@cmd/infra/aws/iam.go` around lines 118 - 124, Update awsEBSCSIPermPolicy to
define a CSI ownership tag and restrict ec2:LockSnapshot to snapshot resources
carrying that tag. Apply the same ownership/resource constraints to
ec2:CreateTags and ec2:DeleteTags so the controller cannot retag unrelated
snapshots to bypass the lock condition, while preserving unrelated EC2
permissions.

Location: ptr.To("global"),
}
privateDNSZonePromise, err := privateZoneClient.BeginCreateOrUpdate(ctx, resourceGroupName, name+"-azurecluster."+baseDomain, privateZoneParams, nil)
privateDNSZonePromise, err := privateZoneClient.BeginCreateOrUpdate(ctx, resourceGroupName, name+"."+baseDomain, privateZoneParams, nil)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve exact Azure DNS-zone identity during the naming transition.

The new name.baseDomain format has no legacy discriminator. A retry for a legacy cluster can create a second private DNS zone. A preserved resource-group destroy can delete unrelated resources whose names start with name..

  • cmd/infra/azure/networking.go#L185-L185: Look up and reuse the current or legacy DNS zone before creating a new current-format zone.
  • cmd/infra/azure/destroy.go#L225-L229: Match only an exact private DNS zone identity. Pass the base domain or use an immutable ownership tag. Do not delete all resource types by name prefix.
📍 Affects 2 files
  • cmd/infra/azure/networking.go#L185-L185 (this comment)
  • cmd/infra/azure/destroy.go#L225-L229
🤖 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 `@cmd/infra/azure/networking.go` at line 185, The private DNS zone provisioning
and cleanup paths must preserve exact zone identity across the naming
transition. In cmd/infra/azure/networking.go lines 185-185, update the flow
around BeginCreateOrUpdate to first look up and reuse the current or legacy DNS
zone before creating a current-format zone. In cmd/infra/azure/destroy.go lines
225-229, update the destroy matching logic to require the exact private DNS zone
identity using the base domain or an immutable ownership tag, and avoid deleting
unrelated resource types by name prefix.

Comment on lines +255 to +277
if _, err := k8sutil.DeleteIfNeeded(ctx, r.Client, externalPrivateRoute); err != nil {
return err
}
// Reconcile the external public route
if _, err := createOrUpdate(ctx, r.Client, externalPublicRoute, func() error {
hostname := ""
if serviceStrategy.Route != nil {
hostname = serviceStrategy.Route.Hostname
}
return kas.ReconcileExternalPublicRoute(externalPublicRoute, p.OwnerReference, hostname)
}); err != nil {
return fmt.Errorf("failed to reconcile apiserver external public route %s: %w", externalPublicRoute.Name, err)
}
} else {
// Remove the external public route if it exists
err := r.Client.Get(ctx, client.ObjectKeyFromObject(externalPublicRoute), externalPublicRoute)
if err != nil {
if !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to check whether apiserver external public route exists: %w", err)
if _, err := k8sutil.DeleteIfNeeded(ctx, r.Client, externalPublicRoute); err != nil {
return err
}
// Reconcile the external private route only when a hostname is configured.
// Private clusters without external DNS (no hostname) use only the internal route.
if hostname != "" {
if _, err := createOrUpdate(ctx, r.Client, externalPrivateRoute, func() error {
return kas.ReconcileExternalPrivateRoute(externalPrivateRoute, p.OwnerReference, hostname)
}); err != nil {
return fmt.Errorf("failed to reconcile apiserver external private route %s: %w", externalPrivateRoute.Name, err)
}
} else {
if err := r.Client.Delete(ctx, externalPublicRoute); err != nil {
return fmt.Errorf("failed to delete apiserver external public route: %w", err)
if _, err := k8sutil.DeleteIfNeeded(ctx, r.Client, externalPrivateRoute); err != nil {
return err

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

Wrap route deletion errors with route context.

The three DeleteIfNeeded calls return raw errors. Include the route name and deletion action. This makes reconciliation failures diagnosable.

Proposed fix
 if _, err := k8sutil.DeleteIfNeeded(ctx, r.Client, externalPrivateRoute); err != nil {
-	return err
+	return fmt.Errorf("failed to delete apiserver external private route %s: %w", externalPrivateRoute.Name, err)
 }

As per coding guidelines, “Wrap errors with context when rethrowing.”

🤖 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 `@control-plane-operator/controllers/hostedcontrolplane/infra/infra.go` around
lines 255 - 277, Wrap each of the three DeleteIfNeeded errors in the external
route reconciliation branches with contextual errors that identify the deletion
action and the affected route name. Update the handlers for externalPrivateRoute
and externalPublicRoute deletions while preserving the existing error
propagation behavior.

Source: Coding guidelines

Comment on lines +180 to +187
switch {
case strategy.Route != nil && strategy.Route.Hostname != "":
host = strategy.Route.Hostname
case svc.Status.LoadBalancer.Ingress[0].Hostname != "":
host = svc.Status.LoadBalancer.Ingress[0].Hostname
case svc.Status.LoadBalancer.Ingress[0].IP != "":
host = svc.Status.LoadBalancer.Ingress[0].IP
}

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline control-plane-operator/controllers/hostedcontrolplane/kas/service_test.go --items all
rg -n -C 8 'ReconcileServiceStatus|Route.*Hostname|LoadBalancer.*Ingress' \
  control-plane-operator/controllers/hostedcontrolplane/kas/service_test.go

Repository: openshift/hypershift

Length of output: 816


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- implementation ---'
cat -n control-plane-operator/controllers/hostedcontrolplane/kas/service.go | sed -n '130,215p'

printf '%s\n' '--- tests ---'
cat -n control-plane-operator/controllers/hostedcontrolplane/kas/service_test.go | sed -n '1,430p'

printf '%s\n' '--- package references ---'
rg -n -C 5 'ReconcileServiceStatus|strategy\.Route|LoadBalancer\.Ingress|Status\.LoadBalancer' \
  control-plane-operator/controllers/hostedcontrolplane/kas

Repository: openshift/hypershift

Length of output: 29927


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- all tests and callers ---'
rg -n -C 6 'ReconcileServiceStatus\(' .
rg -n -C 8 'func CollectLBMessageIfNotProvisioned|CollectLBMessageIfNotProvisioned\(' \
  support control-plane-operator

printf '%s\n' '--- route/status implementation context ---'
cat -n control-plane-operator/controllers/hostedcontrolplane/kas/service.go | sed -n '340,430p'

printf '%s\n' '--- relevant change summary ---'
git diff --stat
git status --short

Repository: openshift/hypershift

Length of output: 26899


Add unit tests for Route hostname fallbacks.

service_test.go has no coverage for ReconcileServiceStatus. Add cases where strategy.Route.Hostname is empty and the Service status contains a hostname or an IP.

🤖 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 `@control-plane-operator/controllers/hostedcontrolplane/kas/service.go` around
lines 180 - 187, Add unit tests in service_test.go covering
ReconcileServiceStatus when strategy.Route.Hostname is empty, verifying it falls
back first to svc.Status.LoadBalancer.Ingress[0].Hostname and then to the
ingress IP when no hostname is present. Preserve the existing Route hostname
precedence and assert the resulting status for each fallback case.

Source: Coding guidelines


### How These Layers Compose

```

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

Specify a language for the ASCII flow block.

Add text after the opening fence to satisfy markdownlint MD040.

Proposed fix
-```
+```text
📝 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
```
🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 68-68: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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 `@docs/content/reference/e2e-v2-test-flow.md` at line 68, Specify the language
on the ASCII flow code block by changing its opening fence to use the text
language, satisfying markdownlint MD040.

Source: Linters/SAST tools

Comment thread go.mod
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/openshift/api v0.0.0-20260715165912-72066cc9718b
github.com/openshift/api v0.0.0-20260805160557-b61243060d5f

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 | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

for module in . api hack/tools; do
  echo "== ${module} =="
  (cd "${module}" && go mod tidy -diff && go mod verify)
done

if rg -n --type go 'github\.com/gogo/protobuf' .; then
  echo "A Go source file still imports github.com/gogo/protobuf" >&2
  exit 1
fi

Repository: openshift/hypershift

Length of output: 50376


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== module files =="
git ls-files 'go.mod' 'go.sum' 'api/go.mod' 'api/go.sum' 'hack/tools/go.mod' 'hack/tools/go.sum' \
  'vendor/modules.txt' 'api/vendor/modules.txt' 'hack/tools/vendor/modules.txt'

echo "== gogo references outside vendored trees =="
rg -n --glob '*.go' --glob '!vendor/**' --glob '!**/vendor/**' \
  'github\.com/gogo/protobuf' . || true

echo "== gogo references in module manifests and vendor metadata =="
rg -n 'github\.com/gogo/protobuf' \
  go.mod go.sum api/go.mod api/go.sum hack/tools/go.mod hack/tools/go.sum \
  vendor/modules.txt api/vendor/modules.txt hack/tools/vendor/modules.txt 2>/dev/null || true

echo "== vendor metadata presence and counts =="
for f in vendor/modules.txt api/vendor/modules.txt hack/tools/vendor/modules.txt; do
  if test -f "$f"; then
    printf '%s: ' "$f"
    wc -l < "$f"
  else
    echo "$f: absent"
  fi
done

echo "== repository status summary =="
git status --short

Repository: openshift/hypershift

Length of output: 3109


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

modules = [
    ("go.mod", "go.sum", "vendor/modules.txt"),
    ("api/go.mod", "api/go.sum", "api/vendor/modules.txt"),
    ("hack/tools/go.mod", "hack/tools/go.sum", "hack/tools/vendor/modules.txt"),
]

for mod_path, sum_path, vendor_path in modules:
    print(f"== {mod_path} ==")
    mod = Path(mod_path)
    if not mod.exists():
        print("module manifest: absent")
        continue
    text = mod.read_text()
    gogo_mod = re.findall(r'^\s*github\.com/gogo/protobuf\s+(\S+)', text, re.M)
    print("gogo in go.mod:", gogo_mod or "none")

    sums = Path(sum_path)
    print("go.sum:", "present" if sums.exists() else "absent")
    if sums.exists():
        gogo_sum = [line for line in sums.read_text().splitlines()
                    if line.startswith("github.com/gogo/protobuf ")]
        print("gogo in go.sum:", gogo_sum or "none")

    vendor = Path(vendor_path)
    print("vendor/modules.txt:", "present" if vendor.exists() else "absent")
    if vendor.exists():
        lines = vendor.read_text().splitlines()
        hits = [line for line in lines if "github.com/gogo/protobuf" in line]
        print("gogo in vendor metadata:", hits or "none")
        print("vendor metadata module entries:", sum(line.startswith("# ") for line in lines))
PY

Repository: openshift/hypershift

Length of output: 2221


🌐 Web query:

OSV API query for all modules changed in openshift/hypershift go.mod, including github.com/openshift/api at pseudo-version b61243060d5f and the removed github.com/gogo/protobuf dependency

💡 Result:

The Open Source Vulnerability (OSV) API is designed to query for known security vulnerabilities associated with specific commits or package versions [1][2][3]. It is not a tool to inspect or compare changes between go.mod files, such as tracking the addition, removal, or pseudo-version updates of specific dependencies like github.com/openshift/api or github.com/gogo/protobuf in the openshift/hypershift repository [2][4]. To track dependency changes within the openshift/hypershift go.mod file, you should use native Git or Go tooling rather than the OSV API [5][6]. Recommended approaches: 1. Use git diff to see changes between specific commits: git diff -- go.mod 2. Use go mod graph or go mod why to inspect dependency trees: go mod graph go mod why -m github.com/gogo/protobuf 3. If you are specifically trying to identify if any removed dependencies or updated pseudo-versions introduced security risks, you can use the OSV API to query those specific packages at their known versions [2][4]. For example, if you wanted to check for vulnerabilities in a specific version of a dependency: curl -X POST -d '{"version": "", "package": {"name": "<PACKAGE_NAME>", "ecosystem": "Go"}}' "https://api.osv.dev/v1/query" The OSV API focuses on vulnerability databases (VEX/OSV records) [1][4], whereas dependency management and change tracking in Go are handled by the Go toolchain and repository version control systems [5][6].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

for module in . api hack/tools; do
  echo "== graph: ${module} =="
  (cd "$module" && go list -m -json all) > "$tmpdir/$(echo "$module" | tr / _).json"
done

python3 - "$tmpdir" <<'PY'
import json
import sys
from pathlib import Path
from urllib.request import Request, urlopen

root = Path(sys.argv[1])
graphs = {}
queries = []
seen = set()

for path in sorted(root.glob("*.json")):
    decoder = json.JSONDecoder()
    text = path.read_text()
    pos = 0
    modules = []
    while pos < len(text):
        while pos < len(text) and text[pos].isspace():
            pos += 1
        if pos >= len(text):
            break
        obj, end = decoder.raw_decode(text, pos)
        pos = end
        modules.append(obj)
        module = obj.get("Path")
        version = obj.get("Version")
        if module and version:
            key = (module, version)
            if key not in seen:
                seen.add(key)
                queries.append({
                    "package": {"ecosystem": "Go", "name": module},
                    "version": version,
                })
    graphs[path.name] = modules
    print(f"{path.name}: {len(modules)} modules")

print(f"unique OSV queries: {len(queries)}")
for start in range(0, len(queries), 100):
    batch = {"queries": queries[start:start + 100]}
    req = Request(
        "https://api.osv.dev/v1/querybatch",
        data=json.dumps(batch).encode(),
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urlopen(req, timeout=60) as response:
        result = json.load(response)
    findings = []
    for query, item in zip(batch["queries"], result.get("results", [])):
        vulns = item.get("vulns", [])
        if vulns:
            findings.append((query, [v.get("id") for v in vulns]))
    for query, vuln_ids in findings:
        print("OSV finding:", query["package"]["name"], query["version"], vuln_ids)
PY

echo "== vendor manifest module references =="
for f in vendor/modules.txt api/vendor/modules.txt hack/tools/vendor/modules.txt; do
  echo "-- $f"
  awk '/^# / {print}' "$f" | sed -n '1,5p;$p'
done

Repository: openshift/hypershift

Length of output: 268


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

for module in . api hack/tools; do
  echo "== graph: ${module} =="
  (cd "$module" && go list -mod=readonly -m -json all) \
    > "$tmpdir/$(echo "$module" | tr / _).json"
done

python3 - "$tmpdir" <<'PY'
import json
import sys
from pathlib import Path
from urllib.request import Request, urlopen

root = Path(sys.argv[1])
decoder = json.JSONDecoder()
queries = []
seen = set()

for path in sorted(root.glob("*.json")):
    text = path.read_text()
    pos = 0
    count = 0
    while pos < len(text):
        while pos < len(text) and text[pos].isspace():
            pos += 1
        if pos >= len(text):
            break
        obj, pos = decoder.raw_decode(text, pos)
        count += 1
        module = obj.get("Path")
        version = obj.get("Version")
        if module and version and (module, version) not in seen:
            seen.add((module, version))
            queries.append({
                "package": {"ecosystem": "Go", "name": module},
                "version": version,
            })
    print(f"{path.name}: {count} resolved modules")

print(f"unique OSV queries: {len(queries)}")
for start in range(0, len(queries), 100):
    batch = {"queries": queries[start:start + 100]}
    req = Request(
        "https://api.osv.dev/v1/querybatch",
        data=json.dumps(batch).encode(),
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urlopen(req, timeout=60) as response:
        result = json.load(response)
    for query, item in zip(batch["queries"], result.get("results", [])):
        vuln_ids = [v.get("id") for v in item.get("vulns", [])]
        if vuln_ids:
            print("OSV finding:", query["package"]["name"], query["version"], vuln_ids)
PY

Repository: openshift/hypershift

Length of output: 2623


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

for module in . api hack/tools; do
  (cd "$module" && go list -mod=readonly -m -json all) \
    > "$tmpdir/$(echo "$module" | tr / _).json"
done

python3 - "$tmpdir" <<'PY'
import json
import subprocess
import sys
from pathlib import Path

decoder = json.JSONDecoder()
queries = []
seen = set()

for path in sorted(Path(sys.argv[1]).glob("*.json")):
    text = path.read_text()
    pos = 0
    while pos < len(text):
        while pos < len(text) and text[pos].isspace():
            pos += 1
        if pos >= len(text):
            break
        obj, pos = decoder.raw_decode(text, pos)
        module = obj.get("Path")
        version = obj.get("Version")
        if module and version and (module, version) not in seen:
            seen.add((module, version))
            queries.append({
                "package": {"ecosystem": "Go", "name": module},
                "version": version,
            })

print(f"unique OSV queries: {len(queries)}")
for start in range(0, len(queries), 100):
    payload = json.dumps({"queries": queries[start:start + 100]})
    proc = subprocess.run(
        [
            "curl", "--silent", "--show-error", "--insecure",
            "--request", "POST",
            "--header", "Content-Type: application/json",
            "--data-binary", "`@-`",
            "https://api.osv.dev/v1/querybatch",
        ],
        input=payload,
        text=True,
        capture_output=True,
        check=False,
    )
    if proc.returncode:
        print(f"OSV transport error in batch {start // 100 + 1}: {proc.stderr.strip()}")
        continue
    try:
        result = json.loads(proc.stdout)
    except json.JSONDecodeError as exc:
        print(f"OSV response error in batch {start // 100 + 1}: {exc}")
        continue
    for query, item in zip(
        json.loads(payload)["queries"], result.get("results", [])
    ):
        vuln_ids = [v.get("id") for v in item.get("vulns", [])]
        if vuln_ids:
            print("OSV finding:", query["package"]["name"],
                  query["version"], vuln_ids)
PY

Repository: openshift/hypershift

Length of output: 2972


Resolve the OSV findings in the resulting module graphs.

The resolved graphs contain known vulnerabilities, including GO-2026-5932 in golang.org/x/crypto, GO-2026-5942 in golang.org/x/net, GO-2026-5970 in golang.org/x/text, and findings in Docker, etcd, OpenTelemetry, and AWS modules. Update affected modules to fixed versions or document approved exceptions, then rerun SCA for ., api, and hack/tools.

🤖 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 `@go.mod` at line 66, Update the Go module dependencies in go.mod and
corresponding go.sum entries to fixed versions that eliminate the reported OSV
findings across the module graphs, including golang.org/x/crypto,
golang.org/x/net, golang.org/x/text, Docker, etcd, OpenTelemetry, and AWS
modules. If any finding cannot be upgraded, document its approved exception;
then rerun SCA for ., api, and hack/tools and verify the findings are resolved.

Comment thread hack/tools/go.mod
module github.com/openshift/hypershift/hack/tools

go 1.25.7
go 1.26.0

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.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n \
  --glob 'go.mod' \
  --glob 'go.work' \
  --glob 'Dockerfile*' \
  --glob '*.yaml' \
  --glob '*.yml' \
  --glob 'Makefile*' \
  --glob '*.sh' \
  '(^go |^toolchain |GO_VERSION|golang:|go1\.[0-9]+)' .

Repository: openshift/hypershift

Length of output: 10948


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- tracked toolchain/config files ---'
git ls-files | rg '(^|/)(go\.mod|go\.work|Dockerfile[^/]*|Makefile[^/]*|.*\.(ya?ml|sh))$' |
  rg -v '(^|/)vendor/|(^|/)api/vendor/|(^|/)hack/tools/vendor/' |
  sort

printf '%s\n' '--- active Go/toolchain references ---'
rg -n \
  --glob '!vendor/**' \
  --glob '!api/vendor/**' \
  --glob '!hack/tools/vendor/**' \
  --glob 'go.mod' \
  --glob 'go.work' \
  --glob 'Dockerfile*' \
  --glob '*.yaml' \
  --glob '*.yml' \
  --glob 'Makefile*' \
  --glob '*.sh' \
  --glob '*.json' \
  --glob '*.toml' \
  '(^go |^toolchain |setup-go|go-version|GO_VERSION|golang:|go1\.[0-9]+|GOTOOLCHAIN)' .

Repository: openshift/hypershift

Length of output: 50377


🌐 Web query:

Go 1.26 release history latest patch release Go 1.26.5 July 7 2026 security fixes

💡 Result:

Go 1.26.5 is the latest patch release in the Go 1.26 series as of August 6, 2026. It was officially released on July 7, 2026 [1][2][3]. This minor release included two critical security fixes [3]: 1. CVE-2026-39822: Fixed a vulnerability in the os package where an os.Root escape could occur via a symlink when a path ended in a trailing slash [3]. 2. CVE-2026-42505: Addressed a privacy leak in the crypto/tls package regarding Encrypted Client Hello (ECH), which allowed observers to de-anonymize server hostnames [3]. In addition to these security patches, Go 1.26.5 contained bug fixes for the compiler, runtime, and the go command, as well as the net, os, and syscall packages [1]. The Go 1.26 series began with the major release of Go 1.26.0 on February 10, 2026 [1][4]. Following this, the project has issued regular minor releases to address bugs and security concerns [1][2]. As of early August 2026, active development has moved toward future patch updates, such as the upcoming Go 1.26.6, which is currently being prepared with additional backported fixes [5].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
import os, re, subprocess

tracked = subprocess.check_output(["git", "ls-files"], text=True).splitlines()
names = {
    "go.mod", "go.work", "Dockerfile", "Makefile",
}
patterns = ("Dockerfile", "Makefile")
suffixes = (".yaml", ".yml", ".sh", ".json", ".toml")
needle = re.compile(
    r"(^\s*(?:go|toolchain)\s+1\.|"
    r"\b(?:GO_VERSION|go-version|GOTOOLCHAIN)\b|"
    r"\bsetup-go\b|\bgolang:[^\s]+|\bgo1\.[0-9]+)"
)
skip_parts = {"vendor", "testdata"}
files = []
for path in tracked:
    parts = path.split("/")
    base = parts[-1]
    if skip_parts.intersection(parts):
        continue
    if base in names or base.startswith(patterns) or base.endswith(suffixes):
        files.append(path)

for path in files:
    try:
        lines = open(path, encoding="utf-8", errors="replace").read().splitlines()
    except OSError:
        continue
    for lineno, line in enumerate(lines, 1):
        if needle.search(line):
            print(f"{path}:{lineno}:{line}")
PY

printf '%s\n' '--- hack/tools module and direct references ---'
rg -n \
  --glob '!vendor/**' \
  --glob '!**/testdata/**' \
  'hack/tools|go -C hack/tools|tools/go\.mod|go 1\.26\.0|GO_VERSION=1\.26|go-version:.*1\.26|golang:1\.26' \
  . | head -n 300

Repository: openshift/hypershift

Length of output: 11235


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- workflow setup-go contexts ---'
sed -n '1,115p' .github/workflows/test-reusable.yaml
sed -n '1,55p' .github/workflows/gocacheprog-test-reusable.yaml

printf '%s\n' '--- runner image Go installation context ---'
sed -n '1,45p' Dockerfile.github-actions-runner

printf '%s\n' '--- module directives ---'
for f in go.mod api/go.mod hack/tools/go.mod hack/workspace/go.work contrib/ci/gocacheprog/go.mod; do
  printf '%s\n' "--- $f"
  sed -n '1,8p' "$f"
done

Repository: openshift/hypershift

Length of output: 7808


🌐 Web query:

actions/setup-go go-version-file go.mod exact version directive official documentation

💡 Result:

When using the go-version-file input in the actions/setup-go GitHub Action, the action reads the Go version directly from your go.mod file [1][2]. Behavior and Version Resolution: - Toolchain Directive: The action prioritizes the toolchain directive if it is present in the go.mod file [1][2]. - Go Directive: If no toolchain directive exists, the action falls back to the go directive [1][2]. - Exact Version: - If the go.mod directive specifies a patch version (e.g., go 1.25.0), the action will use that exact version [1]. - If the directive omits the patch version (e.g., go 1.25), the action will search for and use the latest available patch version for that minor release [1]. Usage: To use this, provide the path to your go.mod file in your workflow file [1][2]: yaml - uses: actions/checkout@v7 - uses: actions/setup-go@v7 with: go-version-file: 'go.mod' Key Considerations: - Precedence: If both go-version and go-version-file inputs are provided, the go-version input takes precedence and go-version-file will be ignored [1][2]. - File Existence: The action will fail with an error if the file specified in go-version-file does not exist [1]. - Supported Files: In addition to go.mod, this input also supports go.work, .go-version, and .tool-versions files [1][3].

Citations:


Use a patched Go toolchain in CI.

actions/setup-go reads the root go.mod and installs Go 1.26.0 exactly. Set the workflow and Dockerfile.github-actions-runner to Go 1.26.5 or a later patched Go 1.26.x release.

🤖 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 `@hack/tools/go.mod` at line 3, Update the Go version declared by the
hack/tools module and the corresponding CI workflow and
Dockerfile.github-actions-runner configuration to Go 1.26.5 or a later patched
Go 1.26.x release. Ensure all three toolchain references stay aligned so
actions/setup-go does not install unpatched Go 1.26.0.

Comment on lines +53 to +63
resolve := func(variant string) {
if _, ok := resolved[variant]; ok {
return
}
entry, err := manifest.LookupCluster(variant)
if err != nil {
missing = append(missing, variant)
return
}
resolved[variant] = entry
}

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

Deduplicate missing variants.

Line 54 only skips variants that resolved successfully. A missing variant is not added to resolved. Repeated missing variants are added to missing more than once.

Track visited variants before LookupCluster.

Proposed fix
 func (m TestMatrix) ResolveVariants(manifest *ClusterManifest) (map[string]ClusterEntry, error) {
 	resolved := make(map[string]ClusterEntry)
+	seen := make(map[string]struct{})
 	var missing []string
 
 	resolve := func(variant string) {
-		if _, ok := resolved[variant]; ok {
+		if _, ok := seen[variant]; ok {
 			return
 		}
+		seen[variant] = struct{}{}
 		entry, err := manifest.LookupCluster(variant)
📝 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
resolve := func(variant string) {
if _, ok := resolved[variant]; ok {
return
}
entry, err := manifest.LookupCluster(variant)
if err != nil {
missing = append(missing, variant)
return
}
resolved[variant] = entry
}
seen := make(map[string]struct{})
resolve := func(variant string) {
if _, ok := seen[variant]; ok {
return
}
seen[variant] = struct{}{}
entry, err := manifest.LookupCluster(variant)
if err != nil {
missing = append(missing, variant)
return
}
resolved[variant] = entry
}
🤖 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 `@test/e2e/v2/lifecycle/platform.go` around lines 53 - 63, Update the resolve
closure to track each variant as visited before calling manifest.LookupCluster,
so repeated missing variants are skipped as well as successfully resolved ones.
Preserve the existing missing append and resolved assignment behavior for the
first lookup.

Comment on lines +424 to +426
// No cleanup needed: osImageStream is immutable once set (CEL validation
// rejects removal), and we set it to the version-derived default which is
// semantically equivalent to the original unset state.

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 | 🏗️ Heavy lift

Do not leave the default NodePool mutated.

This test permanently changes spec.osImageStream on the existing default NodePool. The field remains explicit after the test because it is immutable. NodePoolOSImageStreamDefaultStatusTest then skips its default-resolution check when it sees that explicit value at Lines 260-264.

Run this validation against an isolated fixture that lifecycle teardown deletes. Do not mutate the shared default NodePool.

🤖 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 `@test/e2e/v2/tests/nodepool_osimagestream_test.go` around lines 424 - 426,
Update the test setup around NodePoolOSImageStreamDefaultStatusTest to use an
isolated NodePool fixture whose lifecycle teardown deletes it, rather than
mutating the shared default NodePool. Preserve the version-derived default
validation while ensuring spec.osImageStream changes cannot persist and cause
later default-resolution checks to be skipped.

Sources: Coding guidelines, Path instructions

@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.50000% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.96%. Comparing base (1d48188) to head (81a7168).

Files with missing lines Patch % Lines
...figoperator/controllers/webhookvalidation/setup.go 0.00% 27 Missing ⚠️
...controllers/webhookvalidation/webhookvalidation.go 90.16% 4 Missing and 2 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #9238   +/-   ##
=======================================
  Coverage   44.96%   44.96%           
=======================================
  Files         778      780    +2     
  Lines       97444    97488   +44     
=======================================
+ Hits        43819    43840   +21     
- Misses      50602    50626   +24     
+ Partials     3023     3022    -1     
Files with missing lines Coverage Δ
...-plane-operator/hostedclusterconfigoperator/cmd.go 0.00% <ø> (ø)
...rconfigoperator/controllers/resources/resources.go 57.54% <ø> (-0.30%) ⬇️
...controllers/webhookvalidation/webhookvalidation.go 90.16% <90.16%> (ø)
...figoperator/controllers/webhookvalidation/setup.go 0.00% <0.00%> (ø)
Flag Coverage Δ
cmd-support 38.64% <ø> (ø)
cpo-hostedcontrolplane 47.24% <ø> (ø)
cpo-other 45.67% <62.50%> (+<0.01%) ⬆️
hypershift-operator 55.00% <ø> (ø)
other 34.32% <ø> (ø)

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.

bryan-cox and others added 2 commits August 6, 2026 07:00
The HCCO [Feature:WebhookValidation] e2e test has a 47% pass rate because
ensureGuestAdmissionWebhooksAreValid() runs at the tail of a 15+ sub-reconciler
chain in the monolithic resources controller, causing 60+ second delays between
webhook creation and deletion. Extract webhook validation into its own controller
with direct watches on ValidatingWebhookConfiguration and MutatingWebhookConfiguration,
so reconciliation triggers immediately on webhook events without waiting for the
full resources controller chain.

The new controller encodes the webhook type (validating/mutating) in the
request namespace field so Reconcile() targets only the type that fired,
avoiding a redundant Get for the other kind.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 1-minute timeout on the [Feature:WebhookValidation] e2e test was too
aggressive. Webhook deletion depends on the HCCO controller reconcile
cycle, which may take longer than 60 seconds in CI environments under load.
Increase to 3 minutes to reduce flakiness while the dedicated webhook
validation controller settles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@bryan-cox bryan-cox closed this Aug 6, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@bryan-cox: This pull request references Jira Issue OCPBUGS-105193. The bug has been updated to no longer refer to the pull request using the external bug tracker. All external bug links have been closed. The bug has been moved to the NEW state.

Details

In response to this:

What this PR does / why we need it:

Extracts ensureGuestAdmissionWebhooksAreValid() from the monolithic HCCO resources controller into a dedicated webhook-validation controller. The [Feature:WebhookValidation] e2e test has a 47% pass rate on e2e-v2-azure-self-managed because webhook validation runs at the tail of a 15+ sub-reconciler chain, causing 60+ second delays between webhook creation and deletion.

The new controller watches ValidatingWebhookConfiguration and MutatingWebhookConfiguration directly, so reconciliation triggers immediately on webhook events. It encodes the webhook type in the request namespace field so Reconcile() targets only the type that fired, avoiding a redundant Get for the other kind.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/OCPBUGS-105193

Special notes for your reviewer:

  • The HCCO manager cache already includes VWC and MWC with labels.Everything() (no label filter) — see operator/config.go:129-130 — so the resources controller retains its watches for ensureResourceCreationIsBlocked
  • Two commits: (1) the extraction refactor, (2) e2e timeout bump (separate rationale)
  • 13 unit tests: 7 table-driven reconcile cases + 2 error-path tests + 4 URL validation tests

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

Summary by CodeRabbit

  • New Features

  • Private clusters now use internal API server routes correctly, including when no hostname is configured.

  • Added DNS resolution checks for Azure external ignition endpoints.

  • Improved global pull-secret synchronization with readiness checks and rolling updates.

  • Added lifecycle manifests and informing-test reports for clearer end-to-end test results.

  • Expanded AWS storage capabilities for snapshot restores, volume status, copying, and snapshot locking.

  • Bug Fixes

  • Improved Azure private DNS cleanup and legacy resource detection.

  • Corrected limited-support metric reporting and route status handling.

  • Documentation

  • Added comprehensive end-to-end test-flow guidance and a July 2026 progress report.

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 Aug 6, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bryan-cox

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

The pull request process is described 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 the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 6, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@bryan-cox: This pull request references Jira Issue OCPBUGS-105193, 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)

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:

Extracts ensureGuestAdmissionWebhooksAreValid() from the monolithic HCCO resources controller into a dedicated webhook-validation controller. The [Feature:WebhookValidation] e2e test has a 47% pass rate on e2e-v2-azure-self-managed because webhook validation runs at the tail of a 15+ sub-reconciler chain, causing 60+ second delays between webhook creation and deletion.

The new controller watches ValidatingWebhookConfiguration and MutatingWebhookConfiguration directly, so reconciliation triggers immediately on webhook events. It encodes the webhook type in the request namespace field so Reconcile() targets only the type that fired, avoiding a redundant Get for the other kind.

Which issue(s) this PR fixes:

Fixes https://issues.redhat.com/browse/OCPBUGS-105193

Special notes for your reviewer:

  • The HCCO manager cache already includes VWC and MWC with labels.Everything() (no label filter) — see operator/config.go:129-130 — so the resources controller retains its watches for ensureResourceCreationIsBlocked
  • Two commits: (1) the extraction refactor, (2) e2e timeout bump (separate rationale)
  • 13 unit tests: 7 table-driven reconcile cases + 2 error-path tests + 4 URL validation tests

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

Summary by CodeRabbit

  • New Features

  • Added automated validation for admission webhooks targeting restricted control-plane services.

  • Preserves webhooks explicitly allowed for guest use and external or service-reference configurations.

  • Bug Fixes

  • Improves reliability of webhook cleanup by handling missing resources and retry-safe deletion.

  • Tests

  • Expanded coverage for allowed, disallowed, mixed, and error scenarios.

  • Extended the end-to-end validation wait time to improve test stability.

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.

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. area/api Indicates the PR includes changes for the API area/ci-tooling Indicates the PR includes changes for CI or tooling area/cli Indicates the PR includes changes for CLI area/control-plane-operator Indicates the PR includes changes for the control plane operator - in an OCP release 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/aws PR/issue for AWS (AWSPlatform) platform area/platform/azure PR/issue for Azure (AzurePlatform) platform area/platform/gcp PR/issue for GCP (GCPPlatform) platform area/platform/kubevirt PR/issue for KubeVirt (KubevirtPlatform) platform area/platform/none PR/issue for None (NonePlatform) platform - user-supplied infrastructure area/platform/openstack PR/issue for OpenStack (OpenStackPlatform) platform area/platform/powervs PR/issue for PowerVS (PowerVSPlatform) platform area/testing Indicates the PR includes changes for e2e testing do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. 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.

2 participants