OCPBUGS-105193: extract HCCO webhook validation into a dedicated controller - #9238
OCPBUGS-105193: extract HCCO webhook validation into a dedicated controller#9238bryan-cox wants to merge 2 commits into
Conversation
|
Pipeline controller notification For optional jobs, comment This repository is configured in: LGTM mode |
|
Skipping CI for Draft Pull Request. |
|
@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
The bug has been updated to refer to the pull request using the external bug tracker. DetailsIn response to this:
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. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Central YAML (inherited) Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (7)
Cache: Disabled due to data retention organization setting Knowledge base: Disabled due to data retention organization setting 📝 WalkthroughWalkthroughThe 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
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 1 warning)
✅ Passed checks (9 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@bryan-cox: This pull request references Jira Issue OCPBUGS-105193, which is valid. 3 validation(s) were run on this bug
DetailsIn response to this:
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. |
There was a problem hiding this comment.
Actionable comments posted: 18
🧹 Nitpick comments (3)
control-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation.go (1)
74-90: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueConsider making the type assertions safe.
Both accessors use unchecked type assertions.
Reconcilepairs 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 valueAdd a case for an unrecognized webhook type.
The
defaultbranch of the switch inReconcilereturns no error and takes no action. No test covers it. A case with an unexpectedreq.Namespacewould pin that contract.Consider also replacing
expectWebhookGoneandexpectWebhookAlivewith 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 valueUse the required test-case description format.
Start each table test name with
When. Includeit shouldafter 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
⛔ Files ignored due to path filters (273)
api/go.sumis excluded by!**/*.sumapi/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/ExternalOIDCExternalClaimsSourcing.yamlis excluded by!**/zz_generated.featuregated-crd-manifests/**api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/IngressComponentRouteLabels.yamlis excluded by!**/zz_generated.featuregated-crd-manifests/**api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedclusters.hypershift.openshift.io/KMSEncryption.yamlis excluded by!**/zz_generated.featuregated-crd-manifests/**api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/ExternalOIDCExternalClaimsSourcing.yamlis excluded by!**/zz_generated.featuregated-crd-manifests/**api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/IngressComponentRouteLabels.yamlis excluded by!**/zz_generated.featuregated-crd-manifests/**api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/hostedcontrolplanes.hypershift.openshift.io/KMSEncryption.yamlis excluded by!**/zz_generated.featuregated-crd-manifests/**api/vendor/github.com/openshift/api/config/v1/types_authentication.gois excluded by!**/vendor/**api/vendor/github.com/openshift/api/config/v1/types_infrastructure.gois excluded by!**/vendor/**api/vendor/github.com/openshift/api/config/v1/types_ingress.gois excluded by!**/vendor/**api/vendor/github.com/openshift/api/config/v1/types_kmsencryption.gois excluded by!**/vendor/**api/vendor/github.com/openshift/api/config/v1/zz_generated.featuregated-crd-manifests.yamlis excluded by!**/vendor/**,!**/zz_generated*api/vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.gois excluded by!**/vendor/**,!**/zz_generated*.go,!**/zz_generated.swagger_doc_generated.go,!**/zz_generated*api/vendor/github.com/openshift/api/operator/v1/types.gois excluded by!**/vendor/**api/vendor/github.com/openshift/api/operator/v1/types_kmsencryption.gois excluded by!**/vendor/**api/vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.gois excluded by!**/vendor/**,!**/zz_generated*.go,!**/zz_generated*api/vendor/github.com/openshift/api/operator/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!**/zz_generated*.go,!**/zz_generated*api/vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.gois excluded by!**/vendor/**,!**/zz_generated*.go,!**/zz_generated.swagger_doc_generated.go,!**/zz_generated*api/vendor/modules.txtis 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.yamlis excluded by!**/testdata/**cmd/infra/aws/delegating_client.gois excluded by!cmd/infra/aws/delegating_client.gocmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/hostedclusters-Hypershift-CustomNoUpgrade.crd.yamlis excluded by!**/zz_generated.crd-manifests/**,!cmd/install/assets/**/*.yamlcmd/install/assets/crds/hypershift-operator/zz_generated.crd-manifests/hostedcontrolplanes-Hypershift-CustomNoUpgrade.crd.yamlis excluded by!**/zz_generated.crd-manifests/**,!cmd/install/assets/**/*.yamlcontrol-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.yamlis excluded by!**/testdata/**docs/content/reference/aggregated-docs.mdis excluded by!docs/content/reference/aggregated-docs.mdgo.sumis excluded by!**/*.sumvendor/github.com/fxamacker/cbor/v2/.golangci.ymlis excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/cache.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/decode.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/decode_map_utils.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/diagnose.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/doc.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/encode.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/simplevalue.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/stream.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/structfields.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/tag.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/fxamacker/cbor/v2/valid.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/.cliff.tomlis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/.gitignoreis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/.golangci.ymlis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/NOTICEis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/SECURITY.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/errors.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/ifaces.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/options.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonpointer/pointer.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonreference/.cliff.tomlis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonreference/.gitignoreis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonreference/.golangci.ymlis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonreference/NOTICEis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonreference/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonreference/SECURITY.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/jsonreference/reference.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/.gitignoreis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/CONTRIBUTORS.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/SECURITY.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/go.workis excluded by!**/*.work,!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/jsonname/go_name_provider.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/jsonname/ifaces.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/jsonname/name_provider.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/jsonutils/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/loading/doc.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/loading/loading.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/loading/options.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-openapi/swag/mangling/BENCHMARK.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/AUTHORSis excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/CONTRIBUTORSis excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/gogoproto/Makefileis excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/gogoproto/doc.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/github.com/gogo/protobuf/gogoproto/gogo.pb.goldenis excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/gogoproto/gogo.protois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/gogoproto/helper.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/Makefileis excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/clone.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/custom_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/decode.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/deprecated.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/discard.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/duration.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/duration_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/encode.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/encode_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/equal.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/extensions.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/extensions_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/lib.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/lib_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/message_set.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/pointer_reflect.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/pointer_reflect_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/pointer_unsafe.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/properties.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/properties_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/skip_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/table_marshal.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/table_marshal_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/table_merge.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/table_unmarshal.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/table_unmarshal_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/text.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/text_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/text_parser.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/timestamp.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/timestamp_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/wrappers.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/proto/wrappers_gogo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/Makefileis excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/helper.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/googleapis/gax-go/v2/.release-please-manifest.jsonis excluded by!vendor/**,!**/vendor/**vendor/github.com/googleapis/gax-go/v2/CHANGES.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/googleapis/gax-go/v2/internal/version.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/googleapis/gax-go/v2/release-please-config.jsonis excluded by!vendor/**,!**/vendor/**vendor/github.com/googleapis/gax-go/v2/telemetry.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/config/v1/types_authentication.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/config/v1/types_infrastructure.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/config/v1/types_ingress.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/config/v1/types_kmsencryption.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/config/v1/zz_generated.featuregated-crd-manifests.yamlis excluded by!vendor/**,!**/vendor/**,!**/zz_generated*vendor/github.com/openshift/api/config/v1/zz_generated.swagger_doc_generated.gois 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.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/config/v1alpha1/zz_generated.deepcopy.gois excluded by!vendor/**,!**/vendor/**,!**/zz_generated*.go,!**/zz_generated*vendor/github.com/openshift/api/config/v1alpha1/zz_generated.model_name.gois excluded by!vendor/**,!**/vendor/**,!**/zz_generated*.go,!**/zz_generated*vendor/github.com/openshift/api/config/v1alpha1/zz_generated.swagger_doc_generated.gois excluded by!vendor/**,!**/vendor/**,!**/zz_generated*.go,!**/zz_generated.swagger_doc_generated.go,!**/zz_generated*vendor/github.com/openshift/api/envtest-releases.yamlis excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/features.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/machineconfiguration/v1/types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/machineconfiguration/v1/zz_generated.featuregated-crd-manifests.yamlis excluded by!vendor/**,!**/vendor/**,!**/zz_generated*vendor/github.com/openshift/api/machineconfiguration/v1/zz_generated.swagger_doc_generated.gois excluded by!vendor/**,!**/vendor/**,!**/zz_generated*.go,!**/zz_generated.swagger_doc_generated.go,!**/zz_generated*vendor/github.com/openshift/api/operator/v1/types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/operator/v1/types_kmsencryption.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/operator/v1/zz_generated.deepcopy.gois excluded by!vendor/**,!**/vendor/**,!**/zz_generated*.go,!**/zz_generated*vendor/github.com/openshift/api/operator/v1/zz_generated.model_name.gois excluded by!vendor/**,!**/vendor/**,!**/zz_generated*.go,!**/zz_generated*vendor/github.com/openshift/api/operator/v1/zz_generated.swagger_doc_generated.gois excluded by!vendor/**,!**/vendor/**,!**/zz_generated*.go,!**/zz_generated.swagger_doc_generated.go,!**/zz_generated*vendor/github.com/openshift/api/route/v1/generated.protois excluded by!vendor/**,!**/vendor/**,!**/generated.protovendor/github.com/openshift/api/route/v1/types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift/api/route/v1/zz_generated.featuregated-crd-manifests.yamlis excluded by!vendor/**,!**/vendor/**,!**/zz_generated*vendor/go.etcd.io/etcd/api/v3/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/authpb/auth.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/go.etcd.io/etcd/api/v3/authpb/auth.protois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/authpb/deprecated.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/etcdserver.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/go.etcd.io/etcd/api/v3/etcdserverpb/etcdserver.protois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/raft_internal.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/go.etcd.io/etcd/api/v3/etcdserverpb/raft_internal.protois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/raft_internal_stringer.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc.protois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/etcdserverpb/rpc_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/go.etcd.io/etcd/api/v3/etcdserverpb/util.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/membershippb/membership.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/go.etcd.io/etcd/api/v3/membershippb/membership.protois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/mvccpb/deprecated.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/mvccpb/extension.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/mvccpb/kv.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/go.etcd.io/etcd/api/v3/mvccpb/kv.protois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/version/version.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/api/v3/versionpb/version.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/go.etcd.io/etcd/api/v3/versionpb/version.protois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_opts.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/listener_tls.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/transport/timeout_transport.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/types/set.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/types/urls.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/pkg/v3/verify/verify.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/.gomodguard.yamlis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/auth.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/block_logger.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/client.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/compare.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/config.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/kv.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/lease.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/logger.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/maintenance.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/op.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/retry.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/retry_interceptor.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/txn.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/client/v3/watch.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/pkg/v3/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/pkg/v3/cpuutil/endian.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/pkg/v3/netutil/host_normalize.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/pkg/v3/netutil/netutil.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/server/v3/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/server/v3/etcdserver/api/membership/cluster.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/server/v3/etcdserver/api/membership/store.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/server/v3/etcdserver/api/membership/storev2.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/etcd/server/v3/etcdserver/errors/errors.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/.go-versionis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/.golangci.yamlis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/README.mdis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/bootstrap.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/code-of-conduct.mdis excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/confchange/confchange.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/confchange/restore.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/doc.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/log.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/log_unstable.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/node.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/quorum/majority.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/raft.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/raftpb/alias.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/raftpb/confchange.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/raftpb/confstate.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/raftpb/raft.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/go.etcd.io/raft/v3/raftpb/raft.protois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/raftpb/util.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/rawnode.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/read_only.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/state_trace.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/state_trace_nop.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/status.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/storage.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/tracker/tracker.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/types.gois excluded by!vendor/**,!**/vendor/**vendor/go.etcd.io/raft/v3/util.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/compute/v1/compute-api.jsonis excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/compute/v1/compute-gen.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/compute/v1/compute2-gen.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/compute/v1/compute3-gen.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/dns/v1/dns-api.jsonis excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/iam/v1/iam-api.jsonis excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/iam/v1/iam-gen.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/internal/version.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/storage/v1/storage-api.jsonis excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/api/storage/v1/storage-gen.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/balancer/balancer.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/dialoptions.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/encoding/encoding.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/encoding/gzip/gzip.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/experimental/balancer/weight/weight.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/google.golang.org/grpc/internal/envconfig/envconfig.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/envconfig/xds.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/resolver/config_selector.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/stats/labels.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/transport/client_stream.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/transport/controlbuf.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/transport/flowcontrol.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/transport/handler_server.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/transport/http2_client.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/transport/http2_server.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/transport/internal/internal.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/internal/transport/transport.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.gois excluded by!**/*.pb.go,!vendor/**,!**/vendor/**,!**/*.pb.govendor/google.golang.org/grpc/rpc_util.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/server.gois excluded by!vendor/**,!**/vendor/**vendor/google.golang.org/grpc/version.gois excluded by!vendor/**,!**/vendor/**vendor/k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/helpers.gois excluded by!vendor/**,!**/vendor/**vendor/k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/types.gois excluded by!vendor/**,!**/vendor/**vendor/k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1/zz_generated.deepcopy.gois excluded by!vendor/**,!**/vendor/**,!**/zz_generated*.go,!**/zz_generated*vendor/modules.txtis excluded by!vendor/**,!**/vendor/**
📒 Files selected for processing (72)
.github/workflows/rebase.yaml.github/workflows/restructure-commits.yaml.github/workflows/reusable-claude-on-pr.yamlOWNERS_ALIASESapi/go.modcmd/cluster/aws/create.gocmd/cluster/azure/create.gocmd/cluster/core/create.gocmd/cluster/core/create_test.gocmd/cluster/gcp/create.gocmd/cluster/kubevirt/create.gocmd/cluster/none/create.gocmd/cluster/openstack/create.gocmd/cluster/powervs/create.gocmd/infra/aws/iam.gocmd/infra/azure/destroy.gocmd/infra/azure/networking.gocontrib/konflux/cpo_4_14_stream.yamlcontrol-plane-operator/controllers/hostedcontrolplane/infra/infra.gocontrol-plane-operator/controllers/hostedcontrolplane/infra/infra_test.gocontrol-plane-operator/controllers/hostedcontrolplane/kas/service.gocontrol-plane-operator/hostedclusterconfigoperator/cmd.gocontrol-plane-operator/hostedclusterconfigoperator/controllers/globalps/globalps.gocontrol-plane-operator/hostedclusterconfigoperator/controllers/globalps/globalps_test.gocontrol-plane-operator/hostedclusterconfigoperator/controllers/globalps/setup.gocontrol-plane-operator/hostedclusterconfigoperator/controllers/globalps/setup_test.gocontrol-plane-operator/hostedclusterconfigoperator/controllers/resources/resources.gocontrol-plane-operator/hostedclusterconfigoperator/controllers/resources/resources_test.gocontrol-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/setup.gocontrol-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation.gocontrol-plane-operator/hostedclusterconfigoperator/controllers/webhookvalidation/webhookvalidation_test.godocs/content/blog/2026-07-progress-report.mddocs/content/blog/index.mddocs/content/how-to/ci/triage/presubmit-failures.mddocs/content/how-to/ci/v2-testing/ci-pipeline.mddocs/content/how-to/ci/v2-testing/debugging.mddocs/content/how-to/ci/v2-testing/index.mddocs/content/how-to/ci/v2-testing/test-flow.mddocs/content/reference/e2e-v2-test-flow.mddocs/mkdocs.ymlgo.modhack/tools/go.modhypershift-operator/controllers/hostedcluster/hostedcluster_controller.gohypershift-operator/controllers/hostedcluster/hostedcluster_controller_test.gohypershift-operator/controllers/hostedcluster/hostedcluster_webhook.gohypershift-operator/controllers/hostedcluster/hostedcluster_webhook_test.gohypershift-operator/controllers/hostedcluster/metrics/metrics.gohypershift-operator/controllers/hostedcluster/metrics/metrics_test.gohypershift-operator/controllers/hostedcluster/reconcile_legacy.gohypershift-operator/controllers/nodepool/conditions.gohypershift-operator/controllers/nodepool/conditions_test.gohypershift-operator/controllers/nodepool/nodepool_controller.gosupport/awsapi/ec2.gosupport/globalconfig/dns.gosupport/globalconfig/dns_test.gotest/e2e/OWNERStest/e2e/v2/AGENTS.mdtest/e2e/v2/cmd/create-guests/main.gotest/e2e/v2/cmd/destroy-guests/main.gotest/e2e/v2/cmd/dump-guests/main.gotest/e2e/v2/cmd/run-tests/main.gotest/e2e/v2/internal/fail_handler.gotest/e2e/v2/internal/junit.gotest/e2e/v2/internal/junit_test.gotest/e2e/v2/lifecycle/aws.gotest/e2e/v2/lifecycle/azure.gotest/e2e/v2/lifecycle/manifest.gotest/e2e/v2/lifecycle/manifest_test.gotest/e2e/v2/lifecycle/platform.gotest/e2e/v2/tests/hosted_cluster_security_test.gotest/e2e/v2/tests/nodepool_osimagestream_test.gotest/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
| 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 |
There was a problem hiding this comment.
🩺 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 || trueRepository: 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:
- 1: https://stackoverflow.com/questions/74957218/what-is-the-difference-between-pull-request-and-pull-request-target-event-in-git
- 2: https://tenki.cloud/blog/github-actions-permissions-model
- 3: https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/
- 4: https://docs.github.com/en/pull-requests/how-tos/work-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork
- 5: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork
- 6: Update changelog.yml so there are necessary permissions to run on PRs from forked repos hashicorp/terraform#36434
- 7: Secrets to forked PRs aws-actions/configure-aws-credentials#837
🏁 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
doneRepository: 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
| "ec2:ModifyVolume", | ||
| "ec2:DescribeAvailabilityZones", | ||
| "ec2:EnableFastSnapshotRestores", | ||
| "ec2:DescribeInstanceTypes", | ||
| "ec2:DescribeVolumeStatus", | ||
| "ec2:CopyVolumes", | ||
| "ec2:LockSnapshot" |
There was a problem hiding this comment.
🔒 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/awsRepository: 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/awsRepository: 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:
- 1: https://docs.aws.amazon.com/ebs/latest/userguide/snapshot-lock-iam.html
- 2: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference%5Fpolicies%5Factions-resources-contextkeys.html
- 3: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html
- 4: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-policy-structure.html
- 5: https://repost.aws/knowledge-center/iam-ec2-resource-tags
🏁 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])
PYRepository: 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('---')
PYRepository: 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, andec2: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) |
There was a problem hiding this comment.
🗄️ 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.
| 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 |
There was a problem hiding this comment.
📐 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
| 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 | ||
| } |
There was a problem hiding this comment.
📐 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.goRepository: 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/kasRepository: 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 --shortRepository: 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 | ||
|
|
||
| ``` |
There was a problem hiding this comment.
📐 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.
| ``` |
🧰 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
| 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 |
There was a problem hiding this comment.
🗄️ 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
fiRepository: 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 --shortRepository: 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))
PYRepository: 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:
- 1: https://google.github.io/osv.dev/api/
- 2: https://google.github.io/osv.dev/post-v1-query/
- 3: https://google.github.io/osv.dev/quickstart/
- 4: https://safeguard.sh/resources/blog/osv-vulnerability-database-api-cookbook
- 5: 4a9f2b9
- 6: OCPBUGS-66211: fix(vendor): bump openshift/api dependency to current master #7345
🏁 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'
doneRepository: 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)
PYRepository: 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)
PYRepository: 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.
| module github.com/openshift/hypershift/hack/tools | ||
|
|
||
| go 1.25.7 | ||
| go 1.26.0 |
There was a problem hiding this comment.
🔒 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:
- 1: https://go.dev/doc/devel/release
- 2: https://versionlog.com/golang/1.26/
- 3: https://groups.google.com/g/golang-announce/c/OrmQE_Yp5Sc
- 4: https://go.dev/blog/go1.26
- 5: https://dev.golang.org/release
🏁 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 300Repository: 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"
doneRepository: 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:
- 1: https://github.com/actions/setup-go/blob/main/docs/advanced-usage.md
- 2: https://github.com/actions/setup-go/blob/main/README.md
- 3: https://github.com/actions/setup-go/blob/main/action.yml
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.
| 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 | ||
| } |
There was a problem hiding this comment.
🎯 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.
| 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.
| // 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. |
There was a problem hiding this comment.
🎯 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 Report❌ Patch coverage is 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
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
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>
3e3e6f3 to
81a7168
Compare
|
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: 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. DetailsIn response to this:
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. |
|
[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 DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
@bryan-cox: This pull request references Jira Issue OCPBUGS-105193, which is valid. 3 validation(s) were run on this bug
The bug has been updated to refer to the pull request using the external bug tracker. DetailsIn response to this:
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. |
What this PR does / why we need it:
Extracts
ensureGuestAdmissionWebhooksAreValid()from the monolithic HCCO resources controller into a dedicatedwebhook-validationcontroller. The[Feature:WebhookValidation]e2e test has a 47% pass rate one2e-v2-azure-self-managedbecause 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
ValidatingWebhookConfigurationandMutatingWebhookConfigurationdirectly, so reconciliation triggers immediately on webhook events. It encodes the webhook type in the request namespace field soReconcile()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:
labels.Everything()(no label filter) — seeoperator/config.go:129-130— so the resources controller retains its watches forensureResourceCreationIsBlockedChecklist:
Always review AI generated responses prior to use.
Generated with Claude Code via openshift-developer plugin
Summary by CodeRabbit
New Features
Bug Fixes
Tests