From 9356561c86fac25c674432d2fe2e8b17be342d82 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Wed, 29 May 2019 21:30:25 -0700 Subject: [PATCH 1/8] codegen: split admissionregistration.v1beta1/Webhook into MutatingWebhook and ValidatingWebhook --- api/openapi-spec/swagger.json | 197 ++- .../v1beta1/zz_generated.conversion.go | 166 +- .../v1beta1/zz_generated.defaults.go | 4 +- .../zz_generated.deepcopy.go | 166 +- .../v1beta1/generated.pb.go | 1349 +++++++++++------ .../v1beta1/generated.proto | 376 +++-- .../v1beta1/types_swagger_doc_generated.go | 51 +- .../v1beta1/zz_generated.deepcopy.go | 166 +- 8 files changed, 1645 insertions(+), 830 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 67c37dd0f9f..b73d0089728 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -1,5 +1,58 @@ { "definitions": { + "io.k8s.api.admissionregistration.v1beta1.MutatingWebhook": { + "description": "MutatingWebhook describes an admission webhook and the resources and operations it applies to.", + "properties": { + "admissionReviewVersions": { + "description": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy. Default to `['v1beta1']`.", + "items": { + "type": "string" + }, + "type": "array" + }, + "clientConfig": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.WebhookClientConfig", + "description": "ClientConfig defines how to communicate with the hook. Required" + }, + "failurePolicy": { + "description": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", + "type": "string" + }, + "matchPolicy": { + "description": "matchPolicy defines how the \"rules\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.\n\nDefaults to \"Exact\"", + "type": "string" + }, + "name": { + "description": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", + "type": "string" + }, + "namespaceSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." + }, + "rules": { + "description": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.RuleWithOperations" + }, + "type": "array" + }, + "sideEffects": { + "description": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", + "type": "string" + }, + "timeoutSeconds": { + "description": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", + "format": "int32", + "type": "integer" + } + }, + "required": [ + "name", + "clientConfig" + ], + "type": "object" + }, "io.k8s.api.admissionregistration.v1beta1.MutatingWebhookConfiguration": { "description": "MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object.", "properties": { @@ -18,7 +71,7 @@ "webhooks": { "description": "Webhooks is a list of webhooks and the affected resources and operations.", "items": { - "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.Webhook" + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.MutatingWebhook" }, "type": "array", "x-kubernetes-patch-merge-key": "name", @@ -134,77 +187,8 @@ ], "type": "object" }, - "io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration": { - "description": "ValidatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and object without changing it.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", - "type": "string" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", - "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata." - }, - "webhooks": { - "description": "Webhooks is a list of webhooks and the affected resources and operations.", - "items": { - "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.Webhook" - }, - "type": "array", - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge" - } - }, - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "admissionregistration.k8s.io", - "kind": "ValidatingWebhookConfiguration", - "version": "v1beta1" - } - ] - }, - "io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfigurationList": { - "description": "ValidatingWebhookConfigurationList is a list of ValidatingWebhookConfiguration.", - "properties": { - "apiVersion": { - "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", - "type": "string" - }, - "items": { - "description": "List of ValidatingWebhookConfiguration.", - "items": { - "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration" - }, - "type": "array" - }, - "kind": { - "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", - "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" - } - }, - "required": [ - "items" - ], - "type": "object", - "x-kubernetes-group-version-kind": [ - { - "group": "admissionregistration.k8s.io", - "kind": "ValidatingWebhookConfigurationList", - "version": "v1beta1" - } - ] - }, - "io.k8s.api.admissionregistration.v1beta1.Webhook": { - "description": "Webhook describes an admission webhook and the resources and operations it applies to.", + "io.k8s.api.admissionregistration.v1beta1.ValidatingWebhook": { + "description": "ValidatingWebhook describes an admission webhook and the resources and operations it applies to.", "properties": { "admissionReviewVersions": { "description": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy. Default to `['v1beta1']`.", @@ -256,6 +240,75 @@ ], "type": "object" }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration": { + "description": "ValidatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and object without changing it.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta", + "description": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata." + }, + "webhooks": { + "description": "Webhooks is a list of webhooks and the affected resources and operations.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ValidatingWebhook" + }, + "type": "array", + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge" + } + }, + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingWebhookConfiguration", + "version": "v1beta1" + } + ] + }, + "io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfigurationList": { + "description": "ValidatingWebhookConfigurationList is a list of ValidatingWebhookConfiguration.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type": "string" + }, + "items": { + "description": "List of ValidatingWebhookConfiguration.", + "items": { + "$ref": "#/definitions/io.k8s.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration" + }, + "type": "array" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta", + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds" + } + }, + "required": [ + "items" + ], + "type": "object", + "x-kubernetes-group-version-kind": [ + { + "group": "admissionregistration.k8s.io", + "kind": "ValidatingWebhookConfigurationList", + "version": "v1beta1" + } + ] + }, "io.k8s.api.admissionregistration.v1beta1.WebhookClientConfig": { "description": "WebhookClientConfig contains the information to make a TLS connection with the webhook", "properties": { diff --git a/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go b/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go index 516e2068f7b..3f296682ed1 100644 --- a/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go @@ -37,6 +37,16 @@ func init() { // RegisterConversions adds conversion functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*v1beta1.MutatingWebhook)(nil), (*admissionregistration.MutatingWebhook)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_MutatingWebhook_To_admissionregistration_MutatingWebhook(a.(*v1beta1.MutatingWebhook), b.(*admissionregistration.MutatingWebhook), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*admissionregistration.MutatingWebhook)(nil), (*v1beta1.MutatingWebhook)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_admissionregistration_MutatingWebhook_To_v1beta1_MutatingWebhook(a.(*admissionregistration.MutatingWebhook), b.(*v1beta1.MutatingWebhook), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*v1beta1.MutatingWebhookConfiguration)(nil), (*admissionregistration.MutatingWebhookConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_MutatingWebhookConfiguration_To_admissionregistration_MutatingWebhookConfiguration(a.(*v1beta1.MutatingWebhookConfiguration), b.(*admissionregistration.MutatingWebhookConfiguration), scope) }); err != nil { @@ -87,6 +97,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*v1beta1.ValidatingWebhook)(nil), (*admissionregistration.ValidatingWebhook)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_ValidatingWebhook_To_admissionregistration_ValidatingWebhook(a.(*v1beta1.ValidatingWebhook), b.(*admissionregistration.ValidatingWebhook), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*admissionregistration.ValidatingWebhook)(nil), (*v1beta1.ValidatingWebhook)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_admissionregistration_ValidatingWebhook_To_v1beta1_ValidatingWebhook(a.(*admissionregistration.ValidatingWebhook), b.(*v1beta1.ValidatingWebhook), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*v1beta1.ValidatingWebhookConfiguration)(nil), (*admissionregistration.ValidatingWebhookConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_ValidatingWebhookConfiguration_To_admissionregistration_ValidatingWebhookConfiguration(a.(*v1beta1.ValidatingWebhookConfiguration), b.(*admissionregistration.ValidatingWebhookConfiguration), scope) }); err != nil { @@ -107,16 +127,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.Webhook)(nil), (*admissionregistration.Webhook)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_Webhook_To_admissionregistration_Webhook(a.(*v1beta1.Webhook), b.(*admissionregistration.Webhook), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*admissionregistration.Webhook)(nil), (*v1beta1.Webhook)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_admissionregistration_Webhook_To_v1beta1_Webhook(a.(*admissionregistration.Webhook), b.(*v1beta1.Webhook), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*v1beta1.WebhookClientConfig)(nil), (*admissionregistration.WebhookClientConfig)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_WebhookClientConfig_To_admissionregistration_WebhookClientConfig(a.(*v1beta1.WebhookClientConfig), b.(*admissionregistration.WebhookClientConfig), scope) }); err != nil { @@ -130,13 +140,53 @@ func RegisterConversions(s *runtime.Scheme) error { return nil } +func autoConvert_v1beta1_MutatingWebhook_To_admissionregistration_MutatingWebhook(in *v1beta1.MutatingWebhook, out *admissionregistration.MutatingWebhook, s conversion.Scope) error { + out.Name = in.Name + if err := Convert_v1beta1_WebhookClientConfig_To_admissionregistration_WebhookClientConfig(&in.ClientConfig, &out.ClientConfig, s); err != nil { + return err + } + out.Rules = *(*[]admissionregistration.RuleWithOperations)(unsafe.Pointer(&in.Rules)) + out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) + out.MatchPolicy = (*admissionregistration.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) + out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) + out.SideEffects = (*admissionregistration.SideEffectClass)(unsafe.Pointer(in.SideEffects)) + out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) + out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) + return nil +} + +// Convert_v1beta1_MutatingWebhook_To_admissionregistration_MutatingWebhook is an autogenerated conversion function. +func Convert_v1beta1_MutatingWebhook_To_admissionregistration_MutatingWebhook(in *v1beta1.MutatingWebhook, out *admissionregistration.MutatingWebhook, s conversion.Scope) error { + return autoConvert_v1beta1_MutatingWebhook_To_admissionregistration_MutatingWebhook(in, out, s) +} + +func autoConvert_admissionregistration_MutatingWebhook_To_v1beta1_MutatingWebhook(in *admissionregistration.MutatingWebhook, out *v1beta1.MutatingWebhook, s conversion.Scope) error { + out.Name = in.Name + if err := Convert_admissionregistration_WebhookClientConfig_To_v1beta1_WebhookClientConfig(&in.ClientConfig, &out.ClientConfig, s); err != nil { + return err + } + out.Rules = *(*[]v1beta1.RuleWithOperations)(unsafe.Pointer(&in.Rules)) + out.FailurePolicy = (*v1beta1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) + out.MatchPolicy = (*v1beta1.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) + out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) + out.SideEffects = (*v1beta1.SideEffectClass)(unsafe.Pointer(in.SideEffects)) + out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) + out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) + return nil +} + +// Convert_admissionregistration_MutatingWebhook_To_v1beta1_MutatingWebhook is an autogenerated conversion function. +func Convert_admissionregistration_MutatingWebhook_To_v1beta1_MutatingWebhook(in *admissionregistration.MutatingWebhook, out *v1beta1.MutatingWebhook, s conversion.Scope) error { + return autoConvert_admissionregistration_MutatingWebhook_To_v1beta1_MutatingWebhook(in, out, s) +} + func autoConvert_v1beta1_MutatingWebhookConfiguration_To_admissionregistration_MutatingWebhookConfiguration(in *v1beta1.MutatingWebhookConfiguration, out *admissionregistration.MutatingWebhookConfiguration, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if in.Webhooks != nil { in, out := &in.Webhooks, &out.Webhooks - *out = make([]admissionregistration.Webhook, len(*in)) + *out = make([]admissionregistration.MutatingWebhook, len(*in)) for i := range *in { - if err := Convert_v1beta1_Webhook_To_admissionregistration_Webhook(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta1_MutatingWebhook_To_admissionregistration_MutatingWebhook(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -155,9 +205,9 @@ func autoConvert_admissionregistration_MutatingWebhookConfiguration_To_v1beta1_M out.ObjectMeta = in.ObjectMeta if in.Webhooks != nil { in, out := &in.Webhooks, &out.Webhooks - *out = make([]v1beta1.Webhook, len(*in)) + *out = make([]v1beta1.MutatingWebhook, len(*in)) for i := range *in { - if err := Convert_admissionregistration_Webhook_To_v1beta1_Webhook(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_admissionregistration_MutatingWebhook_To_v1beta1_MutatingWebhook(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -296,13 +346,53 @@ func Convert_admissionregistration_ServiceReference_To_v1beta1_ServiceReference( return autoConvert_admissionregistration_ServiceReference_To_v1beta1_ServiceReference(in, out, s) } +func autoConvert_v1beta1_ValidatingWebhook_To_admissionregistration_ValidatingWebhook(in *v1beta1.ValidatingWebhook, out *admissionregistration.ValidatingWebhook, s conversion.Scope) error { + out.Name = in.Name + if err := Convert_v1beta1_WebhookClientConfig_To_admissionregistration_WebhookClientConfig(&in.ClientConfig, &out.ClientConfig, s); err != nil { + return err + } + out.Rules = *(*[]admissionregistration.RuleWithOperations)(unsafe.Pointer(&in.Rules)) + out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) + out.MatchPolicy = (*admissionregistration.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) + out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) + out.SideEffects = (*admissionregistration.SideEffectClass)(unsafe.Pointer(in.SideEffects)) + out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) + out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) + return nil +} + +// Convert_v1beta1_ValidatingWebhook_To_admissionregistration_ValidatingWebhook is an autogenerated conversion function. +func Convert_v1beta1_ValidatingWebhook_To_admissionregistration_ValidatingWebhook(in *v1beta1.ValidatingWebhook, out *admissionregistration.ValidatingWebhook, s conversion.Scope) error { + return autoConvert_v1beta1_ValidatingWebhook_To_admissionregistration_ValidatingWebhook(in, out, s) +} + +func autoConvert_admissionregistration_ValidatingWebhook_To_v1beta1_ValidatingWebhook(in *admissionregistration.ValidatingWebhook, out *v1beta1.ValidatingWebhook, s conversion.Scope) error { + out.Name = in.Name + if err := Convert_admissionregistration_WebhookClientConfig_To_v1beta1_WebhookClientConfig(&in.ClientConfig, &out.ClientConfig, s); err != nil { + return err + } + out.Rules = *(*[]v1beta1.RuleWithOperations)(unsafe.Pointer(&in.Rules)) + out.FailurePolicy = (*v1beta1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) + out.MatchPolicy = (*v1beta1.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) + out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) + out.SideEffects = (*v1beta1.SideEffectClass)(unsafe.Pointer(in.SideEffects)) + out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) + out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) + return nil +} + +// Convert_admissionregistration_ValidatingWebhook_To_v1beta1_ValidatingWebhook is an autogenerated conversion function. +func Convert_admissionregistration_ValidatingWebhook_To_v1beta1_ValidatingWebhook(in *admissionregistration.ValidatingWebhook, out *v1beta1.ValidatingWebhook, s conversion.Scope) error { + return autoConvert_admissionregistration_ValidatingWebhook_To_v1beta1_ValidatingWebhook(in, out, s) +} + func autoConvert_v1beta1_ValidatingWebhookConfiguration_To_admissionregistration_ValidatingWebhookConfiguration(in *v1beta1.ValidatingWebhookConfiguration, out *admissionregistration.ValidatingWebhookConfiguration, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if in.Webhooks != nil { in, out := &in.Webhooks, &out.Webhooks - *out = make([]admissionregistration.Webhook, len(*in)) + *out = make([]admissionregistration.ValidatingWebhook, len(*in)) for i := range *in { - if err := Convert_v1beta1_Webhook_To_admissionregistration_Webhook(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_v1beta1_ValidatingWebhook_To_admissionregistration_ValidatingWebhook(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -321,9 +411,9 @@ func autoConvert_admissionregistration_ValidatingWebhookConfiguration_To_v1beta1 out.ObjectMeta = in.ObjectMeta if in.Webhooks != nil { in, out := &in.Webhooks, &out.Webhooks - *out = make([]v1beta1.Webhook, len(*in)) + *out = make([]v1beta1.ValidatingWebhook, len(*in)) for i := range *in { - if err := Convert_admissionregistration_Webhook_To_v1beta1_Webhook(&(*in)[i], &(*out)[i], s); err != nil { + if err := Convert_admissionregistration_ValidatingWebhook_To_v1beta1_ValidatingWebhook(&(*in)[i], &(*out)[i], s); err != nil { return err } } @@ -380,46 +470,6 @@ func Convert_admissionregistration_ValidatingWebhookConfigurationList_To_v1beta1 return autoConvert_admissionregistration_ValidatingWebhookConfigurationList_To_v1beta1_ValidatingWebhookConfigurationList(in, out, s) } -func autoConvert_v1beta1_Webhook_To_admissionregistration_Webhook(in *v1beta1.Webhook, out *admissionregistration.Webhook, s conversion.Scope) error { - out.Name = in.Name - if err := Convert_v1beta1_WebhookClientConfig_To_admissionregistration_WebhookClientConfig(&in.ClientConfig, &out.ClientConfig, s); err != nil { - return err - } - out.Rules = *(*[]admissionregistration.RuleWithOperations)(unsafe.Pointer(&in.Rules)) - out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) - out.MatchPolicy = (*admissionregistration.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) - out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) - out.SideEffects = (*admissionregistration.SideEffectClass)(unsafe.Pointer(in.SideEffects)) - out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) - out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) - return nil -} - -// Convert_v1beta1_Webhook_To_admissionregistration_Webhook is an autogenerated conversion function. -func Convert_v1beta1_Webhook_To_admissionregistration_Webhook(in *v1beta1.Webhook, out *admissionregistration.Webhook, s conversion.Scope) error { - return autoConvert_v1beta1_Webhook_To_admissionregistration_Webhook(in, out, s) -} - -func autoConvert_admissionregistration_Webhook_To_v1beta1_Webhook(in *admissionregistration.Webhook, out *v1beta1.Webhook, s conversion.Scope) error { - out.Name = in.Name - if err := Convert_admissionregistration_WebhookClientConfig_To_v1beta1_WebhookClientConfig(&in.ClientConfig, &out.ClientConfig, s); err != nil { - return err - } - out.Rules = *(*[]v1beta1.RuleWithOperations)(unsafe.Pointer(&in.Rules)) - out.FailurePolicy = (*v1beta1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) - out.MatchPolicy = (*v1beta1.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) - out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) - out.SideEffects = (*v1beta1.SideEffectClass)(unsafe.Pointer(in.SideEffects)) - out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) - out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) - return nil -} - -// Convert_admissionregistration_Webhook_To_v1beta1_Webhook is an autogenerated conversion function. -func Convert_admissionregistration_Webhook_To_v1beta1_Webhook(in *admissionregistration.Webhook, out *v1beta1.Webhook, s conversion.Scope) error { - return autoConvert_admissionregistration_Webhook_To_v1beta1_Webhook(in, out, s) -} - func autoConvert_v1beta1_WebhookClientConfig_To_admissionregistration_WebhookClientConfig(in *v1beta1.WebhookClientConfig, out *admissionregistration.WebhookClientConfig, s conversion.Scope) error { out.URL = (*string)(unsafe.Pointer(in.URL)) if in.Service != nil { diff --git a/pkg/apis/admissionregistration/v1beta1/zz_generated.defaults.go b/pkg/apis/admissionregistration/v1beta1/zz_generated.defaults.go index 8c49d7a9822..8159b2c9583 100644 --- a/pkg/apis/admissionregistration/v1beta1/zz_generated.defaults.go +++ b/pkg/apis/admissionregistration/v1beta1/zz_generated.defaults.go @@ -47,7 +47,7 @@ func RegisterDefaults(scheme *runtime.Scheme) error { func SetObjectDefaults_MutatingWebhookConfiguration(in *v1beta1.MutatingWebhookConfiguration) { for i := range in.Webhooks { a := &in.Webhooks[i] - SetDefaults_Webhook(a) + SetDefaults_MutatingWebhook(a) if a.ClientConfig.Service != nil { SetDefaults_ServiceReference(a.ClientConfig.Service) } @@ -68,7 +68,7 @@ func SetObjectDefaults_MutatingWebhookConfigurationList(in *v1beta1.MutatingWebh func SetObjectDefaults_ValidatingWebhookConfiguration(in *v1beta1.ValidatingWebhookConfiguration) { for i := range in.Webhooks { a := &in.Webhooks[i] - SetDefaults_Webhook(a) + SetDefaults_ValidatingWebhook(a) if a.ClientConfig.Service != nil { SetDefaults_ServiceReference(a.ClientConfig.Service) } diff --git a/pkg/apis/admissionregistration/zz_generated.deepcopy.go b/pkg/apis/admissionregistration/zz_generated.deepcopy.go index 9e94111d6ea..f6598b94846 100644 --- a/pkg/apis/admissionregistration/zz_generated.deepcopy.go +++ b/pkg/apis/admissionregistration/zz_generated.deepcopy.go @@ -25,6 +25,60 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MutatingWebhook) DeepCopyInto(out *MutatingWebhook) { + *out = *in + in.ClientConfig.DeepCopyInto(&out.ClientConfig) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]RuleWithOperations, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.FailurePolicy != nil { + in, out := &in.FailurePolicy, &out.FailurePolicy + *out = new(FailurePolicyType) + **out = **in + } + if in.MatchPolicy != nil { + in, out := &in.MatchPolicy, &out.MatchPolicy + *out = new(MatchPolicyType) + **out = **in + } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.SideEffects != nil { + in, out := &in.SideEffects, &out.SideEffects + *out = new(SideEffectClass) + **out = **in + } + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } + if in.AdmissionReviewVersions != nil { + in, out := &in.AdmissionReviewVersions, &out.AdmissionReviewVersions + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MutatingWebhook. +func (in *MutatingWebhook) DeepCopy() *MutatingWebhook { + if in == nil { + return nil + } + out := new(MutatingWebhook) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MutatingWebhookConfiguration) DeepCopyInto(out *MutatingWebhookConfiguration) { *out = *in @@ -32,7 +86,7 @@ func (in *MutatingWebhookConfiguration) DeepCopyInto(out *MutatingWebhookConfigu in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) if in.Webhooks != nil { in, out := &in.Webhooks, &out.Webhooks - *out = make([]Webhook, len(*in)) + *out = make([]MutatingWebhook, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -170,6 +224,60 @@ func (in *ServiceReference) DeepCopy() *ServiceReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValidatingWebhook) DeepCopyInto(out *ValidatingWebhook) { + *out = *in + in.ClientConfig.DeepCopyInto(&out.ClientConfig) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]RuleWithOperations, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.FailurePolicy != nil { + in, out := &in.FailurePolicy, &out.FailurePolicy + *out = new(FailurePolicyType) + **out = **in + } + if in.MatchPolicy != nil { + in, out := &in.MatchPolicy, &out.MatchPolicy + *out = new(MatchPolicyType) + **out = **in + } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.SideEffects != nil { + in, out := &in.SideEffects, &out.SideEffects + *out = new(SideEffectClass) + **out = **in + } + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } + if in.AdmissionReviewVersions != nil { + in, out := &in.AdmissionReviewVersions, &out.AdmissionReviewVersions + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValidatingWebhook. +func (in *ValidatingWebhook) DeepCopy() *ValidatingWebhook { + if in == nil { + return nil + } + out := new(ValidatingWebhook) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ValidatingWebhookConfiguration) DeepCopyInto(out *ValidatingWebhookConfiguration) { *out = *in @@ -177,7 +285,7 @@ func (in *ValidatingWebhookConfiguration) DeepCopyInto(out *ValidatingWebhookCon in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) if in.Webhooks != nil { in, out := &in.Webhooks, &out.Webhooks - *out = make([]Webhook, len(*in)) + *out = make([]ValidatingWebhook, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -236,60 +344,6 @@ func (in *ValidatingWebhookConfigurationList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Webhook) DeepCopyInto(out *Webhook) { - *out = *in - in.ClientConfig.DeepCopyInto(&out.ClientConfig) - if in.Rules != nil { - in, out := &in.Rules, &out.Rules - *out = make([]RuleWithOperations, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.FailurePolicy != nil { - in, out := &in.FailurePolicy, &out.FailurePolicy - *out = new(FailurePolicyType) - **out = **in - } - if in.MatchPolicy != nil { - in, out := &in.MatchPolicy, &out.MatchPolicy - *out = new(MatchPolicyType) - **out = **in - } - if in.NamespaceSelector != nil { - in, out := &in.NamespaceSelector, &out.NamespaceSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - if in.SideEffects != nil { - in, out := &in.SideEffects, &out.SideEffects - *out = new(SideEffectClass) - **out = **in - } - if in.TimeoutSeconds != nil { - in, out := &in.TimeoutSeconds, &out.TimeoutSeconds - *out = new(int32) - **out = **in - } - if in.AdmissionReviewVersions != nil { - in, out := &in.AdmissionReviewVersions, &out.AdmissionReviewVersions - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Webhook. -func (in *Webhook) DeepCopy() *Webhook { - if in == nil { - return nil - } - out := new(Webhook) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WebhookClientConfig) DeepCopyInto(out *WebhookClientConfig) { *out = *in diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go index f3c73dc4824..5057c38c4f2 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go @@ -24,14 +24,15 @@ limitations under the License. k8s.io/kubernetes/vendor/k8s.io/api/admissionregistration/v1beta1/generated.proto It has these top-level messages: + MutatingWebhook MutatingWebhookConfiguration MutatingWebhookConfigurationList Rule RuleWithOperations ServiceReference + ValidatingWebhook ValidatingWebhookConfiguration ValidatingWebhookConfigurationList - Webhook WebhookClientConfig */ package v1beta1 @@ -58,61 +59,156 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +func (m *MutatingWebhook) Reset() { *m = MutatingWebhook{} } +func (*MutatingWebhook) ProtoMessage() {} +func (*MutatingWebhook) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} } + func (m *MutatingWebhookConfiguration) Reset() { *m = MutatingWebhookConfiguration{} } func (*MutatingWebhookConfiguration) ProtoMessage() {} func (*MutatingWebhookConfiguration) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{0} + return fileDescriptorGenerated, []int{1} } func (m *MutatingWebhookConfigurationList) Reset() { *m = MutatingWebhookConfigurationList{} } func (*MutatingWebhookConfigurationList) ProtoMessage() {} func (*MutatingWebhookConfigurationList) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{1} + return fileDescriptorGenerated, []int{2} } func (m *Rule) Reset() { *m = Rule{} } func (*Rule) ProtoMessage() {} -func (*Rule) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{2} } +func (*Rule) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{3} } func (m *RuleWithOperations) Reset() { *m = RuleWithOperations{} } func (*RuleWithOperations) ProtoMessage() {} -func (*RuleWithOperations) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{3} } +func (*RuleWithOperations) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{4} } func (m *ServiceReference) Reset() { *m = ServiceReference{} } func (*ServiceReference) ProtoMessage() {} -func (*ServiceReference) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{4} } +func (*ServiceReference) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{5} } + +func (m *ValidatingWebhook) Reset() { *m = ValidatingWebhook{} } +func (*ValidatingWebhook) ProtoMessage() {} +func (*ValidatingWebhook) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{6} } func (m *ValidatingWebhookConfiguration) Reset() { *m = ValidatingWebhookConfiguration{} } func (*ValidatingWebhookConfiguration) ProtoMessage() {} func (*ValidatingWebhookConfiguration) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{5} + return fileDescriptorGenerated, []int{7} } func (m *ValidatingWebhookConfigurationList) Reset() { *m = ValidatingWebhookConfigurationList{} } func (*ValidatingWebhookConfigurationList) ProtoMessage() {} func (*ValidatingWebhookConfigurationList) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{6} + return fileDescriptorGenerated, []int{8} } -func (m *Webhook) Reset() { *m = Webhook{} } -func (*Webhook) ProtoMessage() {} -func (*Webhook) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{7} } - func (m *WebhookClientConfig) Reset() { *m = WebhookClientConfig{} } func (*WebhookClientConfig) ProtoMessage() {} -func (*WebhookClientConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{8} } +func (*WebhookClientConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{9} } func init() { + proto.RegisterType((*MutatingWebhook)(nil), "k8s.io.api.admissionregistration.v1beta1.MutatingWebhook") proto.RegisterType((*MutatingWebhookConfiguration)(nil), "k8s.io.api.admissionregistration.v1beta1.MutatingWebhookConfiguration") proto.RegisterType((*MutatingWebhookConfigurationList)(nil), "k8s.io.api.admissionregistration.v1beta1.MutatingWebhookConfigurationList") proto.RegisterType((*Rule)(nil), "k8s.io.api.admissionregistration.v1beta1.Rule") proto.RegisterType((*RuleWithOperations)(nil), "k8s.io.api.admissionregistration.v1beta1.RuleWithOperations") proto.RegisterType((*ServiceReference)(nil), "k8s.io.api.admissionregistration.v1beta1.ServiceReference") + proto.RegisterType((*ValidatingWebhook)(nil), "k8s.io.api.admissionregistration.v1beta1.ValidatingWebhook") proto.RegisterType((*ValidatingWebhookConfiguration)(nil), "k8s.io.api.admissionregistration.v1beta1.ValidatingWebhookConfiguration") proto.RegisterType((*ValidatingWebhookConfigurationList)(nil), "k8s.io.api.admissionregistration.v1beta1.ValidatingWebhookConfigurationList") - proto.RegisterType((*Webhook)(nil), "k8s.io.api.admissionregistration.v1beta1.Webhook") proto.RegisterType((*WebhookClientConfig)(nil), "k8s.io.api.admissionregistration.v1beta1.WebhookClientConfig") } +func (m *MutatingWebhook) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MutatingWebhook) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + dAtA[i] = 0x12 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.ClientConfig.Size())) + n1, err := m.ClientConfig.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + if len(m.Rules) > 0 { + for _, msg := range m.Rules { + dAtA[i] = 0x1a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.FailurePolicy != nil { + dAtA[i] = 0x22 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.FailurePolicy))) + i += copy(dAtA[i:], *m.FailurePolicy) + } + if m.NamespaceSelector != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.NamespaceSelector.Size())) + n2, err := m.NamespaceSelector.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + if m.SideEffects != nil { + dAtA[i] = 0x32 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.SideEffects))) + i += copy(dAtA[i:], *m.SideEffects) + } + if m.TimeoutSeconds != nil { + dAtA[i] = 0x38 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(*m.TimeoutSeconds)) + } + if len(m.AdmissionReviewVersions) > 0 { + for _, s := range m.AdmissionReviewVersions { + dAtA[i] = 0x42 + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } + if m.MatchPolicy != nil { + dAtA[i] = 0x4a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.MatchPolicy))) + i += copy(dAtA[i:], *m.MatchPolicy) + } + return i, nil +} + func (m *MutatingWebhookConfiguration) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -131,11 +227,11 @@ func (m *MutatingWebhookConfiguration) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintGenerated(dAtA, i, uint64(m.ObjectMeta.Size())) - n1, err := m.ObjectMeta.MarshalTo(dAtA[i:]) + n3, err := m.ObjectMeta.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n1 + i += n3 if len(m.Webhooks) > 0 { for _, msg := range m.Webhooks { dAtA[i] = 0x12 @@ -169,11 +265,11 @@ func (m *MutatingWebhookConfigurationList) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintGenerated(dAtA, i, uint64(m.ListMeta.Size())) - n2, err := m.ListMeta.MarshalTo(dAtA[i:]) + n4, err := m.ListMeta.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n2 + i += n4 if len(m.Items) > 0 { for _, msg := range m.Items { dAtA[i] = 0x12 @@ -291,11 +387,11 @@ func (m *RuleWithOperations) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintGenerated(dAtA, i, uint64(m.Rule.Size())) - n3, err := m.Rule.MarshalTo(dAtA[i:]) + n5, err := m.Rule.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n3 + i += n5 return i, nil } @@ -336,7 +432,7 @@ func (m *ServiceReference) MarshalTo(dAtA []byte) (int, error) { return i, nil } -func (m *ValidatingWebhookConfiguration) Marshal() (dAtA []byte, err error) { +func (m *ValidatingWebhook) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalTo(dAtA) @@ -346,83 +442,7 @@ func (m *ValidatingWebhookConfiguration) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ValidatingWebhookConfiguration) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintGenerated(dAtA, i, uint64(m.ObjectMeta.Size())) - n4, err := m.ObjectMeta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n4 - if len(m.Webhooks) > 0 { - for _, msg := range m.Webhooks { - dAtA[i] = 0x12 - i++ - i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - return i, nil -} - -func (m *ValidatingWebhookConfigurationList) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ValidatingWebhookConfigurationList) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintGenerated(dAtA, i, uint64(m.ListMeta.Size())) - n5, err := m.ListMeta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n5 - if len(m.Items) > 0 { - for _, msg := range m.Items { - dAtA[i] = 0x12 - i++ - i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - return i, nil -} - -func (m *Webhook) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Webhook) MarshalTo(dAtA []byte) (int, error) { +func (m *ValidatingWebhook) MarshalTo(dAtA []byte) (int, error) { var i int _ = i var l int @@ -502,6 +522,82 @@ func (m *Webhook) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *ValidatingWebhookConfiguration) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ValidatingWebhookConfiguration) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.ObjectMeta.Size())) + n8, err := m.ObjectMeta.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n8 + if len(m.Webhooks) > 0 { + for _, msg := range m.Webhooks { + dAtA[i] = 0x12 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + +func (m *ValidatingWebhookConfigurationList) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ValidatingWebhookConfigurationList) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.ListMeta.Size())) + n9, err := m.ListMeta.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n9 + if len(m.Items) > 0 { + for _, msg := range m.Items { + dAtA[i] = 0x12 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + return i, nil +} + func (m *WebhookClientConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -521,11 +617,11 @@ func (m *WebhookClientConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintGenerated(dAtA, i, uint64(m.Service.Size())) - n8, err := m.Service.MarshalTo(dAtA[i:]) + n10, err := m.Service.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n8 + i += n10 } if m.CABundle != nil { dAtA[i] = 0x12 @@ -551,6 +647,47 @@ func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return offset + 1 } +func (m *MutatingWebhook) Size() (n int) { + var l int + _ = l + l = len(m.Name) + n += 1 + l + sovGenerated(uint64(l)) + l = m.ClientConfig.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Rules) > 0 { + for _, e := range m.Rules { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + if m.FailurePolicy != nil { + l = len(*m.FailurePolicy) + n += 1 + l + sovGenerated(uint64(l)) + } + if m.NamespaceSelector != nil { + l = m.NamespaceSelector.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.SideEffects != nil { + l = len(*m.SideEffects) + n += 1 + l + sovGenerated(uint64(l)) + } + if m.TimeoutSeconds != nil { + n += 1 + sovGenerated(uint64(*m.TimeoutSeconds)) + } + if len(m.AdmissionReviewVersions) > 0 { + for _, s := range m.AdmissionReviewVersions { + l = len(s) + n += 1 + l + sovGenerated(uint64(l)) + } + } + if m.MatchPolicy != nil { + l = len(*m.MatchPolicy) + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + func (m *MutatingWebhookConfiguration) Size() (n int) { var l int _ = l @@ -638,35 +775,7 @@ func (m *ServiceReference) Size() (n int) { return n } -func (m *ValidatingWebhookConfiguration) Size() (n int) { - var l int - _ = l - l = m.ObjectMeta.Size() - n += 1 + l + sovGenerated(uint64(l)) - if len(m.Webhooks) > 0 { - for _, e := range m.Webhooks { - l = e.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - } - return n -} - -func (m *ValidatingWebhookConfigurationList) Size() (n int) { - var l int - _ = l - l = m.ListMeta.Size() - n += 1 + l + sovGenerated(uint64(l)) - if len(m.Items) > 0 { - for _, e := range m.Items { - l = e.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - } - return n -} - -func (m *Webhook) Size() (n int) { +func (m *ValidatingWebhook) Size() (n int) { var l int _ = l l = len(m.Name) @@ -707,6 +816,34 @@ func (m *Webhook) Size() (n int) { return n } +func (m *ValidatingWebhookConfiguration) Size() (n int) { + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Webhooks) > 0 { + for _, e := range m.Webhooks { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *ValidatingWebhookConfigurationList) Size() (n int) { + var l int + _ = l + l = m.ListMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.Items) > 0 { + for _, e := range m.Items { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + func (m *WebhookClientConfig) Size() (n int) { var l int _ = l @@ -738,13 +875,31 @@ func sovGenerated(x uint64) (n int) { func sozGenerated(x uint64) (n int) { return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (this *MutatingWebhook) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MutatingWebhook{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `ClientConfig:` + strings.Replace(strings.Replace(this.ClientConfig.String(), "WebhookClientConfig", "WebhookClientConfig", 1), `&`, ``, 1) + `,`, + `Rules:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Rules), "RuleWithOperations", "RuleWithOperations", 1), `&`, ``, 1) + `,`, + `FailurePolicy:` + valueToStringGenerated(this.FailurePolicy) + `,`, + `NamespaceSelector:` + strings.Replace(fmt.Sprintf("%v", this.NamespaceSelector), "LabelSelector", "k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector", 1) + `,`, + `SideEffects:` + valueToStringGenerated(this.SideEffects) + `,`, + `TimeoutSeconds:` + valueToStringGenerated(this.TimeoutSeconds) + `,`, + `AdmissionReviewVersions:` + fmt.Sprintf("%v", this.AdmissionReviewVersions) + `,`, + `MatchPolicy:` + valueToStringGenerated(this.MatchPolicy) + `,`, + `}`, + }, "") + return s +} func (this *MutatingWebhookConfiguration) String() string { if this == nil { return "nil" } s := strings.Join([]string{`&MutatingWebhookConfiguration{`, `ObjectMeta:` + strings.Replace(strings.Replace(this.ObjectMeta.String(), "ObjectMeta", "k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta", 1), `&`, ``, 1) + `,`, - `Webhooks:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Webhooks), "Webhook", "Webhook", 1), `&`, ``, 1) + `,`, + `Webhooks:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Webhooks), "MutatingWebhook", "MutatingWebhook", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -797,13 +952,31 @@ func (this *ServiceReference) String() string { }, "") return s } +func (this *ValidatingWebhook) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ValidatingWebhook{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `ClientConfig:` + strings.Replace(strings.Replace(this.ClientConfig.String(), "WebhookClientConfig", "WebhookClientConfig", 1), `&`, ``, 1) + `,`, + `Rules:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Rules), "RuleWithOperations", "RuleWithOperations", 1), `&`, ``, 1) + `,`, + `FailurePolicy:` + valueToStringGenerated(this.FailurePolicy) + `,`, + `NamespaceSelector:` + strings.Replace(fmt.Sprintf("%v", this.NamespaceSelector), "LabelSelector", "k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector", 1) + `,`, + `SideEffects:` + valueToStringGenerated(this.SideEffects) + `,`, + `TimeoutSeconds:` + valueToStringGenerated(this.TimeoutSeconds) + `,`, + `AdmissionReviewVersions:` + fmt.Sprintf("%v", this.AdmissionReviewVersions) + `,`, + `MatchPolicy:` + valueToStringGenerated(this.MatchPolicy) + `,`, + `}`, + }, "") + return s +} func (this *ValidatingWebhookConfiguration) String() string { if this == nil { return "nil" } s := strings.Join([]string{`&ValidatingWebhookConfiguration{`, `ObjectMeta:` + strings.Replace(strings.Replace(this.ObjectMeta.String(), "ObjectMeta", "k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta", 1), `&`, ``, 1) + `,`, - `Webhooks:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Webhooks), "Webhook", "Webhook", 1), `&`, ``, 1) + `,`, + `Webhooks:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Webhooks), "ValidatingWebhook", "ValidatingWebhook", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -819,24 +992,6 @@ func (this *ValidatingWebhookConfigurationList) String() string { }, "") return s } -func (this *Webhook) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&Webhook{`, - `Name:` + fmt.Sprintf("%v", this.Name) + `,`, - `ClientConfig:` + strings.Replace(strings.Replace(this.ClientConfig.String(), "WebhookClientConfig", "WebhookClientConfig", 1), `&`, ``, 1) + `,`, - `Rules:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Rules), "RuleWithOperations", "RuleWithOperations", 1), `&`, ``, 1) + `,`, - `FailurePolicy:` + valueToStringGenerated(this.FailurePolicy) + `,`, - `NamespaceSelector:` + strings.Replace(fmt.Sprintf("%v", this.NamespaceSelector), "LabelSelector", "k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector", 1) + `,`, - `SideEffects:` + valueToStringGenerated(this.SideEffects) + `,`, - `TimeoutSeconds:` + valueToStringGenerated(this.TimeoutSeconds) + `,`, - `AdmissionReviewVersions:` + fmt.Sprintf("%v", this.AdmissionReviewVersions) + `,`, - `MatchPolicy:` + valueToStringGenerated(this.MatchPolicy) + `,`, - `}`, - }, "") - return s -} func (this *WebhookClientConfig) String() string { if this == nil { return "nil" @@ -857,6 +1012,318 @@ func valueToStringGenerated(v interface{}) string { pv := reflect.Indirect(rv).Interface() return fmt.Sprintf("*%v", pv) } +func (m *MutatingWebhook) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MutatingWebhook: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MutatingWebhook: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClientConfig", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ClientConfig.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rules", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Rules = append(m.Rules, RuleWithOperations{}) + if err := m.Rules[len(m.Rules)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FailurePolicy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := FailurePolicyType(dAtA[iNdEx:postIndex]) + m.FailurePolicy = &s + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamespaceSelector", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NamespaceSelector == nil { + m.NamespaceSelector = &k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector{} + } + if err := m.NamespaceSelector.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SideEffects", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := SideEffectClass(dAtA[iNdEx:postIndex]) + m.SideEffects = &s + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutSeconds", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.TimeoutSeconds = &v + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AdmissionReviewVersions", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AdmissionReviewVersions = append(m.AdmissionReviewVersions, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MatchPolicy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := MatchPolicyType(dAtA[iNdEx:postIndex]) + m.MatchPolicy = &s + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MutatingWebhookConfiguration) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -942,7 +1409,7 @@ func (m *MutatingWebhookConfiguration) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Webhooks = append(m.Webhooks, Webhook{}) + m.Webhooks = append(m.Webhooks, MutatingWebhook{}) if err := m.Webhooks[len(m.Webhooks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -1513,7 +1980,7 @@ func (m *ServiceReference) Unmarshal(dAtA []byte) error { } return nil } -func (m *ValidatingWebhookConfiguration) Unmarshal(dAtA []byte) error { +func (m *ValidatingWebhook) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1536,232 +2003,10 @@ func (m *ValidatingWebhookConfiguration) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ValidatingWebhookConfiguration: wiretype end group for non-group") + return fmt.Errorf("proto: ValidatingWebhook: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ValidatingWebhookConfiguration: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Webhooks", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Webhooks = append(m.Webhooks, Webhook{}) - if err := m.Webhooks[len(m.Webhooks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ValidatingWebhookConfigurationList) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ValidatingWebhookConfigurationList: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ValidatingWebhookConfigurationList: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Items = append(m.Items, ValidatingWebhookConfiguration{}) - if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Webhook) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Webhook: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Webhook: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ValidatingWebhook: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -2047,6 +2292,228 @@ func (m *Webhook) Unmarshal(dAtA []byte) error { } return nil } +func (m *ValidatingWebhookConfiguration) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ValidatingWebhookConfiguration: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatingWebhookConfiguration: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Webhooks", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Webhooks = append(m.Webhooks, ValidatingWebhook{}) + if err := m.Webhooks[len(m.Webhooks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ValidatingWebhookConfigurationList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ValidatingWebhookConfigurationList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ValidatingWebhookConfigurationList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, ValidatingWebhookConfiguration{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *WebhookClientConfig) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2301,69 +2768,71 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 1021 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x55, 0x4f, 0x6f, 0xe3, 0x44, - 0x14, 0xaf, 0x37, 0x09, 0x89, 0x27, 0xed, 0xee, 0x76, 0xf8, 0xb3, 0x61, 0x59, 0xc5, 0x51, 0x0e, - 0x28, 0x12, 0xac, 0x4d, 0x0b, 0x42, 0x68, 0x05, 0x42, 0x75, 0x61, 0xa1, 0x52, 0xbb, 0x5b, 0x26, - 0xfb, 0x47, 0x42, 0x1c, 0x98, 0x38, 0x2f, 0xc9, 0x90, 0xc4, 0x63, 0x79, 0xc6, 0x29, 0xbd, 0xf1, - 0x11, 0xf8, 0x0a, 0x9c, 0xf8, 0x14, 0x1c, 0xb8, 0xf5, 0xb8, 0x17, 0xc4, 0x9e, 0x2c, 0x6a, 0xce, - 0x20, 0x71, 0xed, 0x09, 0xcd, 0xd8, 0x89, 0x93, 0xa6, 0xed, 0x66, 0x2f, 0x1c, 0xb8, 0x79, 0x7e, - 0xef, 0xfd, 0xde, 0x7b, 0xbf, 0x99, 0xf7, 0x9e, 0xd1, 0x57, 0xc3, 0x8f, 0x84, 0xcd, 0xb8, 0x33, - 0x8c, 0x3a, 0x10, 0xfa, 0x20, 0x41, 0x38, 0x13, 0xf0, 0xbb, 0x3c, 0x74, 0x32, 0x03, 0x0d, 0x98, - 0x43, 0xbb, 0x63, 0x26, 0x04, 0xe3, 0x7e, 0x08, 0x7d, 0x26, 0x64, 0x48, 0x25, 0xe3, 0xbe, 0x33, - 0xd9, 0xea, 0x80, 0xa4, 0x5b, 0x4e, 0x1f, 0x7c, 0x08, 0xa9, 0x84, 0xae, 0x1d, 0x84, 0x5c, 0x72, - 0xdc, 0x4a, 0x99, 0x36, 0x0d, 0x98, 0x7d, 0x21, 0xd3, 0xce, 0x98, 0xb7, 0xef, 0xf6, 0x99, 0x1c, - 0x44, 0x1d, 0xdb, 0xe3, 0x63, 0xa7, 0xcf, 0xfb, 0xdc, 0xd1, 0x01, 0x3a, 0x51, 0x4f, 0x9f, 0xf4, - 0x41, 0x7f, 0xa5, 0x81, 0x6f, 0x7f, 0x90, 0x97, 0x34, 0xa6, 0xde, 0x80, 0xf9, 0x10, 0x1e, 0x3b, - 0xc1, 0xb0, 0xaf, 0x00, 0xe1, 0x8c, 0x41, 0x52, 0x67, 0xb2, 0x54, 0xce, 0x6d, 0xe7, 0x32, 0x56, - 0x18, 0xf9, 0x92, 0x8d, 0x61, 0x89, 0xf0, 0xe1, 0x8b, 0x08, 0xc2, 0x1b, 0xc0, 0x98, 0x9e, 0xe7, - 0x35, 0x7f, 0x33, 0xd0, 0x9d, 0x83, 0x48, 0x52, 0xc9, 0xfc, 0xfe, 0x53, 0xe8, 0x0c, 0x38, 0x1f, - 0xee, 0x72, 0xbf, 0xc7, 0xfa, 0x51, 0x2a, 0x1b, 0x7f, 0x8b, 0x2a, 0xaa, 0xc8, 0x2e, 0x95, 0xb4, - 0x66, 0x34, 0x8c, 0x56, 0x75, 0xfb, 0x3d, 0x3b, 0xbf, 0xab, 0x59, 0x2e, 0x3b, 0x18, 0xf6, 0x15, - 0x20, 0x6c, 0xe5, 0x6d, 0x4f, 0xb6, 0xec, 0x87, 0x9d, 0xef, 0xc0, 0x93, 0x07, 0x20, 0xa9, 0x8b, - 0x4f, 0x62, 0x6b, 0x2d, 0x89, 0x2d, 0x94, 0x63, 0x64, 0x16, 0x15, 0xb7, 0x51, 0x25, 0xcb, 0x2c, - 0x6a, 0xd7, 0x1a, 0x85, 0x56, 0x75, 0x7b, 0xcb, 0x5e, 0xf5, 0x35, 0xec, 0x8c, 0xe9, 0x16, 0x55, - 0x0a, 0x52, 0x39, 0xca, 0x02, 0x35, 0xff, 0x32, 0x50, 0xe3, 0x2a, 0x5d, 0xfb, 0x4c, 0x48, 0xfc, - 0xcd, 0x92, 0x36, 0x7b, 0x35, 0x6d, 0x8a, 0xad, 0x95, 0xdd, 0xcc, 0x94, 0x55, 0xa6, 0xc8, 0x9c, - 0xae, 0x21, 0x2a, 0x31, 0x09, 0xe3, 0xa9, 0xa8, 0xfb, 0xab, 0x8b, 0xba, 0xaa, 0x70, 0x77, 0x23, - 0x4b, 0x59, 0xda, 0x53, 0xc1, 0x49, 0x9a, 0xa3, 0xf9, 0xab, 0x81, 0x8a, 0x24, 0x1a, 0x01, 0x7e, - 0x07, 0x99, 0x34, 0x60, 0x5f, 0x84, 0x3c, 0x0a, 0x44, 0xcd, 0x68, 0x14, 0x5a, 0xa6, 0xbb, 0x91, - 0xc4, 0x96, 0xb9, 0x73, 0xb8, 0x97, 0x82, 0x24, 0xb7, 0xe3, 0x2d, 0x54, 0xa5, 0x01, 0x7b, 0x02, - 0xa1, 0x2a, 0x25, 0x2d, 0xd4, 0x74, 0x6f, 0x24, 0xb1, 0x55, 0xdd, 0x39, 0xdc, 0x9b, 0xc2, 0x64, - 0xde, 0x47, 0xc5, 0x0f, 0x41, 0xf0, 0x28, 0xf4, 0x40, 0xd4, 0x0a, 0x79, 0x7c, 0x32, 0x05, 0x49, - 0x6e, 0xc7, 0xef, 0xa2, 0x92, 0xf0, 0x78, 0x00, 0xb5, 0x62, 0xc3, 0x68, 0x99, 0xee, 0x1b, 0xaa, - 0xec, 0xb6, 0x02, 0xce, 0x62, 0xcb, 0xd4, 0x1f, 0x8f, 0x8e, 0x03, 0x20, 0xa9, 0x53, 0xf3, 0x67, - 0x03, 0x61, 0xa5, 0xe1, 0x29, 0x93, 0x83, 0x87, 0x01, 0xa4, 0x7a, 0x05, 0xfe, 0x14, 0x21, 0x3e, - 0x3b, 0x65, 0x92, 0x2c, 0xdd, 0x4d, 0x33, 0xf4, 0x2c, 0xb6, 0x36, 0x66, 0x27, 0x1d, 0x72, 0x8e, - 0x82, 0x0f, 0x51, 0x31, 0x8c, 0x46, 0x50, 0xbb, 0xb6, 0xf4, 0xc4, 0x2f, 0x78, 0x07, 0x55, 0x8c, - 0xbb, 0x9e, 0xdd, 0xb7, 0xbe, 0x5e, 0xa2, 0x23, 0x35, 0x7f, 0x32, 0xd0, 0xcd, 0x36, 0x84, 0x13, - 0xe6, 0x01, 0x81, 0x1e, 0x84, 0xe0, 0x7b, 0x80, 0x1d, 0x64, 0xfa, 0x74, 0x0c, 0x22, 0xa0, 0x1e, - 0xe8, 0x76, 0x32, 0xdd, 0xcd, 0x8c, 0x6b, 0x3e, 0x98, 0x1a, 0x48, 0xee, 0x83, 0x1b, 0xa8, 0xa8, - 0x0e, 0xba, 0x2e, 0x33, 0xcf, 0xa3, 0x7c, 0x89, 0xb6, 0xe0, 0x3b, 0xa8, 0x18, 0x50, 0x39, 0xa8, - 0x15, 0xb4, 0x47, 0x45, 0x59, 0x0f, 0xa9, 0x1c, 0x10, 0x8d, 0x6a, 0x2b, 0x0f, 0xa5, 0xbe, 0xdc, - 0x52, 0x66, 0xe5, 0xa1, 0x24, 0x1a, 0x6d, 0xfe, 0x6e, 0xa0, 0xfa, 0x13, 0x3a, 0x62, 0xdd, 0xff, - 0xdd, 0x6c, 0xff, 0x63, 0xa0, 0xe6, 0xd5, 0xca, 0xfe, 0x83, 0xe9, 0x1e, 0x2f, 0x4e, 0xf7, 0x97, - 0xab, 0xcb, 0xba, 0xba, 0xf4, 0x4b, 0xe6, 0xfb, 0xef, 0x12, 0x2a, 0x67, 0xee, 0xb3, 0xbe, 0x31, - 0x2e, 0xed, 0x9b, 0x23, 0xb4, 0xee, 0x8d, 0x18, 0xf8, 0x32, 0x0d, 0x9d, 0x75, 0xfe, 0x27, 0x2f, - 0x7d, 0xf5, 0xbb, 0x73, 0x41, 0xdc, 0xd7, 0xb2, 0x44, 0xeb, 0xf3, 0x28, 0x59, 0x48, 0x84, 0x29, - 0x2a, 0xa9, 0x01, 0x49, 0x37, 0x43, 0x75, 0xfb, 0xe3, 0x97, 0x9b, 0xb5, 0xc5, 0xc1, 0xcf, 0x6f, - 0x42, 0xd9, 0x04, 0x49, 0x23, 0xe3, 0x7d, 0xb4, 0xd1, 0xa3, 0x6c, 0x14, 0x85, 0x70, 0xc8, 0x47, - 0xcc, 0x3b, 0xce, 0x76, 0xcb, 0xdb, 0x49, 0x6c, 0x6d, 0xdc, 0x9f, 0x37, 0x9c, 0xc5, 0xd6, 0xe6, - 0x02, 0xa0, 0x17, 0xc3, 0x22, 0x19, 0x7f, 0x8f, 0x36, 0x67, 0x03, 0xd9, 0x86, 0x11, 0x78, 0x92, - 0x87, 0xb5, 0x92, 0xbe, 0xae, 0xf7, 0x57, 0xec, 0x16, 0xda, 0x81, 0xd1, 0x94, 0xea, 0xbe, 0x9e, - 0xc4, 0xd6, 0xe6, 0x83, 0xf3, 0x11, 0xc9, 0x72, 0x12, 0xfc, 0x19, 0xaa, 0x0a, 0xd6, 0x85, 0xcf, - 0x7b, 0x3d, 0xf0, 0xa4, 0xa8, 0xbd, 0xa2, 0x55, 0x34, 0xd5, 0xee, 0x6d, 0xe7, 0xf0, 0x59, 0x6c, - 0xdd, 0xc8, 0x8f, 0xbb, 0x23, 0x2a, 0x04, 0x99, 0xa7, 0xe1, 0x7b, 0xe8, 0xba, 0xfa, 0xbd, 0xf3, - 0x48, 0xb6, 0xc1, 0xe3, 0x7e, 0x57, 0xd4, 0xca, 0x7a, 0x1b, 0xe0, 0x24, 0xb6, 0xae, 0x3f, 0x5a, - 0xb0, 0x90, 0x73, 0x9e, 0xf8, 0x31, 0xba, 0x35, 0x7b, 0x13, 0x02, 0x13, 0x06, 0x47, 0xb3, 0x3f, - 0x41, 0x45, 0x6f, 0xd9, 0xb7, 0x92, 0xd8, 0xba, 0xb5, 0x73, 0xb1, 0x0b, 0xb9, 0x8c, 0xab, 0x84, - 0x8d, 0xa9, 0xf4, 0x06, 0xd9, 0xf3, 0x98, 0xb9, 0xb0, 0x83, 0x1c, 0x56, 0xc2, 0xe6, 0x8e, 0xfa, - 0x69, 0xe6, 0x69, 0xcd, 0x5f, 0x0c, 0xf4, 0xea, 0x05, 0x5d, 0x88, 0x29, 0x2a, 0x8b, 0x74, 0xf3, - 0x66, 0x43, 0x7d, 0x6f, 0xf5, 0x1e, 0x3b, 0xbf, 0xb2, 0xdd, 0x6a, 0x12, 0x5b, 0xe5, 0x29, 0x3a, - 0x8d, 0x8b, 0x5b, 0xa8, 0xe2, 0x51, 0x37, 0xf2, 0xbb, 0xd9, 0x3f, 0x63, 0xdd, 0x5d, 0x57, 0x4b, - 0x60, 0x77, 0x27, 0xc5, 0xc8, 0xcc, 0x8a, 0xdf, 0x44, 0x85, 0x28, 0x1c, 0x65, 0xeb, 0xb9, 0x9c, - 0xc4, 0x56, 0xe1, 0x31, 0xd9, 0x27, 0x0a, 0x73, 0xef, 0x9e, 0x9c, 0xd6, 0xd7, 0x9e, 0x9d, 0xd6, - 0xd7, 0x9e, 0x9f, 0xd6, 0xd7, 0x7e, 0x48, 0xea, 0xc6, 0x49, 0x52, 0x37, 0x9e, 0x25, 0x75, 0xe3, - 0x79, 0x52, 0x37, 0xfe, 0x48, 0xea, 0xc6, 0x8f, 0x7f, 0xd6, 0xd7, 0xbe, 0x2e, 0x67, 0xa5, 0xfd, - 0x1b, 0x00, 0x00, 0xff, 0xff, 0xb0, 0x64, 0x23, 0xd6, 0xd3, 0x0a, 0x00, 0x00, + // 1056 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x55, 0x4d, 0x6f, 0xe3, 0x44, + 0x18, 0xae, 0x9b, 0x84, 0x26, 0x93, 0x76, 0xbb, 0x1d, 0x3e, 0x36, 0x94, 0x55, 0x1c, 0xe5, 0x80, + 0x22, 0xc1, 0xda, 0xb4, 0x20, 0x04, 0x0b, 0x08, 0xd5, 0x85, 0x85, 0x4a, 0xed, 0x6e, 0x99, 0xee, + 0x87, 0xc4, 0x87, 0xc4, 0xc4, 0x99, 0x24, 0x43, 0x62, 0x8f, 0xe5, 0x19, 0xa7, 0xf4, 0xc6, 0x4f, + 0xe0, 0x2f, 0x70, 0xe2, 0x57, 0x70, 0xe0, 0xd6, 0xe3, 0x1e, 0xf7, 0x82, 0x45, 0xcd, 0x99, 0x03, + 0x17, 0x84, 0x7a, 0x42, 0x33, 0x9e, 0xd8, 0xf9, 0x68, 0x97, 0x00, 0xd2, 0x5e, 0x76, 0x6f, 0x7e, + 0x9f, 0x77, 0xde, 0x8f, 0x67, 0xe6, 0xf1, 0xfb, 0x82, 0xcf, 0x06, 0xef, 0x70, 0x8b, 0x32, 0x7b, + 0x10, 0xb5, 0x49, 0xe8, 0x13, 0x41, 0xb8, 0x3d, 0x22, 0x7e, 0x87, 0x85, 0xb6, 0x76, 0xe0, 0x80, + 0xda, 0xb8, 0xe3, 0x51, 0xce, 0x29, 0xf3, 0x43, 0xd2, 0xa3, 0x5c, 0x84, 0x58, 0x50, 0xe6, 0xdb, + 0xa3, 0xad, 0x36, 0x11, 0x78, 0xcb, 0xee, 0x11, 0x9f, 0x84, 0x58, 0x90, 0x8e, 0x15, 0x84, 0x4c, + 0x30, 0xd8, 0x4a, 0x23, 0x2d, 0x1c, 0x50, 0xeb, 0xc2, 0x48, 0x4b, 0x47, 0x6e, 0xde, 0xe8, 0x51, + 0xd1, 0x8f, 0xda, 0x96, 0xcb, 0x3c, 0xbb, 0xc7, 0x7a, 0xcc, 0x56, 0x09, 0xda, 0x51, 0x57, 0x59, + 0xca, 0x50, 0x5f, 0x69, 0xe2, 0xcd, 0xb7, 0xf2, 0x96, 0x3c, 0xec, 0xf6, 0xa9, 0x4f, 0xc2, 0x13, + 0x3b, 0x18, 0xf4, 0x24, 0xc0, 0x6d, 0x8f, 0x08, 0x6c, 0x8f, 0xe6, 0xda, 0xd9, 0xb4, 0x2f, 0x8b, + 0x0a, 0x23, 0x5f, 0x50, 0x8f, 0xcc, 0x05, 0xbc, 0xfd, 0x4f, 0x01, 0xdc, 0xed, 0x13, 0x0f, 0xcf, + 0xc6, 0x35, 0xff, 0x2c, 0x81, 0xf5, 0x83, 0x48, 0x60, 0x41, 0xfd, 0xde, 0x03, 0xd2, 0xee, 0x33, + 0x36, 0x80, 0x0d, 0x50, 0xf4, 0xb1, 0x47, 0x6a, 0x46, 0xc3, 0x68, 0x55, 0x9c, 0xd5, 0xd3, 0xd8, + 0x5c, 0x4a, 0x62, 0xb3, 0x78, 0x1b, 0x7b, 0x04, 0x29, 0x0f, 0x3c, 0x06, 0xab, 0xee, 0x90, 0x12, + 0x5f, 0xec, 0x32, 0xbf, 0x4b, 0x7b, 0xb5, 0xe5, 0x86, 0xd1, 0xaa, 0x6e, 0x7f, 0x60, 0x2d, 0x7a, + 0x89, 0x96, 0x2e, 0xb5, 0x3b, 0x91, 0xc4, 0x79, 0x41, 0x17, 0x5a, 0x9d, 0x44, 0xd1, 0x54, 0x21, + 0x88, 0x41, 0x29, 0x8c, 0x86, 0x84, 0xd7, 0x0a, 0x8d, 0x42, 0xab, 0xba, 0xfd, 0xfe, 0xe2, 0x15, + 0x51, 0x34, 0x24, 0x0f, 0xa8, 0xe8, 0xdf, 0x09, 0x48, 0xea, 0xe1, 0xce, 0x9a, 0x2e, 0x58, 0x92, + 0x3e, 0x8e, 0xd2, 0xcc, 0x70, 0x1f, 0xac, 0x75, 0x31, 0x1d, 0x46, 0x21, 0x39, 0x64, 0x43, 0xea, + 0x9e, 0xd4, 0x8a, 0xea, 0x1a, 0x5e, 0x4d, 0x62, 0x73, 0xed, 0xd6, 0xa4, 0xe3, 0x3c, 0x36, 0x37, + 0xa6, 0x80, 0xbb, 0x27, 0x01, 0x41, 0xd3, 0xc1, 0xf0, 0x5b, 0xb0, 0x21, 0x6f, 0x8c, 0x07, 0xd8, + 0x25, 0x47, 0x64, 0x48, 0x5c, 0xc1, 0xc2, 0x5a, 0x49, 0x5d, 0xd7, 0x9b, 0x13, 0xcd, 0x67, 0x6f, + 0x66, 0x05, 0x83, 0x9e, 0x04, 0xb8, 0x25, 0xa5, 0x61, 0x8d, 0xb6, 0xac, 0x7d, 0xdc, 0x26, 0xc3, + 0x71, 0xa8, 0xf3, 0x62, 0x12, 0x9b, 0x1b, 0xb7, 0x67, 0x33, 0xa2, 0xf9, 0x22, 0xf0, 0x23, 0x50, + 0xe5, 0xb4, 0x43, 0x3e, 0xee, 0x76, 0x89, 0x2b, 0x78, 0xed, 0x39, 0xc5, 0xa2, 0x99, 0xc4, 0x66, + 0xf5, 0x28, 0x87, 0xcf, 0x63, 0x73, 0x3d, 0x37, 0x77, 0x87, 0x98, 0x73, 0x34, 0x19, 0x06, 0x6f, + 0x82, 0x2b, 0x52, 0x3e, 0x2c, 0x12, 0x47, 0xc4, 0x65, 0x7e, 0x87, 0xd7, 0x56, 0x1a, 0x46, 0xab, + 0xe4, 0xc0, 0x24, 0x36, 0xaf, 0xdc, 0x9d, 0xf2, 0xa0, 0x99, 0x93, 0xf0, 0x1e, 0xb8, 0x96, 0xbd, + 0x09, 0x22, 0x23, 0x4a, 0x8e, 0xef, 0x93, 0x50, 0x1a, 0xbc, 0x56, 0x6e, 0x14, 0x5a, 0x15, 0xe7, + 0x95, 0x24, 0x36, 0xaf, 0xed, 0x5c, 0x7c, 0x04, 0x5d, 0x16, 0x2b, 0x89, 0x79, 0x58, 0xb8, 0x7d, + 0xfd, 0x3c, 0x95, 0x9c, 0xd8, 0x41, 0x0e, 0x4b, 0x62, 0x13, 0xa6, 0x7a, 0x9a, 0xc9, 0xb0, 0xe6, + 0x2f, 0x06, 0xb8, 0x3e, 0x23, 0xfc, 0x54, 0x63, 0x51, 0x2a, 0x0f, 0xf8, 0x35, 0x28, 0xcb, 0x27, + 0xe8, 0x60, 0x81, 0xd5, 0x9f, 0x50, 0xdd, 0x7e, 0x63, 0xb1, 0x07, 0xbb, 0xd3, 0xfe, 0x86, 0xb8, + 0xe2, 0x80, 0x08, 0xec, 0x40, 0xad, 0x30, 0x90, 0x63, 0x28, 0xcb, 0x0a, 0xbf, 0x00, 0x65, 0x5d, + 0x99, 0xd7, 0x96, 0x95, 0x9e, 0xdf, 0x5d, 0x5c, 0xcf, 0x33, 0xbd, 0x3b, 0x45, 0x59, 0x0a, 0x95, + 0x8f, 0x75, 0xc2, 0xe6, 0xef, 0x06, 0x68, 0x3c, 0x8e, 0xdf, 0x3e, 0xe5, 0x02, 0x7e, 0x39, 0xc7, + 0xd1, 0x5a, 0x50, 0x94, 0x94, 0xa7, 0x0c, 0xaf, 0x6a, 0x86, 0xe5, 0x31, 0x32, 0xc1, 0x6f, 0x00, + 0x4a, 0x54, 0x10, 0x6f, 0x4c, 0xee, 0xd6, 0x7f, 0x26, 0x37, 0xd5, 0x78, 0xfe, 0xdb, 0xee, 0xc9, + 0xe4, 0x28, 0xad, 0xd1, 0xfc, 0xd9, 0x00, 0x45, 0xf9, 0x1f, 0xc3, 0xd7, 0x40, 0x05, 0x07, 0xf4, + 0x93, 0x90, 0x45, 0x01, 0xaf, 0x19, 0x4a, 0x67, 0x6b, 0x49, 0x6c, 0x56, 0x76, 0x0e, 0xf7, 0x52, + 0x10, 0xe5, 0x7e, 0xb8, 0x05, 0xaa, 0x38, 0xa0, 0x99, 0x2c, 0x97, 0xd5, 0xf1, 0x75, 0xa9, 0xa5, + 0x9d, 0xc3, 0xbd, 0x4c, 0x8a, 0x93, 0x67, 0x64, 0xfe, 0x90, 0x70, 0x16, 0x85, 0xae, 0x1e, 0x43, + 0x3a, 0x3f, 0x1a, 0x83, 0x28, 0xf7, 0xc3, 0xd7, 0x41, 0x89, 0xbb, 0x2c, 0x20, 0x7a, 0x88, 0xbc, + 0x24, 0xdb, 0x3e, 0x92, 0xc0, 0x79, 0x6c, 0x56, 0xd4, 0x87, 0x52, 0x66, 0x7a, 0xa8, 0xf9, 0xa3, + 0x01, 0xe0, 0xfc, 0x9c, 0x82, 0x1f, 0x02, 0xc0, 0x32, 0x4b, 0x53, 0x32, 0x95, 0xaa, 0x32, 0xf4, + 0x3c, 0x36, 0xd7, 0x32, 0x4b, 0xa5, 0x9c, 0x08, 0x81, 0x87, 0xa0, 0x28, 0x67, 0x9b, 0x1e, 0xd3, + 0xd6, 0xbf, 0x1b, 0x9a, 0xf9, 0x02, 0x90, 0x16, 0x52, 0x99, 0x9a, 0x3f, 0x18, 0xe0, 0xea, 0x11, + 0x09, 0x47, 0xd4, 0x25, 0x88, 0x74, 0x49, 0x48, 0x7c, 0x97, 0x40, 0x1b, 0x54, 0xb2, 0x31, 0xa4, + 0x97, 0xc7, 0x86, 0x8e, 0xad, 0x64, 0x23, 0x0b, 0xe5, 0x67, 0xb2, 0x45, 0xb3, 0x7c, 0xe9, 0xa2, + 0xb9, 0x0e, 0x8a, 0x01, 0x16, 0xfd, 0x5a, 0x41, 0x9d, 0x28, 0x4b, 0xef, 0x21, 0x16, 0x7d, 0xa4, + 0x50, 0xe5, 0x65, 0xa1, 0x50, 0x97, 0x5b, 0xd2, 0x5e, 0x16, 0x0a, 0xa4, 0xd0, 0xe6, 0x5f, 0x25, + 0xb0, 0x71, 0x1f, 0x0f, 0x69, 0xe7, 0xd9, 0x72, 0x7b, 0xb6, 0xdc, 0x9e, 0xae, 0xe5, 0x76, 0x66, + 0x80, 0xfa, 0x9c, 0xf4, 0x9f, 0xf4, 0x7a, 0xfb, 0x6a, 0x6e, 0xbd, 0xbd, 0xb7, 0xb8, 0xa2, 0xe7, + 0xba, 0x9f, 0x5b, 0x70, 0x7f, 0x18, 0xa0, 0xf9, 0x78, 0x8e, 0x4f, 0x60, 0xc5, 0x79, 0xd3, 0x2b, + 0xee, 0xd3, 0xff, 0x41, 0x70, 0x91, 0x25, 0xf7, 0x93, 0x01, 0x9e, 0xbf, 0x60, 0xba, 0x40, 0x0c, + 0x56, 0x78, 0x3a, 0x8d, 0x35, 0xc7, 0x9b, 0x8b, 0x37, 0x32, 0x3b, 0xc6, 0x9d, 0x6a, 0x12, 0x9b, + 0x2b, 0x63, 0x74, 0x9c, 0x17, 0xb6, 0x40, 0xd9, 0xc5, 0x4e, 0xe4, 0x77, 0xf4, 0x1e, 0x59, 0x75, + 0x56, 0xe5, 0x9d, 0xec, 0xee, 0xa4, 0x18, 0xca, 0xbc, 0xf0, 0x65, 0x50, 0x88, 0xc2, 0xa1, 0x1e, + 0xd9, 0x2b, 0x49, 0x6c, 0x16, 0xee, 0xa1, 0x7d, 0x24, 0x31, 0xe7, 0xc6, 0xe9, 0x59, 0x7d, 0xe9, + 0xe1, 0x59, 0x7d, 0xe9, 0xd1, 0x59, 0x7d, 0xe9, 0xbb, 0xa4, 0x6e, 0x9c, 0x26, 0x75, 0xe3, 0x61, + 0x52, 0x37, 0x1e, 0x25, 0x75, 0xe3, 0xd7, 0xa4, 0x6e, 0x7c, 0xff, 0x5b, 0x7d, 0xe9, 0xf3, 0x15, + 0xdd, 0xda, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x03, 0xf8, 0x1e, 0xdf, 0xe8, 0x0d, 0x00, 0x00, } diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto index c3b3c58638e..8e8ee02c1df 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto @@ -28,135 +28,8 @@ import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto"; // Package-wide variables from generator "generated". option go_package = "v1beta1"; -// MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object. -message MutatingWebhookConfiguration { - // Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. - // +optional - optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; - - // Webhooks is a list of webhooks and the affected resources and operations. - // +optional - // +patchMergeKey=name - // +patchStrategy=merge - repeated Webhook Webhooks = 2; -} - -// MutatingWebhookConfigurationList is a list of MutatingWebhookConfiguration. -message MutatingWebhookConfigurationList { - // Standard list metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds - // +optional - optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; - - // List of MutatingWebhookConfiguration. - repeated MutatingWebhookConfiguration items = 2; -} - -// Rule is a tuple of APIGroups, APIVersion, and Resources.It is recommended -// to make sure that all the tuple expansions are valid. -message Rule { - // APIGroups is the API groups the resources belong to. '*' is all groups. - // If '*' is present, the length of the slice must be one. - // Required. - repeated string apiGroups = 1; - - // APIVersions is the API versions the resources belong to. '*' is all versions. - // If '*' is present, the length of the slice must be one. - // Required. - repeated string apiVersions = 2; - - // Resources is a list of resources this rule applies to. - // - // For example: - // 'pods' means pods. - // 'pods/log' means the log subresource of pods. - // '*' means all resources, but not subresources. - // 'pods/*' means all subresources of pods. - // '*/scale' means all scale subresources. - // '*/*' means all resources and their subresources. - // - // If wildcard is present, the validation rule will ensure resources do not - // overlap with each other. - // - // Depending on the enclosing object, subresources might not be allowed. - // Required. - repeated string resources = 3; - - // scope specifies the scope of this rule. - // Valid values are "Cluster", "Namespaced", and "*" - // "Cluster" means that only cluster-scoped resources will match this rule. - // Namespace API objects are cluster-scoped. - // "Namespaced" means that only namespaced resources will match this rule. - // "*" means that there are no scope restrictions. - // Subresources match the scope of their parent resource. - // Default is "*". - // - // +optional - optional string scope = 4; -} - -// RuleWithOperations is a tuple of Operations and Resources. It is recommended to make -// sure that all the tuple expansions are valid. -message RuleWithOperations { - // Operations is the operations the admission hook cares about - CREATE, UPDATE, or * - // for all operations. - // If '*' is present, the length of the slice must be one. - // Required. - repeated string operations = 1; - - // Rule is embedded, it describes other criteria of the rule, like - // APIGroups, APIVersions, Resources, etc. - optional Rule rule = 2; -} - -// ServiceReference holds a reference to Service.legacy.k8s.io -message ServiceReference { - // `namespace` is the namespace of the service. - // Required - optional string namespace = 1; - - // `name` is the name of the service. - // Required - optional string name = 2; - - // `path` is an optional URL path which will be sent in any request to - // this service. - // +optional - optional string path = 3; - - // If specified, the port on the service that hosting webhook. - // Default to 443 for backward compatibility. - // `port` should be a valid port number (1-65535, inclusive). - // +optional - optional int32 port = 4; -} - -// ValidatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and object without changing it. -message ValidatingWebhookConfiguration { - // Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. - // +optional - optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; - - // Webhooks is a list of webhooks and the affected resources and operations. - // +optional - // +patchMergeKey=name - // +patchStrategy=merge - repeated Webhook Webhooks = 2; -} - -// ValidatingWebhookConfigurationList is a list of ValidatingWebhookConfiguration. -message ValidatingWebhookConfigurationList { - // Standard list metadata. - // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds - // +optional - optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; - - // List of ValidatingWebhookConfiguration. - repeated ValidatingWebhookConfiguration items = 2; -} - -// Webhook describes an admission webhook and the resources and operations it applies to. -message Webhook { +// MutatingWebhook describes an admission webhook and the resources and operations it applies to. +message MutatingWebhook { // The name of the admission webhook. // Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where // "imagepolicy" is the name of the webhook, and kubernetes.io is the name @@ -273,6 +146,251 @@ message Webhook { repeated string admissionReviewVersions = 8; } +// MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object. +message MutatingWebhookConfiguration { + // Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + + // Webhooks is a list of webhooks and the affected resources and operations. + // +optional + // +patchMergeKey=name + // +patchStrategy=merge + repeated MutatingWebhook Webhooks = 2; +} + +// MutatingWebhookConfigurationList is a list of MutatingWebhookConfiguration. +message MutatingWebhookConfigurationList { + // Standard list metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + + // List of MutatingWebhookConfiguration. + repeated MutatingWebhookConfiguration items = 2; +} + +// Rule is a tuple of APIGroups, APIVersion, and Resources.It is recommended +// to make sure that all the tuple expansions are valid. +message Rule { + // APIGroups is the API groups the resources belong to. '*' is all groups. + // If '*' is present, the length of the slice must be one. + // Required. + repeated string apiGroups = 1; + + // APIVersions is the API versions the resources belong to. '*' is all versions. + // If '*' is present, the length of the slice must be one. + // Required. + repeated string apiVersions = 2; + + // Resources is a list of resources this rule applies to. + // + // For example: + // 'pods' means pods. + // 'pods/log' means the log subresource of pods. + // '*' means all resources, but not subresources. + // 'pods/*' means all subresources of pods. + // '*/scale' means all scale subresources. + // '*/*' means all resources and their subresources. + // + // If wildcard is present, the validation rule will ensure resources do not + // overlap with each other. + // + // Depending on the enclosing object, subresources might not be allowed. + // Required. + repeated string resources = 3; + + // scope specifies the scope of this rule. + // Valid values are "Cluster", "Namespaced", and "*" + // "Cluster" means that only cluster-scoped resources will match this rule. + // Namespace API objects are cluster-scoped. + // "Namespaced" means that only namespaced resources will match this rule. + // "*" means that there are no scope restrictions. + // Subresources match the scope of their parent resource. + // Default is "*". + // + // +optional + optional string scope = 4; +} + +// RuleWithOperations is a tuple of Operations and Resources. It is recommended to make +// sure that all the tuple expansions are valid. +message RuleWithOperations { + // Operations is the operations the admission hook cares about - CREATE, UPDATE, or * + // for all operations. + // If '*' is present, the length of the slice must be one. + // Required. + repeated string operations = 1; + + // Rule is embedded, it describes other criteria of the rule, like + // APIGroups, APIVersions, Resources, etc. + optional Rule rule = 2; +} + +// ServiceReference holds a reference to Service.legacy.k8s.io +message ServiceReference { + // `namespace` is the namespace of the service. + // Required + optional string namespace = 1; + + // `name` is the name of the service. + // Required + optional string name = 2; + + // `path` is an optional URL path which will be sent in any request to + // this service. + // +optional + optional string path = 3; + + // If specified, the port on the service that hosting webhook. + // Default to 443 for backward compatibility. + // `port` should be a valid port number (1-65535, inclusive). + // +optional + optional int32 port = 4; +} + +// ValidatingWebhook describes an admission webhook and the resources and operations it applies to. +message ValidatingWebhook { + // The name of the admission webhook. + // Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where + // "imagepolicy" is the name of the webhook, and kubernetes.io is the name + // of the organization. + // Required. + optional string name = 1; + + // ClientConfig defines how to communicate with the hook. + // Required + optional WebhookClientConfig clientConfig = 2; + + // Rules describes what operations on what resources/subresources the webhook cares about. + // The webhook cares about an operation if it matches _any_ Rule. + // However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks + // from putting the cluster in a state which cannot be recovered from without completely + // disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called + // on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects. + repeated RuleWithOperations rules = 3; + + // FailurePolicy defines how unrecognized errors from the admission endpoint are handled - + // allowed values are Ignore or Fail. Defaults to Ignore. + // +optional + optional string failurePolicy = 4; + + // matchPolicy defines how the "rules" list is used to match incoming requests. + // Allowed values are "Exact" or "Equivalent". + // + // - Exact: match a request only if it exactly matches a specified rule. + // For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + // but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + // a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook. + // + // - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + // For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + // and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + // a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook. + // + // Defaults to "Exact" + // +optional + optional string matchPolicy = 9; + + // NamespaceSelector decides whether to run the webhook on an object based + // on whether the namespace for that object matches the selector. If the + // object itself is a namespace, the matching is performed on + // object.metadata.labels. If the object is another cluster scoped resource, + // it never skips the webhook. + // + // For example, to run the webhook on any objects whose namespace is not + // associated with "runlevel" of "0" or "1"; you will set the selector as + // follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "runlevel", + // "operator": "NotIn", + // "values": [ + // "0", + // "1" + // ] + // } + // ] + // } + // + // If instead you want to only run the webhook on any objects whose + // namespace is associated with the "environment" of "prod" or "staging"; + // you will set the selector as follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "environment", + // "operator": "In", + // "values": [ + // "prod", + // "staging" + // ] + // } + // ] + // } + // + // See + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // for more examples of label selectors. + // + // Default to the empty LabelSelector, which matches everything. + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector namespaceSelector = 5; + + // SideEffects states whether this webhookk has side effects. + // Acceptable values are: Unknown, None, Some, NoneOnDryRun + // Webhooks with side effects MUST implement a reconciliation system, since a request may be + // rejected by a future step in the admission change and the side effects therefore need to be undone. + // Requests with the dryRun attribute will be auto-rejected if they match a webhook with + // sideEffects == Unknown or Some. Defaults to Unknown. + // +optional + optional string sideEffects = 6; + + // TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, + // the webhook call will be ignored or the API call will fail based on the + // failure policy. + // The timeout value must be between 1 and 30 seconds. + // Default to 30 seconds. + // +optional + optional int32 timeoutSeconds = 7; + + // AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` + // versions the Webhook expects. API server will try to use first version in + // the list which it supports. If none of the versions specified in this list + // supported by API server, validation will fail for this object. + // If a persisted webhook configuration specifies allowed versions and does not + // include any versions known to the API Server, calls to the webhook will fail + // and be subject to the failure policy. + // Default to `['v1beta1']`. + // +optional + repeated string admissionReviewVersions = 8; +} + +// ValidatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and object without changing it. +message ValidatingWebhookConfiguration { + // Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + + // Webhooks is a list of webhooks and the affected resources and operations. + // +optional + // +patchMergeKey=name + // +patchStrategy=merge + repeated ValidatingWebhook Webhooks = 2; +} + +// ValidatingWebhookConfigurationList is a list of ValidatingWebhookConfiguration. +message ValidatingWebhookConfigurationList { + // Standard list metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + + // List of ValidatingWebhookConfiguration. + repeated ValidatingWebhookConfiguration items = 2; +} + // WebhookClientConfig contains the information to make a TLS // connection with the webhook message WebhookClientConfig { diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go index 43ea31306b2..e580d834c22 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go @@ -27,6 +27,23 @@ package v1beta1 // Those methods can be generated by using hack/update-generated-swagger-docs.sh // AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT. +var map_MutatingWebhook = map[string]string{ + "": "MutatingWebhook describes an admission webhook and the resources and operations it applies to.", + "name": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", + "clientConfig": "ClientConfig defines how to communicate with the hook. Required", + "rules": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", + "failurePolicy": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", + "matchPolicy": "matchPolicy defines how the \"rules\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.\n\nDefaults to \"Exact\"", + "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.", + "sideEffects": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", + "timeoutSeconds": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", + "admissionReviewVersions": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy. Default to `['v1beta1']`.", +} + +func (MutatingWebhook) SwaggerDoc() map[string]string { + return map_MutatingWebhook +} + var map_MutatingWebhookConfiguration = map[string]string{ "": "MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object.", "metadata": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata.", @@ -80,6 +97,23 @@ func (ServiceReference) SwaggerDoc() map[string]string { return map_ServiceReference } +var map_ValidatingWebhook = map[string]string{ + "": "ValidatingWebhook describes an admission webhook and the resources and operations it applies to.", + "name": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", + "clientConfig": "ClientConfig defines how to communicate with the hook. Required", + "rules": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", + "failurePolicy": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", + "matchPolicy": "matchPolicy defines how the \"rules\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.\n\nDefaults to \"Exact\"", + "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.", + "sideEffects": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", + "timeoutSeconds": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", + "admissionReviewVersions": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy. Default to `['v1beta1']`.", +} + +func (ValidatingWebhook) SwaggerDoc() map[string]string { + return map_ValidatingWebhook +} + var map_ValidatingWebhookConfiguration = map[string]string{ "": "ValidatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and object without changing it.", "metadata": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata.", @@ -100,23 +134,6 @@ func (ValidatingWebhookConfigurationList) SwaggerDoc() map[string]string { return map_ValidatingWebhookConfigurationList } -var map_Webhook = map[string]string{ - "": "Webhook describes an admission webhook and the resources and operations it applies to.", - "name": "The name of the admission webhook. Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where \"imagepolicy\" is the name of the webhook, and kubernetes.io is the name of the organization. Required.", - "clientConfig": "ClientConfig defines how to communicate with the hook. Required", - "rules": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", - "failurePolicy": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", - "matchPolicy": "matchPolicy defines how the \"rules\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.\n\nDefaults to \"Exact\"", - "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.", - "sideEffects": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", - "timeoutSeconds": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", - "admissionReviewVersions": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy. Default to `['v1beta1']`.", -} - -func (Webhook) SwaggerDoc() map[string]string { - return map_Webhook -} - var map_WebhookClientConfig = map[string]string{ "": "WebhookClientConfig contains the information to make a TLS connection with the webhook", "url": "`url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified.\n\nThe `host` should not refer to a service running in the cluster; use the `service` field instead. The host might be resolved via external DNS in some apiservers (e.g., `kube-apiserver` cannot resolve in-cluster DNS as that would be a layering violation). `host` may also be an IP address.\n\nPlease note that using `localhost` or `127.0.0.1` as a `host` is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.\n\nThe scheme must be \"https\"; the URL must begin with \"https://\".\n\nA path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.\n\nAttempting to use a user or basic auth e.g. \"user:password@\" is not allowed. Fragments (\"#...\") and query parameters (\"?...\") are not allowed, either.", diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go index f2dea030b49..05bfa5c256e 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go @@ -25,6 +25,60 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MutatingWebhook) DeepCopyInto(out *MutatingWebhook) { + *out = *in + in.ClientConfig.DeepCopyInto(&out.ClientConfig) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]RuleWithOperations, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.FailurePolicy != nil { + in, out := &in.FailurePolicy, &out.FailurePolicy + *out = new(FailurePolicyType) + **out = **in + } + if in.MatchPolicy != nil { + in, out := &in.MatchPolicy, &out.MatchPolicy + *out = new(MatchPolicyType) + **out = **in + } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.SideEffects != nil { + in, out := &in.SideEffects, &out.SideEffects + *out = new(SideEffectClass) + **out = **in + } + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } + if in.AdmissionReviewVersions != nil { + in, out := &in.AdmissionReviewVersions, &out.AdmissionReviewVersions + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MutatingWebhook. +func (in *MutatingWebhook) DeepCopy() *MutatingWebhook { + if in == nil { + return nil + } + out := new(MutatingWebhook) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MutatingWebhookConfiguration) DeepCopyInto(out *MutatingWebhookConfiguration) { *out = *in @@ -32,7 +86,7 @@ func (in *MutatingWebhookConfiguration) DeepCopyInto(out *MutatingWebhookConfigu in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) if in.Webhooks != nil { in, out := &in.Webhooks, &out.Webhooks - *out = make([]Webhook, len(*in)) + *out = make([]MutatingWebhook, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -175,6 +229,60 @@ func (in *ServiceReference) DeepCopy() *ServiceReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ValidatingWebhook) DeepCopyInto(out *ValidatingWebhook) { + *out = *in + in.ClientConfig.DeepCopyInto(&out.ClientConfig) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]RuleWithOperations, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.FailurePolicy != nil { + in, out := &in.FailurePolicy, &out.FailurePolicy + *out = new(FailurePolicyType) + **out = **in + } + if in.MatchPolicy != nil { + in, out := &in.MatchPolicy, &out.MatchPolicy + *out = new(MatchPolicyType) + **out = **in + } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.SideEffects != nil { + in, out := &in.SideEffects, &out.SideEffects + *out = new(SideEffectClass) + **out = **in + } + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } + if in.AdmissionReviewVersions != nil { + in, out := &in.AdmissionReviewVersions, &out.AdmissionReviewVersions + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValidatingWebhook. +func (in *ValidatingWebhook) DeepCopy() *ValidatingWebhook { + if in == nil { + return nil + } + out := new(ValidatingWebhook) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ValidatingWebhookConfiguration) DeepCopyInto(out *ValidatingWebhookConfiguration) { *out = *in @@ -182,7 +290,7 @@ func (in *ValidatingWebhookConfiguration) DeepCopyInto(out *ValidatingWebhookCon in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) if in.Webhooks != nil { in, out := &in.Webhooks, &out.Webhooks - *out = make([]Webhook, len(*in)) + *out = make([]ValidatingWebhook, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -241,60 +349,6 @@ func (in *ValidatingWebhookConfigurationList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Webhook) DeepCopyInto(out *Webhook) { - *out = *in - in.ClientConfig.DeepCopyInto(&out.ClientConfig) - if in.Rules != nil { - in, out := &in.Rules, &out.Rules - *out = make([]RuleWithOperations, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.FailurePolicy != nil { - in, out := &in.FailurePolicy, &out.FailurePolicy - *out = new(FailurePolicyType) - **out = **in - } - if in.MatchPolicy != nil { - in, out := &in.MatchPolicy, &out.MatchPolicy - *out = new(MatchPolicyType) - **out = **in - } - if in.NamespaceSelector != nil { - in, out := &in.NamespaceSelector, &out.NamespaceSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - if in.SideEffects != nil { - in, out := &in.SideEffects, &out.SideEffects - *out = new(SideEffectClass) - **out = **in - } - if in.TimeoutSeconds != nil { - in, out := &in.TimeoutSeconds, &out.TimeoutSeconds - *out = new(int32) - **out = **in - } - if in.AdmissionReviewVersions != nil { - in, out := &in.AdmissionReviewVersions, &out.AdmissionReviewVersions - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Webhook. -func (in *Webhook) DeepCopy() *Webhook { - if in == nil { - return nil - } - out := new(Webhook) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WebhookClientConfig) DeepCopyInto(out *WebhookClientConfig) { *out = *in From 55ecc45455f191c404e355097bf1beae9c42f895 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Wed, 29 May 2019 21:30:45 -0700 Subject: [PATCH 2/8] split admissionregistration.v1beta1/Webhook into MutatingWebhook and ValidatingWebhook --- hack/.golint_failures | 1 + .../admissionregistration/fuzzer/fuzzer.go | 16 +- pkg/apis/admissionregistration/types.go | 118 +++++++++++++- .../admissionregistration/v1beta1/defaults.go | 30 +++- .../validation/validation.go | 69 +++++++- .../validation/validation_test.go | 98 +++++------ .../admissionregistration/v1beta1/types.go | 126 ++++++++++++++- .../src/k8s.io/apiserver/pkg/admission/BUILD | 13 +- .../pkg/admission/configuration/BUILD | 1 + .../configuration/mutating_webhook_manager.go | 17 +- .../mutating_webhook_manager_test.go | 13 +- .../validating_webhook_manager.go | 19 +-- .../validating_webhook_manager_test.go | 13 +- .../pkg/admission/plugin/webhook/BUILD | 41 +++++ .../pkg/admission/plugin/webhook/accessors.go | 139 ++++++++++++++++ .../admission/plugin/webhook/generic/BUILD | 2 + .../plugin/webhook/generic/interfaces.go | 7 +- .../plugin/webhook/generic/webhook.go | 27 ++-- .../plugin/webhook/generic/webhook_test.go | 31 ++-- .../plugin/webhook/mutating/dispatcher.go | 35 ++-- .../plugin/webhook/mutating/plugin_test.go | 6 +- .../admission/plugin/webhook/namespace/BUILD | 3 +- .../plugin/webhook/namespace/matcher.go | 6 +- .../plugin/webhook/namespace/matcher_test.go | 5 +- .../plugin/webhook/testing/testcase.go | 153 ++++++++++++------ .../pkg/admission/plugin/webhook/util/BUILD | 2 +- .../plugin/webhook/util/client_config.go | 39 ++--- .../plugin/webhook/validating/dispatcher.go | 37 ++--- .../plugin/webhook/validating/plugin_test.go | 4 +- test/e2e/apimachinery/webhook.go | 30 ++-- .../admissionwebhook/admission_test.go | 7 +- .../admissionwebhook/broken_webhook_test.go | 2 +- test/integration/examples/webhook_test.go | 4 +- vendor/modules.txt | 1 + 34 files changed, 846 insertions(+), 269 deletions(-) create mode 100644 staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD create mode 100644 staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go diff --git a/hack/.golint_failures b/hack/.golint_failures index e3ce8377b9f..e1743258e59 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -423,6 +423,7 @@ staging/src/k8s.io/apimachinery/pkg/watch staging/src/k8s.io/apiserver/pkg/admission staging/src/k8s.io/apiserver/pkg/admission/configuration staging/src/k8s.io/apiserver/pkg/admission/initializer +staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1 staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts diff --git a/pkg/apis/admissionregistration/fuzzer/fuzzer.go b/pkg/apis/admissionregistration/fuzzer/fuzzer.go index 45f226ff655..6e84fa54502 100644 --- a/pkg/apis/admissionregistration/fuzzer/fuzzer.go +++ b/pkg/apis/admissionregistration/fuzzer/fuzzer.go @@ -33,7 +33,21 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { obj.Scope = &s } }, - func(obj *admissionregistration.Webhook, c fuzz.Continue) { + func(obj *admissionregistration.ValidatingWebhook, c fuzz.Continue) { + c.FuzzNoCustom(obj) // fuzz self without calling this function again + p := admissionregistration.FailurePolicyType("Fail") + obj.FailurePolicy = &p + m := admissionregistration.MatchPolicyType("Exact") + obj.MatchPolicy = &m + s := admissionregistration.SideEffectClassUnknown + obj.SideEffects = &s + if obj.TimeoutSeconds == nil { + i := int32(30) + obj.TimeoutSeconds = &i + } + obj.AdmissionReviewVersions = []string{"v1beta1"} + }, + func(obj *admissionregistration.MutatingWebhook, c fuzz.Continue) { c.FuzzNoCustom(obj) // fuzz self without calling this function again p := admissionregistration.FailurePolicyType("Fail") obj.FailurePolicy = &p diff --git a/pkg/apis/admissionregistration/types.go b/pkg/apis/admissionregistration/types.go index a17ceb205e0..cb1624b59ad 100644 --- a/pkg/apis/admissionregistration/types.go +++ b/pkg/apis/admissionregistration/types.go @@ -123,7 +123,7 @@ type ValidatingWebhookConfiguration struct { metav1.ObjectMeta // Webhooks is a list of webhooks and the affected resources and operations. // +optional - Webhooks []Webhook + Webhooks []ValidatingWebhook } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -149,7 +149,7 @@ type MutatingWebhookConfiguration struct { metav1.ObjectMeta // Webhooks is a list of webhooks and the affected resources and operations. // +optional - Webhooks []Webhook + Webhooks []MutatingWebhook } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -165,8 +165,118 @@ type MutatingWebhookConfigurationList struct { Items []MutatingWebhookConfiguration } -// Webhook describes an admission webhook and the resources and operations it applies to. -type Webhook struct { +// ValidatingWebhook describes an admission webhook and the resources and operations it applies to. +type ValidatingWebhook struct { + // The name of the admission webhook. + // Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where + // "imagepolicy" is the name of the webhook, and kubernetes.io is the name + // of the organization. + // Required. + Name string + + // ClientConfig defines how to communicate with the hook. + // Required + ClientConfig WebhookClientConfig + + // Rules describes what operations on what resources/subresources the webhook cares about. + // The webhook cares about an operation if it matches _any_ Rule. + Rules []RuleWithOperations + + // FailurePolicy defines how unrecognized errors from the admission endpoint are handled - + // allowed values are Ignore or Fail. Defaults to Ignore. + // +optional + FailurePolicy *FailurePolicyType + + // matchPolicy defines how the "rules" list is used to match incoming requests. + // Allowed values are "Exact" or "Equivalent". + // + // - Exact: match a request only if it exactly matches a specified rule. + // For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + // but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + // a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook. + // + // - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + // For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + // and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + // a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook. + // + // +optional + MatchPolicy *MatchPolicyType + + // NamespaceSelector decides whether to run the webhook on an object based + // on whether the namespace for that object matches the selector. If the + // object itself is a namespace, the matching is performed on + // object.metadata.labels. If the object is another cluster scoped resource, + // it never skips the webhook. + // + // For example, to run the webhook on any objects whose namespace is not + // associated with "runlevel" of "0" or "1"; you will set the selector as + // follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "runlevel", + // "operator": "NotIn", + // "values": [ + // "0", + // "1" + // ] + // } + // ] + // } + // + // If instead you want to only run the webhook on any objects whose + // namespace is associated with the "environment" of "prod" or "staging"; + // you will set the selector as follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "environment", + // "operator": "In", + // "values": [ + // "prod", + // "staging" + // ] + // } + // ] + // } + // + // See + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // for more examples of label selectors. + // + // Default to the empty LabelSelector, which matches everything. + // +optional + NamespaceSelector *metav1.LabelSelector + + // SideEffects states whether this webhookk has side effects. + // Acceptable values are: Unknown, None, Some, NoneOnDryRun + // Webhooks with side effects MUST implement a reconciliation system, since a request may be + // rejected by a future step in the admission change and the side effects therefore need to be undone. + // Requests with the dryRun attribute will be auto-rejected if they match a webhook with + // sideEffects == Unknown or Some. Defaults to Unknown. + // +optional + SideEffects *SideEffectClass + + // TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, + // the webhook call will be ignored or the API call will fail based on the + // failure policy. + // The timeout value must be between 1 and 30 seconds. + // +optional + TimeoutSeconds *int32 + + // AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` + // versions the Webhook expects. API server will try to use first version in + // the list which it supports. If none of the versions specified in this list + // supported by API server, validation will fail for this object. + // If the webhook configuration has already been persisted with a version apiserver + // does not understand, calls to the webhook will fail and be subject to the failure policy. + // +optional + AdmissionReviewVersions []string +} + +// MutatingWebhook describes an admission webhook and the resources and operations it applies to. +type MutatingWebhook struct { // The name of the admission webhook. // Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where // "imagepolicy" is the name of the webhook, and kubernetes.io is the name diff --git a/pkg/apis/admissionregistration/v1beta1/defaults.go b/pkg/apis/admissionregistration/v1beta1/defaults.go index 2ae7d62d247..b529dfd00bb 100644 --- a/pkg/apis/admissionregistration/v1beta1/defaults.go +++ b/pkg/apis/admissionregistration/v1beta1/defaults.go @@ -27,7 +27,35 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error { return RegisterDefaults(scheme) } -func SetDefaults_Webhook(obj *admissionregistrationv1beta1.Webhook) { +func SetDefaults_ValidatingWebhook(obj *admissionregistrationv1beta1.ValidatingWebhook) { + if obj.FailurePolicy == nil { + policy := admissionregistrationv1beta1.Ignore + obj.FailurePolicy = &policy + } + if obj.MatchPolicy == nil { + policy := admissionregistrationv1beta1.Exact + obj.MatchPolicy = &policy + } + if obj.NamespaceSelector == nil { + selector := metav1.LabelSelector{} + obj.NamespaceSelector = &selector + } + if obj.SideEffects == nil { + // TODO: revisit/remove this default and possibly make the field required when promoting to v1 + unknown := admissionregistrationv1beta1.SideEffectClassUnknown + obj.SideEffects = &unknown + } + if obj.TimeoutSeconds == nil { + obj.TimeoutSeconds = new(int32) + *obj.TimeoutSeconds = 30 + } + + if len(obj.AdmissionReviewVersions) == 0 { + obj.AdmissionReviewVersions = []string{admissionregistrationv1beta1.SchemeGroupVersion.Version} + } +} + +func SetDefaults_MutatingWebhook(obj *admissionregistrationv1beta1.MutatingWebhook) { if obj.FailurePolicy == nil { policy := admissionregistrationv1beta1.Ignore obj.FailurePolicy = &policy diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go index 5cb804a420e..8fc4efe3544 100644 --- a/pkg/apis/admissionregistration/validation/validation.go +++ b/pkg/apis/admissionregistration/validation/validation.go @@ -201,7 +201,7 @@ func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingW func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration, requireRecognizedVersion bool) field.ErrorList { allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata")) for i, hook := range e.Webhooks { - allErrors = append(allErrors, validateWebhook(&hook, field.NewPath("webhooks").Index(i))...) + allErrors = append(allErrors, validateValidatingWebhook(&hook, field.NewPath("webhooks").Index(i))...) allErrors = append(allErrors, validateAdmissionReviewVersions(hook.AdmissionReviewVersions, requireRecognizedVersion, field.NewPath("webhooks").Index(i).Child("admissionReviewVersions"))...) } return allErrors @@ -214,13 +214,50 @@ func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebho func validateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration, requireRecognizedVersion bool) field.ErrorList { allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata")) for i, hook := range e.Webhooks { - allErrors = append(allErrors, validateWebhook(&hook, field.NewPath("webhooks").Index(i))...) + allErrors = append(allErrors, validateMutatingWebhook(&hook, field.NewPath("webhooks").Index(i))...) allErrors = append(allErrors, validateAdmissionReviewVersions(hook.AdmissionReviewVersions, requireRecognizedVersion, field.NewPath("webhooks").Index(i).Child("admissionReviewVersions"))...) } return allErrors } -func validateWebhook(hook *admissionregistration.Webhook, fldPath *field.Path) field.ErrorList { +func validateValidatingWebhook(hook *admissionregistration.ValidatingWebhook, fldPath *field.Path) field.ErrorList { + var allErrors field.ErrorList + // hook.Name must be fully qualified + allErrors = append(allErrors, utilvalidation.IsFullyQualifiedName(fldPath.Child("name"), hook.Name)...) + + for i, rule := range hook.Rules { + allErrors = append(allErrors, validateRuleWithOperations(&rule, fldPath.Child("rules").Index(i))...) + } + if hook.FailurePolicy != nil && !supportedFailurePolicies.Has(string(*hook.FailurePolicy)) { + allErrors = append(allErrors, field.NotSupported(fldPath.Child("failurePolicy"), *hook.FailurePolicy, supportedFailurePolicies.List())) + } + if hook.MatchPolicy != nil && !supportedMatchPolicies.Has(string(*hook.MatchPolicy)) { + allErrors = append(allErrors, field.NotSupported(fldPath.Child("matchPolicy"), *hook.MatchPolicy, supportedMatchPolicies.List())) + } + if hook.SideEffects != nil && !supportedSideEffectClasses.Has(string(*hook.SideEffects)) { + allErrors = append(allErrors, field.NotSupported(fldPath.Child("sideEffects"), *hook.SideEffects, supportedSideEffectClasses.List())) + } + if hook.TimeoutSeconds != nil && (*hook.TimeoutSeconds > 30 || *hook.TimeoutSeconds < 1) { + allErrors = append(allErrors, field.Invalid(fldPath.Child("timeoutSeconds"), *hook.TimeoutSeconds, "the timeout value must be between 1 and 30 seconds")) + } + + if hook.NamespaceSelector != nil { + allErrors = append(allErrors, metav1validation.ValidateLabelSelector(hook.NamespaceSelector, fldPath.Child("namespaceSelector"))...) + } + + cc := hook.ClientConfig + switch { + case (cc.URL == nil) == (cc.Service == nil): + allErrors = append(allErrors, field.Required(fldPath.Child("clientConfig"), "exactly one of url or service is required")) + case cc.URL != nil: + allErrors = append(allErrors, webhook.ValidateWebhookURL(fldPath.Child("clientConfig").Child("url"), *cc.URL, true)...) + case cc.Service != nil: + allErrors = append(allErrors, webhook.ValidateWebhookService(fldPath.Child("clientConfig").Child("service"), cc.Service.Name, cc.Service.Namespace, cc.Service.Path, cc.Service.Port)...) + } + return allErrors +} + +func validateMutatingWebhook(hook *admissionregistration.MutatingWebhook, fldPath *field.Path) field.ErrorList { var allErrors field.ErrorList // hook.Name must be fully qualified allErrors = append(allErrors, utilvalidation.IsFullyQualifiedName(fldPath.Child("name"), hook.Name)...) @@ -309,9 +346,27 @@ func validateRuleWithOperations(ruleWithOperations *admissionregistration.RuleWi return allErrors } -// hasAcceptedAdmissionReviewVersions returns true if all webhooks have at least one +// mutatingHasAcceptedAdmissionReviewVersions returns true if all webhooks have at least one // admission review version this apiserver accepts. -func hasAcceptedAdmissionReviewVersions(webhooks []admissionregistration.Webhook) bool { +func mutatingHasAcceptedAdmissionReviewVersions(webhooks []admissionregistration.MutatingWebhook) bool { + for _, hook := range webhooks { + hasRecognizedVersion := false + for _, version := range hook.AdmissionReviewVersions { + if isAcceptedAdmissionReviewVersion(version) { + hasRecognizedVersion = true + break + } + } + if !hasRecognizedVersion && len(hook.AdmissionReviewVersions) > 0 { + return false + } + } + return true +} + +// validatingHasAcceptedAdmissionReviewVersions returns true if all webhooks have at least one +// admission review version this apiserver accepts. +func validatingHasAcceptedAdmissionReviewVersions(webhooks []admissionregistration.ValidatingWebhook) bool { for _, hook := range webhooks { hasRecognizedVersion := false for _, version := range hook.AdmissionReviewVersions { @@ -328,9 +383,9 @@ func hasAcceptedAdmissionReviewVersions(webhooks []admissionregistration.Webhook } func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList { - return validateValidatingWebhookConfiguration(newC, hasAcceptedAdmissionReviewVersions(oldC.Webhooks)) + return validateValidatingWebhookConfiguration(newC, validatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks)) } func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.MutatingWebhookConfiguration) field.ErrorList { - return validateMutatingWebhookConfiguration(newC, hasAcceptedAdmissionReviewVersions(oldC.Webhooks)) + return validateMutatingWebhookConfiguration(newC, mutatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks)) } diff --git a/pkg/apis/admissionregistration/validation/validation_test.go b/pkg/apis/admissionregistration/validation/validation_test.go index e206a549eec..73c08eda43b 100644 --- a/pkg/apis/admissionregistration/validation/validation_test.go +++ b/pkg/apis/admissionregistration/validation/validation_test.go @@ -28,7 +28,7 @@ func strPtr(s string) *string { return &s } func int32Ptr(i int32) *int32 { return &i } -func newValidatingWebhookConfiguration(hooks []admissionregistration.Webhook, defaultAdmissionReviewVersions bool) *admissionregistration.ValidatingWebhookConfiguration { +func newValidatingWebhookConfiguration(hooks []admissionregistration.ValidatingWebhook, defaultAdmissionReviewVersions bool) *admissionregistration.ValidatingWebhookConfiguration { // If the test case did not specify an AdmissionReviewVersions, default it so the test passes as // this field will be defaulted in production code. for i := range hooks { @@ -57,7 +57,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }{ { name: "should fail on bad AdmissionReviewVersion value", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -68,7 +68,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "should pass on valid AdmissionReviewVersion", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -79,7 +79,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "should pass on mix of accepted and unaccepted AdmissionReviewVersion", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -90,7 +90,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "should fail on invalid AdmissionReviewVersion", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -101,7 +101,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "should fail on duplicate AdmissionReviewVersion", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -112,7 +112,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "all Webhooks must have a fully qualified name", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -130,7 +130,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "Operations must not be empty or nil", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", Rules: []admissionregistration.RuleWithOperations{ @@ -157,7 +157,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "\"\" is NOT a valid operation", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", Rules: []admissionregistration.RuleWithOperations{ @@ -176,7 +176,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "operation must be either create/update/delete/connect", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", Rules: []admissionregistration.RuleWithOperations{ @@ -195,7 +195,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "wildcard operation cannot be mixed with other strings", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", Rules: []admissionregistration.RuleWithOperations{ @@ -214,7 +214,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: `resource "*" can co-exist with resources that have subresources`, - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -233,7 +233,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: `resource "*" cannot mix with resources that don't have subresources`, - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -253,7 +253,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "resource a/* cannot mix with a/x", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -273,7 +273,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "resource a/* can mix with a", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -292,7 +292,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "resource */a cannot mix with x/a", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -312,7 +312,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "resource */* cannot mix with other resources", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -332,7 +332,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "FailurePolicy can only be \"Ignore\" or \"Fail\"", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -346,7 +346,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\"", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -360,7 +360,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "both service and URL missing", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{}, @@ -370,7 +370,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "both service and URL provided", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -387,7 +387,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "blank URL", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -399,7 +399,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "wrong scheme", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -411,7 +411,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "missing host", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -423,7 +423,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "fragment", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -435,7 +435,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "query", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -447,7 +447,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "user", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -459,7 +459,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "just totally wrong", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -471,7 +471,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "path must start with slash", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -488,7 +488,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "path accepts slash", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -505,7 +505,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "path accepts no trailing slash", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -522,7 +522,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "path fails //", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -539,7 +539,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "path no empty step", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -555,7 +555,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`, }, { name: "path no empty step 2", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -572,7 +572,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "path no non-subdomain", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -590,7 +590,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { name: "invalid port 0", config: newValidatingWebhookConfiguration( - []admissionregistration.Webhook{ + []admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -608,7 +608,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { name: "invalid port >65535", config: newValidatingWebhookConfiguration( - []admissionregistration.Webhook{ + []admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: admissionregistration.WebhookClientConfig{ @@ -625,7 +625,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "timeout seconds cannot be greater than 30", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -636,7 +636,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "timeout seconds cannot be smaller than 1", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -647,7 +647,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "timeout seconds must be positive", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -658,7 +658,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, { name: "valid timeout seconds", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -707,14 +707,14 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { }{ { name: "should pass on valid new AdmissionReviewVersion", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, AdmissionReviewVersions: []string{"v1beta1"}, }, }, true), - oldconfig: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -724,14 +724,14 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { }, { name: "should pass on invalid AdmissionReviewVersion with invalid previous versions", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"}, }, }, true), - oldconfig: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -742,14 +742,14 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { }, { name: "should fail on invalid AdmissionReviewVersion with valid previous versions", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, AdmissionReviewVersions: []string{"invalid-v1"}, }, }, true), - oldconfig: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, @@ -760,14 +760,14 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { }, { name: "should fail on invalid AdmissionReviewVersion with missing previous versions", - config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, AdmissionReviewVersions: []string{"invalid-v1"}, }, }, true), - oldconfig: newValidatingWebhookConfiguration([]admissionregistration.Webhook{ + oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go index d3a034d2e3f..4e2d694d545 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go @@ -124,7 +124,7 @@ type ValidatingWebhookConfiguration struct { // +optional // +patchMergeKey=name // +patchStrategy=merge - Webhooks []Webhook `json:"webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"` + Webhooks []ValidatingWebhook `json:"webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -154,7 +154,7 @@ type MutatingWebhookConfiguration struct { // +optional // +patchMergeKey=name // +patchStrategy=merge - Webhooks []Webhook `json:"webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"` + Webhooks []MutatingWebhook `json:"webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -170,8 +170,126 @@ type MutatingWebhookConfigurationList struct { Items []MutatingWebhookConfiguration `json:"items" protobuf:"bytes,2,rep,name=items"` } -// Webhook describes an admission webhook and the resources and operations it applies to. -type Webhook struct { +// ValidatingWebhook describes an admission webhook and the resources and operations it applies to. +type ValidatingWebhook struct { + // The name of the admission webhook. + // Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where + // "imagepolicy" is the name of the webhook, and kubernetes.io is the name + // of the organization. + // Required. + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` + + // ClientConfig defines how to communicate with the hook. + // Required + ClientConfig WebhookClientConfig `json:"clientConfig" protobuf:"bytes,2,opt,name=clientConfig"` + + // Rules describes what operations on what resources/subresources the webhook cares about. + // The webhook cares about an operation if it matches _any_ Rule. + // However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks + // from putting the cluster in a state which cannot be recovered from without completely + // disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called + // on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects. + Rules []RuleWithOperations `json:"rules,omitempty" protobuf:"bytes,3,rep,name=rules"` + + // FailurePolicy defines how unrecognized errors from the admission endpoint are handled - + // allowed values are Ignore or Fail. Defaults to Ignore. + // +optional + FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" protobuf:"bytes,4,opt,name=failurePolicy,casttype=FailurePolicyType"` + + // matchPolicy defines how the "rules" list is used to match incoming requests. + // Allowed values are "Exact" or "Equivalent". + // + // - Exact: match a request only if it exactly matches a specified rule. + // For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + // but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + // a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook. + // + // - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + // For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, + // and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`, + // a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook. + // + // Defaults to "Exact" + // +optional + MatchPolicy *MatchPolicyType `json:"matchPolicy,omitempty" protobuf:"bytes,9,opt,name=matchPolicy,casttype=MatchPolicyType"` + + // NamespaceSelector decides whether to run the webhook on an object based + // on whether the namespace for that object matches the selector. If the + // object itself is a namespace, the matching is performed on + // object.metadata.labels. If the object is another cluster scoped resource, + // it never skips the webhook. + // + // For example, to run the webhook on any objects whose namespace is not + // associated with "runlevel" of "0" or "1"; you will set the selector as + // follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "runlevel", + // "operator": "NotIn", + // "values": [ + // "0", + // "1" + // ] + // } + // ] + // } + // + // If instead you want to only run the webhook on any objects whose + // namespace is associated with the "environment" of "prod" or "staging"; + // you will set the selector as follows: + // "namespaceSelector": { + // "matchExpressions": [ + // { + // "key": "environment", + // "operator": "In", + // "values": [ + // "prod", + // "staging" + // ] + // } + // ] + // } + // + // See + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // for more examples of label selectors. + // + // Default to the empty LabelSelector, which matches everything. + // +optional + NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,5,opt,name=namespaceSelector"` + + // SideEffects states whether this webhookk has side effects. + // Acceptable values are: Unknown, None, Some, NoneOnDryRun + // Webhooks with side effects MUST implement a reconciliation system, since a request may be + // rejected by a future step in the admission change and the side effects therefore need to be undone. + // Requests with the dryRun attribute will be auto-rejected if they match a webhook with + // sideEffects == Unknown or Some. Defaults to Unknown. + // +optional + SideEffects *SideEffectClass `json:"sideEffects,omitempty" protobuf:"bytes,6,opt,name=sideEffects,casttype=SideEffectClass"` + + // TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, + // the webhook call will be ignored or the API call will fail based on the + // failure policy. + // The timeout value must be between 1 and 30 seconds. + // Default to 30 seconds. + // +optional + TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty" protobuf:"varint,7,opt,name=timeoutSeconds"` + + // AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` + // versions the Webhook expects. API server will try to use first version in + // the list which it supports. If none of the versions specified in this list + // supported by API server, validation will fail for this object. + // If a persisted webhook configuration specifies allowed versions and does not + // include any versions known to the API Server, calls to the webhook will fail + // and be subject to the failure policy. + // Default to `['v1beta1']`. + // +optional + AdmissionReviewVersions []string `json:"admissionReviewVersions,omitempty" protobuf:"bytes,8,rep,name=admissionReviewVersions"` +} + +// MutatingWebhook describes an admission webhook and the resources and operations it applies to. +type MutatingWebhook struct { // The name of the admission webhook. // Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where // "imagepolicy" is the name of the webhook, and kubernetes.io is the name diff --git a/staging/src/k8s.io/apiserver/pkg/admission/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/BUILD index 3d2483328e8..f89ee0ec022 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/BUILD @@ -80,18 +80,7 @@ filegroup( "//staging/src/k8s.io/apiserver/pkg/admission/initializer:all-srcs", "//staging/src/k8s.io/apiserver/pkg/admission/metrics:all-srcs", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/config:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/initializer:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/rules:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util:all-srcs", - "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:all-srcs", "//staging/src/k8s.io/apiserver/pkg/admission/testing:all-srcs", ], tags = ["automanaged"], diff --git a/staging/src/k8s.io/apiserver/pkg/admission/configuration/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/configuration/BUILD index 34432781415..3b8448659b2 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/configuration/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/configuration/BUILD @@ -39,6 +39,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library", "//staging/src/k8s.io/client-go/listers/admissionregistration/v1beta1:go_default_library", diff --git a/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go b/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go index 4b2256e118b..bacf61722c3 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go @@ -24,6 +24,7 @@ import ( "k8s.io/api/admissionregistration/v1beta1" "k8s.io/apimachinery/pkg/labels" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apiserver/pkg/admission/plugin/webhook" "k8s.io/apiserver/pkg/admission/plugin/webhook/generic" "k8s.io/client-go/informers" admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1" @@ -48,7 +49,7 @@ func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) g } // Start with an empty list - manager.configuration.Store(&v1beta1.MutatingWebhookConfiguration{}) + manager.configuration.Store([]webhook.WebhookAccessor{}) // On any change, rebuild the config informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ @@ -61,8 +62,8 @@ func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) g } // Webhooks returns the merged MutatingWebhookConfiguration. -func (m *mutatingWebhookConfigurationManager) Webhooks() []v1beta1.Webhook { - return m.configuration.Load().(*v1beta1.MutatingWebhookConfiguration).Webhooks +func (m *mutatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccessor { + return m.configuration.Load().([]webhook.WebhookAccessor) } func (m *mutatingWebhookConfigurationManager) HasSynced() bool { @@ -78,16 +79,18 @@ func (m *mutatingWebhookConfigurationManager) updateConfiguration() { m.configuration.Store(mergeMutatingWebhookConfigurations(configurations)) } -func mergeMutatingWebhookConfigurations(configurations []*v1beta1.MutatingWebhookConfiguration) *v1beta1.MutatingWebhookConfiguration { - var ret v1beta1.MutatingWebhookConfiguration +func mergeMutatingWebhookConfigurations(configurations []*v1beta1.MutatingWebhookConfiguration) []webhook.WebhookAccessor { // The internal order of webhooks for each configuration is provided by the user // but configurations themselves can be in any order. As we are going to run these // webhooks in serial, they are sorted here to have a deterministic order. sort.SliceStable(configurations, MutatingWebhookConfigurationSorter(configurations).ByName) + accessors := []webhook.WebhookAccessor{} for _, c := range configurations { - ret.Webhooks = append(ret.Webhooks, c.Webhooks...) + for i := range c.Webhooks { + accessors = append(accessors, webhook.NewMutatingWebhookAccessor(&c.Webhooks[i])) + } } - return &ret + return accessors } type MutatingWebhookConfigurationSorter []*v1beta1.MutatingWebhookConfiguration diff --git a/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager_test.go b/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager_test.go index 9bc037f5a40..828c1843025 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager_test.go @@ -45,7 +45,7 @@ func TestGetMutatingWebhookConfig(t *testing.T) { webhookConfiguration := &v1beta1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{Name: "webhook1"}, - Webhooks: []v1beta1.Webhook{{Name: "webhook1.1"}}, + Webhooks: []v1beta1.MutatingWebhook{{Name: "webhook1.1"}}, } mutatingInformer := informerFactory.Admissionregistration().V1beta1().MutatingWebhookConfigurations() @@ -57,7 +57,14 @@ func TestGetMutatingWebhookConfig(t *testing.T) { if len(configurations) == 0 { t.Errorf("expected non empty webhooks") } - if !reflect.DeepEqual(configurations, webhookConfiguration.Webhooks) { - t.Errorf("Expected\n%#v\ngot\n%#v", webhookConfiguration.Webhooks, configurations) + for i := range configurations { + h, ok := configurations[i].GetMutatingWebhook() + if !ok { + t.Errorf("Expected mutating webhook") + continue + } + if !reflect.DeepEqual(h, &webhookConfiguration.Webhooks[i]) { + t.Errorf("Expected\n%#v\ngot\n%#v", &webhookConfiguration.Webhooks[i], h) + } } } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go b/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go index 9258258f64b..bcce1e70f92 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go @@ -24,6 +24,7 @@ import ( "k8s.io/api/admissionregistration/v1beta1" "k8s.io/apimachinery/pkg/labels" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apiserver/pkg/admission/plugin/webhook" "k8s.io/apiserver/pkg/admission/plugin/webhook/generic" "k8s.io/client-go/informers" admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1" @@ -48,7 +49,7 @@ func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory) } // Start with an empty list - manager.configuration.Store(&v1beta1.ValidatingWebhookConfiguration{}) + manager.configuration.Store([]webhook.WebhookAccessor{}) // On any change, rebuild the config informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ @@ -61,8 +62,8 @@ func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory) } // Webhooks returns the merged ValidatingWebhookConfiguration. -func (v *validatingWebhookConfigurationManager) Webhooks() []v1beta1.Webhook { - return v.configuration.Load().(*v1beta1.ValidatingWebhookConfiguration).Webhooks +func (v *validatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccessor { + return v.configuration.Load().([]webhook.WebhookAccessor) } // HasSynced returns true if the shared informers have synced. @@ -79,15 +80,15 @@ func (v *validatingWebhookConfigurationManager) updateConfiguration() { v.configuration.Store(mergeValidatingWebhookConfigurations(configurations)) } -func mergeValidatingWebhookConfigurations( - configurations []*v1beta1.ValidatingWebhookConfiguration, -) *v1beta1.ValidatingWebhookConfiguration { +func mergeValidatingWebhookConfigurations(configurations []*v1beta1.ValidatingWebhookConfiguration) []webhook.WebhookAccessor { sort.SliceStable(configurations, ValidatingWebhookConfigurationSorter(configurations).ByName) - var ret v1beta1.ValidatingWebhookConfiguration + accessors := []webhook.WebhookAccessor{} for _, c := range configurations { - ret.Webhooks = append(ret.Webhooks, c.Webhooks...) + for i := range c.Webhooks { + accessors = append(accessors, webhook.NewValidatingWebhookAccessor(&c.Webhooks[i])) + } } - return &ret + return accessors } type ValidatingWebhookConfigurationSorter []*v1beta1.ValidatingWebhookConfiguration diff --git a/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager_test.go b/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager_test.go index 153b4df486f..d99d99b46f2 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager_test.go @@ -46,7 +46,7 @@ func TestGetValidatingWebhookConfig(t *testing.T) { webhookConfiguration := &v1beta1.ValidatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{Name: "webhook1"}, - Webhooks: []v1beta1.Webhook{{Name: "webhook1.1"}}, + Webhooks: []v1beta1.ValidatingWebhook{{Name: "webhook1.1"}}, } validatingInformer := informerFactory.Admissionregistration().V1beta1().ValidatingWebhookConfigurations() @@ -59,7 +59,14 @@ func TestGetValidatingWebhookConfig(t *testing.T) { if len(configurations) == 0 { t.Errorf("expected non empty webhooks") } - if !reflect.DeepEqual(configurations, webhookConfiguration.Webhooks) { - t.Errorf("Expected\n%#v\ngot\n%#v", webhookConfiguration.Webhooks, configurations) + for i := range configurations { + h, ok := configurations[i].GetValidatingWebhook() + if !ok { + t.Errorf("Expected validating webhook") + continue + } + if !reflect.DeepEqual(h, &webhookConfiguration.Webhooks[i]) { + t.Errorf("Expected\n%#v\ngot\n%#v", &webhookConfiguration.Webhooks[i], h) + } } } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD new file mode 100644 index 00000000000..7787247d2c9 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["accessors.go"], + importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook", + importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook", + visibility = ["//visibility:public"], + deps = [ + "//staging/src/k8s.io/api/admissionregistration/v1beta1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/config:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/initializer:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/rules:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating:all-srcs", + ], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go new file mode 100644 index 00000000000..362333c50f5 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go @@ -0,0 +1,139 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package webhook + +import ( + "k8s.io/api/admissionregistration/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// WebhookAccessor provides a common interface to both mutating and validating webhook types. +type WebhookAccessor interface { + // GetName gets the webhook Name field. + GetName() string + // GetClientConfig gets the webhook ClientConfig field. + GetClientConfig() v1beta1.WebhookClientConfig + // GetRules gets the webhook Rules field. + GetRules() []v1beta1.RuleWithOperations + // GetFailurePolicy gets the webhook FailurePolicy field. + GetFailurePolicy() *v1beta1.FailurePolicyType + // GetMatchPolicy gets the webhook MatchPolicy field. + GetMatchPolicy() *v1beta1.MatchPolicyType + // GetNamespaceSelector gets the webhook NamespaceSelector field. + GetNamespaceSelector() *metav1.LabelSelector + // GetSideEffects gets the webhook SideEffects field. + GetSideEffects() *v1beta1.SideEffectClass + // GetTimeoutSeconds gets the webhook TimeoutSeconds field. + GetTimeoutSeconds() *int32 + // GetAdmissionReviewVersions gets the webhook AdmissionReviewVersions field. + GetAdmissionReviewVersions() []string + + // GetMutatingWebhook if the accessor contains a MutatingWebhook, returns it and true, else returns false. + GetMutatingWebhook() (*v1beta1.MutatingWebhook, bool) + // GetValidatingWebhook if the accessor contains a ValidatingWebhook, returns it and true, else returns false. + GetValidatingWebhook() (*v1beta1.ValidatingWebhook, bool) +} + +// NewMutatingWebhookAccessor creates an accessor for a MutatingWebhook. +func NewMutatingWebhookAccessor(h *v1beta1.MutatingWebhook) WebhookAccessor { + return mutatingWebhookAccessor{h} +} + +type mutatingWebhookAccessor struct { + *v1beta1.MutatingWebhook +} + +func (m mutatingWebhookAccessor) GetName() string { + return m.Name +} +func (m mutatingWebhookAccessor) GetClientConfig() v1beta1.WebhookClientConfig { + return m.ClientConfig +} +func (m mutatingWebhookAccessor) GetRules() []v1beta1.RuleWithOperations { + return m.Rules +} +func (m mutatingWebhookAccessor) GetFailurePolicy() *v1beta1.FailurePolicyType { + return m.FailurePolicy +} +func (m mutatingWebhookAccessor) GetMatchPolicy() *v1beta1.MatchPolicyType { + return m.MatchPolicy +} +func (m mutatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector { + return m.NamespaceSelector +} +func (m mutatingWebhookAccessor) GetSideEffects() *v1beta1.SideEffectClass { + return m.SideEffects +} +func (m mutatingWebhookAccessor) GetTimeoutSeconds() *int32 { + return m.TimeoutSeconds +} +func (m mutatingWebhookAccessor) GetAdmissionReviewVersions() []string { + return m.AdmissionReviewVersions +} + +func (m mutatingWebhookAccessor) GetMutatingWebhook() (*v1beta1.MutatingWebhook, bool) { + return m.MutatingWebhook, true +} + +func (m mutatingWebhookAccessor) GetValidatingWebhook() (*v1beta1.ValidatingWebhook, bool) { + return nil, false +} + +// NewValidatingWebhookAccessor creates an accessor for a ValidatingWebhook. +func NewValidatingWebhookAccessor(h *v1beta1.ValidatingWebhook) WebhookAccessor { + return validatingWebhookAccessor{h} +} + +type validatingWebhookAccessor struct { + *v1beta1.ValidatingWebhook +} + +func (v validatingWebhookAccessor) GetName() string { + return v.Name +} +func (v validatingWebhookAccessor) GetClientConfig() v1beta1.WebhookClientConfig { + return v.ClientConfig +} +func (v validatingWebhookAccessor) GetRules() []v1beta1.RuleWithOperations { + return v.Rules +} +func (v validatingWebhookAccessor) GetFailurePolicy() *v1beta1.FailurePolicyType { + return v.FailurePolicy +} +func (v validatingWebhookAccessor) GetMatchPolicy() *v1beta1.MatchPolicyType { + return v.MatchPolicy +} +func (v validatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector { + return v.NamespaceSelector +} +func (v validatingWebhookAccessor) GetSideEffects() *v1beta1.SideEffectClass { + return v.SideEffects +} +func (v validatingWebhookAccessor) GetTimeoutSeconds() *int32 { + return v.TimeoutSeconds +} +func (v validatingWebhookAccessor) GetAdmissionReviewVersions() []string { + return v.AdmissionReviewVersions +} + +func (v validatingWebhookAccessor) GetMutatingWebhook() (*v1beta1.MutatingWebhook, bool) { + return nil, false +} + +func (v validatingWebhookAccessor) GetValidatingWebhook() (*v1beta1.ValidatingWebhook, bool) { + return v.ValidatingWebhook, true +} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/BUILD index 7cdf2e46b48..7329da6c251 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/BUILD @@ -18,6 +18,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/config:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/rules:go_default_library", @@ -56,6 +57,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:go_default_library", "//staging/src/k8s.io/apiserver/pkg/apis/example:go_default_library", "//staging/src/k8s.io/apiserver/pkg/apis/example/v1:go_default_library", diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go index c57189543fb..227502de84e 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go @@ -19,15 +19,15 @@ package generic import ( "context" - "k8s.io/api/admissionregistration/v1beta1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" + "k8s.io/apiserver/pkg/admission/plugin/webhook" ) // Source can list dynamic webhook plugins. type Source interface { - Webhooks() []v1beta1.Webhook + Webhooks() []webhook.WebhookAccessor HasSynced() bool } @@ -51,8 +51,7 @@ type VersionedAttributes struct { // WebhookInvocation describes how to call a webhook, including the resource and subresource the webhook registered for, // and the kind that should be sent to the webhook. type WebhookInvocation struct { - Webhook *v1beta1.Webhook - + Webhook webhook.WebhookAccessor Resource schema.GroupVersionResource Subresource string Kind schema.GroupVersionKind diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go index 30a9b5e7b5e..99415292cbf 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go @@ -27,10 +27,11 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer" + "k8s.io/apiserver/pkg/admission/plugin/webhook" "k8s.io/apiserver/pkg/admission/plugin/webhook/config" "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace" "k8s.io/apiserver/pkg/admission/plugin/webhook/rules" - "k8s.io/apiserver/pkg/util/webhook" + webhookutil "k8s.io/apiserver/pkg/util/webhook" "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" ) @@ -42,7 +43,7 @@ type Webhook struct { sourceFactory sourceFactory hookSource Source - clientManager *webhook.ClientManager + clientManager *webhookutil.ClientManager namespaceMatcher *namespace.Matcher dispatcher Dispatcher } @@ -53,7 +54,7 @@ var ( ) type sourceFactory func(f informers.SharedInformerFactory) Source -type dispatcherFactory func(cm *webhook.ClientManager) Dispatcher +type dispatcherFactory func(cm *webhookutil.ClientManager) Dispatcher // NewWebhook creates a new generic admission webhook. func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory sourceFactory, dispatcherFactory dispatcherFactory) (*Webhook, error) { @@ -62,17 +63,17 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory return nil, err } - cm, err := webhook.NewClientManager(admissionv1beta1.SchemeGroupVersion, admissionv1beta1.AddToScheme) + cm, err := webhookutil.NewClientManager(admissionv1beta1.SchemeGroupVersion, admissionv1beta1.AddToScheme) if err != nil { return nil, err } - authInfoResolver, err := webhook.NewDefaultAuthenticationInfoResolver(kubeconfigFile) + authInfoResolver, err := webhookutil.NewDefaultAuthenticationInfoResolver(kubeconfigFile) if err != nil { return nil, err } // Set defaults which may be overridden later. cm.SetAuthenticationInfoResolver(authInfoResolver) - cm.SetServiceResolver(webhook.NewDefaultServiceResolver()) + cm.SetServiceResolver(webhookutil.NewDefaultServiceResolver()) return &Webhook{ Handler: handler, @@ -86,13 +87,13 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory // SetAuthenticationInfoResolverWrapper sets the // AuthenticationInfoResolverWrapper. // TODO find a better way wire this, but keep this pull small for now. -func (a *Webhook) SetAuthenticationInfoResolverWrapper(wrapper webhook.AuthenticationInfoResolverWrapper) { +func (a *Webhook) SetAuthenticationInfoResolverWrapper(wrapper webhookutil.AuthenticationInfoResolverWrapper) { a.clientManager.SetAuthenticationInfoResolverWrapper(wrapper) } // SetServiceResolver sets a service resolver for the webhook admission plugin. // Passing a nil resolver does not have an effect, instead a default one will be used. -func (a *Webhook) SetServiceResolver(sr webhook.ServiceResolver) { +func (a *Webhook) SetServiceResolver(sr webhookutil.ServiceResolver) { a.clientManager.SetServiceResolver(sr) } @@ -128,10 +129,10 @@ func (a *Webhook) ValidateInitialization() error { // shouldCallHook returns invocation details if the webhook should be called, nil if the webhook should not be called, // or an error if an error was encountered during evaluation. -func (a *Webhook) shouldCallHook(h *v1beta1.Webhook, attr admission.Attributes, o admission.ObjectInterfaces) (*WebhookInvocation, *apierrors.StatusError) { +func (a *Webhook) shouldCallHook(h webhook.WebhookAccessor, attr admission.Attributes, o admission.ObjectInterfaces) (*WebhookInvocation, *apierrors.StatusError) { var err *apierrors.StatusError var invocation *WebhookInvocation - for _, r := range h.Rules { + for _, r := range h.GetRules() { m := rules.Matcher{Rule: r, Attr: attr} if m.Matches() { invocation = &WebhookInvocation{ @@ -143,12 +144,12 @@ func (a *Webhook) shouldCallHook(h *v1beta1.Webhook, attr admission.Attributes, break } } - if invocation == nil && h.MatchPolicy != nil && *h.MatchPolicy == v1beta1.Equivalent { + if invocation == nil && h.GetMatchPolicy() != nil && *h.GetMatchPolicy() == v1beta1.Equivalent { attrWithOverride := &attrWithResourceOverride{Attributes: attr} equivalents := o.GetEquivalentResourceMapper().EquivalentResourcesFor(attr.GetResource(), attr.GetSubresource()) // honor earlier rules first OuterLoop: - for _, r := range h.Rules { + for _, r := range h.GetRules() { // see if the rule matches any of the equivalent resources for _, equivalent := range equivalents { if equivalent == attr.GetResource() { @@ -207,7 +208,7 @@ func (a *Webhook) Dispatch(attr admission.Attributes, o admission.ObjectInterfac var relevantHooks []*WebhookInvocation for i := range hooks { - invocation, err := a.shouldCallHook(&hooks[i], attr, o) + invocation, err := a.shouldCallHook(hooks[i], attr, o) if err != nil { return err } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go index b214122ad38..c2ddbb7b1f8 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" + "k8s.io/apiserver/pkg/admission/plugin/webhook" "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace" ) @@ -61,7 +62,7 @@ func TestShouldCallHook(t *testing.T) { testcases := []struct { name string - webhook *v1beta1.Webhook + webhook *v1beta1.ValidatingWebhook attrs admission.Attributes expectCall bool @@ -72,13 +73,13 @@ func TestShouldCallHook(t *testing.T) { }{ { name: "no rules (just write)", - webhook: &v1beta1.Webhook{Rules: []v1beta1.RuleWithOperations{}}, + webhook: &v1beta1.ValidatingWebhook{Rules: []v1beta1.RuleWithOperations{}}, attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil), expectCall: false, }, { name: "invalid kind lookup", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, MatchPolicy: &equivalentMatch, Rules: []v1beta1.RuleWithOperations{{ @@ -91,7 +92,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "wildcard rule, match as requested", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, @@ -105,7 +106,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "specific rules, prefer exact match", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, @@ -125,7 +126,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "specific rules, match miss", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, @@ -139,7 +140,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "specific rules, exact match miss", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &exactMatch, NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ @@ -154,7 +155,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "specific rules, equivalent match, prefer extensions", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &equivalentMatch, NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ @@ -172,7 +173,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "specific rules, equivalent match, prefer apps", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &equivalentMatch, NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ @@ -191,7 +192,7 @@ func TestShouldCallHook(t *testing.T) { { name: "specific rules, subresource prefer exact match", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, @@ -211,7 +212,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "specific rules, subresource match miss", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, @@ -225,7 +226,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "specific rules, subresource exact match miss", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &exactMatch, NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ @@ -240,7 +241,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "specific rules, subresource equivalent match, prefer extensions", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &equivalentMatch, NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ @@ -258,7 +259,7 @@ func TestShouldCallHook(t *testing.T) { }, { name: "specific rules, subresource equivalent match, prefer apps", - webhook: &v1beta1.Webhook{ + webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &equivalentMatch, NamespaceSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ @@ -278,7 +279,7 @@ func TestShouldCallHook(t *testing.T) { for _, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { - invocation, err := a.shouldCallHook(testcase.webhook, testcase.attrs, interfaces) + invocation, err := a.shouldCallHook(webhook.NewValidatingWebhookAccessor(testcase.webhook), testcase.attrs, interfaces) if err != nil { if len(testcase.expectErr) == 0 { t.Fatal(err) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go index 33190ff1214..b206d2a5393 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go @@ -39,16 +39,16 @@ import ( "k8s.io/apiserver/pkg/admission/plugin/webhook/generic" "k8s.io/apiserver/pkg/admission/plugin/webhook/request" "k8s.io/apiserver/pkg/admission/plugin/webhook/util" - "k8s.io/apiserver/pkg/util/webhook" + webhookutil "k8s.io/apiserver/pkg/util/webhook" ) type mutatingDispatcher struct { - cm *webhook.ClientManager + cm *webhookutil.ClientManager plugin *Plugin } -func newMutatingDispatcher(p *Plugin) func(cm *webhook.ClientManager) generic.Dispatcher { - return func(cm *webhook.ClientManager) generic.Dispatcher { +func newMutatingDispatcher(p *Plugin) func(cm *webhookutil.ClientManager) generic.Dispatcher { + return func(cm *webhookutil.ClientManager) generic.Dispatcher { return &mutatingDispatcher{cm, p} } } @@ -58,7 +58,10 @@ var _ generic.Dispatcher = &mutatingDispatcher{} func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces, relevantHooks []*generic.WebhookInvocation) error { var versionedAttr *generic.VersionedAttributes for _, invocation := range relevantHooks { - hook := invocation.Webhook + hook, ok := invocation.Webhook.GetMutatingWebhook() + if !ok { + return fmt.Errorf("mutating webhook dispatch requires v1beta1.MutatingWebhook, but got %T", hook) + } if versionedAttr == nil { // First webhook, create versioned attributes var err error @@ -73,14 +76,14 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib } t := time.Now() - err := a.callAttrMutatingHook(ctx, invocation, versionedAttr, o) + err := a.callAttrMutatingHook(ctx, hook, invocation, versionedAttr, o) admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, versionedAttr.Attributes, "admit", hook.Name) if err == nil { continue } ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore - if callErr, ok := err.(*webhook.ErrCallingWebhook); ok { + if callErr, ok := err.(*webhookutil.ErrCallingWebhook); ok { if ignoreClientCallFailures { klog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr) utilruntime.HandleError(callErr) @@ -100,11 +103,11 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib } // note that callAttrMutatingHook updates attr -func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes, o admission.ObjectInterfaces) error { - h := invocation.Webhook + +func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.MutatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes, o admission.ObjectInterfaces) error { if attr.Attributes.IsDryRun() { if h.SideEffects == nil { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")} + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")} } if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) { return webhookerrors.NewDryRunUnsupportedErr(h.Name) @@ -113,15 +116,15 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, invocatio // Currently dispatcher only supports `v1beta1` AdmissionReview // TODO: Make the dispatcher capable of sending multiple AdmissionReview versions - if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, h) { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReview")} + if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, invocation.Webhook) { + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReview")} } // Make the webhook request request := request.CreateAdmissionReview(attr, invocation) - client, err := a.cm.HookClient(util.HookClientConfigForWebhook(h)) + client, err := a.cm.HookClient(util.HookClientConfigForWebhook(invocation.Webhook)) if err != nil { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err} + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } response := &admissionv1beta1.AdmissionReview{} r := client.Post().Context(ctx).Body(&request) @@ -129,11 +132,11 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, invocatio r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second) } if err := r.Do().Into(response); err != nil { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err} + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } if response.Response == nil { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")} + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")} } for k, v := range response.Response.AuditAnnotations { diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin_test.go index 4a1a1c328b4..b8f98eedffb 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin_test.go @@ -46,7 +46,7 @@ func TestAdmit(t *testing.T) { defer close(stopCh) testCases := append(webhooktesting.NewMutatingTestCases(serverURL), - webhooktesting.NewNonMutatingTestCases(serverURL)...) + webhooktesting.ConvertToMutatingTestCases(webhooktesting.NewNonMutatingTestCases(serverURL))...) for _, tt := range testCases { wh, err := NewMutatingWebhook(nil) @@ -56,7 +56,7 @@ func TestAdmit(t *testing.T) { } ns := "webhook-test" - client, informer := webhooktesting.NewFakeDataSource(ns, tt.Webhooks, true, stopCh) + client, informer := webhooktesting.NewFakeMutatingDataSource(ns, tt.Webhooks, stopCh) wh.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32)))) wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL)) @@ -136,7 +136,7 @@ func TestAdmitCachedClient(t *testing.T) { for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) { ns := "webhook-test" - client, informer := webhooktesting.NewFakeDataSource(ns, tt.Webhooks, true, stopCh) + client, informer := webhooktesting.NewFakeMutatingDataSource(ns, webhooktesting.ConvertToMutatingWebhooks(tt.Webhooks), stopCh) // override the webhook source. The client cache will stay the same. cacheMisses := new(int32) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/BUILD index 6950843f754..24198a9ee3f 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/BUILD @@ -10,13 +10,13 @@ go_library( importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace", visibility = ["//visibility:public"], deps = [ - "//staging/src/k8s.io/api/admissionregistration/v1beta1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/client-go/listers/core/v1:go_default_library", ], @@ -34,6 +34,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go index 78dbd528286..4530de3e608 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher.go @@ -19,13 +19,13 @@ package namespace import ( "fmt" - "k8s.io/api/admissionregistration/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apiserver/pkg/admission" + "k8s.io/apiserver/pkg/admission/plugin/webhook" clientset "k8s.io/client-go/kubernetes" corelisters "k8s.io/client-go/listers/core/v1" ) @@ -86,7 +86,7 @@ func (m *Matcher) GetNamespaceLabels(attr admission.Attributes) (map[string]stri // MatchNamespaceSelector decideds whether the request matches the // namespaceSelctor of the webhook. Only when they match, the webhook is called. -func (m *Matcher) MatchNamespaceSelector(h *v1beta1.Webhook, attr admission.Attributes) (bool, *apierrors.StatusError) { +func (m *Matcher) MatchNamespaceSelector(h webhook.WebhookAccessor, attr admission.Attributes) (bool, *apierrors.StatusError) { namespaceName := attr.GetNamespace() if len(namespaceName) == 0 && attr.GetResource().Resource != "namespaces" { // If the request is about a cluster scoped resource, and it is not a @@ -96,7 +96,7 @@ func (m *Matcher) MatchNamespaceSelector(h *v1beta1.Webhook, attr admission.Attr return true, nil } // TODO: adding an LRU cache to cache the translation - selector, err := metav1.LabelSelectorAsSelector(h.NamespaceSelector) + selector, err := metav1.LabelSelectorAsSelector(h.GetNamespaceSelector()) if err != nil { return false, apierrors.NewInternalError(err) } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher_test.go index 616ff7bbe31..4633cac8ecc 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher_test.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" + "k8s.io/apiserver/pkg/admission/plugin/webhook" ) type fakeNamespaceLister struct { @@ -114,12 +115,12 @@ func TestGetNamespaceLabels(t *testing.T) { } func TestNotExemptClusterScopedResource(t *testing.T) { - hook := ®istrationv1beta1.Webhook{ + hook := ®istrationv1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, } attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, &metav1.CreateOptions{}, false, nil) matcher := Matcher{} - matches, err := matcher.MatchNamespaceSelector(hook, attr) + matches, err := matcher.MatchNamespaceSelector(webhook.NewValidatingWebhookAccessor(hook), attr) if err != nil { t.Fatal(err) } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go index ee5467478ca..ec8e6b552c7 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go @@ -49,8 +49,8 @@ var sideEffectsNone = registrationv1beta1.SideEffectClassNone var sideEffectsSome = registrationv1beta1.SideEffectClassSome var sideEffectsNoneOnDryRun = registrationv1beta1.SideEffectClassNoneOnDryRun -// NewFakeDataSource returns a mock client and informer returning the given webhooks. -func NewFakeDataSource(name string, webhooks []registrationv1beta1.Webhook, mutating bool, stopCh <-chan struct{}) (clientset kubernetes.Interface, factory informers.SharedInformerFactory) { +// NewFakeValidatingDataSource returns a mock client and informer returning the given webhooks. +func NewFakeValidatingDataSource(name string, webhooks []registrationv1beta1.ValidatingWebhook, stopCh <-chan struct{}) (clientset kubernetes.Interface, factory informers.SharedInformerFactory) { var objs = []runtime.Object{ &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -61,21 +61,37 @@ func NewFakeDataSource(name string, webhooks []registrationv1beta1.Webhook, muta }, }, } - if mutating { - objs = append(objs, ®istrationv1beta1.MutatingWebhookConfiguration{ + objs = append(objs, ®istrationv1beta1.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-webhooks", + }, + Webhooks: webhooks, + }) + + client := fakeclientset.NewSimpleClientset(objs...) + informerFactory := informers.NewSharedInformerFactory(client, 0) + + return client, informerFactory +} + +// NewFakeMutatingDataSource returns a mock client and informer returning the given webhooks. +func NewFakeMutatingDataSource(name string, webhooks []registrationv1beta1.MutatingWebhook, stopCh <-chan struct{}) (clientset kubernetes.Interface, factory informers.SharedInformerFactory) { + var objs = []runtime.Object{ + &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-webhooks", + Name: name, + Labels: map[string]string{ + "runlevel": "0", + }, }, - Webhooks: webhooks, - }) - } else { - objs = append(objs, ®istrationv1beta1.ValidatingWebhookConfiguration{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-webhooks", - }, - Webhooks: webhooks, - }) + }, } + objs = append(objs, ®istrationv1beta1.MutatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-webhooks", + }, + Webhooks: webhooks, + }) client := fakeclientset.NewSimpleClientset(objs...) informerFactory := informers.NewSharedInformerFactory(client, 0) @@ -181,10 +197,10 @@ func (c urlConfigGenerator) ccfgURL(urlPath string) registrationv1beta1.WebhookC } } -// Test is a webhook test case. -type Test struct { +// ValidatingTest is a validating webhook test case. +type ValidatingTest struct { Name string - Webhooks []registrationv1beta1.Webhook + Webhooks []registrationv1beta1.ValidatingWebhook Path string IsCRD bool IsDryRun bool @@ -196,19 +212,52 @@ type Test struct { ExpectStatusCode int32 } +// MutatingTest is a mutating webhook test case. +type MutatingTest struct { + Name string + Webhooks []registrationv1beta1.MutatingWebhook + Path string + IsCRD bool + IsDryRun bool + AdditionalLabels map[string]string + ExpectLabels map[string]string + ExpectAllow bool + ErrorContains string + ExpectAnnotations map[string]string + ExpectStatusCode int32 +} + +// ConvertToMutatingTestCases converts a validating test case to a mutating one for test purposes. +func ConvertToMutatingTestCases(tests []ValidatingTest) []MutatingTest { + r := make([]MutatingTest, len(tests)) + for i, t := range tests { + r[i] = MutatingTest{t.Name, ConvertToMutatingWebhooks(t.Webhooks), t.Path, t.IsCRD, t.IsDryRun, t.AdditionalLabels, t.ExpectLabels, t.ExpectAllow, t.ErrorContains, t.ExpectAnnotations, t.ExpectStatusCode} + } + return r +} + +// ConvertToMutatingWebhooks converts a validating webhook to a mutating one for test purposes. +func ConvertToMutatingWebhooks(webhooks []registrationv1beta1.ValidatingWebhook) []registrationv1beta1.MutatingWebhook { + mutating := make([]registrationv1beta1.MutatingWebhook, len(webhooks)) + for i, h := range webhooks { + mutating[i] = registrationv1beta1.MutatingWebhook{h.Name, h.ClientConfig, h.Rules, h.FailurePolicy, h.MatchPolicy, h.NamespaceSelector, h.SideEffects, h.TimeoutSeconds, h.AdmissionReviewVersions} + } + return mutating +} + // NewNonMutatingTestCases returns test cases with a given base url. // All test cases in NewNonMutatingTestCases have no Patch set in // AdmissionResponse. The test cases are used by both MutatingAdmissionWebhook // and ValidatingAdmissionWebhook. -func NewNonMutatingTestCases(url *url.URL) []Test { +func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { policyFail := registrationv1beta1.Fail policyIgnore := registrationv1beta1.Ignore ccfgURL := urlConfigGenerator{url}.ccfgURL - return []Test{ + return []ValidatingTest{ { Name: "no match", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "nomatch", ClientConfig: ccfgSVC("disallow"), Rules: []registrationv1beta1.RuleWithOperations{{ @@ -221,7 +270,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & allow", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "allow.example.com", ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, @@ -233,7 +282,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & disallow", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "disallow", ClientConfig: ccfgSVC("disallow"), Rules: matchEverythingRules, @@ -245,7 +294,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & disallow ii", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "disallowReason", ClientConfig: ccfgSVC("disallowReason"), Rules: matchEverythingRules, @@ -257,7 +306,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & disallow & but allowed because namespaceSelector exempt the ns", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "disallow", ClientConfig: ccfgSVC("disallow"), Rules: newMatchEverythingRules(), @@ -275,7 +324,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & disallow & but allowed because namespaceSelector exempt the ns ii", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "disallow", ClientConfig: ccfgSVC("disallow"), Rules: newMatchEverythingRules(), @@ -292,7 +341,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & fail (but allow because fail open)", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "internalErr A", ClientConfig: ccfgSVC("internalErr"), Rules: matchEverythingRules, @@ -319,7 +368,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & fail (but disallow because fail close on nil FailurePolicy)", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "internalErr A", ClientConfig: ccfgSVC("internalErr"), NamespaceSelector: &metav1.LabelSelector{}, @@ -343,7 +392,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & fail (but fail because fail closed)", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "internalErr A", ClientConfig: ccfgSVC("internalErr"), Rules: matchEverythingRules, @@ -370,7 +419,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & allow (url)", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "allow.example.com", ClientConfig: ccfgURL("allow"), Rules: matchEverythingRules, @@ -382,7 +431,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match & disallow (url)", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "disallow", ClientConfig: ccfgURL("disallow"), Rules: matchEverythingRules, @@ -393,7 +442,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { ErrorContains: "without explanation", }, { Name: "absent response and fail open", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "nilResponse", ClientConfig: ccfgURL("nilResponse"), FailurePolicy: &policyIgnore, @@ -405,7 +454,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "absent response and fail closed", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "nilResponse", ClientConfig: ccfgURL("nilResponse"), FailurePolicy: &policyFail, @@ -418,7 +467,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "no match dry run", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "nomatch", ClientConfig: ccfgSVC("allow"), Rules: []registrationv1beta1.RuleWithOperations{{ @@ -433,7 +482,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match dry run side effects Unknown", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "allow", ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, @@ -447,7 +496,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match dry run side effects None", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "allow", ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, @@ -461,7 +510,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match dry run side effects Some", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "allow", ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, @@ -475,7 +524,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "match dry run side effects NoneOnDryRun", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "allow", ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, @@ -489,7 +538,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test { }, { Name: "illegal annotation format", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "invalidAnnotation", ClientConfig: ccfgURL("invalidAnnotation"), Rules: matchEverythingRules, @@ -506,11 +555,11 @@ func NewNonMutatingTestCases(url *url.URL) []Test { // NewMutatingTestCases returns test cases with a given base url. // All test cases in NewMutatingTestCases have Patch set in // AdmissionResponse. The test cases are only used by both MutatingAdmissionWebhook. -func NewMutatingTestCases(url *url.URL) []Test { - return []Test{ +func NewMutatingTestCases(url *url.URL) []MutatingTest { + return []MutatingTest{ { Name: "match & remove label", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.MutatingWebhook{{ Name: "removelabel.example.com", ClientConfig: ccfgSVC("removeLabel"), Rules: matchEverythingRules, @@ -524,7 +573,7 @@ func NewMutatingTestCases(url *url.URL) []Test { }, { Name: "match & add label", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.MutatingWebhook{{ Name: "addLabel", ClientConfig: ccfgSVC("addLabel"), Rules: matchEverythingRules, @@ -536,7 +585,7 @@ func NewMutatingTestCases(url *url.URL) []Test { }, { Name: "match CRD & add label", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.MutatingWebhook{{ Name: "addLabel", ClientConfig: ccfgSVC("addLabel"), Rules: matchEverythingRules, @@ -549,7 +598,7 @@ func NewMutatingTestCases(url *url.URL) []Test { }, { Name: "match CRD & remove label", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.MutatingWebhook{{ Name: "removelabel.example.com", ClientConfig: ccfgSVC("removeLabel"), Rules: matchEverythingRules, @@ -564,7 +613,7 @@ func NewMutatingTestCases(url *url.URL) []Test { }, { Name: "match & invalid mutation", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.MutatingWebhook{{ Name: "invalidMutation", ClientConfig: ccfgSVC("invalidMutation"), Rules: matchEverythingRules, @@ -576,7 +625,7 @@ func NewMutatingTestCases(url *url.URL) []Test { }, { Name: "match & remove label dry run unsupported", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.MutatingWebhook{{ Name: "removeLabel", ClientConfig: ccfgSVC("removeLabel"), Rules: matchEverythingRules, @@ -596,7 +645,7 @@ func NewMutatingTestCases(url *url.URL) []Test { // CachedTest is a test case for the client manager. type CachedTest struct { Name string - Webhooks []registrationv1beta1.Webhook + Webhooks []registrationv1beta1.ValidatingWebhook ExpectAllow bool ExpectCacheMiss bool } @@ -609,7 +658,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { return []CachedTest{ { Name: "uncached: service webhook, path 'allow'", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "cache1", ClientConfig: ccfgSVC("allow"), Rules: newMatchEverythingRules(), @@ -622,7 +671,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { }, { Name: "uncached: service webhook, path 'internalErr'", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "cache2", ClientConfig: ccfgSVC("internalErr"), Rules: newMatchEverythingRules(), @@ -635,7 +684,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { }, { Name: "cached: service webhook, path 'allow'", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "cache3", ClientConfig: ccfgSVC("allow"), Rules: newMatchEverythingRules(), @@ -648,7 +697,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { }, { Name: "uncached: url webhook, path 'allow'", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "cache4", ClientConfig: ccfgURL("allow"), Rules: newMatchEverythingRules(), @@ -661,7 +710,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { }, { Name: "cached: service webhook, path 'allow'", - Webhooks: []registrationv1beta1.Webhook{{ + Webhooks: []registrationv1beta1.ValidatingWebhook{{ Name: "cache5", ClientConfig: ccfgURL("allow"), Rules: newMatchEverythingRules(), diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util/BUILD index dd9ed4916d0..3a9b036f76e 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util/BUILD @@ -7,7 +7,7 @@ go_library( importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/util", visibility = ["//visibility:public"], deps = [ - "//staging/src/k8s.io/api/admissionregistration/v1beta1:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util/client_config.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util/client_config.go index 8f489639aaa..b9907286fc5 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util/client_config.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util/client_config.go @@ -17,38 +17,39 @@ limitations under the License. package util import ( - "k8s.io/api/admissionregistration/v1beta1" - "k8s.io/apiserver/pkg/util/webhook" + "k8s.io/apiserver/pkg/admission/plugin/webhook" + webhookutil "k8s.io/apiserver/pkg/util/webhook" ) -// HookClientConfigForWebhook construct a webhook.ClientConfig using a v1beta1.Webhook API object. -// webhook.ClientConfig is used to create a HookClient and the purpose of the config struct is to -// share that with other packages that need to create a HookClient. -func HookClientConfigForWebhook(w *v1beta1.Webhook) webhook.ClientConfig { - ret := webhook.ClientConfig{Name: w.Name, CABundle: w.ClientConfig.CABundle} - if w.ClientConfig.URL != nil { - ret.URL = *w.ClientConfig.URL +// HookClientConfigForWebhook construct a webhookutil.ClientConfig using a WebhookAccessor to access +// v1beta1.MutatingWebhook and v1beta1.ValidatingWebhook API objects. webhookutil.ClientConfig is used +// to create a HookClient and the purpose of the config struct is to share that with other packages +// that need to create a HookClient. +func HookClientConfigForWebhook(w webhook.WebhookAccessor) webhookutil.ClientConfig { + ret := webhookutil.ClientConfig{Name: w.GetName(), CABundle: w.GetClientConfig().CABundle} + if w.GetClientConfig().URL != nil { + ret.URL = *w.GetClientConfig().URL } - if w.ClientConfig.Service != nil { - ret.Service = &webhook.ClientConfigService{ - Name: w.ClientConfig.Service.Name, - Namespace: w.ClientConfig.Service.Namespace, + if w.GetClientConfig().Service != nil { + ret.Service = &webhookutil.ClientConfigService{ + Name: w.GetClientConfig().Service.Name, + Namespace: w.GetClientConfig().Service.Namespace, } - if w.ClientConfig.Service.Port != nil { - ret.Service.Port = *w.ClientConfig.Service.Port + if w.GetClientConfig().Service.Port != nil { + ret.Service.Port = *w.GetClientConfig().Service.Port } else { ret.Service.Port = 443 } - if w.ClientConfig.Service.Path != nil { - ret.Service.Path = *w.ClientConfig.Service.Path + if w.GetClientConfig().Service.Path != nil { + ret.Service.Path = *w.GetClientConfig().Service.Path } } return ret } // HasAdmissionReviewVersion check whether a version is accepted by a given webhook. -func HasAdmissionReviewVersion(a string, w *v1beta1.Webhook) bool { - for _, b := range w.AdmissionReviewVersions { +func HasAdmissionReviewVersion(a string, w webhook.WebhookAccessor) bool { + for _, b := range w.GetAdmissionReviewVersions() { if b == a { return true } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go index d7421e2b53b..0505103d26d 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go @@ -22,8 +22,6 @@ import ( "sync" "time" - "k8s.io/klog" - admissionv1beta1 "k8s.io/api/admission/v1beta1" "k8s.io/api/admissionregistration/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -35,14 +33,15 @@ import ( "k8s.io/apiserver/pkg/admission/plugin/webhook/generic" "k8s.io/apiserver/pkg/admission/plugin/webhook/request" "k8s.io/apiserver/pkg/admission/plugin/webhook/util" - "k8s.io/apiserver/pkg/util/webhook" + webhookutil "k8s.io/apiserver/pkg/util/webhook" + "k8s.io/klog" ) type validatingDispatcher struct { - cm *webhook.ClientManager + cm *webhookutil.ClientManager } -func newValidatingDispatcher(cm *webhook.ClientManager) generic.Dispatcher { +func newValidatingDispatcher(cm *webhookutil.ClientManager) generic.Dispatcher { return &validatingDispatcher{cm} } @@ -69,18 +68,21 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr for i := range relevantHooks { go func(invocation *generic.WebhookInvocation) { defer wg.Done() - hook := invocation.Webhook + hook, ok := invocation.Webhook.GetValidatingWebhook() + if !ok { + utilruntime.HandleError(fmt.Errorf("validating webhook dispatch requires v1beta1.ValidatingWebhook, but got %T", hook)) + return + } versionedAttr := versionedAttrs[invocation.Kind] - t := time.Now() - err := d.callHook(ctx, invocation, versionedAttr) + err := d.callHook(ctx, hook, invocation, versionedAttr) admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, versionedAttr.Attributes, "validating", hook.Name) if err == nil { return } ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore - if callErr, ok := err.(*webhook.ErrCallingWebhook); ok { + if callErr, ok := err.(*webhookutil.ErrCallingWebhook); ok { if ignoreClientCallFailures { klog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr) utilruntime.HandleError(callErr) @@ -115,11 +117,10 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr return errs[0] } -func (d *validatingDispatcher) callHook(ctx context.Context, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes) error { - h := invocation.Webhook +func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.ValidatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes) error { if attr.Attributes.IsDryRun() { if h.SideEffects == nil { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")} + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")} } if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) { return webhookerrors.NewDryRunUnsupportedErr(h.Name) @@ -128,15 +129,15 @@ func (d *validatingDispatcher) callHook(ctx context.Context, invocation *generic // Currently dispatcher only supports `v1beta1` AdmissionReview // TODO: Make the dispatcher capable of sending multiple AdmissionReview versions - if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, h) { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReviewRequest")} + if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, invocation.Webhook) { + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReviewRequest")} } // Make the webhook request request := request.CreateAdmissionReview(attr, invocation) - client, err := d.cm.HookClient(util.HookClientConfigForWebhook(h)) + client, err := d.cm.HookClient(util.HookClientConfigForWebhook(invocation.Webhook)) if err != nil { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err} + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } response := &admissionv1beta1.AdmissionReview{} r := client.Post().Context(ctx).Body(&request) @@ -144,11 +145,11 @@ func (d *validatingDispatcher) callHook(ctx context.Context, invocation *generic r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second) } if err := r.Do().Into(response); err != nil { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err} + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } if response.Response == nil { - return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")} + return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")} } for k, v := range response.Response.AuditAnnotations { key := h.Name + "/" + k diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin_test.go index 0ed2a9744c3..a2aec191e39 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin_test.go @@ -51,7 +51,7 @@ func TestValidate(t *testing.T) { } ns := "webhook-test" - client, informer := webhooktesting.NewFakeDataSource(ns, tt.Webhooks, false, stopCh) + client, informer := webhooktesting.NewFakeValidatingDataSource(ns, tt.Webhooks, stopCh) wh.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32)))) wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL)) @@ -116,7 +116,7 @@ func TestValidateCachedClient(t *testing.T) { for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) { ns := "webhook-test" - client, informer := webhooktesting.NewFakeDataSource(ns, tt.Webhooks, false, stopCh) + client, informer := webhooktesting.NewFakeValidatingDataSource(ns, tt.Webhooks, stopCh) // override the webhook source. The client cache will stay the same. cacheMisses := new(int32) diff --git a/test/e2e/apimachinery/webhook.go b/test/e2e/apimachinery/webhook.go index 2c4ba66379b..00e7731f891 100644 --- a/test/e2e/apimachinery/webhook.go +++ b/test/e2e/apimachinery/webhook.go @@ -436,7 +436,7 @@ func registerWebhook(f *framework.Framework, context *certContext) func() { ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.ValidatingWebhook{ { Name: "deny-unwanted-pod-container-name-and-label.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -517,7 +517,7 @@ func registerWebhookForAttachingPod(f *framework.Framework, context *certContext ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.ValidatingWebhook{ { Name: "deny-attaching-pod.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -561,7 +561,7 @@ func registerMutatingWebhookForConfigMap(f *framework.Framework, context *certCo ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.MutatingWebhook{ { Name: "adding-configmap-data-stage-1.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -638,7 +638,7 @@ func registerMutatingWebhookForPod(f *framework.Framework, context *certContext) ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.MutatingWebhook{ { Name: "adding-init-container.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -841,8 +841,8 @@ func testAttachingPodWebhook(f *framework.Framework) { // failingWebhook returns a webhook with rule of create configmaps, // but with an invalid client config so that server cannot communicate with it -func failingWebhook(namespace, name string) v1beta1.Webhook { - return v1beta1.Webhook{ +func failingWebhook(namespace, name string) v1beta1.ValidatingWebhook { + return v1beta1.ValidatingWebhook{ Name: name, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{v1beta1.Create}, @@ -889,7 +889,7 @@ func registerFailClosedWebhook(f *framework.Framework, context *certContext) fun ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.ValidatingWebhook{ // Server cannot talk to this webhook, so it always fails. // Because this webhook is configured fail-closed, request should be rejected after the call fails. hook, @@ -945,7 +945,7 @@ func registerValidatingWebhookForWebhookConfigurations(f *framework.Framework, c ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.ValidatingWebhook{ { Name: "deny-webhook-configuration-deletions.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -998,7 +998,7 @@ func registerMutatingWebhookForWebhookConfigurations(f *framework.Framework, con ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.MutatingWebhook{ { Name: "add-label-to-webhook-configurations.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -1050,7 +1050,7 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework) { ObjectMeta: metav1.ObjectMeta{ Name: dummyValidatingWebhookConfigName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.ValidatingWebhook{ { Name: "dummy-validating-webhook.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -1098,7 +1098,7 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework) { ObjectMeta: metav1.ObjectMeta{ Name: dummyMutatingWebhookConfigName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.MutatingWebhook{ { Name: "dummy-mutating-webhook.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -1306,7 +1306,7 @@ func registerWebhookForCustomResource(f *framework.Framework, context *certConte ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.ValidatingWebhook{ { Name: "deny-unwanted-custom-resource-data.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -1348,7 +1348,7 @@ func registerMutatingWebhookForCustomResource(f *framework.Framework, context *c ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.MutatingWebhook{ { Name: "mutate-custom-resource-data-stage-1.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -1543,7 +1543,7 @@ func registerValidatingWebhookForCRD(f *framework.Framework, context *certContex ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.ValidatingWebhook{ { Name: "deny-crd-with-unwanted-label.k8s.io", Rules: []v1beta1.RuleWithOperations{{ @@ -1649,7 +1649,7 @@ func registerSlowWebhook(f *framework.Framework, context *certContext, policy *v ObjectMeta: metav1.ObjectMeta{ Name: configName, }, - Webhooks: []v1beta1.Webhook{ + Webhooks: []v1beta1.ValidatingWebhook{ { Name: "allow-configmap-with-delay-webhook.k8s.io", Rules: []v1beta1.RuleWithOperations{{ diff --git a/test/integration/apiserver/admissionwebhook/admission_test.go b/test/integration/apiserver/admissionwebhook/admission_test.go index c39ec915abf..b243f5a24df 100644 --- a/test/integration/apiserver/admissionwebhook/admission_test.go +++ b/test/integration/apiserver/admissionwebhook/admission_test.go @@ -283,6 +283,9 @@ func (h *holder) record(phase string, converted bool, request *v1beta1.Admission return } + if debug { + h.t.Logf("recording: %#v = %s %#v %v", webhookOptions{phase: phase, converted: converted}, request.Operation, request.Resource, request.SubResource) + } h.recorded[webhookOptions{phase: phase, converted: converted}] = request } @@ -1287,7 +1290,7 @@ func createV1beta1ValidationWebhook(client clientset.Interface, endpoint, conver // Attaching Admission webhook to API server _, err := client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(&admissionv1beta1.ValidatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{Name: "admission.integration.test"}, - Webhooks: []admissionv1beta1.Webhook{ + Webhooks: []admissionv1beta1.ValidatingWebhook{ { Name: "admission.integration.test", ClientConfig: admissionv1beta1.WebhookClientConfig{ @@ -1323,7 +1326,7 @@ func createV1beta1MutationWebhook(client clientset.Interface, endpoint, converte // Attaching Mutation webhook to API server _, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionv1beta1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{Name: "mutation.integration.test"}, - Webhooks: []admissionv1beta1.Webhook{ + Webhooks: []admissionv1beta1.MutatingWebhook{ { Name: "mutation.integration.test", ClientConfig: admissionv1beta1.WebhookClientConfig{ diff --git a/test/integration/apiserver/admissionwebhook/broken_webhook_test.go b/test/integration/apiserver/admissionwebhook/broken_webhook_test.go index 7a08d4cd4da..061bd07d590 100644 --- a/test/integration/apiserver/admissionwebhook/broken_webhook_test.go +++ b/test/integration/apiserver/admissionwebhook/broken_webhook_test.go @@ -155,7 +155,7 @@ func brokenWebhookConfig(name string) *admissionregistrationv1beta1.ValidatingWe ObjectMeta: metav1.ObjectMeta{ Name: name, }, - Webhooks: []admissionregistrationv1beta1.Webhook{ + Webhooks: []admissionregistrationv1beta1.ValidatingWebhook{ { Name: "broken-webhook.k8s.io", Rules: []admissionregistrationv1beta1.RuleWithOperations{{ diff --git a/test/integration/examples/webhook_test.go b/test/integration/examples/webhook_test.go index 611925a5f96..d5ce5db6bc7 100644 --- a/test/integration/examples/webhook_test.go +++ b/test/integration/examples/webhook_test.go @@ -22,7 +22,7 @@ import ( "time" admissionv1beta1 "k8s.io/api/admissionregistration/v1beta1" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" auditinternal "k8s.io/apiserver/pkg/apis/audit" @@ -65,7 +65,7 @@ func TestWebhookLoopback(t *testing.T) { fail := admissionv1beta1.Fail _, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionv1beta1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{Name: "webhooktest.example.com"}, - Webhooks: []admissionv1beta1.Webhook{{ + Webhooks: []admissionv1beta1.MutatingWebhook{{ Name: "webhooktest.example.com", ClientConfig: admissionv1beta1.WebhookClientConfig{ Service: &admissionv1beta1.ServiceReference{Namespace: "default", Name: "kubernetes", Path: &webhookPath}, diff --git a/vendor/modules.txt b/vendor/modules.txt index 9ba9d302cfb..5aa65435c93 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1184,6 +1184,7 @@ k8s.io/apiserver/pkg/admission/configuration k8s.io/apiserver/pkg/admission/initializer k8s.io/apiserver/pkg/admission/metrics k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle +k8s.io/apiserver/pkg/admission/plugin/webhook k8s.io/apiserver/pkg/admission/plugin/webhook/config k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1 From 939a04f0edebc70157b7a8ebff51479f12162dfb Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Wed, 29 May 2019 22:31:03 -0700 Subject: [PATCH 3/8] codegen: Add mutating admission webhook reinvocation --- api/openapi-spec/swagger.json | 4 + .../v1beta1/zz_generated.conversion.go | 2 + .../zz_generated.deepcopy.go | 5 + .../v1beta1/generated.pb.go | 177 +++++++++++------- .../v1beta1/generated.proto | 18 ++ .../v1beta1/types_swagger_doc_generated.go | 1 + .../v1beta1/zz_generated.deepcopy.go | 5 + 7 files changed, 145 insertions(+), 67 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index b73d0089728..6c55e630c05 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -30,6 +30,10 @@ "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." }, + "reinvocationPolicy": { + "description": "reinvocationPolicy indicates whether this webhook should be called multiple times as part of a single admission evaluation. Allowed values are \"Never\" and \"IfNeeded\".\n\nNever: the webhook will not be called more than once in a single admission evaluation.\n\nIfNeeded: the webhook will be called at least one additional time as part of the admission evaluation if the object being admitted is modified by other admission plugins after the initial webhook call. Webhooks that specify this option *must* be idempotent, able to process objects they previously admitted. Note: * the number of additional invocations is not guaranteed to be exactly one. * if additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again. * webhooks that use this option may be reordered to minimize the number of additional invocations. * to validate an object after all mutations are guaranteed complete, use a validating admission webhook instead.\n\nDefaults to \"Never\".", + "type": "string" + }, "rules": { "description": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", "items": { diff --git a/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go b/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go index 3f296682ed1..933b8a21015 100644 --- a/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go @@ -152,6 +152,7 @@ func autoConvert_v1beta1_MutatingWebhook_To_admissionregistration_MutatingWebhoo out.SideEffects = (*admissionregistration.SideEffectClass)(unsafe.Pointer(in.SideEffects)) out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) + out.ReinvocationPolicy = (*admissionregistration.ReinvocationPolicyType)(unsafe.Pointer(in.ReinvocationPolicy)) return nil } @@ -172,6 +173,7 @@ func autoConvert_admissionregistration_MutatingWebhook_To_v1beta1_MutatingWebhoo out.SideEffects = (*v1beta1.SideEffectClass)(unsafe.Pointer(in.SideEffects)) out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) + out.ReinvocationPolicy = (*v1beta1.ReinvocationPolicyType)(unsafe.Pointer(in.ReinvocationPolicy)) return nil } diff --git a/pkg/apis/admissionregistration/zz_generated.deepcopy.go b/pkg/apis/admissionregistration/zz_generated.deepcopy.go index f6598b94846..aaf5f897f39 100644 --- a/pkg/apis/admissionregistration/zz_generated.deepcopy.go +++ b/pkg/apis/admissionregistration/zz_generated.deepcopy.go @@ -66,6 +66,11 @@ func (in *MutatingWebhook) DeepCopyInto(out *MutatingWebhook) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.ReinvocationPolicy != nil { + in, out := &in.ReinvocationPolicy, &out.ReinvocationPolicy + *out = new(ReinvocationPolicyType) + **out = **in + } return } diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go index 5057c38c4f2..43be1141c62 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go @@ -206,6 +206,12 @@ func (m *MutatingWebhook) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintGenerated(dAtA, i, uint64(len(*m.MatchPolicy))) i += copy(dAtA[i:], *m.MatchPolicy) } + if m.ReinvocationPolicy != nil { + dAtA[i] = 0x52 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.ReinvocationPolicy))) + i += copy(dAtA[i:], *m.ReinvocationPolicy) + } return i, nil } @@ -685,6 +691,10 @@ func (m *MutatingWebhook) Size() (n int) { l = len(*m.MatchPolicy) n += 1 + l + sovGenerated(uint64(l)) } + if m.ReinvocationPolicy != nil { + l = len(*m.ReinvocationPolicy) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -889,6 +899,7 @@ func (this *MutatingWebhook) String() string { `TimeoutSeconds:` + valueToStringGenerated(this.TimeoutSeconds) + `,`, `AdmissionReviewVersions:` + fmt.Sprintf("%v", this.AdmissionReviewVersions) + `,`, `MatchPolicy:` + valueToStringGenerated(this.MatchPolicy) + `,`, + `ReinvocationPolicy:` + valueToStringGenerated(this.ReinvocationPolicy) + `,`, `}`, }, "") return s @@ -1303,6 +1314,36 @@ func (m *MutatingWebhook) Unmarshal(dAtA []byte) error { s := MatchPolicyType(dAtA[iNdEx:postIndex]) m.MatchPolicy = &s iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReinvocationPolicy", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := ReinvocationPolicyType(dAtA[iNdEx:postIndex]) + m.ReinvocationPolicy = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -2768,71 +2809,73 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 1056 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x55, 0x4d, 0x6f, 0xe3, 0x44, - 0x18, 0xae, 0x9b, 0x84, 0x26, 0x93, 0x76, 0xbb, 0x1d, 0x3e, 0x36, 0x94, 0x55, 0x1c, 0xe5, 0x80, - 0x22, 0xc1, 0xda, 0xb4, 0x20, 0x04, 0x0b, 0x08, 0xd5, 0x85, 0x85, 0x4a, 0xed, 0x6e, 0x99, 0xee, - 0x87, 0xc4, 0x87, 0xc4, 0xc4, 0x99, 0x24, 0x43, 0x62, 0x8f, 0xe5, 0x19, 0xa7, 0xf4, 0xc6, 0x4f, - 0xe0, 0x2f, 0x70, 0xe2, 0x57, 0x70, 0xe0, 0xd6, 0xe3, 0x1e, 0xf7, 0x82, 0x45, 0xcd, 0x99, 0x03, - 0x17, 0x84, 0x7a, 0x42, 0x33, 0x9e, 0xd8, 0xf9, 0x68, 0x97, 0x00, 0xd2, 0x5e, 0x76, 0x6f, 0x7e, - 0x9f, 0x77, 0xde, 0x8f, 0x67, 0xe6, 0xf1, 0xfb, 0x82, 0xcf, 0x06, 0xef, 0x70, 0x8b, 0x32, 0x7b, - 0x10, 0xb5, 0x49, 0xe8, 0x13, 0x41, 0xb8, 0x3d, 0x22, 0x7e, 0x87, 0x85, 0xb6, 0x76, 0xe0, 0x80, - 0xda, 0xb8, 0xe3, 0x51, 0xce, 0x29, 0xf3, 0x43, 0xd2, 0xa3, 0x5c, 0x84, 0x58, 0x50, 0xe6, 0xdb, - 0xa3, 0xad, 0x36, 0x11, 0x78, 0xcb, 0xee, 0x11, 0x9f, 0x84, 0x58, 0x90, 0x8e, 0x15, 0x84, 0x4c, - 0x30, 0xd8, 0x4a, 0x23, 0x2d, 0x1c, 0x50, 0xeb, 0xc2, 0x48, 0x4b, 0x47, 0x6e, 0xde, 0xe8, 0x51, - 0xd1, 0x8f, 0xda, 0x96, 0xcb, 0x3c, 0xbb, 0xc7, 0x7a, 0xcc, 0x56, 0x09, 0xda, 0x51, 0x57, 0x59, - 0xca, 0x50, 0x5f, 0x69, 0xe2, 0xcd, 0xb7, 0xf2, 0x96, 0x3c, 0xec, 0xf6, 0xa9, 0x4f, 0xc2, 0x13, - 0x3b, 0x18, 0xf4, 0x24, 0xc0, 0x6d, 0x8f, 0x08, 0x6c, 0x8f, 0xe6, 0xda, 0xd9, 0xb4, 0x2f, 0x8b, - 0x0a, 0x23, 0x5f, 0x50, 0x8f, 0xcc, 0x05, 0xbc, 0xfd, 0x4f, 0x01, 0xdc, 0xed, 0x13, 0x0f, 0xcf, - 0xc6, 0x35, 0xff, 0x2c, 0x81, 0xf5, 0x83, 0x48, 0x60, 0x41, 0xfd, 0xde, 0x03, 0xd2, 0xee, 0x33, - 0x36, 0x80, 0x0d, 0x50, 0xf4, 0xb1, 0x47, 0x6a, 0x46, 0xc3, 0x68, 0x55, 0x9c, 0xd5, 0xd3, 0xd8, - 0x5c, 0x4a, 0x62, 0xb3, 0x78, 0x1b, 0x7b, 0x04, 0x29, 0x0f, 0x3c, 0x06, 0xab, 0xee, 0x90, 0x12, - 0x5f, 0xec, 0x32, 0xbf, 0x4b, 0x7b, 0xb5, 0xe5, 0x86, 0xd1, 0xaa, 0x6e, 0x7f, 0x60, 0x2d, 0x7a, - 0x89, 0x96, 0x2e, 0xb5, 0x3b, 0x91, 0xc4, 0x79, 0x41, 0x17, 0x5a, 0x9d, 0x44, 0xd1, 0x54, 0x21, - 0x88, 0x41, 0x29, 0x8c, 0x86, 0x84, 0xd7, 0x0a, 0x8d, 0x42, 0xab, 0xba, 0xfd, 0xfe, 0xe2, 0x15, - 0x51, 0x34, 0x24, 0x0f, 0xa8, 0xe8, 0xdf, 0x09, 0x48, 0xea, 0xe1, 0xce, 0x9a, 0x2e, 0x58, 0x92, - 0x3e, 0x8e, 0xd2, 0xcc, 0x70, 0x1f, 0xac, 0x75, 0x31, 0x1d, 0x46, 0x21, 0x39, 0x64, 0x43, 0xea, - 0x9e, 0xd4, 0x8a, 0xea, 0x1a, 0x5e, 0x4d, 0x62, 0x73, 0xed, 0xd6, 0xa4, 0xe3, 0x3c, 0x36, 0x37, - 0xa6, 0x80, 0xbb, 0x27, 0x01, 0x41, 0xd3, 0xc1, 0xf0, 0x5b, 0xb0, 0x21, 0x6f, 0x8c, 0x07, 0xd8, - 0x25, 0x47, 0x64, 0x48, 0x5c, 0xc1, 0xc2, 0x5a, 0x49, 0x5d, 0xd7, 0x9b, 0x13, 0xcd, 0x67, 0x6f, - 0x66, 0x05, 0x83, 0x9e, 0x04, 0xb8, 0x25, 0xa5, 0x61, 0x8d, 0xb6, 0xac, 0x7d, 0xdc, 0x26, 0xc3, - 0x71, 0xa8, 0xf3, 0x62, 0x12, 0x9b, 0x1b, 0xb7, 0x67, 0x33, 0xa2, 0xf9, 0x22, 0xf0, 0x23, 0x50, - 0xe5, 0xb4, 0x43, 0x3e, 0xee, 0x76, 0x89, 0x2b, 0x78, 0xed, 0x39, 0xc5, 0xa2, 0x99, 0xc4, 0x66, - 0xf5, 0x28, 0x87, 0xcf, 0x63, 0x73, 0x3d, 0x37, 0x77, 0x87, 0x98, 0x73, 0x34, 0x19, 0x06, 0x6f, - 0x82, 0x2b, 0x52, 0x3e, 0x2c, 0x12, 0x47, 0xc4, 0x65, 0x7e, 0x87, 0xd7, 0x56, 0x1a, 0x46, 0xab, - 0xe4, 0xc0, 0x24, 0x36, 0xaf, 0xdc, 0x9d, 0xf2, 0xa0, 0x99, 0x93, 0xf0, 0x1e, 0xb8, 0x96, 0xbd, - 0x09, 0x22, 0x23, 0x4a, 0x8e, 0xef, 0x93, 0x50, 0x1a, 0xbc, 0x56, 0x6e, 0x14, 0x5a, 0x15, 0xe7, - 0x95, 0x24, 0x36, 0xaf, 0xed, 0x5c, 0x7c, 0x04, 0x5d, 0x16, 0x2b, 0x89, 0x79, 0x58, 0xb8, 0x7d, - 0xfd, 0x3c, 0x95, 0x9c, 0xd8, 0x41, 0x0e, 0x4b, 0x62, 0x13, 0xa6, 0x7a, 0x9a, 0xc9, 0xb0, 0xe6, - 0x2f, 0x06, 0xb8, 0x3e, 0x23, 0xfc, 0x54, 0x63, 0x51, 0x2a, 0x0f, 0xf8, 0x35, 0x28, 0xcb, 0x27, - 0xe8, 0x60, 0x81, 0xd5, 0x9f, 0x50, 0xdd, 0x7e, 0x63, 0xb1, 0x07, 0xbb, 0xd3, 0xfe, 0x86, 0xb8, - 0xe2, 0x80, 0x08, 0xec, 0x40, 0xad, 0x30, 0x90, 0x63, 0x28, 0xcb, 0x0a, 0xbf, 0x00, 0x65, 0x5d, - 0x99, 0xd7, 0x96, 0x95, 0x9e, 0xdf, 0x5d, 0x5c, 0xcf, 0x33, 0xbd, 0x3b, 0x45, 0x59, 0x0a, 0x95, - 0x8f, 0x75, 0xc2, 0xe6, 0xef, 0x06, 0x68, 0x3c, 0x8e, 0xdf, 0x3e, 0xe5, 0x02, 0x7e, 0x39, 0xc7, - 0xd1, 0x5a, 0x50, 0x94, 0x94, 0xa7, 0x0c, 0xaf, 0x6a, 0x86, 0xe5, 0x31, 0x32, 0xc1, 0x6f, 0x00, - 0x4a, 0x54, 0x10, 0x6f, 0x4c, 0xee, 0xd6, 0x7f, 0x26, 0x37, 0xd5, 0x78, 0xfe, 0xdb, 0xee, 0xc9, - 0xe4, 0x28, 0xad, 0xd1, 0xfc, 0xd9, 0x00, 0x45, 0xf9, 0x1f, 0xc3, 0xd7, 0x40, 0x05, 0x07, 0xf4, - 0x93, 0x90, 0x45, 0x01, 0xaf, 0x19, 0x4a, 0x67, 0x6b, 0x49, 0x6c, 0x56, 0x76, 0x0e, 0xf7, 0x52, - 0x10, 0xe5, 0x7e, 0xb8, 0x05, 0xaa, 0x38, 0xa0, 0x99, 0x2c, 0x97, 0xd5, 0xf1, 0x75, 0xa9, 0xa5, - 0x9d, 0xc3, 0xbd, 0x4c, 0x8a, 0x93, 0x67, 0x64, 0xfe, 0x90, 0x70, 0x16, 0x85, 0xae, 0x1e, 0x43, - 0x3a, 0x3f, 0x1a, 0x83, 0x28, 0xf7, 0xc3, 0xd7, 0x41, 0x89, 0xbb, 0x2c, 0x20, 0x7a, 0x88, 0xbc, - 0x24, 0xdb, 0x3e, 0x92, 0xc0, 0x79, 0x6c, 0x56, 0xd4, 0x87, 0x52, 0x66, 0x7a, 0xa8, 0xf9, 0xa3, - 0x01, 0xe0, 0xfc, 0x9c, 0x82, 0x1f, 0x02, 0xc0, 0x32, 0x4b, 0x53, 0x32, 0x95, 0xaa, 0x32, 0xf4, - 0x3c, 0x36, 0xd7, 0x32, 0x4b, 0xa5, 0x9c, 0x08, 0x81, 0x87, 0xa0, 0x28, 0x67, 0x9b, 0x1e, 0xd3, - 0xd6, 0xbf, 0x1b, 0x9a, 0xf9, 0x02, 0x90, 0x16, 0x52, 0x99, 0x9a, 0x3f, 0x18, 0xe0, 0xea, 0x11, - 0x09, 0x47, 0xd4, 0x25, 0x88, 0x74, 0x49, 0x48, 0x7c, 0x97, 0x40, 0x1b, 0x54, 0xb2, 0x31, 0xa4, - 0x97, 0xc7, 0x86, 0x8e, 0xad, 0x64, 0x23, 0x0b, 0xe5, 0x67, 0xb2, 0x45, 0xb3, 0x7c, 0xe9, 0xa2, - 0xb9, 0x0e, 0x8a, 0x01, 0x16, 0xfd, 0x5a, 0x41, 0x9d, 0x28, 0x4b, 0xef, 0x21, 0x16, 0x7d, 0xa4, - 0x50, 0xe5, 0x65, 0xa1, 0x50, 0x97, 0x5b, 0xd2, 0x5e, 0x16, 0x0a, 0xa4, 0xd0, 0xe6, 0x5f, 0x25, - 0xb0, 0x71, 0x1f, 0x0f, 0x69, 0xe7, 0xd9, 0x72, 0x7b, 0xb6, 0xdc, 0x9e, 0xae, 0xe5, 0x76, 0x66, - 0x80, 0xfa, 0x9c, 0xf4, 0x9f, 0xf4, 0x7a, 0xfb, 0x6a, 0x6e, 0xbd, 0xbd, 0xb7, 0xb8, 0xa2, 0xe7, - 0xba, 0x9f, 0x5b, 0x70, 0x7f, 0x18, 0xa0, 0xf9, 0x78, 0x8e, 0x4f, 0x60, 0xc5, 0x79, 0xd3, 0x2b, - 0xee, 0xd3, 0xff, 0x41, 0x70, 0x91, 0x25, 0xf7, 0x93, 0x01, 0x9e, 0xbf, 0x60, 0xba, 0x40, 0x0c, - 0x56, 0x78, 0x3a, 0x8d, 0x35, 0xc7, 0x9b, 0x8b, 0x37, 0x32, 0x3b, 0xc6, 0x9d, 0x6a, 0x12, 0x9b, - 0x2b, 0x63, 0x74, 0x9c, 0x17, 0xb6, 0x40, 0xd9, 0xc5, 0x4e, 0xe4, 0x77, 0xf4, 0x1e, 0x59, 0x75, - 0x56, 0xe5, 0x9d, 0xec, 0xee, 0xa4, 0x18, 0xca, 0xbc, 0xf0, 0x65, 0x50, 0x88, 0xc2, 0xa1, 0x1e, - 0xd9, 0x2b, 0x49, 0x6c, 0x16, 0xee, 0xa1, 0x7d, 0x24, 0x31, 0xe7, 0xc6, 0xe9, 0x59, 0x7d, 0xe9, - 0xe1, 0x59, 0x7d, 0xe9, 0xd1, 0x59, 0x7d, 0xe9, 0xbb, 0xa4, 0x6e, 0x9c, 0x26, 0x75, 0xe3, 0x61, - 0x52, 0x37, 0x1e, 0x25, 0x75, 0xe3, 0xd7, 0xa4, 0x6e, 0x7c, 0xff, 0x5b, 0x7d, 0xe9, 0xf3, 0x15, - 0xdd, 0xda, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x03, 0xf8, 0x1e, 0xdf, 0xe8, 0x0d, 0x00, 0x00, + // 1088 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x55, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xcf, 0xc6, 0x76, 0x63, 0x8f, 0x93, 0xb6, 0x19, 0xa0, 0x35, 0xa5, 0xf2, 0x5a, 0x3e, 0x20, + 0x4b, 0xd0, 0xdd, 0x26, 0x20, 0x04, 0x05, 0x84, 0xb2, 0x81, 0x42, 0xa4, 0xa4, 0x0d, 0x93, 0xfe, + 0x91, 0xf8, 0x23, 0x75, 0xbc, 0x7e, 0xb6, 0x07, 0xdb, 0x3b, 0xab, 0x9d, 0x59, 0x87, 0xdc, 0xf8, + 0x08, 0x7c, 0x05, 0x4e, 0x7c, 0x0a, 0x0e, 0xdc, 0x72, 0xec, 0x31, 0x17, 0x56, 0x64, 0x39, 0x73, + 0xe0, 0x86, 0x72, 0x42, 0x3b, 0x3b, 0x5e, 0xff, 0x4b, 0x8a, 0x01, 0xa9, 0x17, 0x7a, 0xdb, 0xf7, + 0x7b, 0xf3, 0x7e, 0xef, 0xbd, 0x99, 0xdf, 0xbe, 0x87, 0x3e, 0xef, 0xbd, 0x2b, 0x2c, 0xc6, 0xed, + 0x5e, 0xd8, 0x84, 0xc0, 0x03, 0x09, 0xc2, 0x1e, 0x82, 0xd7, 0xe2, 0x81, 0xad, 0x1d, 0xd4, 0x67, + 0x36, 0x6d, 0x0d, 0x98, 0x10, 0x8c, 0x7b, 0x01, 0x74, 0x98, 0x90, 0x01, 0x95, 0x8c, 0x7b, 0xf6, + 0x70, 0xa3, 0x09, 0x92, 0x6e, 0xd8, 0x1d, 0xf0, 0x20, 0xa0, 0x12, 0x5a, 0x96, 0x1f, 0x70, 0xc9, + 0x71, 0x23, 0x8d, 0xb4, 0xa8, 0xcf, 0xac, 0x73, 0x23, 0x2d, 0x1d, 0x79, 0xe3, 0x56, 0x87, 0xc9, + 0x6e, 0xd8, 0xb4, 0x5c, 0x3e, 0xb0, 0x3b, 0xbc, 0xc3, 0x6d, 0x45, 0xd0, 0x0c, 0xdb, 0xca, 0x52, + 0x86, 0xfa, 0x4a, 0x89, 0x6f, 0xbc, 0x3d, 0x2e, 0x69, 0x40, 0xdd, 0x2e, 0xf3, 0x20, 0x38, 0xb2, + 0xfd, 0x5e, 0x27, 0x01, 0x84, 0x3d, 0x00, 0x49, 0xed, 0xe1, 0x5c, 0x39, 0x37, 0xec, 0x8b, 0xa2, + 0x82, 0xd0, 0x93, 0x6c, 0x00, 0x73, 0x01, 0xef, 0xfc, 0x5d, 0x80, 0x70, 0xbb, 0x30, 0xa0, 0xb3, + 0x71, 0xf5, 0x93, 0x4b, 0xe8, 0xca, 0x5e, 0x28, 0xa9, 0x64, 0x5e, 0xe7, 0x31, 0x34, 0xbb, 0x9c, + 0xf7, 0x70, 0x0d, 0xe5, 0x3d, 0x3a, 0x80, 0x8a, 0x51, 0x33, 0x1a, 0x25, 0x67, 0xf5, 0x38, 0x32, + 0x97, 0xe2, 0xc8, 0xcc, 0xdf, 0xa3, 0x03, 0x20, 0xca, 0x83, 0x0f, 0xd1, 0xaa, 0xdb, 0x67, 0xe0, + 0xc9, 0x6d, 0xee, 0xb5, 0x59, 0xa7, 0xb2, 0x5c, 0x33, 0x1a, 0xe5, 0xcd, 0x0f, 0xad, 0x45, 0x2f, + 0xd1, 0xd2, 0xa9, 0xb6, 0x27, 0x48, 0x9c, 0x97, 0x75, 0xa2, 0xd5, 0x49, 0x94, 0x4c, 0x25, 0xc2, + 0x14, 0x15, 0x82, 0xb0, 0x0f, 0xa2, 0x92, 0xab, 0xe5, 0x1a, 0xe5, 0xcd, 0x0f, 0x16, 0xcf, 0x48, + 0xc2, 0x3e, 0x3c, 0x66, 0xb2, 0x7b, 0xdf, 0x87, 0xd4, 0x23, 0x9c, 0x35, 0x9d, 0xb0, 0x90, 0xf8, + 0x04, 0x49, 0x99, 0xf1, 0x2e, 0x5a, 0x6b, 0x53, 0xd6, 0x0f, 0x03, 0xd8, 0xe7, 0x7d, 0xe6, 0x1e, + 0x55, 0xf2, 0xea, 0x1a, 0x5e, 0x8f, 0x23, 0x73, 0xed, 0xee, 0xa4, 0xe3, 0x2c, 0x32, 0xd7, 0xa7, + 0x80, 0x07, 0x47, 0x3e, 0x90, 0xe9, 0x60, 0xfc, 0x2d, 0x5a, 0x4f, 0x6e, 0x4c, 0xf8, 0xd4, 0x85, + 0x03, 0xe8, 0x83, 0x2b, 0x79, 0x50, 0x29, 0xa8, 0xeb, 0x7a, 0x6b, 0xa2, 0xf8, 0xec, 0xcd, 0x2c, + 0xbf, 0xd7, 0x49, 0x00, 0x61, 0x25, 0xd2, 0xb0, 0x86, 0x1b, 0xd6, 0x2e, 0x6d, 0x42, 0x7f, 0x14, + 0xea, 0xbc, 0x12, 0x47, 0xe6, 0xfa, 0xbd, 0x59, 0x46, 0x32, 0x9f, 0x04, 0x7f, 0x8c, 0xca, 0x82, + 0xb5, 0xe0, 0x93, 0x76, 0x1b, 0x5c, 0x29, 0x2a, 0x97, 0x54, 0x17, 0xf5, 0x38, 0x32, 0xcb, 0x07, + 0x63, 0xf8, 0x2c, 0x32, 0xaf, 0x8c, 0xcd, 0xed, 0x3e, 0x15, 0x82, 0x4c, 0x86, 0xe1, 0x3b, 0xe8, + 0x72, 0x22, 0x1f, 0x1e, 0xca, 0x03, 0x70, 0xb9, 0xd7, 0x12, 0x95, 0x95, 0x9a, 0xd1, 0x28, 0x38, + 0x38, 0x8e, 0xcc, 0xcb, 0x0f, 0xa6, 0x3c, 0x64, 0xe6, 0x24, 0x7e, 0x88, 0xae, 0x67, 0x6f, 0x42, + 0x60, 0xc8, 0xe0, 0xf0, 0x11, 0x04, 0x89, 0x21, 0x2a, 0xc5, 0x5a, 0xae, 0x51, 0x72, 0x5e, 0x8b, + 0x23, 0xf3, 0xfa, 0xd6, 0xf9, 0x47, 0xc8, 0x45, 0xb1, 0x49, 0x63, 0x03, 0x2a, 0xdd, 0xae, 0x7e, + 0x9e, 0xd2, 0xb8, 0xb1, 0xbd, 0x31, 0x9c, 0x34, 0x36, 0x61, 0xaa, 0xa7, 0x99, 0x0c, 0xc3, 0x4f, + 0x10, 0x0e, 0x80, 0x79, 0x43, 0xee, 0x2a, 0x35, 0x68, 0x32, 0xa4, 0xc8, 0x6e, 0xc7, 0x91, 0x89, + 0xc9, 0x9c, 0xf7, 0x2c, 0x32, 0xaf, 0xcd, 0xa3, 0x8a, 0xfa, 0x1c, 0xae, 0xfa, 0x2f, 0x06, 0xba, + 0x39, 0xf3, 0x6b, 0xa5, 0x2a, 0x0e, 0x53, 0x01, 0xe2, 0x27, 0xa8, 0x98, 0x3c, 0x72, 0x8b, 0x4a, + 0xaa, 0xfe, 0xb5, 0xf2, 0xe6, 0xed, 0xc5, 0x24, 0x71, 0xbf, 0xf9, 0x0d, 0xb8, 0x72, 0x0f, 0x24, + 0x75, 0xb0, 0xd6, 0x30, 0x1a, 0x63, 0x24, 0x63, 0xc5, 0x5f, 0xa2, 0xa2, 0xce, 0x2c, 0x2a, 0xcb, + 0xea, 0x8f, 0x79, 0x6f, 0xf1, 0x3f, 0x66, 0xa6, 0x76, 0x27, 0x9f, 0xa4, 0x22, 0xc5, 0x43, 0x4d, + 0x58, 0xff, 0xdd, 0x40, 0xb5, 0x67, 0xf5, 0xb7, 0xcb, 0x84, 0xc4, 0x5f, 0xcd, 0xf5, 0x68, 0x2d, + 0x28, 0x7b, 0x26, 0xd2, 0x0e, 0xaf, 0xea, 0x0e, 0x8b, 0x23, 0x64, 0xa2, 0xbf, 0x1e, 0x2a, 0x30, + 0x09, 0x83, 0x51, 0x73, 0x77, 0xff, 0x75, 0x73, 0x53, 0x85, 0x8f, 0x07, 0xc3, 0x4e, 0x42, 0x4e, + 0xd2, 0x1c, 0xf5, 0x9f, 0x0d, 0x94, 0x4f, 0x26, 0x05, 0x7e, 0x03, 0x95, 0xa8, 0xcf, 0x3e, 0x0d, + 0x78, 0xe8, 0x8b, 0x8a, 0xa1, 0x94, 0xbc, 0x16, 0x47, 0x66, 0x69, 0x6b, 0x7f, 0x27, 0x05, 0xc9, + 0xd8, 0x8f, 0x37, 0x50, 0x99, 0xfa, 0x2c, 0x13, 0xfe, 0xb2, 0x3a, 0x7e, 0x25, 0x51, 0xeb, 0xd6, + 0xfe, 0x4e, 0x26, 0xf6, 0xc9, 0x33, 0x09, 0x7f, 0x00, 0x82, 0x87, 0x81, 0xab, 0x07, 0x9d, 0xe6, + 0x27, 0x23, 0x90, 0x8c, 0xfd, 0xf8, 0x4d, 0x54, 0x10, 0x2e, 0xf7, 0x41, 0x8f, 0xa9, 0x6b, 0x49, + 0xd9, 0x07, 0x09, 0x70, 0x16, 0x99, 0x25, 0xf5, 0xa1, 0x04, 0x9a, 0x1e, 0xaa, 0xff, 0x68, 0x20, + 0x3c, 0x3f, 0x09, 0xf1, 0x47, 0x08, 0xf1, 0xcc, 0xd2, 0x2d, 0x99, 0x4a, 0x55, 0x19, 0x7a, 0x16, + 0x99, 0x6b, 0x99, 0xa5, 0x28, 0x27, 0x42, 0xf0, 0x3e, 0xca, 0x27, 0xd3, 0x53, 0x2f, 0x02, 0xeb, + 0x9f, 0x8d, 0xe5, 0xf1, 0x8a, 0x49, 0x2c, 0xa2, 0x98, 0xea, 0x3f, 0x18, 0xe8, 0xea, 0x01, 0x04, + 0x43, 0xe6, 0x02, 0x81, 0x36, 0x04, 0xe0, 0xb9, 0x80, 0x6d, 0x54, 0xca, 0x06, 0x9d, 0x5e, 0x4f, + 0xeb, 0x3a, 0xb6, 0x94, 0x0d, 0x45, 0x32, 0x3e, 0x93, 0xad, 0xb2, 0xe5, 0x0b, 0x57, 0xd9, 0x4d, + 0x94, 0xf7, 0xa9, 0xec, 0x56, 0x72, 0xea, 0x44, 0x31, 0xf1, 0xee, 0x53, 0xd9, 0x25, 0x0a, 0x55, + 0x5e, 0x1e, 0x48, 0x75, 0xb9, 0x05, 0xed, 0xe5, 0x81, 0x24, 0x0a, 0xad, 0xff, 0x59, 0x40, 0xeb, + 0x8f, 0x68, 0x9f, 0xb5, 0x5e, 0xac, 0xcf, 0x17, 0xeb, 0xf3, 0x7f, 0xb5, 0x3e, 0xeb, 0xa7, 0x06, + 0xaa, 0xce, 0x49, 0xff, 0x79, 0xaf, 0xb7, 0xaf, 0xe7, 0xd6, 0xdb, 0xfb, 0x8b, 0x2b, 0x7a, 0xae, + 0xfa, 0xb9, 0x05, 0xf7, 0x87, 0x81, 0xea, 0xcf, 0xee, 0xf1, 0x39, 0xac, 0xb8, 0xc1, 0xf4, 0x8a, + 0xfb, 0xec, 0x3f, 0x34, 0xb8, 0xc8, 0x92, 0xfb, 0xc9, 0x40, 0x2f, 0x9d, 0x33, 0x5d, 0x30, 0x45, + 0x2b, 0x22, 0x9d, 0xc6, 0xba, 0xc7, 0x3b, 0x8b, 0x17, 0x32, 0x3b, 0xc6, 0x9d, 0x72, 0x1c, 0x99, + 0x2b, 0x23, 0x74, 0xc4, 0x8b, 0x1b, 0xa8, 0xe8, 0x52, 0x27, 0xf4, 0x5a, 0x7a, 0x8f, 0xac, 0x3a, + 0xab, 0xc9, 0x9d, 0x6c, 0x6f, 0xa5, 0x18, 0xc9, 0xbc, 0xf8, 0x55, 0x94, 0x0b, 0x83, 0xbe, 0x1e, + 0xd9, 0x2b, 0x71, 0x64, 0xe6, 0x1e, 0x92, 0x5d, 0x92, 0x60, 0xce, 0xad, 0xe3, 0xd3, 0xea, 0xd2, + 0xd3, 0xd3, 0xea, 0xd2, 0xc9, 0x69, 0x75, 0xe9, 0xbb, 0xb8, 0x6a, 0x1c, 0xc7, 0x55, 0xe3, 0x69, + 0x5c, 0x35, 0x4e, 0xe2, 0xaa, 0xf1, 0x6b, 0x5c, 0x35, 0xbe, 0xff, 0xad, 0xba, 0xf4, 0xc5, 0x8a, + 0x2e, 0xed, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa7, 0x5c, 0x48, 0x5c, 0x4a, 0x0e, 0x00, 0x00, } diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto index 8e8ee02c1df..377e311273f 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto @@ -144,6 +144,24 @@ message MutatingWebhook { // Default to `['v1beta1']`. // +optional repeated string admissionReviewVersions = 8; + + // reinvocationPolicy indicates whether this webhook should be called multiple times as part of a single admission evaluation. + // Allowed values are "Never" and "IfNeeded". + // + // Never: the webhook will not be called more than once in a single admission evaluation. + // + // IfNeeded: the webhook will be called at least one additional time as part of the admission evaluation + // if the object being admitted is modified by other admission plugins after the initial webhook call. + // Webhooks that specify this option *must* be idempotent, able to process objects they previously admitted. + // Note: + // * the number of additional invocations is not guaranteed to be exactly one. + // * if additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again. + // * webhooks that use this option may be reordered to minimize the number of additional invocations. + // * to validate an object after all mutations are guaranteed complete, use a validating admission webhook instead. + // + // Defaults to "Never". + // +optional + optional string reinvocationPolicy = 10; } // MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object. diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go index e580d834c22..6fb5a636b00 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go @@ -38,6 +38,7 @@ var map_MutatingWebhook = map[string]string{ "sideEffects": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", "timeoutSeconds": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", "admissionReviewVersions": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy. Default to `['v1beta1']`.", + "reinvocationPolicy": "reinvocationPolicy indicates whether this webhook should be called multiple times as part of a single admission evaluation. Allowed values are \"Never\" and \"IfNeeded\".\n\nNever: the webhook will not be called more than once in a single admission evaluation.\n\nIfNeeded: the webhook will be called at least one additional time as part of the admission evaluation if the object being admitted is modified by other admission plugins after the initial webhook call. Webhooks that specify this option *must* be idempotent, able to process objects they previously admitted. Note: * the number of additional invocations is not guaranteed to be exactly one. * if additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again. * webhooks that use this option may be reordered to minimize the number of additional invocations. * to validate an object after all mutations are guaranteed complete, use a validating admission webhook instead.\n\nDefaults to \"Never\".", } func (MutatingWebhook) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go index 05bfa5c256e..746ee492415 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go @@ -66,6 +66,11 @@ func (in *MutatingWebhook) DeepCopyInto(out *MutatingWebhook) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.ReinvocationPolicy != nil { + in, out := &in.ReinvocationPolicy, &out.ReinvocationPolicy + *out = new(ReinvocationPolicyType) + **out = **in + } return } From 95fa928ecb636e8d16af31ab613678c555fc76a3 Mon Sep 17 00:00:00 2001 From: Joe Betz Date: Wed, 29 May 2019 22:31:26 -0700 Subject: [PATCH 4/8] Add mutating admission webhook reinvocation --- .../admissionregistration/fuzzer/fuzzer.go | 2 + pkg/apis/admissionregistration/types.go | 31 ++ .../admissionregistration/v1beta1/defaults.go | 4 + .../validation/validation.go | 8 + .../admissionregistration/v1beta1/types.go | 31 ++ .../src/k8s.io/apiserver/pkg/admission/BUILD | 1 + .../apiserver/pkg/admission/attributes.go | 65 ++- .../configuration/mutating_webhook_manager.go | 8 +- .../validating_webhook_manager.go | 8 +- .../apiserver/pkg/admission/interfaces.go | 19 + .../pkg/admission/plugin/webhook/accessors.go | 23 +- .../plugin/webhook/generic/webhook_test.go | 5 +- .../admission/plugin/webhook/mutating/BUILD | 3 + .../plugin/webhook/mutating/dispatcher.go | 71 +++- .../plugin/webhook/mutating/plugin_test.go | 126 +++--- .../webhook/mutating/reinvocationcontext.go | 68 +++ .../plugin/webhook/namespace/matcher_test.go | 2 +- .../plugin/webhook/testing/testcase.go | 110 +++-- .../plugin/webhook/testing/webhook_server.go | 7 + .../k8s.io/apiserver/pkg/admission/plugins.go | 2 +- .../apiserver/pkg/admission/reinvocation.go | 62 +++ .../apiserver/admissionwebhook/BUILD | 2 + .../admissionwebhook/reinvocation_test.go | 389 ++++++++++++++++++ 23 files changed, 925 insertions(+), 122 deletions(-) create mode 100644 staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/reinvocationcontext.go create mode 100644 staging/src/k8s.io/apiserver/pkg/admission/reinvocation.go create mode 100644 test/integration/apiserver/admissionwebhook/reinvocation_test.go diff --git a/pkg/apis/admissionregistration/fuzzer/fuzzer.go b/pkg/apis/admissionregistration/fuzzer/fuzzer.go index 6e84fa54502..86db2f44cab 100644 --- a/pkg/apis/admissionregistration/fuzzer/fuzzer.go +++ b/pkg/apis/admissionregistration/fuzzer/fuzzer.go @@ -55,6 +55,8 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { obj.MatchPolicy = &m s := admissionregistration.SideEffectClassUnknown obj.SideEffects = &s + n := admissionregistration.NeverReinvocationPolicy + obj.ReinvocationPolicy = &n if obj.TimeoutSeconds == nil { i := int32(30) obj.TimeoutSeconds = &i diff --git a/pkg/apis/admissionregistration/types.go b/pkg/apis/admissionregistration/types.go index cb1624b59ad..198f91acb71 100644 --- a/pkg/apis/admissionregistration/types.go +++ b/pkg/apis/admissionregistration/types.go @@ -383,8 +383,39 @@ type MutatingWebhook struct { // does not understand, calls to the webhook will fail and be subject to the failure policy. // +optional AdmissionReviewVersions []string + + // reinvocationPolicy indicates whether this webhook should be called multiple times as part of a single admission evaluation. + // Allowed values are "Never" and "IfNeeded". + // + // Never: the webhook will not be called more than once in a single admission evaluation. + // + // IfNeeded: the webhook will be called at least one additional time as part of the admission evaluation + // if the object being admitted is modified by other admission plugins after the initial webhook call. + // Webhooks that specify this option *must* be idempotent, and hence able to process objects they previously admitted. + // Note: + // * the number of additional invocations is not guaranteed to be exactly one. + // * if additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again. + // * webhooks that use this option may be reordered to minimize the number of additional invocations. + // * to validate an object after all mutations are guaranteed complete, use a validating admission webhook instead. + // + // Defaults to "Never". + // +optional + ReinvocationPolicy *ReinvocationPolicyType } +// ReinvocationPolicyType specifies what type of policy the admission hook uses. +type ReinvocationPolicyType string + +var ( + // NeverReinvocationPolicy indicates that the webhook must not be called more than once in a + // single admission evaluation. + NeverReinvocationPolicy ReinvocationPolicyType = "Never" + // IfNeededReinvocationPolicy indicates that the webhook may be called at least one + // additional time as part of the admission evaluation if the object being admitted is + // modified by other admission plugins after the initial webhook call. + IfNeededReinvocationPolicy ReinvocationPolicyType = "IfNeeded" +) + // RuleWithOperations is a tuple of Operations and Resources. It is recommended to make // sure that all the tuple expansions are valid. type RuleWithOperations struct { diff --git a/pkg/apis/admissionregistration/v1beta1/defaults.go b/pkg/apis/admissionregistration/v1beta1/defaults.go index b529dfd00bb..63637e7c932 100644 --- a/pkg/apis/admissionregistration/v1beta1/defaults.go +++ b/pkg/apis/admissionregistration/v1beta1/defaults.go @@ -77,6 +77,10 @@ func SetDefaults_MutatingWebhook(obj *admissionregistrationv1beta1.MutatingWebho obj.TimeoutSeconds = new(int32) *obj.TimeoutSeconds = 30 } + if obj.ReinvocationPolicy == nil { + never := admissionregistrationv1beta1.NeverReinvocationPolicy + obj.ReinvocationPolicy = &never + } if len(obj.AdmissionReviewVersions) == 0 { obj.AdmissionReviewVersions = []string{admissionregistrationv1beta1.SchemeGroupVersion.Version} diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go index 8fc4efe3544..4f2b7e457a9 100644 --- a/pkg/apis/admissionregistration/validation/validation.go +++ b/pkg/apis/admissionregistration/validation/validation.go @@ -281,6 +281,9 @@ func validateMutatingWebhook(hook *admissionregistration.MutatingWebhook, fldPat if hook.NamespaceSelector != nil { allErrors = append(allErrors, metav1validation.ValidateLabelSelector(hook.NamespaceSelector, fldPath.Child("namespaceSelector"))...) } + if hook.ReinvocationPolicy != nil && !supportedReinvocationPolicies.Has(string(*hook.ReinvocationPolicy)) { + allErrors = append(allErrors, field.NotSupported(fldPath.Child("reinvocationPolicy"), *hook.ReinvocationPolicy, supportedReinvocationPolicies.List())) + } cc := hook.ClientConfig switch { @@ -319,6 +322,11 @@ var supportedOperations = sets.NewString( string(admissionregistration.Connect), ) +var supportedReinvocationPolicies = sets.NewString( + string(admissionregistration.NeverReinvocationPolicy), + string(admissionregistration.IfNeededReinvocationPolicy), +) + func hasWildcardOperation(operations []admissionregistration.OperationType) bool { for _, o := range operations { if o == admissionregistration.OperationAll { diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go index 4e2d694d545..c18c9b20ec6 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go @@ -404,8 +404,39 @@ type MutatingWebhook struct { // Default to `['v1beta1']`. // +optional AdmissionReviewVersions []string `json:"admissionReviewVersions,omitempty" protobuf:"bytes,8,rep,name=admissionReviewVersions"` + + // reinvocationPolicy indicates whether this webhook should be called multiple times as part of a single admission evaluation. + // Allowed values are "Never" and "IfNeeded". + // + // Never: the webhook will not be called more than once in a single admission evaluation. + // + // IfNeeded: the webhook will be called at least one additional time as part of the admission evaluation + // if the object being admitted is modified by other admission plugins after the initial webhook call. + // Webhooks that specify this option *must* be idempotent, able to process objects they previously admitted. + // Note: + // * the number of additional invocations is not guaranteed to be exactly one. + // * if additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again. + // * webhooks that use this option may be reordered to minimize the number of additional invocations. + // * to validate an object after all mutations are guaranteed complete, use a validating admission webhook instead. + // + // Defaults to "Never". + // +optional + ReinvocationPolicy *ReinvocationPolicyType `json:"reinvocationPolicy,omitempty" protobuf:"bytes,10,opt,name=reinvocationPolicy,casttype=ReinvocationPolicyType"` } +// ReinvocationPolicyType specifies what type of policy the admission hook uses. +type ReinvocationPolicyType string + +const ( + // NeverReinvocationPolicy indicates that the webhook must not be called more than once in a + // single admission evaluation. + NeverReinvocationPolicy ReinvocationPolicyType = "Never" + // IfNeededReinvocationPolicy indicates that the webhook may be called at least one + // additional time as part of the admission evaluation if the object being admitted is + // modified by other admission plugins after the initial webhook call. + IfNeededReinvocationPolicy ReinvocationPolicyType = "IfNeeded" +) + // RuleWithOperations is a tuple of Operations and Resources. It is recommended to make // sure that all the tuple expansions are valid. type RuleWithOperations struct { diff --git a/staging/src/k8s.io/apiserver/pkg/admission/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/BUILD index f89ee0ec022..7da1eb84e00 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/BUILD @@ -42,6 +42,7 @@ go_library( "handler.go", "interfaces.go", "plugins.go", + "reinvocation.go", "util.go", ], importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/admission", diff --git a/staging/src/k8s.io/apiserver/pkg/admission/attributes.go b/staging/src/k8s.io/apiserver/pkg/admission/attributes.go index ad6ca6ba6fc..beea941fc30 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/attributes.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/attributes.go @@ -44,21 +44,24 @@ type attributesRecord struct { // But ValidatingAdmissionWebhook add annotations concurrently. annotations map[string]string annotationsLock sync.RWMutex + + reinvocationContext ReinvocationContext } func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind schema.GroupVersionKind, namespace, name string, resource schema.GroupVersionResource, subresource string, operation Operation, operationOptions runtime.Object, dryRun bool, userInfo user.Info) Attributes { return &attributesRecord{ - kind: kind, - namespace: namespace, - name: name, - resource: resource, - subresource: subresource, - operation: operation, - options: operationOptions, - dryRun: dryRun, - object: object, - oldObject: oldObject, - userInfo: userInfo, + kind: kind, + namespace: namespace, + name: name, + resource: resource, + subresource: subresource, + operation: operation, + options: operationOptions, + dryRun: dryRun, + object: object, + oldObject: oldObject, + userInfo: userInfo, + reinvocationContext: &reinvocationContext{}, } } @@ -140,6 +143,46 @@ func (record *attributesRecord) AddAnnotation(key, value string) error { return nil } +func (record *attributesRecord) GetReinvocationContext() ReinvocationContext { + return record.reinvocationContext +} + +type reinvocationContext struct { + // isReinvoke is true when admission plugins are being reinvoked + isReinvoke bool + // reinvokeRequested is true when an admission plugin requested a re-invocation of the chain + reinvokeRequested bool + // values stores reinvoke context values per plugin. + values map[string]interface{} +} + +func (rc *reinvocationContext) IsReinvoke() bool { + return rc.isReinvoke +} + +func (rc *reinvocationContext) SetIsReinvoke() { + rc.isReinvoke = true +} + +func (rc *reinvocationContext) ShouldReinvoke() bool { + return rc.reinvokeRequested +} + +func (rc *reinvocationContext) SetShouldReinvoke() { + rc.reinvokeRequested = true +} + +func (rc *reinvocationContext) SetValue(plugin string, v interface{}) { + if rc.values == nil { + rc.values = map[string]interface{}{} + } + rc.values[plugin] = v +} + +func (rc *reinvocationContext) Value(plugin string) interface{} { + return rc.values[plugin] +} + func checkKeyFormat(key string) error { parts := strings.Split(key, "/") if len(parts) != 2 { diff --git a/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go b/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go index bacf61722c3..8cff4a254bf 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/configuration/mutating_webhook_manager.go @@ -86,8 +86,14 @@ func mergeMutatingWebhookConfigurations(configurations []*v1beta1.MutatingWebhoo sort.SliceStable(configurations, MutatingWebhookConfigurationSorter(configurations).ByName) accessors := []webhook.WebhookAccessor{} for _, c := range configurations { + // webhook names are not validated for uniqueness, so we check for duplicates and + // add a int suffix to distinguish between them + names := map[string]int{} for i := range c.Webhooks { - accessors = append(accessors, webhook.NewMutatingWebhookAccessor(&c.Webhooks[i])) + n := c.Webhooks[i].Name + uid := fmt.Sprintf("%s/%s/%d", c.Name, n, names[n]) + names[n]++ + accessors = append(accessors, webhook.NewMutatingWebhookAccessor(uid, &c.Webhooks[i])) } } return accessors diff --git a/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go b/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go index bcce1e70f92..804d83fe678 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/configuration/validating_webhook_manager.go @@ -84,8 +84,14 @@ func mergeValidatingWebhookConfigurations(configurations []*v1beta1.ValidatingWe sort.SliceStable(configurations, ValidatingWebhookConfigurationSorter(configurations).ByName) accessors := []webhook.WebhookAccessor{} for _, c := range configurations { + // webhook names are not validated for uniqueness, so we check for duplicates and + // add a int suffix to distinguish between them + names := map[string]int{} for i := range c.Webhooks { - accessors = append(accessors, webhook.NewValidatingWebhookAccessor(&c.Webhooks[i])) + n := c.Webhooks[i].Name + uid := fmt.Sprintf("%s/%s/%d", c.Name, n, names[n]) + names[n]++ + accessors = append(accessors, webhook.NewValidatingWebhookAccessor(uid, &c.Webhooks[i])) } } return accessors diff --git a/staging/src/k8s.io/apiserver/pkg/admission/interfaces.go b/staging/src/k8s.io/apiserver/pkg/admission/interfaces.go index 040a6268b2f..5f6d703b216 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/interfaces.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/interfaces.go @@ -62,6 +62,9 @@ type Attributes interface { // An error is returned if the format of key is invalid. When trying to overwrite annotation with a new value, an error is returned. // Both ValidationInterface and MutationInterface are allowed to add Annotations. AddAnnotation(key, value string) error + + // GetReinvocationContext tracks the admission request information relevant to the re-invocation policy. + GetReinvocationContext() ReinvocationContext } // ObjectInterfaces is an interface used by AdmissionController to get object interfaces @@ -91,6 +94,22 @@ type AnnotationsGetter interface { GetAnnotations() map[string]string } +// ReinvocationContext provides access to the admission related state required to implement the re-invocation policy. +type ReinvocationContext interface { + // IsReinvoke returns true if the current admission check is a re-invocation. + IsReinvoke() bool + // SetIsReinvoke sets the current admission check as a re-invocation. + SetIsReinvoke() + // ShouldReinvoke returns true if any plugin has requested a re-invocation. + ShouldReinvoke() bool + // SetShouldReinvoke signals that a re-invocation is desired. + SetShouldReinvoke() + // AddValue set a value for a plugin name, possibly overriding a previous value. + SetValue(plugin string, v interface{}) + // Value reads a value for a webhook. + Value(plugin string) interface{} +} + // Interface is an abstract, pluggable interface for Admission Control decisions. type Interface interface { // Handles returns true if this admission controller can handle the given operation diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go index 362333c50f5..108e8ff442f 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go @@ -23,7 +23,12 @@ import ( // WebhookAccessor provides a common interface to both mutating and validating webhook types. type WebhookAccessor interface { - // GetName gets the webhook Name field. + // GetUID gets a string that uniquely identifies the webhook. + GetUID() string + + // GetName gets the webhook Name field. Note that the name is scoped to the webhook + // configuration and does not provide a globally unique identity, if a unique identity is + // needed, use GetUID. GetName() string // GetClientConfig gets the webhook ClientConfig field. GetClientConfig() v1beta1.WebhookClientConfig @@ -49,14 +54,18 @@ type WebhookAccessor interface { } // NewMutatingWebhookAccessor creates an accessor for a MutatingWebhook. -func NewMutatingWebhookAccessor(h *v1beta1.MutatingWebhook) WebhookAccessor { - return mutatingWebhookAccessor{h} +func NewMutatingWebhookAccessor(uid string, h *v1beta1.MutatingWebhook) WebhookAccessor { + return mutatingWebhookAccessor{uid: uid, MutatingWebhook: h} } type mutatingWebhookAccessor struct { *v1beta1.MutatingWebhook + uid string } +func (m mutatingWebhookAccessor) GetUID() string { + return m.Name +} func (m mutatingWebhookAccessor) GetName() string { return m.Name } @@ -94,14 +103,18 @@ func (m mutatingWebhookAccessor) GetValidatingWebhook() (*v1beta1.ValidatingWebh } // NewValidatingWebhookAccessor creates an accessor for a ValidatingWebhook. -func NewValidatingWebhookAccessor(h *v1beta1.ValidatingWebhook) WebhookAccessor { - return validatingWebhookAccessor{h} +func NewValidatingWebhookAccessor(uid string, h *v1beta1.ValidatingWebhook) WebhookAccessor { + return validatingWebhookAccessor{uid: uid, ValidatingWebhook: h} } type validatingWebhookAccessor struct { *v1beta1.ValidatingWebhook + uid string } +func (v validatingWebhookAccessor) GetUID() string { + return v.uid +} func (v validatingWebhookAccessor) GetName() string { return v.Name } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go index c2ddbb7b1f8..0d6cc9ea167 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go @@ -17,6 +17,7 @@ limitations under the License. package generic import ( + "fmt" "strings" "testing" @@ -277,9 +278,9 @@ func TestShouldCallHook(t *testing.T) { }, } - for _, testcase := range testcases { + for i, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { - invocation, err := a.shouldCallHook(webhook.NewValidatingWebhookAccessor(testcase.webhook), testcase.attrs, interfaces) + invocation, err := a.shouldCallHook(webhook.NewValidatingWebhookAccessor(fmt.Sprintf("webhook-%d", i), testcase.webhook), testcase.attrs, interfaces) if err != nil { if len(testcase.expectErr) == 0 { t.Fatal(err) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/BUILD index 903afb8753b..27b41c947ad 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/BUILD @@ -6,6 +6,7 @@ go_library( "dispatcher.go", "doc.go", "plugin.go", + "reinvocationcontext.go", ], importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating", importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating", @@ -13,11 +14,13 @@ go_library( deps = [ "//staging/src/k8s.io/api/admission/v1beta1:go_default_library", "//staging/src/k8s.io/api/admissionregistration/v1beta1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/configuration:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/metrics:go_default_library", diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go index b206d2a5393..f1d8ce819d3 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go @@ -24,6 +24,7 @@ import ( "time" jsonpatch "github.com/evanphx/json-patch" + apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/klog" admissionv1beta1 "k8s.io/api/admission/v1beta1" @@ -56,12 +57,32 @@ func newMutatingDispatcher(p *Plugin) func(cm *webhookutil.ClientManager) generi var _ generic.Dispatcher = &mutatingDispatcher{} func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces, relevantHooks []*generic.WebhookInvocation) error { + reinvokeCtx := attr.GetReinvocationContext() + var webhookReinvokeCtx *webhookReinvokeContext + if v := reinvokeCtx.Value(PluginName); v != nil { + webhookReinvokeCtx = v.(*webhookReinvokeContext) + } else { + webhookReinvokeCtx = &webhookReinvokeContext{} + reinvokeCtx.SetValue(PluginName, webhookReinvokeCtx) + } + + if reinvokeCtx.IsReinvoke() && webhookReinvokeCtx.IsOutputChangedSinceLastWebhookInvocation(attr.GetObject()) { + // If the object has changed, we know the in-tree plugin re-invocations have mutated the object, + // and we need to reinvoke all eligible webhooks. + webhookReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins() + } + defer func() { + webhookReinvokeCtx.SetLastWebhookInvocationOutput(attr.GetObject()) + }() var versionedAttr *generic.VersionedAttributes for _, invocation := range relevantHooks { hook, ok := invocation.Webhook.GetMutatingWebhook() if !ok { return fmt.Errorf("mutating webhook dispatch requires v1beta1.MutatingWebhook, but got %T", hook) } + if reinvokeCtx.IsReinvoke() && !webhookReinvokeCtx.ShouldReinvokeWebhook(invocation.Webhook.GetUID()) { + continue + } if versionedAttr == nil { // First webhook, create versioned attributes var err error @@ -76,8 +97,17 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib } t := time.Now() - err := a.callAttrMutatingHook(ctx, hook, invocation, versionedAttr, o) + + changed, err := a.callAttrMutatingHook(ctx, hook, invocation, versionedAttr, o) admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, versionedAttr.Attributes, "admit", hook.Name) + if changed { + // Patch had changed the object. Prepare to reinvoke all previous webhooks that are eligible for re-invocation. + webhookReinvokeCtx.RequireReinvokingPreviouslyInvokedPlugins() + reinvokeCtx.SetShouldReinvoke() + } + if hook.ReinvocationPolicy != nil && *hook.ReinvocationPolicy == v1beta1.IfNeededReinvocationPolicy { + webhookReinvokeCtx.AddReinvocableWebhookToPreviouslyInvoked(invocation.Webhook.GetUID()) + } if err == nil { continue } @@ -99,32 +129,33 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib if versionedAttr != nil && versionedAttr.VersionedObject != nil && versionedAttr.Dirty { return o.GetObjectConvertor().Convert(versionedAttr.VersionedObject, versionedAttr.Attributes.GetObject(), nil) } + return nil } // note that callAttrMutatingHook updates attr -func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.MutatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes, o admission.ObjectInterfaces) error { +func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta1.MutatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes, o admission.ObjectInterfaces) (bool, error) { if attr.Attributes.IsDryRun() { if h.SideEffects == nil { - return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")} + return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook SideEffects is nil")} } if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) { - return webhookerrors.NewDryRunUnsupportedErr(h.Name) + return false, webhookerrors.NewDryRunUnsupportedErr(h.Name) } } // Currently dispatcher only supports `v1beta1` AdmissionReview // TODO: Make the dispatcher capable of sending multiple AdmissionReview versions if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, invocation.Webhook) { - return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReview")} + return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReview")} } // Make the webhook request request := request.CreateAdmissionReview(attr, invocation) client, err := a.cm.HookClient(util.HookClientConfigForWebhook(invocation.Webhook)) if err != nil { - return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err} + return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } response := &admissionv1beta1.AdmissionReview{} r := client.Post().Context(ctx).Body(&request) @@ -132,11 +163,11 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second) } if err := r.Do().Into(response); err != nil { - return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err} + return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } if response.Response == nil { - return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")} + return false, &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("Webhook response was absent")} } for k, v := range response.Response.AuditAnnotations { @@ -147,34 +178,34 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta } if !response.Response.Allowed { - return webhookerrors.ToStatusErr(h.Name, response.Response.Result) + return false, webhookerrors.ToStatusErr(h.Name, response.Response.Result) } patchJS := response.Response.Patch if len(patchJS) == 0 { - return nil + return false, nil } patchObj, err := jsonpatch.DecodePatch(patchJS) if err != nil { - return apierrors.NewInternalError(err) + return false, apierrors.NewInternalError(err) } if len(patchObj) == 0 { - return nil + return false, nil } // if a non-empty patch was provided, and we have no object we can apply it to (e.g. a DELETE admission operation), error if attr.VersionedObject == nil { - return apierrors.NewInternalError(fmt.Errorf("admission webhook %q attempted to modify the object, which is not supported for this operation", h.Name)) + return false, apierrors.NewInternalError(fmt.Errorf("admission webhook %q attempted to modify the object, which is not supported for this operation", h.Name)) } jsonSerializer := json.NewSerializer(json.DefaultMetaFactory, o.GetObjectCreater(), o.GetObjectTyper(), false) objJS, err := runtime.Encode(jsonSerializer, attr.VersionedObject) if err != nil { - return apierrors.NewInternalError(err) + return false, apierrors.NewInternalError(err) } patchedJS, err := patchObj.Apply(objJS) if err != nil { - return apierrors.NewInternalError(err) + return false, apierrors.NewInternalError(err) } var newVersionedObject runtime.Object @@ -185,16 +216,20 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta } else { newVersionedObject, err = o.GetObjectCreater().New(attr.VersionedKind) if err != nil { - return apierrors.NewInternalError(err) + return false, apierrors.NewInternalError(err) } } + // TODO: if we have multiple mutating webhooks, we can remember the json // instead of encoding and decoding for each one. if newVersionedObject, _, err = jsonSerializer.Decode(patchedJS, nil, newVersionedObject); err != nil { - return apierrors.NewInternalError(err) + return false, apierrors.NewInternalError(err) } + + changed := !apiequality.Semantic.DeepEqual(attr.VersionedObject, newVersionedObject) + attr.Dirty = true attr.VersionedObject = newVersionedObject o.GetObjectDefaulter().Default(attr.VersionedObject) - return nil + return changed, nil } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin_test.go index b8f98eedffb..b618e309149 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/plugin_test.go @@ -49,67 +49,77 @@ func TestAdmit(t *testing.T) { webhooktesting.ConvertToMutatingTestCases(webhooktesting.NewNonMutatingTestCases(serverURL))...) for _, tt := range testCases { - wh, err := NewMutatingWebhook(nil) - if err != nil { - t.Errorf("%s: failed to create mutating webhook: %v", tt.Name, err) - continue - } - - ns := "webhook-test" - client, informer := webhooktesting.NewFakeMutatingDataSource(ns, tt.Webhooks, stopCh) - - wh.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32)))) - wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL)) - wh.SetExternalKubeClientSet(client) - wh.SetExternalKubeInformerFactory(informer) - - informer.Start(stopCh) - informer.WaitForCacheSync(stopCh) - - if err = wh.ValidateInitialization(); err != nil { - t.Errorf("%s: failed to validate initialization: %v", tt.Name, err) - continue - } - - var attr admission.Attributes - if tt.IsCRD { - attr = webhooktesting.NewAttributeUnstructured(ns, tt.AdditionalLabels, tt.IsDryRun) - } else { - attr = webhooktesting.NewAttribute(ns, tt.AdditionalLabels, tt.IsDryRun) - } - - err = wh.Admit(attr, objectInterfaces) - if tt.ExpectAllow != (err == nil) { - t.Errorf("%s: expected allowed=%v, but got err=%v", tt.Name, tt.ExpectAllow, err) - } - if tt.ExpectLabels != nil { - if !reflect.DeepEqual(tt.ExpectLabels, attr.GetObject().(metav1.Object).GetLabels()) { - t.Errorf("%s: expected labels '%v', but got '%v'", tt.Name, tt.ExpectLabels, attr.GetObject().(metav1.Object).GetLabels()) + t.Run(tt.Name, func(t *testing.T) { + wh, err := NewMutatingWebhook(nil) + if err != nil { + t.Errorf("failed to create mutating webhook: %v", err) + return } - } - // ErrWebhookRejected is not an error for our purposes - if tt.ErrorContains != "" { - if err == nil || !strings.Contains(err.Error(), tt.ErrorContains) { - t.Errorf("%s: expected an error saying %q, but got: %v", tt.Name, tt.ErrorContains, err) + + ns := "webhook-test" + client, informer := webhooktesting.NewFakeMutatingDataSource(ns, tt.Webhooks, stopCh) + + wh.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32)))) + wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL)) + wh.SetExternalKubeClientSet(client) + wh.SetExternalKubeInformerFactory(informer) + + informer.Start(stopCh) + informer.WaitForCacheSync(stopCh) + + if err = wh.ValidateInitialization(); err != nil { + t.Errorf("failed to validate initialization: %v", err) + return } - } - if statusErr, isStatusErr := err.(*errors.StatusError); err != nil && !isStatusErr { - t.Errorf("%s: expected a StatusError, got %T", tt.Name, err) - } else if isStatusErr { - if statusErr.ErrStatus.Code != tt.ExpectStatusCode { - t.Errorf("%s: expected status code %d, got %d", tt.Name, tt.ExpectStatusCode, statusErr.ErrStatus.Code) + + var attr admission.Attributes + if tt.IsCRD { + attr = webhooktesting.NewAttributeUnstructured(ns, tt.AdditionalLabels, tt.IsDryRun) + } else { + attr = webhooktesting.NewAttribute(ns, tt.AdditionalLabels, tt.IsDryRun) } - } - fakeAttr, ok := attr.(*webhooktesting.FakeAttributes) - if !ok { - t.Errorf("Unexpected error, failed to convert attr to webhooktesting.FakeAttributes") - continue - } - if len(tt.ExpectAnnotations) == 0 { - assert.Empty(t, fakeAttr.GetAnnotations(), tt.Name+": annotations not set as expected.") - } else { - assert.Equal(t, tt.ExpectAnnotations, fakeAttr.GetAnnotations(), tt.Name+": annotations not set as expected.") - } + + err = wh.Admit(attr, objectInterfaces) + if tt.ExpectAllow != (err == nil) { + t.Errorf("expected allowed=%v, but got err=%v", tt.ExpectAllow, err) + } + if tt.ExpectLabels != nil { + if !reflect.DeepEqual(tt.ExpectLabels, attr.GetObject().(metav1.Object).GetLabels()) { + t.Errorf("expected labels '%v', but got '%v'", tt.ExpectLabels, attr.GetObject().(metav1.Object).GetLabels()) + } + } + // ErrWebhookRejected is not an error for our purposes + if tt.ErrorContains != "" { + if err == nil || !strings.Contains(err.Error(), tt.ErrorContains) { + t.Errorf("expected an error saying %q, but got: %v", tt.ErrorContains, err) + } + } + if statusErr, isStatusErr := err.(*errors.StatusError); err != nil && !isStatusErr { + t.Errorf("expected a StatusError, got %T", err) + } else if isStatusErr { + if statusErr.ErrStatus.Code != tt.ExpectStatusCode { + t.Errorf("expected status code %d, got %d", tt.ExpectStatusCode, statusErr.ErrStatus.Code) + } + } + fakeAttr, ok := attr.(*webhooktesting.FakeAttributes) + if !ok { + t.Errorf("Unexpected error, failed to convert attr to webhooktesting.FakeAttributes") + return + } + if len(tt.ExpectAnnotations) == 0 { + assert.Empty(t, fakeAttr.GetAnnotations(), tt.Name+": annotations not set as expected.") + } else { + assert.Equal(t, tt.ExpectAnnotations, fakeAttr.GetAnnotations(), tt.Name+": annotations not set as expected.") + } + reinvocationCtx := fakeAttr.Attributes.GetReinvocationContext() + reinvocationCtx.SetIsReinvoke() + for webhook, expectReinvoke := range tt.ExpectReinvokeWebhooks { + shouldReinvoke := reinvocationCtx.Value(PluginName).(*webhookReinvokeContext).ShouldReinvokeWebhook(webhook) + if expectReinvoke != shouldReinvoke { + t.Errorf("expected reinvocationContext.ShouldReinvokeWebhook(%s)=%t, but got %t", webhook, expectReinvoke, shouldReinvoke) + } + } + }) } } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/reinvocationcontext.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/reinvocationcontext.go new file mode 100644 index 00000000000..de0af221e13 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/reinvocationcontext.go @@ -0,0 +1,68 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mutating + +import ( + apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" +) + +type webhookReinvokeContext struct { + // lastWebhookOutput holds the result of the last webhook admission plugin call + lastWebhookOutput runtime.Object + // previouslyInvokedReinvocableWebhooks holds the set of webhooks that have been invoked and + // should be reinvoked if a later mutation occurs + previouslyInvokedReinvocableWebhooks sets.String + // reinvokeWebhooks holds the set of webhooks that should be reinvoked + reinvokeWebhooks sets.String +} + +func (rc *webhookReinvokeContext) ShouldReinvokeWebhook(webhook string) bool { + return rc.reinvokeWebhooks.Has(webhook) +} + +func (rc *webhookReinvokeContext) IsOutputChangedSinceLastWebhookInvocation(object runtime.Object) bool { + return !apiequality.Semantic.DeepEqual(rc.lastWebhookOutput, object) +} + +func (rc *webhookReinvokeContext) SetLastWebhookInvocationOutput(object runtime.Object) { + if object == nil { + rc.lastWebhookOutput = nil + return + } + rc.lastWebhookOutput = object.DeepCopyObject() +} + +func (rc *webhookReinvokeContext) AddReinvocableWebhookToPreviouslyInvoked(webhook string) { + if rc.previouslyInvokedReinvocableWebhooks == nil { + rc.previouslyInvokedReinvocableWebhooks = sets.NewString() + } + rc.previouslyInvokedReinvocableWebhooks.Insert(webhook) +} + +func (rc *webhookReinvokeContext) RequireReinvokingPreviouslyInvokedPlugins() { + if len(rc.previouslyInvokedReinvocableWebhooks) > 0 { + if rc.reinvokeWebhooks == nil { + rc.reinvokeWebhooks = sets.NewString() + } + for s := range rc.previouslyInvokedReinvocableWebhooks { + rc.reinvokeWebhooks.Insert(s) + } + rc.previouslyInvokedReinvocableWebhooks = sets.NewString() + } +} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher_test.go index 4633cac8ecc..7bdf6c4509b 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace/matcher_test.go @@ -120,7 +120,7 @@ func TestNotExemptClusterScopedResource(t *testing.T) { } attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, &metav1.CreateOptions{}, false, nil) matcher := Matcher{} - matches, err := matcher.MatchNamespaceSelector(webhook.NewValidatingWebhookAccessor(hook), attr) + matches, err := matcher.MatchNamespaceSelector(webhook.NewValidatingWebhookAccessor("mock-hook", hook), attr) if err != nil { t.Fatal(err) } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go index ec8e6b552c7..e3046d63449 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go @@ -49,6 +49,9 @@ var sideEffectsNone = registrationv1beta1.SideEffectClassNone var sideEffectsSome = registrationv1beta1.SideEffectClassSome var sideEffectsNoneOnDryRun = registrationv1beta1.SideEffectClassNoneOnDryRun +var reinvokeNever = registrationv1beta1.NeverReinvocationPolicy +var reinvokeIfNeeded = registrationv1beta1.IfNeededReinvocationPolicy + // NewFakeValidatingDataSource returns a mock client and informer returning the given webhooks. func NewFakeValidatingDataSource(name string, webhooks []registrationv1beta1.ValidatingWebhook, stopCh <-chan struct{}) (clientset kubernetes.Interface, factory informers.SharedInformerFactory) { var objs = []runtime.Object{ @@ -199,39 +202,41 @@ func (c urlConfigGenerator) ccfgURL(urlPath string) registrationv1beta1.WebhookC // ValidatingTest is a validating webhook test case. type ValidatingTest struct { - Name string - Webhooks []registrationv1beta1.ValidatingWebhook - Path string - IsCRD bool - IsDryRun bool - AdditionalLabels map[string]string - ExpectLabels map[string]string - ExpectAllow bool - ErrorContains string - ExpectAnnotations map[string]string - ExpectStatusCode int32 + Name string + Webhooks []registrationv1beta1.ValidatingWebhook + Path string + IsCRD bool + IsDryRun bool + AdditionalLabels map[string]string + ExpectLabels map[string]string + ExpectAllow bool + ErrorContains string + ExpectAnnotations map[string]string + ExpectStatusCode int32 + ExpectReinvokeWebhooks map[string]bool } // MutatingTest is a mutating webhook test case. type MutatingTest struct { - Name string - Webhooks []registrationv1beta1.MutatingWebhook - Path string - IsCRD bool - IsDryRun bool - AdditionalLabels map[string]string - ExpectLabels map[string]string - ExpectAllow bool - ErrorContains string - ExpectAnnotations map[string]string - ExpectStatusCode int32 + Name string + Webhooks []registrationv1beta1.MutatingWebhook + Path string + IsCRD bool + IsDryRun bool + AdditionalLabels map[string]string + ExpectLabels map[string]string + ExpectAllow bool + ErrorContains string + ExpectAnnotations map[string]string + ExpectStatusCode int32 + ExpectReinvokeWebhooks map[string]bool } // ConvertToMutatingTestCases converts a validating test case to a mutating one for test purposes. func ConvertToMutatingTestCases(tests []ValidatingTest) []MutatingTest { r := make([]MutatingTest, len(tests)) for i, t := range tests { - r[i] = MutatingTest{t.Name, ConvertToMutatingWebhooks(t.Webhooks), t.Path, t.IsCRD, t.IsDryRun, t.AdditionalLabels, t.ExpectLabels, t.ExpectAllow, t.ErrorContains, t.ExpectAnnotations, t.ExpectStatusCode} + r[i] = MutatingTest{t.Name, ConvertToMutatingWebhooks(t.Webhooks), t.Path, t.IsCRD, t.IsDryRun, t.AdditionalLabels, t.ExpectLabels, t.ExpectAllow, t.ErrorContains, t.ExpectAnnotations, t.ExpectStatusCode, t.ExpectReinvokeWebhooks} } return r } @@ -240,7 +245,7 @@ func ConvertToMutatingTestCases(tests []ValidatingTest) []MutatingTest { func ConvertToMutatingWebhooks(webhooks []registrationv1beta1.ValidatingWebhook) []registrationv1beta1.MutatingWebhook { mutating := make([]registrationv1beta1.MutatingWebhook, len(webhooks)) for i, h := range webhooks { - mutating[i] = registrationv1beta1.MutatingWebhook{h.Name, h.ClientConfig, h.Rules, h.FailurePolicy, h.MatchPolicy, h.NamespaceSelector, h.SideEffects, h.TimeoutSeconds, h.AdmissionReviewVersions} + mutating[i] = registrationv1beta1.MutatingWebhook{h.Name, h.ClientConfig, h.Rules, h.FailurePolicy, h.MatchPolicy, h.NamespaceSelector, h.SideEffects, h.TimeoutSeconds, h.AdmissionReviewVersions, nil} } return mutating } @@ -639,6 +644,63 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { }, // No need to test everything with the url case, since only the // connection is different. + { + Name: "match & reinvoke if needed policy", + Webhooks: []registrationv1beta1.MutatingWebhook{{ + Name: "addLabel", + ClientConfig: ccfgSVC("addLabel"), + Rules: matchEverythingRules, + NamespaceSelector: &metav1.LabelSelector{}, + AdmissionReviewVersions: []string{"v1beta1"}, + ReinvocationPolicy: &reinvokeIfNeeded, + }, { + Name: "removeLabel", + ClientConfig: ccfgSVC("removeLabel"), + Rules: matchEverythingRules, + NamespaceSelector: &metav1.LabelSelector{}, + AdmissionReviewVersions: []string{"v1beta1"}, + ReinvocationPolicy: &reinvokeIfNeeded, + }}, + AdditionalLabels: map[string]string{"remove": "me"}, + ExpectAllow: true, + ExpectReinvokeWebhooks: map[string]bool{"addLabel": true}, + }, + { + Name: "match & never reinvoke policy", + Webhooks: []registrationv1beta1.MutatingWebhook{{ + Name: "addLabel", + ClientConfig: ccfgSVC("addLabel"), + Rules: matchEverythingRules, + NamespaceSelector: &metav1.LabelSelector{}, + AdmissionReviewVersions: []string{"v1beta1"}, + ReinvocationPolicy: &reinvokeNever, + }}, + ExpectAllow: true, + ExpectReinvokeWebhooks: map[string]bool{"addLabel": false}, + }, + { + Name: "match & never reinvoke policy (by default)", + Webhooks: []registrationv1beta1.MutatingWebhook{{ + Name: "addLabel", + ClientConfig: ccfgSVC("addLabel"), + Rules: matchEverythingRules, + NamespaceSelector: &metav1.LabelSelector{}, + AdmissionReviewVersions: []string{"v1beta1"}, + }}, + ExpectAllow: true, + ExpectReinvokeWebhooks: map[string]bool{"addLabel": false}, + }, + { + Name: "match & no reinvoke", + Webhooks: []registrationv1beta1.MutatingWebhook{{ + Name: "noop", + ClientConfig: ccfgSVC("noop"), + Rules: matchEverythingRules, + NamespaceSelector: &metav1.LabelSelector{}, + AdmissionReviewVersions: []string{"v1beta1"}, + }}, + ExpectAllow: true, + }, } } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/webhook_server.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/webhook_server.go index 5d080745dca..193571cd6f2 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/webhook_server.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/webhook_server.go @@ -138,6 +138,13 @@ func webhookHandler(w http.ResponseWriter, r *http.Request) { }, }, }) + case "/noop": + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(&v1beta1.AdmissionReview{ + Response: &v1beta1.AdmissionResponse{ + Allowed: true, + }, + }) default: http.NotFound(w, r) } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugins.go b/staging/src/k8s.io/apiserver/pkg/admission/plugins.go index bdf087e564f..d37af509c66 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugins.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugins.go @@ -160,7 +160,7 @@ func (ps *Plugins) NewFromPlugins(pluginNames []string, configProvider ConfigPro if len(validationPlugins) != 0 { klog.Infof("Loaded %d validating admission controller(s) successfully in the following order: %s.", len(validationPlugins), strings.Join(validationPlugins, ",")) } - return chainAdmissionHandler(handlers), nil + return newReinvocationHandler(chainAdmissionHandler(handlers)), nil } // InitPlugin creates an instance of the named interface. diff --git a/staging/src/k8s.io/apiserver/pkg/admission/reinvocation.go b/staging/src/k8s.io/apiserver/pkg/admission/reinvocation.go new file mode 100644 index 00000000000..b99e604e05c --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/admission/reinvocation.go @@ -0,0 +1,62 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package admission + +// newReinvocationHandler creates a handler that wraps the provided admission chain and reinvokes it +// if needed according to re-invocation policy of the webhooks. +func newReinvocationHandler(admissionChain Interface) Interface { + return &reinvoker{admissionChain} +} + +type reinvoker struct { + admissionChain Interface +} + +// Admit performs an admission control check using the wrapped admission chain, reinvoking the +// admission chain if needed according to the reinvocation policy. Plugins are expected to check +// the admission attributes' reinvocation context against their reinvocation policy to decide if +// they should re-run, and to update the reinvocation context if they perform any mutations. +func (r *reinvoker) Admit(a Attributes, o ObjectInterfaces) error { + if mutator, ok := r.admissionChain.(MutationInterface); ok { + err := mutator.Admit(a, o) + if err != nil { + return err + } + s := a.GetReinvocationContext() + if s.ShouldReinvoke() { + s.SetIsReinvoke() + // Calling admit a second time will reinvoke all in-tree plugins + // as well as any webhook plugins that need to be reinvoked based on the + // reinvocation policy. + return mutator.Admit(a, o) + } + } + return nil +} + +// Validate performs an admission control check using the wrapped admission chain, and returns immediately on first error. +func (r *reinvoker) Validate(a Attributes, o ObjectInterfaces) error { + if validator, ok := r.admissionChain.(ValidationInterface); ok { + return validator.Validate(a, o) + } + return nil +} + +// Handles will return true if any of the admission chain handlers handle the given operation. +func (r *reinvoker) Handles(operation Operation) bool { + return r.admissionChain.Handles(operation) +} diff --git a/test/integration/apiserver/admissionwebhook/BUILD b/test/integration/apiserver/admissionwebhook/BUILD index 3804acaf435..529a1c848c9 100644 --- a/test/integration/apiserver/admissionwebhook/BUILD +++ b/test/integration/apiserver/admissionwebhook/BUILD @@ -6,6 +6,7 @@ go_test( "admission_test.go", "broken_webhook_test.go", "main_test.go", + "reinvocation_test.go", ], rundir = ".", tags = [ @@ -21,6 +22,7 @@ go_test( "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/extensions/v1beta1:go_default_library", "//staging/src/k8s.io/api/policy/v1beta1:go_default_library", + "//staging/src/k8s.io/api/scheduling/v1:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/test/integration/apiserver/admissionwebhook/reinvocation_test.go b/test/integration/apiserver/admissionwebhook/reinvocation_test.go new file mode 100644 index 00000000000..e1296e1150c --- /dev/null +++ b/test/integration/apiserver/admissionwebhook/reinvocation_test.go @@ -0,0 +1,389 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package admissionwebhook + +import ( + "crypto/tls" + "crypto/x509" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "reflect" + "strings" + "sync" + "testing" + + "k8s.io/api/admission/v1beta1" + admissionv1beta1 "k8s.io/api/admissionregistration/v1beta1" + registrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + schedulingv1 "k8s.io/api/scheduling/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" + "k8s.io/kubernetes/test/integration/framework" +) + +const ( + testReinvocationClientUsername = "webhook-reinvocation-integration-client" +) + +// TestWebhookReinvocationPolicy ensures that the admission webhook reinvocation policy is applied correctly. +func TestWebhookReinvocationPolicy(t *testing.T) { + reinvokeNever := registrationv1beta1.NeverReinvocationPolicy + reinvokeIfNeeded := registrationv1beta1.IfNeededReinvocationPolicy + + type testWebhook struct { + path string + policy *registrationv1beta1.ReinvocationPolicyType + } + + testCases := []struct { + name string + initialPriorityClass string + webhooks []testWebhook + expectLabels map[string]string + expectInvocations map[string]int + expectError bool + errorContains string + }{ + { // in-tree (mutation), webhook (no mutation), no reinvocation required + name: "no reinvocation for in-tree only mutation", + initialPriorityClass: "low-priority", // trigger initial in-tree mutation + webhooks: []testWebhook{ + {path: "/noop", policy: &reinvokeIfNeeded}, + }, + expectInvocations: map[string]int{"/noop": 1}, + }, + { // in-tree (mutation), webhook (mutation), reinvoke in-tree (no-mutation), no webhook reinvocation required + name: "no webhook reinvocation for webhook when no in-tree reinvocation mutations", + initialPriorityClass: "low-priority", // trigger initial in-tree mutation + webhooks: []testWebhook{ + {path: "/addlabel", policy: &reinvokeIfNeeded}, + }, + expectInvocations: map[string]int{"/addlabel": 1}, + }, + { // in-tree (mutation), webhook (mutation), reinvoke in-tree (mutation), webhook (no-mutation), both reinvoked + name: "webhook is reinvoked after in-tree reinvocation", + initialPriorityClass: "low-priority", // trigger initial in-tree mutation + webhooks: []testWebhook{ + // Priority plugin is ordered to run before mutating webhooks + {path: "/setpriority", policy: &reinvokeIfNeeded}, // trigger in-tree reinvoke mutation + }, + expectInvocations: map[string]int{"/setpriority": 2}, + }, + { // in-tree (mutation), webhook A (mutation), webhook B (mutation), reinvoke in-tree (no-mutation), reinvoke webhook A (no-mutation), no reinvocation of webhook B required + name: "no reinvocation of webhook B when in-tree or prior webhook mutations", + initialPriorityClass: "low-priority", // trigger initial in-tree mutation + webhooks: []testWebhook{ + {path: "/addlabel", policy: &reinvokeIfNeeded}, + {path: "/conditionaladdlabel", policy: &reinvokeIfNeeded}, + }, + expectLabels: map[string]string{"x": "true", "a": "true", "b": "true"}, + expectInvocations: map[string]int{"/addlabel": 2, "/conditionaladdlabel": 1}, + }, + { // in-tree (mutation), webhook A (mutation), webhook B (mutation), reinvoke in-tree (no-mutation), reinvoke webhook A (mutation), reinvoke webhook B (mutation), both webhooks reinvoked + name: "all webhooks reinvoked when any webhook reinvocation causes mutation", + initialPriorityClass: "low-priority", // trigger initial in-tree mutation + webhooks: []testWebhook{ + {path: "/settrue", policy: &reinvokeIfNeeded}, + {path: "/setfalse", policy: &reinvokeIfNeeded}, + }, + expectLabels: map[string]string{"x": "true", "fight": "false"}, + expectInvocations: map[string]int{"/settrue": 2, "/setfalse": 2}, + }, + { + name: "invalid priority class set by webhook should result in error from in-tree priority plugin", + webhooks: []testWebhook{ + // Priority plugin is ordered to run before mutating webhooks + {path: "/setinvalidpriority", policy: &reinvokeIfNeeded}, + }, + expectError: true, + errorContains: "no PriorityClass with name invalid was found", + expectInvocations: map[string]int{"/setinvalidpriority": 1}, + }, + { + name: "'reinvoke never' policy respected", + webhooks: []testWebhook{ + {path: "/conditionaladdlabel", policy: &reinvokeNever}, + {path: "/addlabel", policy: &reinvokeNever}, + }, + expectLabels: map[string]string{"x": "true", "a": "true"}, + expectInvocations: map[string]int{"/conditionaladdlabel": 1, "/addlabel": 1}, + }, + { + name: "'reinvoke never' (by default) policy respected", + webhooks: []testWebhook{ + {path: "/conditionaladdlabel", policy: nil}, + {path: "/addlabel", policy: nil}, + }, + expectLabels: map[string]string{"x": "true", "a": "true"}, + expectInvocations: map[string]int{"/conditionaladdlabel": 1, "/addlabel": 1}, + }, + } + + roots := x509.NewCertPool() + if !roots.AppendCertsFromPEM(localhostCert) { + t.Fatal("Failed to append Cert from PEM") + } + cert, err := tls.X509KeyPair(localhostCert, localhostKey) + if err != nil { + t.Fatalf("Failed to build cert with error: %+v", err) + } + + recorder := &invocationRecorder{counts: map[string]int{}} + webhookServer := httptest.NewUnstartedServer(newReinvokeWebhookHandler(recorder)) + webhookServer.TLS = &tls.Config{ + + RootCAs: roots, + Certificates: []tls.Certificate{cert}, + } + webhookServer.StartTLS() + defer webhookServer.Close() + + s := kubeapiservertesting.StartTestServerOrDie(t, kubeapiservertesting.NewDefaultTestServerOptions(), []string{ + "--disable-admission-plugins=ServiceAccount", + }, framework.SharedEtcd()) + defer s.TearDownFn() + + // Configure a client with a distinct user name so that it is easy to distinguish requests + // made by the client from requests made by controllers. We use this to filter out requests + // before recording them to ensure we don't accidentally mistake requests from controllers + // as requests made by the client. + clientConfig := rest.CopyConfig(s.ClientConfig) + clientConfig.Impersonate.UserName = testReinvocationClientUsername + clientConfig.Impersonate.Groups = []string{"system:masters", "system:authenticated"} + client, err := clientset.NewForConfig(clientConfig) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + for priorityClass, priority := range map[string]int{"low-priority": 1, "high-priority": 10} { + _, err = client.SchedulingV1().PriorityClasses().Create(&schedulingv1.PriorityClass{ObjectMeta: metav1.ObjectMeta{Name: priorityClass}, Value: int32(priority)}) + if err != nil { + t.Fatal(err) + } + } + + for i, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + recorder.Reset() + ns := fmt.Sprintf("reinvoke-%d", i) + _, err = client.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}) + if err != nil { + t.Fatal(err) + } + + for i, webhook := range tt.webhooks { + defer registerWebhook(t, client, fmt.Sprintf("admission.integration.test%d", i), webhookServer.URL+webhook.path, webhook.policy)() + } + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: "labeled", + Labels: map[string]string{"x": "true"}, + }, + Spec: corev1.PodSpec{ + Containers: []v1.Container{{ + Name: "fake-name", + Image: "fakeimage", + }}, + }, + } + if tt.initialPriorityClass != "" { + pod.Spec.PriorityClassName = tt.initialPriorityClass + } + obj, err := client.CoreV1().Pods(ns).Create(pod) + + if tt.expectError { + if err == nil { + t.Fatalf("expected error but got none") + } + if tt.errorContains != "" { + if !strings.Contains(err.Error(), tt.errorContains) { + t.Errorf("expected an error saying %q, but got: %v", tt.errorContains, err) + } + } + return + } + + if err != nil { + t.Fatal(err) + } + + if tt.expectLabels != nil { + labels := obj.GetLabels() + if !reflect.DeepEqual(tt.expectLabels, labels) { + t.Errorf("expected labels '%v', but got '%v'", tt.expectLabels, labels) + } + } + + if tt.expectInvocations != nil { + for k, v := range tt.expectInvocations { + if recorder.GetCount(k) != v { + t.Errorf("expected %d invocations of %s, but got %d", v, k, recorder.GetCount(k)) + } + } + } + }) + } +} + +func registerWebhook(t *testing.T, client clientset.Interface, name, endpoint string, reinvocationPolicy *registrationv1beta1.ReinvocationPolicyType) func() { + fail := admissionv1beta1.Fail + hook, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionv1beta1.MutatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{Name: name}, + Webhooks: []admissionv1beta1.MutatingWebhook{{ + Name: name, + ClientConfig: admissionv1beta1.WebhookClientConfig{ + URL: &endpoint, + CABundle: localhostCert, + }, + Rules: []admissionv1beta1.RuleWithOperations{{ + Operations: []admissionv1beta1.OperationType{admissionv1beta1.OperationAll}, + Rule: admissionv1beta1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*/*"}}, + }}, + FailurePolicy: &fail, + ReinvocationPolicy: reinvocationPolicy, + AdmissionReviewVersions: []string{"v1beta1"}, + }}, + }) + if err != nil { + t.Fatal(err) + } + + tearDown := func() { + err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(hook.GetName(), &metav1.DeleteOptions{}) + if err != nil { + t.Fatal(err) + } + } + return tearDown +} + +type invocationRecorder struct { + mu sync.Mutex + counts map[string]int +} + +func (i *invocationRecorder) Reset() { + i.mu.Lock() + defer i.mu.Unlock() + i.counts = map[string]int{} +} + +func (i *invocationRecorder) GetCount(path string) int { + i.mu.Lock() + defer i.mu.Unlock() + return i.counts[path] +} + +func (i *invocationRecorder) IncrementCount(path string) { + i.mu.Lock() + defer i.mu.Unlock() + i.counts[path]++ +} + +func newReinvokeWebhookHandler(recorder *invocationRecorder) http.Handler { + patch := func(w http.ResponseWriter, patch string) { + w.Header().Set("Content-Type", "application/json") + pt := v1beta1.PatchTypeJSONPatch + json.NewEncoder(w).Encode(&v1beta1.AdmissionReview{ + Response: &v1beta1.AdmissionResponse{ + Allowed: true, + PatchType: &pt, + Patch: []byte(patch), + }, + }) + } + allow := func(w http.ResponseWriter) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(&v1beta1.AdmissionReview{ + Response: &v1beta1.AdmissionResponse{ + Allowed: true, + }, + }) + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), 400) + } + review := v1beta1.AdmissionReview{} + if err := json.Unmarshal(data, &review); err != nil { + http.Error(w, err.Error(), 400) + } + if review.Request.UserInfo.Username != testReinvocationClientUsername { + // skip requests not originating from this integration test's client + allow(w) + return + } + + if len(review.Request.Object.Raw) == 0 { + http.Error(w, err.Error(), 400) + } + pod := &corev1.Pod{} + if err := json.Unmarshal(review.Request.Object.Raw, pod); err != nil { + http.Error(w, err.Error(), 400) + } + + recorder.IncrementCount(r.URL.Path) + + switch r.URL.Path { + case "/noop": + allow(w) + case "/settrue": + patch(w, `[{"op": "replace", "path": "/metadata/labels/fight", "value": "true"}]`) + case "/setfalse": + patch(w, `[{"op": "replace", "path": "/metadata/labels/fight", "value": "false"}]`) + case "/addlabel": + labels := pod.GetLabels() + if a, ok := labels["a"]; !ok || a != "true" { + patch(w, `[{"op": "add", "path": "/metadata/labels/a", "value": "true"}]`) + return + } + allow(w) + case "/conditionaladdlabel": // if 'a' is set, set 'b' to true + labels := pod.GetLabels() + if _, ok := labels["a"]; ok { + patch(w, `[{"op": "add", "path": "/metadata/labels/b", "value": "true"}]`) + return + } + allow(w) + case "/setpriority": // sets /spec/priorityClassName to high-priority if it is not already set + if pod.Spec.PriorityClassName != "high-priority" { + if pod.Spec.Priority != nil { + patch(w, `[{"op": "add", "path": "/spec/priorityClassName", "value": "high-priority"},{"op": "remove", "path": "/spec/priority"}]`) + } else { + patch(w, `[{"op": "add", "path": "/spec/priorityClassName", "value": "high-priority"}]`) + } + return + } + allow(w) + case "/setinvalidpriority": + patch(w, `[{"op": "add", "path": "/spec/priorityClassName", "value": "invalid"}]`) + default: + http.NotFound(w, r) + } + }) +} From 70f1b052e33b12a12cf6799469e6ea9981b301f9 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Fri, 17 May 2019 10:44:58 -0700 Subject: [PATCH 5/8] api --- pkg/apis/admissionregistration/types.go | 28 +++++++++++++++++ .../admissionregistration/v1beta1/defaults.go | 8 +++++ .../validation/validation.go | 7 +++++ .../admissionregistration/v1beta1/types.go | 30 ++++++++++++++++++- 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/pkg/apis/admissionregistration/types.go b/pkg/apis/admissionregistration/types.go index 198f91acb71..78b1147c45a 100644 --- a/pkg/apis/admissionregistration/types.go +++ b/pkg/apis/admissionregistration/types.go @@ -249,6 +249,20 @@ type ValidatingWebhook struct { // +optional NamespaceSelector *metav1.LabelSelector + // ObjectSelector decides whether to run the webhook based on if the + // object has matching labels. objectSelector is evaluated against both + // the oldObject and newObject that would be sent to the webhook, and + // is considered to match if either object matches the selector. A null + // object (oldObject in the case of create, or newObject in the case of + // delete) or an object that cannot have labels (like a + // DeploymentRollback or a PodProxyOptions object) is not considered to + // match. + // Use the object selector only if the webhook is opt-in, because end + // users may skip the admission webhook by setting the labels. + // Default to the empty LabelSelector, which matches everything. + // +optional + ObjectSelector *metav1.LabelSelector + // SideEffects states whether this webhookk has side effects. // Acceptable values are: Unknown, None, Some, NoneOnDryRun // Webhooks with side effects MUST implement a reconciliation system, since a request may be @@ -359,6 +373,20 @@ type MutatingWebhook struct { // +optional NamespaceSelector *metav1.LabelSelector + // ObjectSelector decides whether to run the webhook based on if the + // object has matching labels. objectSelector is evaluated against both + // the oldObject and newObject that would be sent to the webhook, and + // is considered to match if either object matches the selector. A null + // object (oldObject in the case of create, or newObject in the case of + // delete) or an object that cannot have labels (like a + // DeploymentRollback or a PodProxyOptions object) is not considered to + // match. + // Use the object selector only if the webhook is opt-in, because end + // users may skip the admission webhook by setting the labels. + // Default to the empty LabelSelector, which matches everything. + // +optional + ObjectSelector *metav1.LabelSelector + // SideEffects states whether this webhookk has side effects. // Acceptable values are: Unknown, None, Some, NoneOnDryRun // Webhooks with side effects MUST implement a reconciliation system, since a request may be diff --git a/pkg/apis/admissionregistration/v1beta1/defaults.go b/pkg/apis/admissionregistration/v1beta1/defaults.go index 63637e7c932..2afa0642b76 100644 --- a/pkg/apis/admissionregistration/v1beta1/defaults.go +++ b/pkg/apis/admissionregistration/v1beta1/defaults.go @@ -40,6 +40,10 @@ func SetDefaults_ValidatingWebhook(obj *admissionregistrationv1beta1.ValidatingW selector := metav1.LabelSelector{} obj.NamespaceSelector = &selector } + if obj.ObjectSelector == nil { + selector := metav1.LabelSelector{} + obj.ObjectSelector = &selector + } if obj.SideEffects == nil { // TODO: revisit/remove this default and possibly make the field required when promoting to v1 unknown := admissionregistrationv1beta1.SideEffectClassUnknown @@ -68,6 +72,10 @@ func SetDefaults_MutatingWebhook(obj *admissionregistrationv1beta1.MutatingWebho selector := metav1.LabelSelector{} obj.NamespaceSelector = &selector } + if obj.ObjectSelector == nil { + selector := metav1.LabelSelector{} + obj.ObjectSelector = &selector + } if obj.SideEffects == nil { // TODO: revisit/remove this default and possibly make the field required when promoting to v1 unknown := admissionregistrationv1beta1.SideEffectClassUnknown diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go index 4f2b7e457a9..ae34a214c43 100644 --- a/pkg/apis/admissionregistration/validation/validation.go +++ b/pkg/apis/admissionregistration/validation/validation.go @@ -245,6 +245,10 @@ func validateValidatingWebhook(hook *admissionregistration.ValidatingWebhook, fl allErrors = append(allErrors, metav1validation.ValidateLabelSelector(hook.NamespaceSelector, fldPath.Child("namespaceSelector"))...) } + if hook.ObjectSelector != nil { + allErrors = append(allErrors, metav1validation.ValidateLabelSelector(hook.ObjectSelector, fldPath.Child("objectSelector"))...) + } + cc := hook.ClientConfig switch { case (cc.URL == nil) == (cc.Service == nil): @@ -281,6 +285,9 @@ func validateMutatingWebhook(hook *admissionregistration.MutatingWebhook, fldPat if hook.NamespaceSelector != nil { allErrors = append(allErrors, metav1validation.ValidateLabelSelector(hook.NamespaceSelector, fldPath.Child("namespaceSelector"))...) } + if hook.ObjectSelector != nil { + allErrors = append(allErrors, metav1validation.ValidateLabelSelector(hook.ObjectSelector, fldPath.Child("objectSelector"))...) + } if hook.ReinvocationPolicy != nil && !supportedReinvocationPolicies.Has(string(*hook.ReinvocationPolicy)) { allErrors = append(allErrors, field.NotSupported(fldPath.Child("reinvocationPolicy"), *hook.ReinvocationPolicy, supportedReinvocationPolicies.List())) } diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go index c18c9b20ec6..44a52dd3f6a 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go @@ -252,13 +252,27 @@ type ValidatingWebhook struct { // } // // See - // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels // for more examples of label selectors. // // Default to the empty LabelSelector, which matches everything. // +optional NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,5,opt,name=namespaceSelector"` + // ObjectSelector decides whether to run the webhook based on if the + // object has matching labels. objectSelector is evaluated against both + // the oldObject and newObject that would be sent to the webhook, and + // is considered to match if either object matches the selector. A null + // object (oldObject in the case of create, or newObject in the case of + // delete) or an object that cannot have labels (like a + // DeploymentRollback or a PodProxyOptions object) is not considered to + // match. + // Use the object selector only if the webhook is opt-in, because end + // users may skip the admission webhook by setting the labels. + // Default to the empty LabelSelector, which matches everything. + // +optional + ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty"` + // SideEffects states whether this webhookk has side effects. // Acceptable values are: Unknown, None, Some, NoneOnDryRun // Webhooks with side effects MUST implement a reconciliation system, since a request may be @@ -377,6 +391,20 @@ type MutatingWebhook struct { // +optional NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,5,opt,name=namespaceSelector"` + // ObjectSelector decides whether to run the webhook based on if the + // object has matching labels. objectSelector is evaluated against both + // the oldObject and newObject that would be sent to the webhook, and + // is considered to match if either object matches the selector. A null + // object (oldObject in the case of create, or newObject in the case of + // delete) or an object that cannot have labels (like a + // DeploymentRollback or a PodProxyOptions object) is not considered to + // match. + // Use the object selector only if the webhook is opt-in, because end + // users may skip the admission webhook by setting the labels. + // Default to the empty LabelSelector, which matches everything. + // +optional + ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty" protobuf:"bytes,11,opt,name=objectSelector"` + // SideEffects states whether this webhookk has side effects. // Acceptable values are: Unknown, None, Some, NoneOnDryRun // Webhooks with side effects MUST implement a reconciliation system, since a request may be From 4f5c47fb99e57aea6ccb33af04f9c5af82c5d927 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Wed, 29 May 2019 15:57:02 -0700 Subject: [PATCH 6/8] generated --- api/openapi-spec/swagger.json | 10 +- .../v1beta1/zz_generated.conversion.go | 4 + .../zz_generated.deepcopy.go | 10 + .../v1beta1/generated.pb.go | 268 ++++++++++++------ .../v1beta1/generated.proto | 30 +- .../admissionregistration/v1beta1/types.go | 2 +- .../v1beta1/types_swagger_doc_generated.go | 4 +- .../v1beta1/zz_generated.deepcopy.go | 10 + .../src/k8s.io/apiserver/pkg/admission/BUILD | 78 +++-- .../pkg/admission/plugin/webhook/BUILD | 1 + .../admission/plugin/webhook/mutating/BUILD | 1 + .../pkg/admission/plugin/webhook/object/BUILD | 50 ++++ .../admission/plugin/webhook/validating/BUILD | 1 + vendor/modules.txt | 1 + 14 files changed, 340 insertions(+), 130 deletions(-) create mode 100644 staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/BUILD diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 6c55e630c05..ac064d820b3 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -30,6 +30,10 @@ "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." }, + "objectSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "ObjectSelector decides whether to run the webhook based on if the object has matching labels. objectSelector is evaluated against both the oldObject and newObject that would be sent to the webhook, and is considered to match if either object matches the selector. A null object (oldObject in the case of create, or newObject in the case of delete) or an object that cannot have labels (like a DeploymentRollback or a PodProxyOptions object) is not considered to match. Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels. Default to the empty LabelSelector, which matches everything." + }, "reinvocationPolicy": { "description": "reinvocationPolicy indicates whether this webhook should be called multiple times as part of a single admission evaluation. Allowed values are \"Never\" and \"IfNeeded\".\n\nNever: the webhook will not be called more than once in a single admission evaluation.\n\nIfNeeded: the webhook will be called at least one additional time as part of the admission evaluation if the object being admitted is modified by other admission plugins after the initial webhook call. Webhooks that specify this option *must* be idempotent, able to process objects they previously admitted. Note: * the number of additional invocations is not guaranteed to be exactly one. * if additional invocations result in further modifications to the object, webhooks are not guaranteed to be invoked again. * webhooks that use this option may be reordered to minimize the number of additional invocations. * to validate an object after all mutations are guaranteed complete, use a validating admission webhook instead.\n\nDefaults to \"Never\".", "type": "string" @@ -219,7 +223,11 @@ }, "namespaceSelector": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", - "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." + "description": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything." + }, + "objectSelector": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector", + "description": "ObjectSelector decides whether to run the webhook based on if the object has matching labels. objectSelector is evaluated against both the oldObject and newObject that would be sent to the webhook, and is considered to match if either object matches the selector. A null object (oldObject in the case of create, or newObject in the case of delete) or an object that cannot have labels (like a DeploymentRollback or a PodProxyOptions object) is not considered to match. Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels. Default to the empty LabelSelector, which matches everything." }, "rules": { "description": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", diff --git a/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go b/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go index 933b8a21015..cc105ce3812 100644 --- a/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go @@ -149,6 +149,7 @@ func autoConvert_v1beta1_MutatingWebhook_To_admissionregistration_MutatingWebhoo out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) out.MatchPolicy = (*admissionregistration.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) + out.ObjectSelector = (*v1.LabelSelector)(unsafe.Pointer(in.ObjectSelector)) out.SideEffects = (*admissionregistration.SideEffectClass)(unsafe.Pointer(in.SideEffects)) out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) @@ -170,6 +171,7 @@ func autoConvert_admissionregistration_MutatingWebhook_To_v1beta1_MutatingWebhoo out.FailurePolicy = (*v1beta1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) out.MatchPolicy = (*v1beta1.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) + out.ObjectSelector = (*v1.LabelSelector)(unsafe.Pointer(in.ObjectSelector)) out.SideEffects = (*v1beta1.SideEffectClass)(unsafe.Pointer(in.SideEffects)) out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) @@ -357,6 +359,7 @@ func autoConvert_v1beta1_ValidatingWebhook_To_admissionregistration_ValidatingWe out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) out.MatchPolicy = (*admissionregistration.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) + out.ObjectSelector = (*v1.LabelSelector)(unsafe.Pointer(in.ObjectSelector)) out.SideEffects = (*admissionregistration.SideEffectClass)(unsafe.Pointer(in.SideEffects)) out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) @@ -377,6 +380,7 @@ func autoConvert_admissionregistration_ValidatingWebhook_To_v1beta1_ValidatingWe out.FailurePolicy = (*v1beta1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) out.MatchPolicy = (*v1beta1.MatchPolicyType)(unsafe.Pointer(in.MatchPolicy)) out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) + out.ObjectSelector = (*v1.LabelSelector)(unsafe.Pointer(in.ObjectSelector)) out.SideEffects = (*v1beta1.SideEffectClass)(unsafe.Pointer(in.SideEffects)) out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) out.AdmissionReviewVersions = *(*[]string)(unsafe.Pointer(&in.AdmissionReviewVersions)) diff --git a/pkg/apis/admissionregistration/zz_generated.deepcopy.go b/pkg/apis/admissionregistration/zz_generated.deepcopy.go index aaf5f897f39..44a84070d35 100644 --- a/pkg/apis/admissionregistration/zz_generated.deepcopy.go +++ b/pkg/apis/admissionregistration/zz_generated.deepcopy.go @@ -51,6 +51,11 @@ func (in *MutatingWebhook) DeepCopyInto(out *MutatingWebhook) { *out = new(v1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.ObjectSelector != nil { + in, out := &in.ObjectSelector, &out.ObjectSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } if in.SideEffects != nil { in, out := &in.SideEffects, &out.SideEffects *out = new(SideEffectClass) @@ -255,6 +260,11 @@ func (in *ValidatingWebhook) DeepCopyInto(out *ValidatingWebhook) { *out = new(v1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.ObjectSelector != nil { + in, out := &in.ObjectSelector, &out.ObjectSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } if in.SideEffects != nil { in, out := &in.SideEffects, &out.SideEffects *out = new(SideEffectClass) diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go index 43be1141c62..b7ab68acb04 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go @@ -212,6 +212,16 @@ func (m *MutatingWebhook) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintGenerated(dAtA, i, uint64(len(*m.ReinvocationPolicy))) i += copy(dAtA[i:], *m.ReinvocationPolicy) } + if m.ObjectSelector != nil { + dAtA[i] = 0x5a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.ObjectSelector.Size())) + n3, err := m.ObjectSelector.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n3 + } return i, nil } @@ -233,11 +243,11 @@ func (m *MutatingWebhookConfiguration) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintGenerated(dAtA, i, uint64(m.ObjectMeta.Size())) - n3, err := m.ObjectMeta.MarshalTo(dAtA[i:]) + n4, err := m.ObjectMeta.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n3 + i += n4 if len(m.Webhooks) > 0 { for _, msg := range m.Webhooks { dAtA[i] = 0x12 @@ -271,11 +281,11 @@ func (m *MutatingWebhookConfigurationList) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintGenerated(dAtA, i, uint64(m.ListMeta.Size())) - n4, err := m.ListMeta.MarshalTo(dAtA[i:]) + n5, err := m.ListMeta.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n4 + i += n5 if len(m.Items) > 0 { for _, msg := range m.Items { dAtA[i] = 0x12 @@ -393,11 +403,11 @@ func (m *RuleWithOperations) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintGenerated(dAtA, i, uint64(m.Rule.Size())) - n5, err := m.Rule.MarshalTo(dAtA[i:]) + n6, err := m.Rule.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n5 + i += n6 return i, nil } @@ -460,11 +470,11 @@ func (m *ValidatingWebhook) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintGenerated(dAtA, i, uint64(m.ClientConfig.Size())) - n6, err := m.ClientConfig.MarshalTo(dAtA[i:]) + n7, err := m.ClientConfig.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n6 + i += n7 if len(m.Rules) > 0 { for _, msg := range m.Rules { dAtA[i] = 0x1a @@ -487,11 +497,11 @@ func (m *ValidatingWebhook) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintGenerated(dAtA, i, uint64(m.NamespaceSelector.Size())) - n7, err := m.NamespaceSelector.MarshalTo(dAtA[i:]) + n8, err := m.NamespaceSelector.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n7 + i += n8 } if m.SideEffects != nil { dAtA[i] = 0x32 @@ -525,6 +535,16 @@ func (m *ValidatingWebhook) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintGenerated(dAtA, i, uint64(len(*m.MatchPolicy))) i += copy(dAtA[i:], *m.MatchPolicy) } + if m.ObjectSelector != nil { + dAtA[i] = 0x52 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.ObjectSelector.Size())) + n9, err := m.ObjectSelector.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n9 + } return i, nil } @@ -546,11 +566,11 @@ func (m *ValidatingWebhookConfiguration) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintGenerated(dAtA, i, uint64(m.ObjectMeta.Size())) - n8, err := m.ObjectMeta.MarshalTo(dAtA[i:]) + n10, err := m.ObjectMeta.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n8 + i += n10 if len(m.Webhooks) > 0 { for _, msg := range m.Webhooks { dAtA[i] = 0x12 @@ -584,11 +604,11 @@ func (m *ValidatingWebhookConfigurationList) MarshalTo(dAtA []byte) (int, error) dAtA[i] = 0xa i++ i = encodeVarintGenerated(dAtA, i, uint64(m.ListMeta.Size())) - n9, err := m.ListMeta.MarshalTo(dAtA[i:]) + n11, err := m.ListMeta.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n9 + i += n11 if len(m.Items) > 0 { for _, msg := range m.Items { dAtA[i] = 0x12 @@ -623,11 +643,11 @@ func (m *WebhookClientConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintGenerated(dAtA, i, uint64(m.Service.Size())) - n10, err := m.Service.MarshalTo(dAtA[i:]) + n12, err := m.Service.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n10 + i += n12 } if m.CABundle != nil { dAtA[i] = 0x12 @@ -695,6 +715,10 @@ func (m *MutatingWebhook) Size() (n int) { l = len(*m.ReinvocationPolicy) n += 1 + l + sovGenerated(uint64(l)) } + if m.ObjectSelector != nil { + l = m.ObjectSelector.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -823,6 +847,10 @@ func (m *ValidatingWebhook) Size() (n int) { l = len(*m.MatchPolicy) n += 1 + l + sovGenerated(uint64(l)) } + if m.ObjectSelector != nil { + l = m.ObjectSelector.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -900,6 +928,7 @@ func (this *MutatingWebhook) String() string { `AdmissionReviewVersions:` + fmt.Sprintf("%v", this.AdmissionReviewVersions) + `,`, `MatchPolicy:` + valueToStringGenerated(this.MatchPolicy) + `,`, `ReinvocationPolicy:` + valueToStringGenerated(this.ReinvocationPolicy) + `,`, + `ObjectSelector:` + strings.Replace(fmt.Sprintf("%v", this.ObjectSelector), "LabelSelector", "k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector", 1) + `,`, `}`, }, "") return s @@ -977,6 +1006,7 @@ func (this *ValidatingWebhook) String() string { `TimeoutSeconds:` + valueToStringGenerated(this.TimeoutSeconds) + `,`, `AdmissionReviewVersions:` + fmt.Sprintf("%v", this.AdmissionReviewVersions) + `,`, `MatchPolicy:` + valueToStringGenerated(this.MatchPolicy) + `,`, + `ObjectSelector:` + strings.Replace(fmt.Sprintf("%v", this.ObjectSelector), "LabelSelector", "k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector", 1) + `,`, `}`, }, "") return s @@ -1344,6 +1374,39 @@ func (m *MutatingWebhook) Unmarshal(dAtA []byte) error { s := ReinvocationPolicyType(dAtA[iNdEx:postIndex]) m.ReinvocationPolicy = &s iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectSelector", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ObjectSelector == nil { + m.ObjectSelector = &k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector{} + } + if err := m.ObjectSelector.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -2312,6 +2375,39 @@ func (m *ValidatingWebhook) Unmarshal(dAtA []byte) error { s := MatchPolicyType(dAtA[iNdEx:postIndex]) m.MatchPolicy = &s iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectSelector", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ObjectSelector == nil { + m.ObjectSelector = &k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector{} + } + if err := m.ObjectSelector.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -2809,73 +2905,75 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 1088 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x55, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xcf, 0xc6, 0x76, 0x63, 0x8f, 0x93, 0xb6, 0x19, 0xa0, 0x35, 0xa5, 0xf2, 0x5a, 0x3e, 0x20, - 0x4b, 0xd0, 0xdd, 0x26, 0x20, 0x04, 0x05, 0x84, 0xb2, 0x81, 0x42, 0xa4, 0xa4, 0x0d, 0x93, 0xfe, - 0x91, 0xf8, 0x23, 0x75, 0xbc, 0x7e, 0xb6, 0x07, 0xdb, 0x3b, 0xab, 0x9d, 0x59, 0x87, 0xdc, 0xf8, - 0x08, 0x7c, 0x05, 0x4e, 0x7c, 0x0a, 0x0e, 0xdc, 0x72, 0xec, 0x31, 0x17, 0x56, 0x64, 0x39, 0x73, - 0xe0, 0x86, 0x72, 0x42, 0x3b, 0x3b, 0x5e, 0xff, 0x4b, 0x8a, 0x01, 0xa9, 0x17, 0x7a, 0xdb, 0xf7, - 0x7b, 0xf3, 0x7e, 0xef, 0xbd, 0x99, 0xdf, 0xbe, 0x87, 0x3e, 0xef, 0xbd, 0x2b, 0x2c, 0xc6, 0xed, - 0x5e, 0xd8, 0x84, 0xc0, 0x03, 0x09, 0xc2, 0x1e, 0x82, 0xd7, 0xe2, 0x81, 0xad, 0x1d, 0xd4, 0x67, - 0x36, 0x6d, 0x0d, 0x98, 0x10, 0x8c, 0x7b, 0x01, 0x74, 0x98, 0x90, 0x01, 0x95, 0x8c, 0x7b, 0xf6, - 0x70, 0xa3, 0x09, 0x92, 0x6e, 0xd8, 0x1d, 0xf0, 0x20, 0xa0, 0x12, 0x5a, 0x96, 0x1f, 0x70, 0xc9, - 0x71, 0x23, 0x8d, 0xb4, 0xa8, 0xcf, 0xac, 0x73, 0x23, 0x2d, 0x1d, 0x79, 0xe3, 0x56, 0x87, 0xc9, - 0x6e, 0xd8, 0xb4, 0x5c, 0x3e, 0xb0, 0x3b, 0xbc, 0xc3, 0x6d, 0x45, 0xd0, 0x0c, 0xdb, 0xca, 0x52, - 0x86, 0xfa, 0x4a, 0x89, 0x6f, 0xbc, 0x3d, 0x2e, 0x69, 0x40, 0xdd, 0x2e, 0xf3, 0x20, 0x38, 0xb2, - 0xfd, 0x5e, 0x27, 0x01, 0x84, 0x3d, 0x00, 0x49, 0xed, 0xe1, 0x5c, 0x39, 0x37, 0xec, 0x8b, 0xa2, - 0x82, 0xd0, 0x93, 0x6c, 0x00, 0x73, 0x01, 0xef, 0xfc, 0x5d, 0x80, 0x70, 0xbb, 0x30, 0xa0, 0xb3, - 0x71, 0xf5, 0x93, 0x4b, 0xe8, 0xca, 0x5e, 0x28, 0xa9, 0x64, 0x5e, 0xe7, 0x31, 0x34, 0xbb, 0x9c, - 0xf7, 0x70, 0x0d, 0xe5, 0x3d, 0x3a, 0x80, 0x8a, 0x51, 0x33, 0x1a, 0x25, 0x67, 0xf5, 0x38, 0x32, - 0x97, 0xe2, 0xc8, 0xcc, 0xdf, 0xa3, 0x03, 0x20, 0xca, 0x83, 0x0f, 0xd1, 0xaa, 0xdb, 0x67, 0xe0, - 0xc9, 0x6d, 0xee, 0xb5, 0x59, 0xa7, 0xb2, 0x5c, 0x33, 0x1a, 0xe5, 0xcd, 0x0f, 0xad, 0x45, 0x2f, - 0xd1, 0xd2, 0xa9, 0xb6, 0x27, 0x48, 0x9c, 0x97, 0x75, 0xa2, 0xd5, 0x49, 0x94, 0x4c, 0x25, 0xc2, - 0x14, 0x15, 0x82, 0xb0, 0x0f, 0xa2, 0x92, 0xab, 0xe5, 0x1a, 0xe5, 0xcd, 0x0f, 0x16, 0xcf, 0x48, - 0xc2, 0x3e, 0x3c, 0x66, 0xb2, 0x7b, 0xdf, 0x87, 0xd4, 0x23, 0x9c, 0x35, 0x9d, 0xb0, 0x90, 0xf8, - 0x04, 0x49, 0x99, 0xf1, 0x2e, 0x5a, 0x6b, 0x53, 0xd6, 0x0f, 0x03, 0xd8, 0xe7, 0x7d, 0xe6, 0x1e, - 0x55, 0xf2, 0xea, 0x1a, 0x5e, 0x8f, 0x23, 0x73, 0xed, 0xee, 0xa4, 0xe3, 0x2c, 0x32, 0xd7, 0xa7, - 0x80, 0x07, 0x47, 0x3e, 0x90, 0xe9, 0x60, 0xfc, 0x2d, 0x5a, 0x4f, 0x6e, 0x4c, 0xf8, 0xd4, 0x85, - 0x03, 0xe8, 0x83, 0x2b, 0x79, 0x50, 0x29, 0xa8, 0xeb, 0x7a, 0x6b, 0xa2, 0xf8, 0xec, 0xcd, 0x2c, - 0xbf, 0xd7, 0x49, 0x00, 0x61, 0x25, 0xd2, 0xb0, 0x86, 0x1b, 0xd6, 0x2e, 0x6d, 0x42, 0x7f, 0x14, - 0xea, 0xbc, 0x12, 0x47, 0xe6, 0xfa, 0xbd, 0x59, 0x46, 0x32, 0x9f, 0x04, 0x7f, 0x8c, 0xca, 0x82, - 0xb5, 0xe0, 0x93, 0x76, 0x1b, 0x5c, 0x29, 0x2a, 0x97, 0x54, 0x17, 0xf5, 0x38, 0x32, 0xcb, 0x07, - 0x63, 0xf8, 0x2c, 0x32, 0xaf, 0x8c, 0xcd, 0xed, 0x3e, 0x15, 0x82, 0x4c, 0x86, 0xe1, 0x3b, 0xe8, - 0x72, 0x22, 0x1f, 0x1e, 0xca, 0x03, 0x70, 0xb9, 0xd7, 0x12, 0x95, 0x95, 0x9a, 0xd1, 0x28, 0x38, - 0x38, 0x8e, 0xcc, 0xcb, 0x0f, 0xa6, 0x3c, 0x64, 0xe6, 0x24, 0x7e, 0x88, 0xae, 0x67, 0x6f, 0x42, - 0x60, 0xc8, 0xe0, 0xf0, 0x11, 0x04, 0x89, 0x21, 0x2a, 0xc5, 0x5a, 0xae, 0x51, 0x72, 0x5e, 0x8b, - 0x23, 0xf3, 0xfa, 0xd6, 0xf9, 0x47, 0xc8, 0x45, 0xb1, 0x49, 0x63, 0x03, 0x2a, 0xdd, 0xae, 0x7e, - 0x9e, 0xd2, 0xb8, 0xb1, 0xbd, 0x31, 0x9c, 0x34, 0x36, 0x61, 0xaa, 0xa7, 0x99, 0x0c, 0xc3, 0x4f, - 0x10, 0x0e, 0x80, 0x79, 0x43, 0xee, 0x2a, 0x35, 0x68, 0x32, 0xa4, 0xc8, 0x6e, 0xc7, 0x91, 0x89, - 0xc9, 0x9c, 0xf7, 0x2c, 0x32, 0xaf, 0xcd, 0xa3, 0x8a, 0xfa, 0x1c, 0xae, 0xfa, 0x2f, 0x06, 0xba, - 0x39, 0xf3, 0x6b, 0xa5, 0x2a, 0x0e, 0x53, 0x01, 0xe2, 0x27, 0xa8, 0x98, 0x3c, 0x72, 0x8b, 0x4a, - 0xaa, 0xfe, 0xb5, 0xf2, 0xe6, 0xed, 0xc5, 0x24, 0x71, 0xbf, 0xf9, 0x0d, 0xb8, 0x72, 0x0f, 0x24, - 0x75, 0xb0, 0xd6, 0x30, 0x1a, 0x63, 0x24, 0x63, 0xc5, 0x5f, 0xa2, 0xa2, 0xce, 0x2c, 0x2a, 0xcb, - 0xea, 0x8f, 0x79, 0x6f, 0xf1, 0x3f, 0x66, 0xa6, 0x76, 0x27, 0x9f, 0xa4, 0x22, 0xc5, 0x43, 0x4d, - 0x58, 0xff, 0xdd, 0x40, 0xb5, 0x67, 0xf5, 0xb7, 0xcb, 0x84, 0xc4, 0x5f, 0xcd, 0xf5, 0x68, 0x2d, - 0x28, 0x7b, 0x26, 0xd2, 0x0e, 0xaf, 0xea, 0x0e, 0x8b, 0x23, 0x64, 0xa2, 0xbf, 0x1e, 0x2a, 0x30, - 0x09, 0x83, 0x51, 0x73, 0x77, 0xff, 0x75, 0x73, 0x53, 0x85, 0x8f, 0x07, 0xc3, 0x4e, 0x42, 0x4e, - 0xd2, 0x1c, 0xf5, 0x9f, 0x0d, 0x94, 0x4f, 0x26, 0x05, 0x7e, 0x03, 0x95, 0xa8, 0xcf, 0x3e, 0x0d, - 0x78, 0xe8, 0x8b, 0x8a, 0xa1, 0x94, 0xbc, 0x16, 0x47, 0x66, 0x69, 0x6b, 0x7f, 0x27, 0x05, 0xc9, - 0xd8, 0x8f, 0x37, 0x50, 0x99, 0xfa, 0x2c, 0x13, 0xfe, 0xb2, 0x3a, 0x7e, 0x25, 0x51, 0xeb, 0xd6, - 0xfe, 0x4e, 0x26, 0xf6, 0xc9, 0x33, 0x09, 0x7f, 0x00, 0x82, 0x87, 0x81, 0xab, 0x07, 0x9d, 0xe6, - 0x27, 0x23, 0x90, 0x8c, 0xfd, 0xf8, 0x4d, 0x54, 0x10, 0x2e, 0xf7, 0x41, 0x8f, 0xa9, 0x6b, 0x49, - 0xd9, 0x07, 0x09, 0x70, 0x16, 0x99, 0x25, 0xf5, 0xa1, 0x04, 0x9a, 0x1e, 0xaa, 0xff, 0x68, 0x20, - 0x3c, 0x3f, 0x09, 0xf1, 0x47, 0x08, 0xf1, 0xcc, 0xd2, 0x2d, 0x99, 0x4a, 0x55, 0x19, 0x7a, 0x16, - 0x99, 0x6b, 0x99, 0xa5, 0x28, 0x27, 0x42, 0xf0, 0x3e, 0xca, 0x27, 0xd3, 0x53, 0x2f, 0x02, 0xeb, - 0x9f, 0x8d, 0xe5, 0xf1, 0x8a, 0x49, 0x2c, 0xa2, 0x98, 0xea, 0x3f, 0x18, 0xe8, 0xea, 0x01, 0x04, - 0x43, 0xe6, 0x02, 0x81, 0x36, 0x04, 0xe0, 0xb9, 0x80, 0x6d, 0x54, 0xca, 0x06, 0x9d, 0x5e, 0x4f, - 0xeb, 0x3a, 0xb6, 0x94, 0x0d, 0x45, 0x32, 0x3e, 0x93, 0xad, 0xb2, 0xe5, 0x0b, 0x57, 0xd9, 0x4d, - 0x94, 0xf7, 0xa9, 0xec, 0x56, 0x72, 0xea, 0x44, 0x31, 0xf1, 0xee, 0x53, 0xd9, 0x25, 0x0a, 0x55, - 0x5e, 0x1e, 0x48, 0x75, 0xb9, 0x05, 0xed, 0xe5, 0x81, 0x24, 0x0a, 0xad, 0xff, 0x59, 0x40, 0xeb, - 0x8f, 0x68, 0x9f, 0xb5, 0x5e, 0xac, 0xcf, 0x17, 0xeb, 0xf3, 0x7f, 0xb5, 0x3e, 0xeb, 0xa7, 0x06, - 0xaa, 0xce, 0x49, 0xff, 0x79, 0xaf, 0xb7, 0xaf, 0xe7, 0xd6, 0xdb, 0xfb, 0x8b, 0x2b, 0x7a, 0xae, - 0xfa, 0xb9, 0x05, 0xf7, 0x87, 0x81, 0xea, 0xcf, 0xee, 0xf1, 0x39, 0xac, 0xb8, 0xc1, 0xf4, 0x8a, - 0xfb, 0xec, 0x3f, 0x34, 0xb8, 0xc8, 0x92, 0xfb, 0xc9, 0x40, 0x2f, 0x9d, 0x33, 0x5d, 0x30, 0x45, - 0x2b, 0x22, 0x9d, 0xc6, 0xba, 0xc7, 0x3b, 0x8b, 0x17, 0x32, 0x3b, 0xc6, 0x9d, 0x72, 0x1c, 0x99, - 0x2b, 0x23, 0x74, 0xc4, 0x8b, 0x1b, 0xa8, 0xe8, 0x52, 0x27, 0xf4, 0x5a, 0x7a, 0x8f, 0xac, 0x3a, - 0xab, 0xc9, 0x9d, 0x6c, 0x6f, 0xa5, 0x18, 0xc9, 0xbc, 0xf8, 0x55, 0x94, 0x0b, 0x83, 0xbe, 0x1e, - 0xd9, 0x2b, 0x71, 0x64, 0xe6, 0x1e, 0x92, 0x5d, 0x92, 0x60, 0xce, 0xad, 0xe3, 0xd3, 0xea, 0xd2, - 0xd3, 0xd3, 0xea, 0xd2, 0xc9, 0x69, 0x75, 0xe9, 0xbb, 0xb8, 0x6a, 0x1c, 0xc7, 0x55, 0xe3, 0x69, - 0x5c, 0x35, 0x4e, 0xe2, 0xaa, 0xf1, 0x6b, 0x5c, 0x35, 0xbe, 0xff, 0xad, 0xba, 0xf4, 0xc5, 0x8a, - 0x2e, 0xed, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa7, 0x5c, 0x48, 0x5c, 0x4a, 0x0e, 0x00, 0x00, + // 1113 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x55, 0x4d, 0x6f, 0x1b, 0xc5, + 0x1b, 0xcf, 0xc6, 0x76, 0x6d, 0x8f, 0x93, 0xa6, 0x99, 0xff, 0x9f, 0xd6, 0x84, 0xca, 0x6b, 0xf9, + 0x80, 0x2c, 0x41, 0x77, 0x9b, 0x80, 0x10, 0x14, 0x10, 0xca, 0x06, 0x0a, 0x91, 0x92, 0x36, 0x4c, + 0xfa, 0x22, 0xf1, 0x22, 0x75, 0xbc, 0x1e, 0xdb, 0x83, 0xed, 0x9d, 0xd5, 0xce, 0xac, 0x43, 0x6e, + 0x7c, 0x04, 0xbe, 0x02, 0x27, 0x3e, 0x05, 0x07, 0x6e, 0xe1, 0xd6, 0x63, 0x2f, 0xac, 0xc8, 0x72, + 0xe2, 0xc0, 0x81, 0x6b, 0x4e, 0x68, 0x66, 0xc7, 0xeb, 0x97, 0x4d, 0x8a, 0x29, 0xa2, 0x17, 0x7a, + 0xdb, 0xf9, 0x3d, 0xf3, 0xfc, 0x9e, 0x97, 0xd9, 0xe7, 0xf9, 0x81, 0x4f, 0xfb, 0x6f, 0x73, 0x8b, + 0x32, 0xbb, 0x1f, 0xb6, 0x48, 0xe0, 0x11, 0x41, 0xb8, 0x3d, 0x22, 0x5e, 0x9b, 0x05, 0xb6, 0x36, + 0x60, 0x9f, 0xda, 0xb8, 0x3d, 0xa4, 0x9c, 0x53, 0xe6, 0x05, 0xa4, 0x4b, 0xb9, 0x08, 0xb0, 0xa0, + 0xcc, 0xb3, 0x47, 0x9b, 0x2d, 0x22, 0xf0, 0xa6, 0xdd, 0x25, 0x1e, 0x09, 0xb0, 0x20, 0x6d, 0xcb, + 0x0f, 0x98, 0x60, 0xb0, 0x99, 0x78, 0x5a, 0xd8, 0xa7, 0xd6, 0xb9, 0x9e, 0x96, 0xf6, 0xdc, 0xb8, + 0xd1, 0xa5, 0xa2, 0x17, 0xb6, 0x2c, 0x97, 0x0d, 0xed, 0x2e, 0xeb, 0x32, 0x5b, 0x11, 0xb4, 0xc2, + 0x8e, 0x3a, 0xa9, 0x83, 0xfa, 0x4a, 0x88, 0x37, 0xde, 0x9c, 0xa4, 0x34, 0xc4, 0x6e, 0x8f, 0x7a, + 0x24, 0x38, 0xb6, 0xfd, 0x7e, 0x57, 0x02, 0xdc, 0x1e, 0x12, 0x81, 0xed, 0x51, 0x26, 0x9d, 0x0d, + 0xfb, 0x22, 0xaf, 0x20, 0xf4, 0x04, 0x1d, 0x92, 0x8c, 0xc3, 0x5b, 0x7f, 0xe5, 0xc0, 0xdd, 0x1e, + 0x19, 0xe2, 0x79, 0xbf, 0xc6, 0x4f, 0x45, 0xb0, 0xb6, 0x1f, 0x0a, 0x2c, 0xa8, 0xd7, 0x7d, 0x48, + 0x5a, 0x3d, 0xc6, 0xfa, 0xb0, 0x0e, 0xf2, 0x1e, 0x1e, 0x92, 0xaa, 0x51, 0x37, 0x9a, 0x65, 0x67, + 0xe5, 0x24, 0x32, 0x97, 0xe2, 0xc8, 0xcc, 0xdf, 0xc1, 0x43, 0x82, 0x94, 0x05, 0x1e, 0x81, 0x15, + 0x77, 0x40, 0x89, 0x27, 0x76, 0x98, 0xd7, 0xa1, 0xdd, 0xea, 0x72, 0xdd, 0x68, 0x56, 0xb6, 0xde, + 0xb7, 0x16, 0x6d, 0xa2, 0xa5, 0x43, 0xed, 0x4c, 0x91, 0x38, 0xff, 0xd7, 0x81, 0x56, 0xa6, 0x51, + 0x34, 0x13, 0x08, 0x62, 0x50, 0x08, 0xc2, 0x01, 0xe1, 0xd5, 0x5c, 0x3d, 0xd7, 0xac, 0x6c, 0xbd, + 0xb7, 0x78, 0x44, 0x14, 0x0e, 0xc8, 0x43, 0x2a, 0x7a, 0x77, 0x7d, 0x92, 0x58, 0xb8, 0xb3, 0xaa, + 0x03, 0x16, 0xa4, 0x8d, 0xa3, 0x84, 0x19, 0xee, 0x81, 0xd5, 0x0e, 0xa6, 0x83, 0x30, 0x20, 0x07, + 0x6c, 0x40, 0xdd, 0xe3, 0x6a, 0x5e, 0xb5, 0xe1, 0xd5, 0x38, 0x32, 0x57, 0x6f, 0x4f, 0x1b, 0xce, + 0x22, 0x73, 0x7d, 0x06, 0xb8, 0x77, 0xec, 0x13, 0x34, 0xeb, 0x0c, 0xbf, 0x06, 0xeb, 0xb2, 0x63, + 0xdc, 0xc7, 0x2e, 0x39, 0x24, 0x03, 0xe2, 0x0a, 0x16, 0x54, 0x0b, 0xaa, 0x5d, 0x6f, 0x4c, 0x25, + 0x9f, 0xbe, 0x99, 0xe5, 0xf7, 0xbb, 0x12, 0xe0, 0x96, 0xfc, 0x35, 0xac, 0xd1, 0xa6, 0xb5, 0x87, + 0x5b, 0x64, 0x30, 0x76, 0x75, 0x5e, 0x8a, 0x23, 0x73, 0xfd, 0xce, 0x3c, 0x23, 0xca, 0x06, 0x81, + 0x1f, 0x82, 0x0a, 0xa7, 0x6d, 0xf2, 0x51, 0xa7, 0x43, 0x5c, 0xc1, 0xab, 0x97, 0x54, 0x15, 0x8d, + 0x38, 0x32, 0x2b, 0x87, 0x13, 0xf8, 0x2c, 0x32, 0xd7, 0x26, 0xc7, 0x9d, 0x01, 0xe6, 0x1c, 0x4d, + 0xbb, 0xc1, 0x5b, 0xe0, 0xb2, 0xfc, 0x7d, 0x58, 0x28, 0x0e, 0x89, 0xcb, 0xbc, 0x36, 0xaf, 0x16, + 0xeb, 0x46, 0xb3, 0xe0, 0xc0, 0x38, 0x32, 0x2f, 0xdf, 0x9b, 0xb1, 0xa0, 0xb9, 0x9b, 0xf0, 0x3e, + 0xb8, 0x96, 0xbe, 0x09, 0x22, 0x23, 0x4a, 0x8e, 0x1e, 0x90, 0x40, 0x1e, 0x78, 0xb5, 0x54, 0xcf, + 0x35, 0xcb, 0xce, 0x2b, 0x71, 0x64, 0x5e, 0xdb, 0x3e, 0xff, 0x0a, 0xba, 0xc8, 0x57, 0x16, 0x36, + 0xc4, 0xc2, 0xed, 0xe9, 0xe7, 0x29, 0x4f, 0x0a, 0xdb, 0x9f, 0xc0, 0xb2, 0xb0, 0xa9, 0xa3, 0x7a, + 0x9a, 0x69, 0x37, 0xf8, 0x08, 0xc0, 0x80, 0x50, 0x6f, 0xc4, 0x5c, 0xf5, 0x37, 0x68, 0x32, 0xa0, + 0xc8, 0x6e, 0xc6, 0x91, 0x09, 0x51, 0xc6, 0x7a, 0x16, 0x99, 0x57, 0xb3, 0xa8, 0xa2, 0x3e, 0x87, + 0x0b, 0x32, 0x70, 0x99, 0xb5, 0xbe, 0x22, 0xae, 0x48, 0xdf, 0xbd, 0xf2, 0xec, 0xef, 0xae, 0xfa, + 0x7d, 0x77, 0x86, 0x0e, 0xcd, 0xd1, 0x37, 0x7e, 0x36, 0xc0, 0xf5, 0xb9, 0x59, 0x4e, 0xc6, 0x26, + 0x4c, 0xfe, 0x78, 0xf8, 0x08, 0x94, 0x24, 0x7b, 0x1b, 0x0b, 0xac, 0x86, 0xbb, 0xb2, 0x75, 0x73, + 0xb1, 0x5c, 0x92, 0xc0, 0xfb, 0x44, 0x60, 0x07, 0xea, 0xa1, 0x01, 0x13, 0x0c, 0xa5, 0xac, 0xf0, + 0x73, 0x50, 0xd2, 0x91, 0x79, 0x75, 0x59, 0x8d, 0xe8, 0x3b, 0x8b, 0x8f, 0xe8, 0x5c, 0xee, 0x4e, + 0x5e, 0x86, 0x42, 0xa5, 0x23, 0x4d, 0xd8, 0xf8, 0xdd, 0x00, 0xf5, 0xa7, 0xd5, 0xb7, 0x47, 0xb9, + 0x80, 0x5f, 0x64, 0x6a, 0xb4, 0x16, 0xec, 0x37, 0xe5, 0x49, 0x85, 0x57, 0x74, 0x85, 0xa5, 0x31, + 0x32, 0x55, 0x5f, 0x1f, 0x14, 0xa8, 0x20, 0xc3, 0x71, 0x71, 0xb7, 0x9f, 0xb9, 0xb8, 0x99, 0xc4, + 0x27, 0x9b, 0x68, 0x57, 0x92, 0xa3, 0x24, 0x46, 0xe3, 0x47, 0x03, 0xe4, 0xe5, 0x6a, 0x82, 0xaf, + 0x81, 0x32, 0xf6, 0xe9, 0xc7, 0x01, 0x0b, 0x7d, 0x5e, 0x35, 0xd4, 0xe8, 0xac, 0xc6, 0x91, 0x59, + 0xde, 0x3e, 0xd8, 0x4d, 0x40, 0x34, 0xb1, 0xc3, 0x4d, 0x50, 0xc1, 0x3e, 0x4d, 0x27, 0x6d, 0x59, + 0x5d, 0x5f, 0x93, 0xe3, 0xb1, 0x7d, 0xb0, 0x9b, 0x4e, 0xd7, 0xf4, 0x1d, 0xc9, 0x1f, 0x10, 0xce, + 0xc2, 0xc0, 0xd5, 0x9b, 0x55, 0xf3, 0xa3, 0x31, 0x88, 0x26, 0x76, 0xf8, 0x3a, 0x28, 0x70, 0x97, + 0xf9, 0x44, 0xef, 0xc5, 0xab, 0x32, 0xed, 0x43, 0x09, 0x9c, 0x45, 0x66, 0x59, 0x7d, 0xa8, 0x89, + 0x48, 0x2e, 0x35, 0xbe, 0x37, 0x00, 0xcc, 0xae, 0x5e, 0xf8, 0x01, 0x00, 0x2c, 0x3d, 0xe9, 0x92, + 0x4c, 0xf5, 0x57, 0xa5, 0xe8, 0x59, 0x64, 0xae, 0xa6, 0x27, 0x45, 0x39, 0xe5, 0x02, 0x0f, 0x40, + 0x5e, 0xae, 0x6b, 0xad, 0x3c, 0xd6, 0xdf, 0xd3, 0x81, 0x89, 0xa6, 0xc9, 0x13, 0x52, 0x4c, 0x8d, + 0xef, 0x0c, 0x70, 0xe5, 0x90, 0x04, 0x23, 0xea, 0x12, 0x44, 0x3a, 0x24, 0x20, 0x9e, 0x4b, 0xa0, + 0x0d, 0xca, 0xe9, 0x66, 0xd5, 0x7a, 0xb8, 0xae, 0x7d, 0xcb, 0xe9, 0x16, 0x46, 0x93, 0x3b, 0xa9, + 0x76, 0x2e, 0x5f, 0xa8, 0x9d, 0xd7, 0x41, 0xde, 0xc7, 0xa2, 0x57, 0xcd, 0xa9, 0x1b, 0x25, 0x69, + 0x3d, 0xc0, 0xa2, 0x87, 0x14, 0xaa, 0xac, 0x2c, 0x10, 0xaa, 0xb9, 0x05, 0x6d, 0x65, 0x81, 0x40, + 0x0a, 0x6d, 0xfc, 0x76, 0x09, 0xac, 0x3f, 0xc0, 0x03, 0xda, 0x7e, 0xa1, 0xd7, 0x2f, 0xf4, 0xfa, + 0xbf, 0xa5, 0xd7, 0x59, 0x35, 0x05, 0xff, 0xae, 0x9a, 0x9e, 0x1a, 0xa0, 0x96, 0x99, 0xb5, 0xe7, + 0xad, 0xa7, 0x5f, 0x66, 0xf4, 0xf4, 0xdd, 0xc5, 0x47, 0x28, 0x93, 0x7d, 0x46, 0x51, 0xff, 0x30, + 0x40, 0xe3, 0xe9, 0x35, 0x3e, 0x07, 0x4d, 0x1d, 0xce, 0x6a, 0xea, 0x27, 0xff, 0xa0, 0xc0, 0x45, + 0x54, 0xf5, 0x07, 0x03, 0xfc, 0xef, 0x9c, 0x75, 0x06, 0x31, 0x28, 0xf2, 0x64, 0xfd, 0xeb, 0x1a, + 0x6f, 0x2d, 0x9e, 0xc8, 0xbc, 0x6e, 0x38, 0x95, 0x38, 0x32, 0x8b, 0x63, 0x74, 0xcc, 0x0b, 0x9b, + 0xa0, 0xe4, 0x62, 0x27, 0xf4, 0xda, 0x5a, 0xb8, 0x56, 0x9c, 0x15, 0xd9, 0x93, 0x9d, 0xed, 0x04, + 0x43, 0xa9, 0x15, 0xbe, 0x0c, 0x72, 0x61, 0x30, 0xd0, 0x1a, 0x51, 0x8c, 0x23, 0x33, 0x77, 0x1f, + 0xed, 0x21, 0x89, 0x39, 0x37, 0x4e, 0x4e, 0x6b, 0x4b, 0x8f, 0x4f, 0x6b, 0x4b, 0x4f, 0x4e, 0x6b, + 0x4b, 0xdf, 0xc4, 0x35, 0xe3, 0x24, 0xae, 0x19, 0x8f, 0xe3, 0x9a, 0xf1, 0x24, 0xae, 0x19, 0xbf, + 0xc4, 0x35, 0xe3, 0xdb, 0x5f, 0x6b, 0x4b, 0x9f, 0x15, 0x75, 0x6a, 0x7f, 0x06, 0x00, 0x00, 0xff, + 0xff, 0xc3, 0x6f, 0x8b, 0x7e, 0x2c, 0x0f, 0x00, 0x00, } diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto index 377e311273f..c133192647d 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto @@ -117,6 +117,20 @@ message MutatingWebhook { // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector namespaceSelector = 5; + // ObjectSelector decides whether to run the webhook based on if the + // object has matching labels. objectSelector is evaluated against both + // the oldObject and newObject that would be sent to the webhook, and + // is considered to match if either object matches the selector. A null + // object (oldObject in the case of create, or newObject in the case of + // delete) or an object that cannot have labels (like a + // DeploymentRollback or a PodProxyOptions object) is not considered to + // match. + // Use the object selector only if the webhook is opt-in, because end + // users may skip the admission webhook by setting the labels. + // Default to the empty LabelSelector, which matches everything. + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector objectSelector = 11; + // SideEffects states whether this webhookk has side effects. // Acceptable values are: Unknown, None, Some, NoneOnDryRun // Webhooks with side effects MUST implement a reconciliation system, since a request may be @@ -349,13 +363,27 @@ message ValidatingWebhook { // } // // See - // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + // https://kubernetes.io/docs/concepts/overview/working-with-objects/labels // for more examples of label selectors. // // Default to the empty LabelSelector, which matches everything. // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector namespaceSelector = 5; + // ObjectSelector decides whether to run the webhook based on if the + // object has matching labels. objectSelector is evaluated against both + // the oldObject and newObject that would be sent to the webhook, and + // is considered to match if either object matches the selector. A null + // object (oldObject in the case of create, or newObject in the case of + // delete) or an object that cannot have labels (like a + // DeploymentRollback or a PodProxyOptions object) is not considered to + // match. + // Use the object selector only if the webhook is opt-in, because end + // users may skip the admission webhook by setting the labels. + // Default to the empty LabelSelector, which matches everything. + // +optional + optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector objectSelector = 10; + // SideEffects states whether this webhookk has side effects. // Acceptable values are: Unknown, None, Some, NoneOnDryRun // Webhooks with side effects MUST implement a reconciliation system, since a request may be diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go index 44a52dd3f6a..6b8c5a23a72 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go @@ -271,7 +271,7 @@ type ValidatingWebhook struct { // users may skip the admission webhook by setting the labels. // Default to the empty LabelSelector, which matches everything. // +optional - ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty"` + ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty" protobuf:"bytes,10,opt,name=objectSelector"` // SideEffects states whether this webhookk has side effects. // Acceptable values are: Unknown, None, Some, NoneOnDryRun diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go index 6fb5a636b00..39e86db9769 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go @@ -35,6 +35,7 @@ var map_MutatingWebhook = map[string]string{ "failurePolicy": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", "matchPolicy": "matchPolicy defines how the \"rules\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.\n\nDefaults to \"Exact\"", "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.", + "objectSelector": "ObjectSelector decides whether to run the webhook based on if the object has matching labels. objectSelector is evaluated against both the oldObject and newObject that would be sent to the webhook, and is considered to match if either object matches the selector. A null object (oldObject in the case of create, or newObject in the case of delete) or an object that cannot have labels (like a DeploymentRollback or a PodProxyOptions object) is not considered to match. Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels. Default to the empty LabelSelector, which matches everything.", "sideEffects": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", "timeoutSeconds": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", "admissionReviewVersions": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy. Default to `['v1beta1']`.", @@ -105,7 +106,8 @@ var map_ValidatingWebhook = map[string]string{ "rules": "Rules describes what operations on what resources/subresources the webhook cares about. The webhook cares about an operation if it matches _any_ Rule. However, in order to prevent ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks from putting the cluster in a state which cannot be recovered from without completely disabling the plugin, ValidatingAdmissionWebhooks and MutatingAdmissionWebhooks are never called on admission requests for ValidatingWebhookConfiguration and MutatingWebhookConfiguration objects.", "failurePolicy": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", "matchPolicy": "matchPolicy defines how the \"rules\" list is used to match incoming requests. Allowed values are \"Exact\" or \"Equivalent\".\n\n- Exact: match a request only if it exactly matches a specified rule. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, but \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the webhook.\n\n- Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1, and \"rules\" only included `apiGroups:[\"apps\"], apiVersions:[\"v1\"], resources: [\"deployments\"]`, a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the webhook.\n\nDefaults to \"Exact\"", - "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.", + "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.", + "objectSelector": "ObjectSelector decides whether to run the webhook based on if the object has matching labels. objectSelector is evaluated against both the oldObject and newObject that would be sent to the webhook, and is considered to match if either object matches the selector. A null object (oldObject in the case of create, or newObject in the case of delete) or an object that cannot have labels (like a DeploymentRollback or a PodProxyOptions object) is not considered to match. Use the object selector only if the webhook is opt-in, because end users may skip the admission webhook by setting the labels. Default to the empty LabelSelector, which matches everything.", "sideEffects": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", "timeoutSeconds": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", "admissionReviewVersions": "AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions the Webhook expects. API server will try to use first version in the list which it supports. If none of the versions specified in this list supported by API server, validation will fail for this object. If a persisted webhook configuration specifies allowed versions and does not include any versions known to the API Server, calls to the webhook will fail and be subject to the failure policy. Default to `['v1beta1']`.", diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go index 746ee492415..c4570d0311b 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go @@ -51,6 +51,11 @@ func (in *MutatingWebhook) DeepCopyInto(out *MutatingWebhook) { *out = new(v1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.ObjectSelector != nil { + in, out := &in.ObjectSelector, &out.ObjectSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } if in.SideEffects != nil { in, out := &in.SideEffects, &out.SideEffects *out = new(SideEffectClass) @@ -260,6 +265,11 @@ func (in *ValidatingWebhook) DeepCopyInto(out *ValidatingWebhook) { *out = new(v1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.ObjectSelector != nil { + in, out := &in.ObjectSelector, &out.ObjectSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } if in.SideEffects != nil { in, out := &in.SideEffects, &out.SideEffects *out = new(SideEffectClass) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/BUILD index 7da1eb84e00..05843e9f17d 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/BUILD @@ -1,9 +1,40 @@ -package(default_visibility = ["//visibility:public"]) +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", +go_library( + name = "go_default_library", + srcs = [ + "attributes.go", + "audit.go", + "chain.go", + "config.go", + "decorator.go", + "errors.go", + "handler.go", + "interfaces.go", + "plugins.go", + "reinvocation.go", + "util.go", + ], + importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/admission", + importpath = "k8s.io/apiserver/pkg/admission", + visibility = ["//visibility:public"], + deps = [ + "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/apis/apiserver:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/apis/audit:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/audit:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", + "//vendor/k8s.io/klog:go_default_library", + "//vendor/sigs.k8s.io/yaml:go_default_library", + ], ) go_test( @@ -30,42 +61,6 @@ go_test( ], ) -go_library( - name = "go_default_library", - srcs = [ - "attributes.go", - "audit.go", - "chain.go", - "config.go", - "decorator.go", - "errors.go", - "handler.go", - "interfaces.go", - "plugins.go", - "reinvocation.go", - "util.go", - ], - importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/admission", - importpath = "k8s.io/apiserver/pkg/admission", - deps = [ - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/apis/apiserver:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/apis/audit:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/audit:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - "//vendor/sigs.k8s.io/yaml:go_default_library", - ], -) - filegroup( name = "package-srcs", srcs = glob(["**"]), @@ -85,4 +80,5 @@ filegroup( "//staging/src/k8s.io/apiserver/pkg/admission/testing:all-srcs", ], tags = ["automanaged"], + visibility = ["//visibility:public"], ) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD index 7787247d2c9..c7d1cfa4556 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/BUILD @@ -29,6 +29,7 @@ filegroup( "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/initializer:all-srcs", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating:all-srcs", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:all-srcs", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object:all-srcs", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request:all-srcs", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/rules:all-srcs", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts:all-srcs", diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/BUILD index 27b41c947ad..0fe2429c86c 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/BUILD @@ -24,6 +24,7 @@ go_library( "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/configuration:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/metrics:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request:go_default_library", diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/BUILD new file mode 100644 index 00000000000..6a069ce315b --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/BUILD @@ -0,0 +1,50 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "matcher.go", + ], + importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/object", + importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/object", + visibility = ["//visibility:public"], + deps = [ + "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", + "//vendor/k8s.io/klog:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = ["matcher_test.go"], + embed = [":go_default_library"], + deps = [ + "//staging/src/k8s.io/api/admissionregistration/v1beta1:go_default_library", + "//staging/src/k8s.io/api/core/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", + ], +) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/BUILD index b005e484bd7..c0147a54753 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/BUILD @@ -19,6 +19,7 @@ go_library( "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/configuration:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/metrics:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/request:go_default_library", diff --git a/vendor/modules.txt b/vendor/modules.txt index 5aa65435c93..45e45a07a9b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1193,6 +1193,7 @@ k8s.io/apiserver/pkg/admission/plugin/webhook/generic k8s.io/apiserver/pkg/admission/plugin/webhook/initializer k8s.io/apiserver/pkg/admission/plugin/webhook/mutating k8s.io/apiserver/pkg/admission/plugin/webhook/namespace +k8s.io/apiserver/pkg/admission/plugin/webhook/object k8s.io/apiserver/pkg/admission/plugin/webhook/request k8s.io/apiserver/pkg/admission/plugin/webhook/rules k8s.io/apiserver/pkg/admission/plugin/webhook/util From 6cf499db6c1dd464c6072706106dec6c5284dff7 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Wed, 29 May 2019 15:56:52 -0700 Subject: [PATCH 7/8] object matcher --- .../pkg/admission/plugin/webhook/accessors.go | 8 ++ .../admission/plugin/webhook/generic/BUILD | 2 + .../plugin/webhook/generic/interfaces.go | 17 ++- .../plugin/webhook/generic/webhook.go | 30 ++-- .../plugin/webhook/generic/webhook_test.go | 17 ++- .../plugin/webhook/mutating/dispatcher.go | 22 ++- .../admission/plugin/webhook/object/doc.go | 20 +++ .../plugin/webhook/object/matcher.go | 59 ++++++++ .../plugin/webhook/object/matcher_test.go | 130 ++++++++++++++++++ .../plugin/webhook/testing/testcase.go | 61 +++++++- .../plugin/webhook/testing/webhook_server.go | 11 ++ .../plugin/webhook/validating/dispatcher.go | 36 +++-- .../plugin/webhook/validating/plugin.go | 6 +- .../admissionwebhook/reinvocation_test.go | 20 ++- 14 files changed, 397 insertions(+), 42 deletions(-) create mode 100644 staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/doc.go create mode 100644 staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher.go create mode 100644 staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher_test.go diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go index 108e8ff442f..b44c72ebfc9 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/accessors.go @@ -40,6 +40,8 @@ type WebhookAccessor interface { GetMatchPolicy() *v1beta1.MatchPolicyType // GetNamespaceSelector gets the webhook NamespaceSelector field. GetNamespaceSelector() *metav1.LabelSelector + // GetObjectSelector gets the webhook ObjectSelector field. + GetObjectSelector() *metav1.LabelSelector // GetSideEffects gets the webhook SideEffects field. GetSideEffects() *v1beta1.SideEffectClass // GetTimeoutSeconds gets the webhook TimeoutSeconds field. @@ -84,6 +86,9 @@ func (m mutatingWebhookAccessor) GetMatchPolicy() *v1beta1.MatchPolicyType { func (m mutatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector { return m.NamespaceSelector } +func (m mutatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector { + return m.ObjectSelector +} func (m mutatingWebhookAccessor) GetSideEffects() *v1beta1.SideEffectClass { return m.SideEffects } @@ -133,6 +138,9 @@ func (v validatingWebhookAccessor) GetMatchPolicy() *v1beta1.MatchPolicyType { func (v validatingWebhookAccessor) GetNamespaceSelector() *metav1.LabelSelector { return v.NamespaceSelector } +func (v validatingWebhookAccessor) GetObjectSelector() *metav1.LabelSelector { + return v.ObjectSelector +} func (v validatingWebhookAccessor) GetSideEffects() *v1beta1.SideEffectClass { return v.SideEffects } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/BUILD index 7329da6c251..6572d3d6103 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/BUILD @@ -21,6 +21,7 @@ go_library( "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/config:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/rules:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library", "//staging/src/k8s.io/client-go/informers:go_default_library", @@ -59,6 +60,7 @@ go_test( "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object:go_default_library", "//staging/src/k8s.io/apiserver/pkg/apis/example:go_default_library", "//staging/src/k8s.io/apiserver/pkg/apis/example/v1:go_default_library", "//staging/src/k8s.io/apiserver/pkg/apis/example2/v1:go_default_library", diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go index 227502de84e..4381691ef81 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/interfaces.go @@ -35,7 +35,7 @@ type Source interface { // variants of the object and old object. type VersionedAttributes struct { // Attributes holds the original admission attributes - Attributes admission.Attributes + admission.Attributes // VersionedOldObject holds Attributes.OldObject (if non-nil), converted to VersionedKind. // It must never be mutated. VersionedOldObject runtime.Object @@ -48,6 +48,14 @@ type VersionedAttributes struct { Dirty bool } +// GetObject overrides the Attributes.GetObject() +func (v *VersionedAttributes) GetObject() runtime.Object { + if v.VersionedObject != nil { + return v.VersionedObject + } + return v.Attributes.GetObject() +} + // WebhookInvocation describes how to call a webhook, including the resource and subresource the webhook registered for, // and the kind that should be sent to the webhook. type WebhookInvocation struct { @@ -59,6 +67,9 @@ type WebhookInvocation struct { // Dispatcher dispatches webhook call to a list of webhooks with admission attributes as argument. type Dispatcher interface { - // Dispatch a request to the webhooks using the given webhooks. A non-nil error means the request is rejected. - Dispatch(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces, hooks []*WebhookInvocation) error + // Dispatch a request to the webhooks. Dispatcher may choose not to + // call a hook, either because the rules of the hook does not match, or + // the namespaceSelector or the objectSelector of the hook does not + // match. A non-nil error means the request is rejected. + Dispatch(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces, hooks []webhook.WebhookAccessor) error } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go index 99415292cbf..e88125ff5b8 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook.go @@ -30,6 +30,7 @@ import ( "k8s.io/apiserver/pkg/admission/plugin/webhook" "k8s.io/apiserver/pkg/admission/plugin/webhook/config" "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace" + "k8s.io/apiserver/pkg/admission/plugin/webhook/object" "k8s.io/apiserver/pkg/admission/plugin/webhook/rules" webhookutil "k8s.io/apiserver/pkg/util/webhook" "k8s.io/client-go/informers" @@ -45,6 +46,7 @@ type Webhook struct { hookSource Source clientManager *webhookutil.ClientManager namespaceMatcher *namespace.Matcher + objectMatcher *object.Matcher dispatcher Dispatcher } @@ -80,6 +82,7 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory sourceFactory: sourceFactory, clientManager: &cm, namespaceMatcher: &namespace.Matcher{}, + objectMatcher: &object.Matcher{}, dispatcher: dispatcherFactory(&cm), }, nil } @@ -127,9 +130,9 @@ func (a *Webhook) ValidateInitialization() error { return nil } -// shouldCallHook returns invocation details if the webhook should be called, nil if the webhook should not be called, +// ShouldCallHook returns invocation details if the webhook should be called, nil if the webhook should not be called, // or an error if an error was encountered during evaluation. -func (a *Webhook) shouldCallHook(h webhook.WebhookAccessor, attr admission.Attributes, o admission.ObjectInterfaces) (*WebhookInvocation, *apierrors.StatusError) { +func (a *Webhook) ShouldCallHook(h webhook.WebhookAccessor, attr admission.Attributes, o admission.ObjectInterfaces) (*WebhookInvocation, *apierrors.StatusError) { var err *apierrors.StatusError var invocation *WebhookInvocation for _, r := range h.GetRules() { @@ -184,6 +187,11 @@ func (a *Webhook) shouldCallHook(h webhook.WebhookAccessor, attr admission.Attri return nil, err } + matches, err = a.objectMatcher.MatchObjectSelector(h, attr) + if !matches || err != nil { + return nil, err + } + return invocation, nil } @@ -206,21 +214,5 @@ func (a *Webhook) Dispatch(attr admission.Attributes, o admission.ObjectInterfac // TODO: Figure out if adding one second timeout make sense here. ctx := context.TODO() - var relevantHooks []*WebhookInvocation - for i := range hooks { - invocation, err := a.shouldCallHook(hooks[i], attr, o) - if err != nil { - return err - } - if invocation != nil { - relevantHooks = append(relevantHooks, invocation) - } - } - - if len(relevantHooks) == 0 { - // no matching hooks - return nil - } - - return a.dispatcher.Dispatch(ctx, attr, o, relevantHooks) + return a.dispatcher.Dispatch(ctx, attr, o, hooks) } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go index 0d6cc9ea167..ad7fc7896e9 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/generic/webhook_test.go @@ -28,10 +28,11 @@ import ( "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission/plugin/webhook" "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace" + "k8s.io/apiserver/pkg/admission/plugin/webhook/object" ) func TestShouldCallHook(t *testing.T) { - a := &Webhook{namespaceMatcher: &namespace.Matcher{}} + a := &Webhook{namespaceMatcher: &namespace.Matcher{}, objectMatcher: &object.Matcher{}} allScopes := v1beta1.AllScopes exactMatch := v1beta1.Exact @@ -82,6 +83,7 @@ func TestShouldCallHook(t *testing.T) { name: "invalid kind lookup", webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, MatchPolicy: &equivalentMatch, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, @@ -95,6 +97,7 @@ func TestShouldCallHook(t *testing.T) { name: "wildcard rule, match as requested", webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes}, @@ -109,6 +112,7 @@ func TestShouldCallHook(t *testing.T) { name: "specific rules, prefer exact match", webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes}, @@ -129,6 +133,7 @@ func TestShouldCallHook(t *testing.T) { name: "specific rules, match miss", webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes}, @@ -144,6 +149,7 @@ func TestShouldCallHook(t *testing.T) { webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &exactMatch, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes}, @@ -159,6 +165,7 @@ func TestShouldCallHook(t *testing.T) { webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &equivalentMatch, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes}, @@ -177,6 +184,7 @@ func TestShouldCallHook(t *testing.T) { webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &equivalentMatch, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes}, @@ -195,6 +203,7 @@ func TestShouldCallHook(t *testing.T) { name: "specific rules, subresource prefer exact match", webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes}, @@ -215,6 +224,7 @@ func TestShouldCallHook(t *testing.T) { name: "specific rules, subresource match miss", webhook: &v1beta1.ValidatingWebhook{ NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes}, @@ -230,6 +240,7 @@ func TestShouldCallHook(t *testing.T) { webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &exactMatch, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes}, @@ -245,6 +256,7 @@ func TestShouldCallHook(t *testing.T) { webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &equivalentMatch, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes}, @@ -263,6 +275,7 @@ func TestShouldCallHook(t *testing.T) { webhook: &v1beta1.ValidatingWebhook{ MatchPolicy: &equivalentMatch, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: []v1beta1.RuleWithOperations{{ Operations: []v1beta1.OperationType{"*"}, Rule: v1beta1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes}, @@ -280,7 +293,7 @@ func TestShouldCallHook(t *testing.T) { for i, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { - invocation, err := a.shouldCallHook(webhook.NewValidatingWebhookAccessor(fmt.Sprintf("webhook-%d", i), testcase.webhook), testcase.attrs, interfaces) + invocation, err := a.ShouldCallHook(webhook.NewValidatingWebhookAccessor(fmt.Sprintf("webhook-%d", i), testcase.webhook), testcase.attrs, interfaces) if err != nil { if len(testcase.expectErr) == 0 { t.Fatal(err) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go index f1d8ce819d3..5c5c41aedbd 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go @@ -36,6 +36,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apiserver/pkg/admission" admissionmetrics "k8s.io/apiserver/pkg/admission/metrics" + "k8s.io/apiserver/pkg/admission/plugin/webhook" webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors" "k8s.io/apiserver/pkg/admission/plugin/webhook/generic" "k8s.io/apiserver/pkg/admission/plugin/webhook/request" @@ -56,7 +57,7 @@ func newMutatingDispatcher(p *Plugin) func(cm *webhookutil.ClientManager) generi var _ generic.Dispatcher = &mutatingDispatcher{} -func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces, relevantHooks []*generic.WebhookInvocation) error { +func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces, hooks []webhook.WebhookAccessor) error { reinvokeCtx := attr.GetReinvocationContext() var webhookReinvokeCtx *webhookReinvokeContext if v := reinvokeCtx.Value(PluginName); v != nil { @@ -75,14 +76,31 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib webhookReinvokeCtx.SetLastWebhookInvocationOutput(attr.GetObject()) }() var versionedAttr *generic.VersionedAttributes - for _, invocation := range relevantHooks { + for _, hook := range hooks { + attrForCheck := attr + if versionedAttr != nil { + attrForCheck = versionedAttr + } + invocation, statusErr := a.plugin.ShouldCallHook(hook, attrForCheck, o) + if statusErr != nil { + return statusErr + } + if invocation == nil { + continue + } hook, ok := invocation.Webhook.GetMutatingWebhook() if !ok { return fmt.Errorf("mutating webhook dispatch requires v1beta1.MutatingWebhook, but got %T", hook) } + // This means that during reinvocation, a webhook will not be + // called for the first time. For example, if the webhook is + // skipped in the first round because of mismatching labels, + // even if the labels become matching, the webhook does not + // get called during reinvocation. if reinvokeCtx.IsReinvoke() && !webhookReinvokeCtx.ShouldReinvokeWebhook(invocation.Webhook.GetUID()) { continue } + if versionedAttr == nil { // First webhook, create versioned attributes var err error diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/doc.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/doc.go new file mode 100644 index 00000000000..93c47344095 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package object defines the utilities that are used by the webhook plugin to +// decide if a webhook should run, as long as either the old object or the new +// object has labels matching the webhook config's objectSelector. +package object // import "k8s.io/apiserver/pkg/admission/plugin/webhook/object" diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher.go new file mode 100644 index 00000000000..be341dd95c9 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher.go @@ -0,0 +1,59 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package object + +import ( + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/admission" + "k8s.io/apiserver/pkg/admission/plugin/webhook" + "k8s.io/klog" +) + +// Matcher decides if a request selected by the ObjectSelector. +type Matcher struct { +} + +func matchObject(obj runtime.Object, selector labels.Selector) bool { + if obj == nil { + return false + } + accessor, err := meta.Accessor(obj) + if err != nil { + klog.V(5).Infof("cannot access metadata of %v: %v", obj, err) + return false + } + return selector.Matches(labels.Set(accessor.GetLabels())) + +} + +// MatchObjectSelector decideds whether the request matches the ObjectSelector +// of the webhook. Only when they match, the webhook is called. +func (m *Matcher) MatchObjectSelector(h webhook.WebhookAccessor, attr admission.Attributes) (bool, *apierrors.StatusError) { + // TODO: adding an LRU cache to cache the translation + selector, err := metav1.LabelSelectorAsSelector(h.GetObjectSelector()) + if err != nil { + return false, apierrors.NewInternalError(err) + } + if selector.Empty() { + return true, nil + } + return matchObject(attr.GetObject(), selector) || matchObject(attr.GetOldObject(), selector), nil +} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher_test.go new file mode 100644 index 00000000000..823fabc9644 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/object/matcher_test.go @@ -0,0 +1,130 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package object + +import ( + "testing" + + "k8s.io/api/admissionregistration/v1beta1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/admission" + "k8s.io/apiserver/pkg/admission/plugin/webhook" +) + +func TestObjectSelector(t *testing.T) { + nodeLevel1 := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "runlevel": "1", + }, + }, + } + nodeLevel2 := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "runlevel": "2", + }, + }, + } + runLevel1Excluder := &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "runlevel", + Operator: metav1.LabelSelectorOpNotIn, + Values: []string{"1"}, + }, + }, + } + matcher := &Matcher{} + allScopes := v1beta1.AllScopes + testcases := []struct { + name string + + objectSelector *metav1.LabelSelector + attrs admission.Attributes + + expectCall bool + }{ + { + name: "empty object selector matches everything", + objectSelector: &metav1.LabelSelector{}, + attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "name", schema.GroupVersionResource{}, "", admission.Create, &metav1.CreateOptions{}, false, nil), + expectCall: true, + }, + { + name: "matches new object", + objectSelector: runLevel1Excluder, + attrs: admission.NewAttributesRecord(nodeLevel2, nil, schema.GroupVersionKind{}, "", "name", schema.GroupVersionResource{}, "", admission.Create, &metav1.CreateOptions{}, false, nil), + expectCall: true, + }, + { + name: "matches old object", + objectSelector: runLevel1Excluder, + attrs: admission.NewAttributesRecord(nil, nodeLevel2, schema.GroupVersionKind{}, "", "name", schema.GroupVersionResource{}, "", admission.Delete, &metav1.DeleteOptions{}, false, nil), + expectCall: true, + }, + { + name: "does not match new object", + objectSelector: runLevel1Excluder, + attrs: admission.NewAttributesRecord(nodeLevel1, nil, schema.GroupVersionKind{}, "", "name", schema.GroupVersionResource{}, "", admission.Create, &metav1.CreateOptions{}, false, nil), + expectCall: false, + }, + { + name: "does not match old object", + objectSelector: runLevel1Excluder, + attrs: admission.NewAttributesRecord(nil, nodeLevel1, schema.GroupVersionKind{}, "", "name", schema.GroupVersionResource{}, "", admission.Create, &metav1.CreateOptions{}, false, nil), + expectCall: false, + }, + { + name: "does not match object that does not implement Object interface", + objectSelector: runLevel1Excluder, + attrs: admission.NewAttributesRecord(&corev1.NodeProxyOptions{}, nil, schema.GroupVersionKind{}, "", "name", schema.GroupVersionResource{}, "", admission.Create, &metav1.CreateOptions{}, false, nil), + expectCall: false, + }, + { + name: "empty selector matches everything, including object that does not implement Object interface", + objectSelector: &metav1.LabelSelector{}, + attrs: admission.NewAttributesRecord(&corev1.NodeProxyOptions{}, nil, schema.GroupVersionKind{}, "", "name", schema.GroupVersionResource{}, "", admission.Create, &metav1.CreateOptions{}, false, nil), + expectCall: true, + }, + } + + for _, testcase := range testcases { + hook := &v1beta1.ValidatingWebhook{ + NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: testcase.objectSelector, + Rules: []v1beta1.RuleWithOperations{{ + Operations: []v1beta1.OperationType{"*"}, + Rule: v1beta1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes}, + }}} + + t.Run(testcase.name, func(t *testing.T) { + match, err := matcher.MatchObjectSelector(webhook.NewValidatingWebhookAccessor("mock-hook", hook), testcase.attrs) + if err != nil { + t.Error(err) + } + if testcase.expectCall && !match { + t.Errorf("expected the webhook to be called") + } + if !testcase.expectCall && match { + t.Errorf("expected the webhook to be called") + } + }) + } +} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go index e3046d63449..4b53c468410 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go @@ -245,7 +245,7 @@ func ConvertToMutatingTestCases(tests []ValidatingTest) []MutatingTest { func ConvertToMutatingWebhooks(webhooks []registrationv1beta1.ValidatingWebhook) []registrationv1beta1.MutatingWebhook { mutating := make([]registrationv1beta1.MutatingWebhook, len(webhooks)) for i, h := range webhooks { - mutating[i] = registrationv1beta1.MutatingWebhook{h.Name, h.ClientConfig, h.Rules, h.FailurePolicy, h.MatchPolicy, h.NamespaceSelector, h.SideEffects, h.TimeoutSeconds, h.AdmissionReviewVersions, nil} + mutating[i] = registrationv1beta1.MutatingWebhook{h.Name, h.ClientConfig, h.Rules, h.FailurePolicy, h.MatchPolicy, h.NamespaceSelector, h.ObjectSelector, h.SideEffects, h.TimeoutSeconds, h.AdmissionReviewVersions, nil} } return mutating } @@ -552,6 +552,30 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { }}, ExpectAllow: true, }, + { + Name: "skip webhook whose objectSelector does not match", + Webhooks: []registrationv1beta1.ValidatingWebhook{{ + Name: "allow.example.com", + ClientConfig: ccfgSVC("allow"), + Rules: matchEverythingRules, + NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, + AdmissionReviewVersions: []string{"v1beta1"}, + }, { + Name: "shouldNotBeCalled", + ClientConfig: ccfgSVC("shouldNotBeCalled"), + NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "label": "nonexistent", + }, + }, + Rules: matchEverythingRules, + AdmissionReviewVersions: []string{"v1beta1"}, + }}, + ExpectAllow: true, + ExpectAnnotations: map[string]string{"allow.example.com/key1": "value1"}, + }, // No need to test everything with the url case, since only the // connection is different. } @@ -642,6 +666,36 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ExpectStatusCode: http.StatusBadRequest, ErrorContains: "does not support dry run", }, + { + Name: "first webhook remove labels, second webhook shouldn't be called", + Webhooks: []registrationv1beta1.MutatingWebhook{{ + Name: "removelabel.example.com", + ClientConfig: ccfgSVC("removeLabel"), + Rules: matchEverythingRules, + NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "remove": "me", + }, + }, + AdmissionReviewVersions: []string{"v1beta1"}, + }, { + Name: "shouldNotBeCalled", + ClientConfig: ccfgSVC("shouldNotBeCalled"), + NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "remove": "me", + }, + }, + Rules: matchEverythingRules, + AdmissionReviewVersions: []string{"v1beta1"}, + }}, + ExpectAllow: true, + AdditionalLabels: map[string]string{"remove": "me"}, + ExpectLabels: map[string]string{"pod.name": "my-pod"}, + ExpectAnnotations: map[string]string{"removelabel.example.com/key1": "value1"}, + }, // No need to test everything with the url case, since only the // connection is different. { @@ -651,6 +705,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("addLabel"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, ReinvocationPolicy: &reinvokeIfNeeded, }, { @@ -658,6 +713,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("removeLabel"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, ReinvocationPolicy: &reinvokeIfNeeded, }}, @@ -672,6 +728,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("addLabel"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, ReinvocationPolicy: &reinvokeNever, }}, @@ -685,6 +742,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("addLabel"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, @@ -697,6 +755,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("noop"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/webhook_server.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/webhook_server.go index 193571cd6f2..6a9ee5ff144 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/webhook_server.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/webhook_server.go @@ -82,6 +82,17 @@ func webhookHandler(w http.ResponseWriter, r *http.Request) { }, }, }) + case "/shouldNotBeCalled": + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(&v1beta1.AdmissionReview{ + Response: &v1beta1.AdmissionResponse{ + Allowed: false, + Result: &metav1.Status{ + Message: "doesn't expect labels to match object selector", + Code: http.StatusForbidden, + }, + }, + }) case "/allow": w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(&v1beta1.AdmissionReview{ diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go index 0505103d26d..dda52c90e57 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go @@ -29,6 +29,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apiserver/pkg/admission" admissionmetrics "k8s.io/apiserver/pkg/admission/metrics" + "k8s.io/apiserver/pkg/admission/plugin/webhook" webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors" "k8s.io/apiserver/pkg/admission/plugin/webhook/generic" "k8s.io/apiserver/pkg/admission/plugin/webhook/request" @@ -38,28 +39,45 @@ import ( ) type validatingDispatcher struct { - cm *webhookutil.ClientManager + cm *webhookutil.ClientManager + plugin *Plugin } -func newValidatingDispatcher(cm *webhookutil.ClientManager) generic.Dispatcher { - return &validatingDispatcher{cm} +func newValidatingDispatcher(p *Plugin) func(cm *webhookutil.ClientManager) generic.Dispatcher { + return func(cm *webhookutil.ClientManager) generic.Dispatcher { + return &validatingDispatcher{cm, p} + } } var _ generic.Dispatcher = &validatingDispatcher{} -func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces, relevantHooks []*generic.WebhookInvocation) error { +func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces, hooks []webhook.WebhookAccessor) error { + var relevantHooks []*generic.WebhookInvocation // Construct all the versions we need to call our webhooks versionedAttrs := map[schema.GroupVersionKind]*generic.VersionedAttributes{} - for _, call := range relevantHooks { - // If we already have this version, continue - if _, ok := versionedAttrs[call.Kind]; ok { + for _, hook := range hooks { + invocation, statusError := d.plugin.ShouldCallHook(hook, attr, o) + if statusError != nil { + return statusError + } + if invocation == nil { continue } - versionedAttr, err := generic.NewVersionedAttributes(attr, call.Kind, o) + relevantHooks = append(relevantHooks, invocation) + // If we already have this version, continue + if _, ok := versionedAttrs[invocation.Kind]; ok { + continue + } + versionedAttr, err := generic.NewVersionedAttributes(attr, invocation.Kind, o) if err != nil { return apierrors.NewInternalError(err) } - versionedAttrs[call.Kind] = versionedAttr + versionedAttrs[invocation.Kind] = versionedAttr + } + + if len(relevantHooks) == 0 { + // no matching hooks + return nil } wg := sync.WaitGroup{} diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go index 388a237c984..30e5c9d3319 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/plugin.go @@ -51,11 +51,13 @@ var _ admission.ValidationInterface = &Plugin{} // NewValidatingAdmissionWebhook returns a generic admission webhook plugin. func NewValidatingAdmissionWebhook(configFile io.Reader) (*Plugin, error) { handler := admission.NewHandler(admission.Connect, admission.Create, admission.Delete, admission.Update) - webhook, err := generic.NewWebhook(handler, configFile, configuration.NewValidatingWebhookConfigurationManager, newValidatingDispatcher) + p := &Plugin{} + var err error + p.Webhook, err = generic.NewWebhook(handler, configFile, configuration.NewValidatingWebhookConfigurationManager, newValidatingDispatcher(p)) if err != nil { return nil, err } - return &Plugin{webhook}, nil + return p, nil } // Validate makes an admission decision based on the request attributes. diff --git a/test/integration/apiserver/admissionwebhook/reinvocation_test.go b/test/integration/apiserver/admissionwebhook/reinvocation_test.go index e1296e1150c..417f616eca1 100644 --- a/test/integration/apiserver/admissionwebhook/reinvocation_test.go +++ b/test/integration/apiserver/admissionwebhook/reinvocation_test.go @@ -52,8 +52,9 @@ func TestWebhookReinvocationPolicy(t *testing.T) { reinvokeIfNeeded := registrationv1beta1.IfNeededReinvocationPolicy type testWebhook struct { - path string - policy *registrationv1beta1.ReinvocationPolicyType + path string + policy *registrationv1beta1.ReinvocationPolicyType + objectSelector *metav1.LabelSelector } testCases := []struct { @@ -110,6 +111,16 @@ func TestWebhookReinvocationPolicy(t *testing.T) { expectLabels: map[string]string{"x": "true", "fight": "false"}, expectInvocations: map[string]int{"/settrue": 2, "/setfalse": 2}, }, + { // in-tree (mutation), webhook A is SKIPPED due to objectSelector not matching, webhook B (mutation), reinvoke in-tree (no-mutation), webhook A is SKIPPED even though the labels match now, because it's not called in the first round. No reinvocation of webhook B required + name: "no reinvocation of webhook B when in-tree or prior webhook mutations", + initialPriorityClass: "low-priority", // trigger initial in-tree mutation + webhooks: []testWebhook{ + {path: "/conditionaladdlabel", policy: &reinvokeIfNeeded, objectSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}}, + {path: "/addlabel", policy: &reinvokeIfNeeded}, + }, + expectLabels: map[string]string{"x": "true", "a": "true"}, + expectInvocations: map[string]int{"/addlabel": 1, "/conditionaladdlabel": 0}, + }, { name: "invalid priority class set by webhook should result in error from in-tree priority plugin", webhooks: []testWebhook{ @@ -193,7 +204,7 @@ func TestWebhookReinvocationPolicy(t *testing.T) { } for i, webhook := range tt.webhooks { - defer registerWebhook(t, client, fmt.Sprintf("admission.integration.test%d", i), webhookServer.URL+webhook.path, webhook.policy)() + defer registerWebhook(t, client, fmt.Sprintf("admission.integration.test%d", i), webhookServer.URL+webhook.path, webhook.policy, webhook.objectSelector)() } pod := &corev1.Pod{ @@ -248,7 +259,7 @@ func TestWebhookReinvocationPolicy(t *testing.T) { } } -func registerWebhook(t *testing.T, client clientset.Interface, name, endpoint string, reinvocationPolicy *registrationv1beta1.ReinvocationPolicyType) func() { +func registerWebhook(t *testing.T, client clientset.Interface, name, endpoint string, reinvocationPolicy *registrationv1beta1.ReinvocationPolicyType, objectSelector *metav1.LabelSelector) func() { fail := admissionv1beta1.Fail hook, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionv1beta1.MutatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{Name: name}, @@ -262,6 +273,7 @@ func registerWebhook(t *testing.T, client clientset.Interface, name, endpoint st Operations: []admissionv1beta1.OperationType{admissionv1beta1.OperationAll}, Rule: admissionv1beta1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*/*"}}, }}, + ObjectSelector: objectSelector, FailurePolicy: &fail, ReinvocationPolicy: reinvocationPolicy, AdmissionReviewVersions: []string{"v1beta1"}, From 7738c7ee8fbbaa79aed2ca221141a6b3b4f826be Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Wed, 29 May 2019 17:20:43 -0700 Subject: [PATCH 8/8] minor changes, propagating interface changes --- .../plugin/webhook/testing/testcase.go | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go index 4b53c468410..fd2d5a0d797 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/testcase.go @@ -269,6 +269,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { Operations: []registrationv1beta1.OperationType{registrationv1beta1.Create}, }}, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, @@ -280,6 +281,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, @@ -292,6 +294,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("disallow"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectStatusCode: http.StatusForbidden, @@ -304,6 +307,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("disallowReason"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectStatusCode: http.StatusForbidden, @@ -322,6 +326,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { Operator: metav1.LabelSelectorOpIn, }}, }, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -340,6 +345,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { Operator: metav1.LabelSelectorOpNotIn, }}, }, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, @@ -351,6 +357,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("internalErr"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyIgnore, AdmissionReviewVersions: []string{"v1beta1"}, }, { @@ -358,6 +365,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("internalErr"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyIgnore, AdmissionReviewVersions: []string{"v1beta1"}, }, { @@ -365,6 +373,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("internalErr"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyIgnore, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -377,18 +386,21 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { Name: "internalErr A", ClientConfig: ccfgSVC("internalErr"), NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: matchEverythingRules, AdmissionReviewVersions: []string{"v1beta1"}, }, { Name: "internalErr B", ClientConfig: ccfgSVC("internalErr"), NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: matchEverythingRules, AdmissionReviewVersions: []string{"v1beta1"}, }, { Name: "internalErr C", ClientConfig: ccfgSVC("internalErr"), NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, Rules: matchEverythingRules, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -402,6 +414,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("internalErr"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyFail, AdmissionReviewVersions: []string{"v1beta1"}, }, { @@ -409,6 +422,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("internalErr"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyFail, AdmissionReviewVersions: []string{"v1beta1"}, }, { @@ -416,6 +430,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("internalErr"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyFail, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -429,6 +444,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgURL("allow"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, @@ -441,6 +457,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgURL("disallow"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectStatusCode: http.StatusForbidden, @@ -453,6 +470,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { FailurePolicy: &policyIgnore, Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, @@ -465,6 +483,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { FailurePolicy: &policyFail, Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectStatusCode: http.StatusInternalServerError, @@ -479,6 +498,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { Operations: []registrationv1beta1.OperationType{registrationv1beta1.Create}, }}, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, SideEffects: &sideEffectsSome, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -492,6 +512,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, SideEffects: &sideEffectsUnknown, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -506,6 +527,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, SideEffects: &sideEffectsNone, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -520,6 +542,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, SideEffects: &sideEffectsSome, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -534,6 +557,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgSVC("allow"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, SideEffects: &sideEffectsNoneOnDryRun, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -548,6 +572,7 @@ func NewNonMutatingTestCases(url *url.URL) []ValidatingTest { ClientConfig: ccfgURL("invalidAnnotation"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, @@ -593,6 +618,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("removeLabel"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, @@ -607,6 +633,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("addLabel"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectAllow: true, @@ -619,6 +646,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("addLabel"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, IsCRD: true, @@ -632,6 +660,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("removeLabel"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, IsCRD: true, @@ -647,6 +676,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("invalidMutation"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, AdmissionReviewVersions: []string{"v1beta1"}, }}, ExpectStatusCode: http.StatusInternalServerError, @@ -659,6 +689,7 @@ func NewMutatingTestCases(url *url.URL) []MutatingTest { ClientConfig: ccfgSVC("removeLabel"), Rules: matchEverythingRules, NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, SideEffects: &sideEffectsUnknown, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -784,6 +815,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { ClientConfig: ccfgSVC("allow"), Rules: newMatchEverythingRules(), NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyIgnore, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -797,6 +829,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { ClientConfig: ccfgSVC("internalErr"), Rules: newMatchEverythingRules(), NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyIgnore, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -810,6 +843,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { ClientConfig: ccfgSVC("allow"), Rules: newMatchEverythingRules(), NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyIgnore, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -823,6 +857,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { ClientConfig: ccfgURL("allow"), Rules: newMatchEverythingRules(), NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyIgnore, AdmissionReviewVersions: []string{"v1beta1"}, }}, @@ -836,6 +871,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest { ClientConfig: ccfgURL("allow"), Rules: newMatchEverythingRules(), NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, FailurePolicy: &policyIgnore, AdmissionReviewVersions: []string{"v1beta1"}, }},