OCPCLOUD-3384, OCPCLOUD-3365: Implement admission action Warn and ensure validations not active when not configured#492
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Central YAML (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughUpgrades Go and Kubernetes module versions; refactors webhook logic to pass CompatibilityRequirement objects into schema/strategy resolvers; adds admit-action handling (WARN/DENY) for validation and pruning, ownership checks for webhook configs, new test warning-mode coverage, and test utilities for capturing REST warnings. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant WebhookServer
participant Controller as Reconciler
participant API as Kubernetes API
participant CR as CompatibilityRequirement
participant Validator
Client->>WebhookServer: Admission request (create/update/prune)
WebhookServer->>API: Get CR (CompatibilityRequirement)
API-->>WebhookServer: Return CR
WebhookServer->>Validator: getValidationStrategy(cr, version) / getStructuralSchema(cr, version)
Validator-->>WebhookServer: strategy + field errors
alt Action == DENY
WebhookServer->>Client: Reject (apiInvalid)
else Action == WARN
WebhookServer->>Client: Allow + Warnings
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 2 warnings)
✅ Passed checks (2 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
|
Pipeline controller notification For optional jobs, comment This repository is configured in: LGTM mode |
|
@JoelSpeed: This pull request references OCPCLOUD-3384 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set. This pull request references OCPCLOUD-3365 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set. 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. |
|
Skipping CI for Draft Pull Request. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
18f2100 to
4478e45
Compare
|
@JoelSpeed: This pull request references OCPCLOUD-3384 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set. This pull request references OCPCLOUD-3365 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set. 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. |
|
@JoelSpeed: This pull request references OCPCLOUD-3384 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set. This pull request references OCPCLOUD-3365 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set. 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: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pkg/controllers/crdcompatibility/reconcile.go (1)
232-249:⚠️ Potential issue | 🔴 CriticalPrevent deletion of non-owned
ValidatingWebhookConfigurationobjects.At Line 247, deletion is unconditional once the object is found. Combined with Line 202-205 (disable path), this can remove a same-named webhook not controlled by this
CompatibilityRequirement.🔒 Proposed fix
func (r *reconcileState) removeObjectValidationWebhook(ctx context.Context, obj *apiextensionsv1alpha1.CompatibilityRequirement) error { webhookConfig := &admissionregistrationv1.ValidatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ Name: obj.Name, }, } @@ if err := r.client.Get(ctx, types.NamespacedName{Name: webhookConfig.Name}, webhookConfig); err != nil { if apierrors.IsNotFound(err) { return nil } return fmt.Errorf("failed to get ValidatingWebhookConfiguration %s: %w", webhookConfig.Name, err) } + + // If we don't own the webhook config, we should not be deleting it. + if !metav1.IsControlledBy(webhookConfig, obj) { + return nil + } if err := r.client.Delete(ctx, webhookConfig); err != nil { return fmt.Errorf("failed to delete ValidatingWebhookConfiguration %s: %w", webhookConfig.Name, err) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/controllers/crdcompatibility/reconcile.go` around lines 232 - 249, The deletion is unconditional in removeObjectValidationWebhook which can remove a same-named ValidatingWebhookConfiguration not owned by this CompatibilityRequirement; before calling r.client.Delete check ownership (use metav1.IsControlledBy or controllerutil.ContainsReference/IsOwned helper) to ensure webhookConfig is owned/controlled by the obj (CompatibilityRequirement) and only delete if true—if not owned, skip deletion and return nil (or a clear no-op) to avoid deleting resources owned by others.
🧹 Nitpick comments (2)
go.mod (1)
19-29: Alignrequireversions withreplacepins to reduce dependency drift.The
replacedirectives pin Kubernetes modules to v0.35.2/v1.35.2, but severalrequireentries lag behind:
- Line 55:
k8s.io/apiextensions-apiserver v0.34.1→ should bev0.35.2- Line 61:
k8s.io/kubernetes v1.34.1→ should bev1.35.2- Line 351:
k8s.io/controller-manager v0.32.1(indirect) → should bev0.35.2- Line 354:
k8s.io/kubelet v0.34.1(indirect) → should bev0.35.2This drift may cause future
go mod tidychurn and makes the intended dependency versions less clear.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@go.mod` around lines 19 - 29, Update the go.mod require entries so they match the pinned versions in the replace directives: change k8s.io/apiextensions-apiserver to v0.35.2, k8s.io/kubernetes to v1.35.2, and ensure indirect requires for k8s.io/controller-manager and k8s.io/kubelet are v0.35.2; run go get <module>@<version> or edit the require block to the listed versions and then run go mod tidy to verify there is no drift between require and replace pins.pkg/controllers/crdcompatibility/reconcile_test.go (1)
381-382: Use the typed NotFound matcher here.Matching
"not found"couples the test to error text and can pass for the wrong failure.test.BeK8SNotFound()is already used in the deletion specs above and keeps these assertions tied to the actual API status.♻️ Suggested cleanup
- Expect(kWithCtx(ctx).Get(noObjectSchemaValidatingWebhook)()).To(MatchError(ContainSubstring("not found"))) - Expect(kWithCtx(ctx).Get(noObjectSchemaMutatingWebhook)()).To(MatchError(ContainSubstring("not found"))) + Expect(kWithCtx(ctx).Get(noObjectSchemaValidatingWebhook)()).To(test.BeK8SNotFound()) + Expect(kWithCtx(ctx).Get(noObjectSchemaMutatingWebhook)()).To(test.BeK8SNotFound()) … - Eventually(kWithCtx(ctx).Get(noObjectSchemaValidatingWebhook)).WithContext(ctx).Should(MatchError(ContainSubstring("not found"))) - Eventually(kWithCtx(ctx).Get(noObjectSchemaMutatingWebhook)).WithContext(ctx).Should(MatchError(ContainSubstring("not found"))) + Eventually(kWithCtx(ctx).Get(noObjectSchemaValidatingWebhook)).WithContext(ctx).Should(test.BeK8SNotFound()) + Eventually(kWithCtx(ctx).Get(noObjectSchemaMutatingWebhook)).WithContext(ctx).Should(test.BeK8SNotFound())Also applies to: 399-400
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/controllers/crdcompatibility/reconcile_test.go` around lines 381 - 382, Replace the fragile string-based error assertions with the typed NotFound matcher: change the two expectations using MatchError(ContainSubstring("not found")) for kWithCtx(ctx).Get(noObjectSchemaValidatingWebhook)() and kWithCtx(ctx).Get(noObjectSchemaMutatingWebhook)() to use test.BeK8SNotFound() instead; also make the same replacement for the identical assertions noted elsewhere in the file (the other occurrence around the deletion specs) so the tests assert Kubernetes NotFound status rather than matching error text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/controllers/crdcompatibility/objectpruning/webhook.go`:
- Around line 119-125: The handler currently only returns early when
ObjectSchemaValidation.Action == CRDAdmitActionWarn, but when
ObjectSchemaValidation is nil the zero-value falls through and causes pruning;
update the early-exit logic in the webhook handler (around
compatibilityRequirement.Spec.ObjectSchemaValidation.Action and the call to
v.getStructuralSchema) to treat unset/cleared validation as disabled by
returning nil unless the action is explicitly
apiextensionsv1alpha1.CRDAdmitActionDeny — i.e., check if
Spec.ObjectSchemaValidation is nil or its Action != CRDAdmitActionDeny and
return nil to keep the admission inert during reconciliation windows.
In `@pkg/controllers/crdcompatibility/objectvalidation/handle_test.go`:
- Around line 853-860: The BeforeEach mutates the package-global cl and relies
on the implicit global scheme; instead create an explicit scheme and avoid
changing global state: build a fresh scheme (e.g. runtime.NewScheme()) and
explicitly register the needed API types (the same apiextensionsv1alpha1 types
registered in pkg/test/envtest.go) and pass that scheme into client.New via a
copied rest.Config (warningConfig), then create a local client variable (e.g.
testCl) from client.New rather than assigning to the package-global cl; if other
helpers require the global, save the oldCl before creating testCl and restore
oldCl in AfterEach (and ensure any client cleanup runs even on panic) so tests
remain isolated.
In `@pkg/controllers/crdcompatibility/reconcile.go`:
- Around line 314-317: The isObjectValidationWebhookEnabled function currently
treats a non-nil MatchConditions slice as enabled even if it's empty; change the
check from osl.MatchConditions != nil to len(osv.MatchConditions) > 0 so an
explicitly empty list is considered not configured, i.e., update the return
expression in isObjectValidationWebhookEnabled to use len(osv.MatchConditions) >
0 (keeping the existing checks for osv.Action and the namespace/object selector
helpers).
---
Outside diff comments:
In `@pkg/controllers/crdcompatibility/reconcile.go`:
- Around line 232-249: The deletion is unconditional in
removeObjectValidationWebhook which can remove a same-named
ValidatingWebhookConfiguration not owned by this CompatibilityRequirement;
before calling r.client.Delete check ownership (use metav1.IsControlledBy or
controllerutil.ContainsReference/IsOwned helper) to ensure webhookConfig is
owned/controlled by the obj (CompatibilityRequirement) and only delete if
true—if not owned, skip deletion and return nil (or a clear no-op) to avoid
deleting resources owned by others.
---
Nitpick comments:
In `@go.mod`:
- Around line 19-29: Update the go.mod require entries so they match the pinned
versions in the replace directives: change k8s.io/apiextensions-apiserver to
v0.35.2, k8s.io/kubernetes to v1.35.2, and ensure indirect requires for
k8s.io/controller-manager and k8s.io/kubelet are v0.35.2; run go get
<module>@<version> or edit the require block to the listed versions and then run
go mod tidy to verify there is no drift between require and replace pins.
In `@pkg/controllers/crdcompatibility/reconcile_test.go`:
- Around line 381-382: Replace the fragile string-based error assertions with
the typed NotFound matcher: change the two expectations using
MatchError(ContainSubstring("not found")) for
kWithCtx(ctx).Get(noObjectSchemaValidatingWebhook)() and
kWithCtx(ctx).Get(noObjectSchemaMutatingWebhook)() to use test.BeK8SNotFound()
instead; also make the same replacement for the identical assertions noted
elsewhere in the file (the other occurrence around the deletion specs) so the
tests assert Kubernetes NotFound status rather than matching error text.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 01a51f56-b065-4956-b2f5-83eb8e268ea7
⛔ Files ignored due to path filters (285)
e2e/go.sumis excluded by!**/*.sumgo.sumis excluded by!**/*.sumgo.workis excluded by!**/*.workmanifests-gen/go.sumis excluded by!**/*.sumvendor/github.com/gogo/protobuf/sortkeys/sortkeys.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/.ci-operator.yamlis excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/AGENTS.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/Dockerfile.ocpis excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/apiextensions/v1alpha1/types_compatibilityrequirement.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/apps/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/apps/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/authorization/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/authorization/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/build/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/build/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/cloudnetwork/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/cloudnetwork/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/config/v1/zz_generated.crd-manifests/0000_10_config-operator_01_networks.crd.yamlis excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/config/v1/zz_generated.crd-manifests/0000_10_openshift-controller-manager_01_builds.crd.yamlis excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/config/v1alpha1/register.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/config/v1alpha1/types_pki.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/config/v1alpha1/zz_generated.deepcopy.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/github.com/openshift/api/config/v1alpha1/zz_generated.featuregated-crd-manifests.yamlis excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/github.com/openshift/api/config/v1alpha1/zz_generated.swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/github.com/openshift/api/features.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/features/features.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/image/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/image/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/network/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/network/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/networkoperator/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/networkoperator/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/oauth/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/oauth/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/openapi/generated_openapi/zz_generated.openapi.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_50_ingress_00_ingresscontrollers.crd.yamlis excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/operator/v1/zz_generated.crd-manifests/0000_70_dns_00_dnses.crd.yamlis excluded by!**/vendor/**,!vendor/**vendor/github.com/openshift/api/project/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/project/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/quota/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/quota/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/route/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/route/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/samples/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/samples/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/security/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/security/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/template/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/template/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/user/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/openshift/api/user/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/.gitignoreis excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/.travis.ymlis excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/LICENSEis excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/README.mdis excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/chain.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/constantdelay.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/cron.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/doc.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/logger.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/option.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/parser.gois excluded by!**/vendor/**,!vendor/**vendor/github.com/robfig/cron/v3/spec.gois excluded by!**/vendor/**,!vendor/**vendor/go.etcd.io/etcd/api/v3/version/version.gois excluded by!**/vendor/**,!vendor/**vendor/go.etcd.io/etcd/client/v3/retry_interceptor.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/admission/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1beta1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1beta1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1beta1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admission/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/admissionregistration/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/admissionregistration/v1alpha1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1alpha1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1alpha1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1alpha1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1alpha1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/admissionregistration/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1beta1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1beta1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/admissionregistration/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/apidiscovery/v2/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apidiscovery/v2/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apidiscovery/v2/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apidiscovery/v2/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/apidiscovery/v2beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apidiscovery/v2beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apidiscovery/v2beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apidiscovery/v2beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/apiserverinternal/v1alpha1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apiserverinternal/v1alpha1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apiserverinternal/v1alpha1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apiserverinternal/v1alpha1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/apps/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/apps/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/apps/v1beta2/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta2/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta2/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta2/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta2/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta2/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/apps/v1beta2/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/authentication/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/authentication/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authentication/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authentication/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/authentication/v1alpha1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/authentication/v1alpha1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authentication/v1alpha1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authentication/v1alpha1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/authentication/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/authentication/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authentication/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authentication/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/authorization/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/authorization/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authorization/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authorization/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/authorization/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/authorization/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authorization/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/authorization/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/autoscaling/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/autoscaling/v2/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/autoscaling/v2beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/autoscaling/v2beta2/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2beta2/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2beta2/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/autoscaling/v2beta2/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/batch/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/batch/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/batch/v1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/batch/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/batch/v1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/batch/v1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/batch/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/batch/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/batch/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/batch/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/batch/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/certificates/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/certificates/v1alpha1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1alpha1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1alpha1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1alpha1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1alpha1/register.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1alpha1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1alpha1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1alpha1/zz_generated.deepcopy.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/certificates/v1alpha1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/certificates/v1alpha1/zz_generated.prerelease-lifecycle.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/certificates/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1beta1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1beta1/register.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1beta1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1beta1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/certificates/v1beta1/zz_generated.deepcopy.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/certificates/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/certificates/v1beta1/zz_generated.prerelease-lifecycle.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/coordination/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/coordination/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/coordination/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/coordination/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/coordination/v1alpha2/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/coordination/v1alpha2/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/coordination/v1alpha2/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/coordination/v1alpha2/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/coordination/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/coordination/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/coordination/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/coordination/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/core/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/core/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/core/v1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/core/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/core/v1/toleration.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/core/v1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/core/v1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/core/v1/zz_generated.deepcopy.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/core/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/discovery/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/discovery/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1beta1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1beta1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/discovery/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/events/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/events/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/events/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/events/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/events/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/events/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/events/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/events/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/extensions/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/extensions/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/extensions/v1beta1/generated.protois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/extensions/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/extensions/v1beta1/types.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/extensions/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/extensions/v1beta1/zz_generated.validations.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/flowcontrol/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/flowcontrol/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/flowcontrol/v1beta2/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1beta2/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1beta2/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1beta2/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/flowcontrol/v1beta3/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1beta3/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1beta3/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/flowcontrol/v1beta3/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/imagepolicy/v1alpha1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/imagepolicy/v1alpha1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/imagepolicy/v1alpha1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/imagepolicy/v1alpha1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/networking/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/networking/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/networking/v1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/networking/v1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/networking/v1beta1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/networking/v1beta1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/networking/v1beta1/generated.protomessage.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**vendor/k8s.io/api/networking/v1beta1/zz_generated.model_name.gois excluded by!**/vendor/**,!vendor/**,!**/zz_generated*vendor/k8s.io/api/node/v1/doc.gois excluded by!**/vendor/**,!vendor/**vendor/k8s.io/api/node/v1/generated.pb.gois excluded by!**/*.pb.go,!**/vendor/**,!vendor/**
📒 Files selected for processing (15)
e2e/go.modgo.modhack/tools/go.modmanifests-gen/go.modpkg/controllers/crdcompatibility/objectpruning/handle_test.gopkg/controllers/crdcompatibility/objectpruning/validator_unit_test.gopkg/controllers/crdcompatibility/objectpruning/webhook.gopkg/controllers/crdcompatibility/objectvalidation/handle_test.gopkg/controllers/crdcompatibility/objectvalidation/suite_test.gopkg/controllers/crdcompatibility/objectvalidation/validator_unit_test.gopkg/controllers/crdcompatibility/objectvalidation/webhook.gopkg/controllers/crdcompatibility/reconcile.gopkg/controllers/crdcompatibility/reconcile_test.gopkg/test/crdbuilder.gopkg/test/warninghandler.go
| if compatibilityRequirement.Spec.ObjectSchemaValidation.Action == apiextensionsv1alpha1.CRDAdmitActionWarn { | ||
| // When set to warn, we do not expect to mutate the object, so we return early without pruning. | ||
| return nil | ||
| } | ||
|
|
||
| schema, err := v.getStructuralSchema(compatibilityRequirement, obj.GroupVersionKind().Version) | ||
| if err != nil { |
There was a problem hiding this comment.
Treat unset object-schema validation as disabled here too.
This only skips pruning for Warn. When ObjectSchemaValidation is cleared, the old MutatingWebhookConfiguration can still hit this handler until reconciliation removes it, and the zero-value action currently falls through to pruning. Returning early for any action other than Deny keeps admission inert during that window.
🛠️ Suggested fix
- if compatibilityRequirement.Spec.ObjectSchemaValidation.Action == apiextensionsv1alpha1.CRDAdmitActionWarn {
- // When set to warn, we do not expect to mutate the object, so we return early without pruning.
- return nil
- }
+ if compatibilityRequirement.Spec.ObjectSchemaValidation.Action != apiextensionsv1alpha1.CRDAdmitActionDeny {
+ // Warn and unset should not mutate the object.
+ return nil
+ }📝 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.
| if compatibilityRequirement.Spec.ObjectSchemaValidation.Action == apiextensionsv1alpha1.CRDAdmitActionWarn { | |
| // When set to warn, we do not expect to mutate the object, so we return early without pruning. | |
| return nil | |
| } | |
| schema, err := v.getStructuralSchema(compatibilityRequirement, obj.GroupVersionKind().Version) | |
| if err != nil { | |
| if compatibilityRequirement.Spec.ObjectSchemaValidation.Action != apiextensionsv1alpha1.CRDAdmitActionDeny { | |
| // Warn and unset should not mutate the object. | |
| return nil | |
| } | |
| schema, err := v.getStructuralSchema(compatibilityRequirement, obj.GroupVersionKind().Version) | |
| if err != nil { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/controllers/crdcompatibility/objectpruning/webhook.go` around lines 119 -
125, The handler currently only returns early when ObjectSchemaValidation.Action
== CRDAdmitActionWarn, but when ObjectSchemaValidation is nil the zero-value
falls through and causes pruning; update the early-exit logic in the webhook
handler (around compatibilityRequirement.Spec.ObjectSchemaValidation.Action and
the call to v.getStructuralSchema) to treat unset/cleared validation as disabled
by returning nil unless the action is explicitly
apiextensionsv1alpha1.CRDAdmitActionDeny — i.e., check if
Spec.ObjectSchemaValidation is nil or its Action != CRDAdmitActionDeny and
return nil to keep the admission inert during reconciliation windows.
There was a problem hiding this comment.
The bot is correct here. This is a race to the api user unless we ensure that the MWC is gone before we update observedGeneration. Probably not a very consequential race, tbh, but a race.
There was a problem hiding this comment.
@mdbooth, agreed — the suggested fix covers that window cleanly. Changing the guard to != CRDAdmitActionDeny means the handler is inert for any action that isn't an explicit deny (including the zero-value/unset case), so pruning won't happen during the period between the CR update and the MWC being removed by the reconciler:
- if compatibilityRequirement.Spec.ObjectSchemaValidation.Action == apiextensionsv1alpha1.CRDAdmitActionWarn {
- // When set to warn, we do not expect to mutate the object, so we return early without pruning.
- return nil
- }
+ if compatibilityRequirement.Spec.ObjectSchemaValidation.Action != apiextensionsv1alpha1.CRDAdmitActionDeny {
+ // Only prune when explicitly set to Deny; Warn and unset should not mutate the object.
+ return nil
+ }| BeforeEach(func() { | ||
| // Create a new client that collects warnings in the test warning handler. | ||
| var err error | ||
| warningHandler = test.NewTestWarningHandler() | ||
| warningConfig := *cfg | ||
| warningConfig.WarningHandlerWithContext = warningHandler | ||
| cl, err = client.New(&warningConfig, client.Options{}) | ||
| Expect(err).ToNot(HaveOccurred()) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Locate warning-mode client creation:"
rg -n --type=go 'client.New\(&warningConfig,\s*client.Options\{\}\)' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go -C2
echo
echo "2) Find StartEnvTest/client initialization and scheme setup:"
rg -n --type=go 'func StartEnvTest|NewScheme|AddToScheme|CompatibilityRequirement' pkg/test -C3
echo
echo "3) Check whether custom API types are registered globally vs per-test scheme:"
rg -n --type=go 'scheme\.Scheme|AddToScheme|Install\(' -C2Repository: openshift/cluster-capi-operator
Length of output: 50388
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== 1) Find cfg initialization in handle_test.go ==="
rg -n --type=go '\bcfg\b' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go | head -30
echo
echo "=== 2) Look for BeforeEach/setup in the test file around the warning handler setup ==="
rg -n --type=go 'var.*cfg|func.*BeforeEach' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go -B2 -A5 | head -50
echo
echo "=== 3) Check how StartEnvTest is used in this package ==="
rg -n --type=go 'StartEnvTest' pkg/controllers/crdcompatibility/objectvalidation/ -B2 -A2
echo
echo "=== 4) Look for other client.New calls in the test file ==="
rg -n --type=go 'client\.New' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go -B3 -A3
echo
echo "=== 5) Check if apiextensionsv1alpha1 is imported in handle_test.go ==="
head -80 pkg/controllers/crdcompatibility/objectvalidation/handle_test.go | grep -E '^import|apiextensionsv1alpha1'Repository: openshift/cluster-capi-operator
Length of output: 244
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Reading handle_test.go to understand test structure ==="
wc -l pkg/controllers/crdcompatibility/objectvalidation/handle_test.go
echo
echo "=== First 100 lines: imports and setup ==="
head -100 pkg/controllers/crdcompatibility/objectvalidation/handle_test.go
echo
echo "=== Lines around 853-870: the code in question with context ==="
sed -n '830,880p' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go
echo
echo "=== Search for all BeforeEach in this file ==="
rg -n 'BeforeEach' pkg/controllers/crdcompatibility/objectvalidation/handle_test.goRepository: openshift/cluster-capi-operator
Length of output: 7035
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Package-level variable declarations ==="
rg -n '^var \(' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go -A 20
echo
echo "=== Outermost BeforeEach (line 81) - full setup ==="
sed -n '81,150p' pkg/controllers/capi-controller/handle_test.go 2>/dev/null || sed -n '81,150p' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go
echo
echo "=== Find where cfg is initialized ==="
rg -n 'cfg.*=' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go | head -20
echo
echo "=== Check testEnv and StarEnvTest usage ==="
rg -n 'testEnv|StartEnvTest' pkg/controllers/crdcompatibility/objectvalidation/handle_test.goRepository: openshift/cluster-capi-operator
Length of output: 118
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Read lines 1-80 to find package-level var declarations ==="
sed -n '1,80p' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go
echo
echo "=== Find var declarations anywhere in file ==="
rg -n '^\s*var\s+' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go
echo
echo "=== Look for testEnv initialization ==="
rg -n 'testEnv\s*=' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go -B2 -A5Repository: openshift/cluster-capi-operator
Length of output: 3784
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for 'cfg' and 'cl' declarations/assignments ==="
rg -n '\bcfg\b' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go | head -20
echo
echo "=== Search for 'cl' variable ==="
rg -n '\bcl\b' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go | head -20
echo
echo "=== Look at lines 849-875 to see full variable block ==="
sed -n '849,875p' pkg/controllers/crdcompatibility/objectvalidation/handle_test.go
echo
echo "=== Check if cfg/cl are declared in outer test file or parent suite setup ==="
find pkg/controllers/crdcompatibility/objectvalidation -name '*.go' -exec grep -l '\bcfg\b.*\*rest.Config\|\bcl\b.*client' {} \;
echo
echo "=== Look for test setup/init file ==="
ls -la pkg/controllers/crdcompatibility/objectvalidation/
</parameter>Repository: openshift/cluster-capi-operator
Length of output: 3366
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Read suite_test.go to find cfg and cl declarations ==="
cat pkg/controllers/crdcompatibility/objectvalidation/suite_test.goRepository: openshift/cluster-capi-operator
Length of output: 5133
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check test.StartEnvTest to understand scheme registration ==="
sed -n '40,100p' pkg/test/envtest.go
echo
echo "=== Verify that apiextensionsv1alpha1 is registered in scheme ==="
grep -n 'apiextensionsv1alpha1' pkg/test/envtest.go
echo
echo "=== Check what scheme package is imported and where ==="
head -30 pkg/test/envtest.go | grep -E '^import|scheme'Repository: openshift/cluster-capi-operator
Length of output: 3275
Use explicit scheme and avoid mutating global client state in BeforeEach.
At line 859, client.New(&warningConfig, client.Options{}) relies on the default scheme.Scheme which includes apiextensionsv1alpha1 types (registered in pkg/test/envtest.go). While this works, it's implicit and brittle. Additionally, reassigning the package-global cl creates test isolation issues: subsequent tests within this Describe block depend on the warning-configured client, and cleanup is not guaranteed if a test panics.
💡 Suggested fix
BeforeEach(func() {
// Create a new client that collects warnings in the test warning handler.
var err error
+ baseClient := cl
warningHandler = test.NewTestWarningHandler()
warningConfig := *cfg
warningConfig.WarningHandlerWithContext = warningHandler
- cl, err = client.New(&warningConfig, client.Options{})
+ cl, err = client.New(&warningConfig, client.Options{
+ Scheme: baseClient.Scheme(),
+ })
Expect(err).ToNot(HaveOccurred())
+
+ DeferCleanup(func() {
+ cl = baseClient
+ })
})📝 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.
| BeforeEach(func() { | |
| // Create a new client that collects warnings in the test warning handler. | |
| var err error | |
| warningHandler = test.NewTestWarningHandler() | |
| warningConfig := *cfg | |
| warningConfig.WarningHandlerWithContext = warningHandler | |
| cl, err = client.New(&warningConfig, client.Options{}) | |
| Expect(err).ToNot(HaveOccurred()) | |
| BeforeEach(func() { | |
| // Create a new client that collects warnings in the test warning handler. | |
| var err error | |
| baseClient := cl | |
| warningHandler = test.NewTestWarningHandler() | |
| warningConfig := *cfg | |
| warningConfig.WarningHandlerWithContext = warningHandler | |
| cl, err = client.New(&warningConfig, client.Options{ | |
| Scheme: baseClient.Scheme(), | |
| }) | |
| Expect(err).ToNot(HaveOccurred()) | |
| DeferCleanup(func() { | |
| cl = baseClient | |
| }) | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/controllers/crdcompatibility/objectvalidation/handle_test.go` around
lines 853 - 860, The BeforeEach mutates the package-global cl and relies on the
implicit global scheme; instead create an explicit scheme and avoid changing
global state: build a fresh scheme (e.g. runtime.NewScheme()) and explicitly
register the needed API types (the same apiextensionsv1alpha1 types registered
in pkg/test/envtest.go) and pass that scheme into client.New via a copied
rest.Config (warningConfig), then create a local client variable (e.g. testCl)
from client.New rather than assigning to the package-global cl; if other helpers
require the global, save the oldCl before creating testCl and restore oldCl in
AfterEach (and ensure any client cleanup runs even on panic) so tests remain
isolated.
| func isObjectValidationWebhookEnabled(obj *apiextensionsv1alpha1.CompatibilityRequirement) bool { | ||
| osv := obj.Spec.ObjectSchemaValidation | ||
| return osv.Action == "" && osv.MatchConditions == nil && labelSelectorIsEmpty(osv.NamespaceSelector) && labelSelectorIsEmpty(osv.ObjectSelector) | ||
| return osv.Action != "" || osv.MatchConditions != nil || !labelSelectorIsEmpty(osv.NamespaceSelector) || !labelSelectorIsEmpty(osv.ObjectSelector) | ||
| } |
There was a problem hiding this comment.
Treat empty MatchConditions as not configured.
Line 316 uses osv.MatchConditions != nil, so an explicit empty list still enables webhooks. That contradicts the “not active when not configured” objective.
🎯 Proposed fix
func isObjectValidationWebhookEnabled(obj *apiextensionsv1alpha1.CompatibilityRequirement) bool {
osv := obj.Spec.ObjectSchemaValidation
- return osv.Action != "" || osv.MatchConditions != nil || !labelSelectorIsEmpty(osv.NamespaceSelector) || !labelSelectorIsEmpty(osv.ObjectSelector)
+ return osv.Action != "" || len(osv.MatchConditions) > 0 || !labelSelectorIsEmpty(osv.NamespaceSelector) || !labelSelectorIsEmpty(osv.ObjectSelector)
}📝 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.
| func isObjectValidationWebhookEnabled(obj *apiextensionsv1alpha1.CompatibilityRequirement) bool { | |
| osv := obj.Spec.ObjectSchemaValidation | |
| return osv.Action == "" && osv.MatchConditions == nil && labelSelectorIsEmpty(osv.NamespaceSelector) && labelSelectorIsEmpty(osv.ObjectSelector) | |
| return osv.Action != "" || osv.MatchConditions != nil || !labelSelectorIsEmpty(osv.NamespaceSelector) || !labelSelectorIsEmpty(osv.ObjectSelector) | |
| } | |
| func isObjectValidationWebhookEnabled(obj *apiextensionsv1alpha1.CompatibilityRequirement) bool { | |
| osv := obj.Spec.ObjectSchemaValidation | |
| return osv.Action != "" || len(osv.MatchConditions) > 0 || !labelSelectorIsEmpty(osv.NamespaceSelector) || !labelSelectorIsEmpty(osv.ObjectSelector) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/controllers/crdcompatibility/reconcile.go` around lines 314 - 317, The
isObjectValidationWebhookEnabled function currently treats a non-nil
MatchConditions slice as enabled even if it's empty; change the check from
osl.MatchConditions != nil to len(osv.MatchConditions) > 0 so an explicitly
empty list is considered not configured, i.e., update the return expression in
isObjectValidationWebhookEnabled to use len(osv.MatchConditions) > 0 (keeping
the existing checks for osv.Action and the namespace/object selector helpers).
There was a problem hiding this comment.
I think the bot is correct here. I did wonder if we would distinguish between empty slices and no value, but I can't think of a reasonable behaviour for the empty value. Even if our validation disallows an empty value, I think it's nice to save somebody the effort of having to check by doing the more defensive test.
There was a problem hiding this comment.
@mdbooth, agreed — len(osv.MatchConditions) > 0 is the more defensive and correct check. The committable suggestion above already has the exact one-line fix ready to apply whenever the author picks it up.
|
@JoelSpeed: This pull request references OCPCLOUD-3384 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set. This pull request references OCPCLOUD-3365 which is a valid jira issue. Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set. 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. |
|
@JoelSpeed: The following test failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
| func isObjectValidationWebhookEnabled(obj *apiextensionsv1alpha1.CompatibilityRequirement) bool { | ||
| osv := obj.Spec.ObjectSchemaValidation | ||
| return osv.Action == "" && osv.MatchConditions == nil && labelSelectorIsEmpty(osv.NamespaceSelector) && labelSelectorIsEmpty(osv.ObjectSelector) | ||
| return osv.Action != "" || osv.MatchConditions != nil || !labelSelectorIsEmpty(osv.NamespaceSelector) || !labelSelectorIsEmpty(osv.ObjectSelector) | ||
| } |
There was a problem hiding this comment.
I think the bot is correct here. I did wonder if we would distinguish between empty slices and no value, but I can't think of a reasonable behaviour for the empty value. Even if our validation disallows an empty value, I think it's nice to save somebody the effort of having to check by doing the more defensive test.
| if err := r.client.Get(ctx, types.NamespacedName{Name: webhookConfig.Name}, existingWebhookConfig); err != nil && !apierrors.IsNotFound(err) { | ||
| return fmt.Errorf("failed to get ValidatingWebhookConfiguration %s: %w", webhookConfig.Name, err) | ||
| } else if err == nil && !metav1.IsControlledBy(existingWebhookConfig, obj) { | ||
| return fmt.Errorf("%w: %s", errWebhookConfigNotControlledByCompatibilityRequirement, webhookConfig.Name) |
| if compatibilityRequirement.Spec.ObjectSchemaValidation.Action == apiextensionsv1alpha1.CRDAdmitActionWarn { | ||
| // When set to warn, we do not expect to mutate the object, so we return early without pruning. | ||
| return nil | ||
| } | ||
|
|
||
| schema, err := v.getStructuralSchema(compatibilityRequirement, obj.GroupVersionKind().Version) | ||
| if err != nil { |
There was a problem hiding this comment.
The bot is correct here. This is a race to the api user unless we ensure that the MWC is gone before we update observedGeneration. Probably not a very consequential race, tbh, but a race.
| retrievedObj.SetName(inputObject.GetName()) | ||
| retrievedObj.SetNamespace(inputObject.GetNamespace()) | ||
|
|
||
| Eventually(kWithCtx(ctx).Get(retrievedObj)).WithContext(ctx).Should(Succeed()) |
There was a problem hiding this comment.
I thought we were doing timeouts too, now?
| scenario.CompatibilityRequirement.Spec.ObjectSchemaValidation.Action = apiextensionsv1alpha1.CRDAdmitActionWarn | ||
| })).WithContext(ctx).Should(Succeed()) | ||
|
|
||
| By("Updating the object again, should not be pruned") |
There was a problem hiding this comment.
I feel like you need to wait here until the CR has a success condition with observedGeneration >= the return from your Update?
This ensures that users configuring admission to warn do not have pruning or rejected writes for object admission
Also ensures that VWC/MWCs are removed when object admission is not configured
Summary by CodeRabbit
New Features
Bug Fixes
Chores