mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
split admissionregistration.v1beta1/Webhook into MutatingWebhook and ValidatingWebhook
This commit is contained in:
parent
9356561c86
commit
55ecc45455
@ -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
|
||||||
staging/src/k8s.io/apiserver/pkg/admission/configuration
|
staging/src/k8s.io/apiserver/pkg/admission/configuration
|
||||||
staging/src/k8s.io/apiserver/pkg/admission/initializer
|
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
|
||||||
staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1
|
staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1
|
||||||
staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts
|
staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testcerts
|
||||||
|
@ -33,7 +33,21 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||||||
obj.Scope = &s
|
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
|
c.FuzzNoCustom(obj) // fuzz self without calling this function again
|
||||||
p := admissionregistration.FailurePolicyType("Fail")
|
p := admissionregistration.FailurePolicyType("Fail")
|
||||||
obj.FailurePolicy = &p
|
obj.FailurePolicy = &p
|
||||||
|
@ -123,7 +123,7 @@ type ValidatingWebhookConfiguration struct {
|
|||||||
metav1.ObjectMeta
|
metav1.ObjectMeta
|
||||||
// Webhooks is a list of webhooks and the affected resources and operations.
|
// Webhooks is a list of webhooks and the affected resources and operations.
|
||||||
// +optional
|
// +optional
|
||||||
Webhooks []Webhook
|
Webhooks []ValidatingWebhook
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
@ -149,7 +149,7 @@ type MutatingWebhookConfiguration struct {
|
|||||||
metav1.ObjectMeta
|
metav1.ObjectMeta
|
||||||
// Webhooks is a list of webhooks and the affected resources and operations.
|
// Webhooks is a list of webhooks and the affected resources and operations.
|
||||||
// +optional
|
// +optional
|
||||||
Webhooks []Webhook
|
Webhooks []MutatingWebhook
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
@ -165,8 +165,118 @@ type MutatingWebhookConfigurationList struct {
|
|||||||
Items []MutatingWebhookConfiguration
|
Items []MutatingWebhookConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Webhook describes an admission webhook and the resources and operations it applies to.
|
// ValidatingWebhook describes an admission webhook and the resources and operations it applies to.
|
||||||
type Webhook struct {
|
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.
|
// The name of the admission webhook.
|
||||||
// Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where
|
// Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where
|
||||||
// "imagepolicy" is the name of the webhook, and kubernetes.io is the name
|
// "imagepolicy" is the name of the webhook, and kubernetes.io is the name
|
||||||
|
@ -27,7 +27,35 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
|||||||
return RegisterDefaults(scheme)
|
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 {
|
if obj.FailurePolicy == nil {
|
||||||
policy := admissionregistrationv1beta1.Ignore
|
policy := admissionregistrationv1beta1.Ignore
|
||||||
obj.FailurePolicy = &policy
|
obj.FailurePolicy = &policy
|
||||||
|
@ -201,7 +201,7 @@ func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingW
|
|||||||
func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration, requireRecognizedVersion bool) field.ErrorList {
|
func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration, requireRecognizedVersion bool) field.ErrorList {
|
||||||
allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
||||||
for i, hook := range e.Webhooks {
|
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"))...)
|
allErrors = append(allErrors, validateAdmissionReviewVersions(hook.AdmissionReviewVersions, requireRecognizedVersion, field.NewPath("webhooks").Index(i).Child("admissionReviewVersions"))...)
|
||||||
}
|
}
|
||||||
return allErrors
|
return allErrors
|
||||||
@ -214,13 +214,50 @@ func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebho
|
|||||||
func validateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration, requireRecognizedVersion bool) field.ErrorList {
|
func validateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration, requireRecognizedVersion bool) field.ErrorList {
|
||||||
allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
||||||
for i, hook := range e.Webhooks {
|
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"))...)
|
allErrors = append(allErrors, validateAdmissionReviewVersions(hook.AdmissionReviewVersions, requireRecognizedVersion, field.NewPath("webhooks").Index(i).Child("admissionReviewVersions"))...)
|
||||||
}
|
}
|
||||||
return allErrors
|
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
|
var allErrors field.ErrorList
|
||||||
// hook.Name must be fully qualified
|
// hook.Name must be fully qualified
|
||||||
allErrors = append(allErrors, utilvalidation.IsFullyQualifiedName(fldPath.Child("name"), hook.Name)...)
|
allErrors = append(allErrors, utilvalidation.IsFullyQualifiedName(fldPath.Child("name"), hook.Name)...)
|
||||||
@ -309,9 +346,27 @@ func validateRuleWithOperations(ruleWithOperations *admissionregistration.RuleWi
|
|||||||
return allErrors
|
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.
|
// 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 {
|
for _, hook := range webhooks {
|
||||||
hasRecognizedVersion := false
|
hasRecognizedVersion := false
|
||||||
for _, version := range hook.AdmissionReviewVersions {
|
for _, version := range hook.AdmissionReviewVersions {
|
||||||
@ -328,9 +383,9 @@ func hasAcceptedAdmissionReviewVersions(webhooks []admissionregistration.Webhook
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList {
|
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 {
|
func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.MutatingWebhookConfiguration) field.ErrorList {
|
||||||
return validateMutatingWebhookConfiguration(newC, hasAcceptedAdmissionReviewVersions(oldC.Webhooks))
|
return validateMutatingWebhookConfiguration(newC, mutatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks))
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func strPtr(s string) *string { return &s }
|
|||||||
|
|
||||||
func int32Ptr(i int32) *int32 { return &i }
|
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
|
// If the test case did not specify an AdmissionReviewVersions, default it so the test passes as
|
||||||
// this field will be defaulted in production code.
|
// this field will be defaulted in production code.
|
||||||
for i := range hooks {
|
for i := range hooks {
|
||||||
@ -57,7 +57,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "should fail on bad AdmissionReviewVersion value",
|
name: "should fail on bad AdmissionReviewVersion value",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -68,7 +68,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should pass on valid AdmissionReviewVersion",
|
name: "should pass on valid AdmissionReviewVersion",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -79,7 +79,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should pass on mix of accepted and unaccepted AdmissionReviewVersion",
|
name: "should pass on mix of accepted and unaccepted AdmissionReviewVersion",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -90,7 +90,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should fail on invalid AdmissionReviewVersion",
|
name: "should fail on invalid AdmissionReviewVersion",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -101,7 +101,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should fail on duplicate AdmissionReviewVersion",
|
name: "should fail on duplicate AdmissionReviewVersion",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -112,7 +112,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all Webhooks must have a fully qualified name",
|
name: "all Webhooks must have a fully qualified name",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -130,7 +130,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Operations must not be empty or nil",
|
name: "Operations must not be empty or nil",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
Rules: []admissionregistration.RuleWithOperations{
|
Rules: []admissionregistration.RuleWithOperations{
|
||||||
@ -157,7 +157,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "\"\" is NOT a valid operation",
|
name: "\"\" is NOT a valid operation",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
Rules: []admissionregistration.RuleWithOperations{
|
Rules: []admissionregistration.RuleWithOperations{
|
||||||
@ -176,7 +176,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "operation must be either create/update/delete/connect",
|
name: "operation must be either create/update/delete/connect",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
Rules: []admissionregistration.RuleWithOperations{
|
Rules: []admissionregistration.RuleWithOperations{
|
||||||
@ -195,7 +195,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wildcard operation cannot be mixed with other strings",
|
name: "wildcard operation cannot be mixed with other strings",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
Rules: []admissionregistration.RuleWithOperations{
|
Rules: []admissionregistration.RuleWithOperations{
|
||||||
@ -214,7 +214,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: `resource "*" can co-exist with resources that have subresources`,
|
name: `resource "*" can co-exist with resources that have subresources`,
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -233,7 +233,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: `resource "*" cannot mix with resources that don't have subresources`,
|
name: `resource "*" cannot mix with resources that don't have subresources`,
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -253,7 +253,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "resource a/* cannot mix with a/x",
|
name: "resource a/* cannot mix with a/x",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -273,7 +273,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "resource a/* can mix with a",
|
name: "resource a/* can mix with a",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -292,7 +292,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "resource */a cannot mix with x/a",
|
name: "resource */a cannot mix with x/a",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -312,7 +312,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "resource */* cannot mix with other resources",
|
name: "resource */* cannot mix with other resources",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -332,7 +332,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "FailurePolicy can only be \"Ignore\" or \"Fail\"",
|
name: "FailurePolicy can only be \"Ignore\" or \"Fail\"",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -346,7 +346,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\"",
|
name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\"",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -360,7 +360,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "both service and URL missing",
|
name: "both service and URL missing",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{},
|
ClientConfig: admissionregistration.WebhookClientConfig{},
|
||||||
@ -370,7 +370,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "both service and URL provided",
|
name: "both service and URL provided",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -387,7 +387,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "blank URL",
|
name: "blank URL",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -399,7 +399,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wrong scheme",
|
name: "wrong scheme",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -411,7 +411,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing host",
|
name: "missing host",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -423,7 +423,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "fragment",
|
name: "fragment",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -435,7 +435,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "query",
|
name: "query",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -447,7 +447,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "user",
|
name: "user",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -459,7 +459,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "just totally wrong",
|
name: "just totally wrong",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -471,7 +471,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "path must start with slash",
|
name: "path must start with slash",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -488,7 +488,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "path accepts slash",
|
name: "path accepts slash",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -505,7 +505,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "path accepts no trailing slash",
|
name: "path accepts no trailing slash",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -522,7 +522,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "path fails //",
|
name: "path fails //",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -539,7 +539,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "path no empty step",
|
name: "path no empty step",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
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`,
|
expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`,
|
||||||
}, {
|
}, {
|
||||||
name: "path no empty step 2",
|
name: "path no empty step 2",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -572,7 +572,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "path no non-subdomain",
|
name: "path no non-subdomain",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -590,7 +590,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid port 0",
|
name: "invalid port 0",
|
||||||
config: newValidatingWebhookConfiguration(
|
config: newValidatingWebhookConfiguration(
|
||||||
[]admissionregistration.Webhook{
|
[]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -608,7 +608,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid port >65535",
|
name: "invalid port >65535",
|
||||||
config: newValidatingWebhookConfiguration(
|
config: newValidatingWebhookConfiguration(
|
||||||
[]admissionregistration.Webhook{
|
[]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: admissionregistration.WebhookClientConfig{
|
ClientConfig: admissionregistration.WebhookClientConfig{
|
||||||
@ -625,7 +625,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "timeout seconds cannot be greater than 30",
|
name: "timeout seconds cannot be greater than 30",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -636,7 +636,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "timeout seconds cannot be smaller than 1",
|
name: "timeout seconds cannot be smaller than 1",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -647,7 +647,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "timeout seconds must be positive",
|
name: "timeout seconds must be positive",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -658,7 +658,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid timeout seconds",
|
name: "valid timeout seconds",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -707,14 +707,14 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "should pass on valid new AdmissionReviewVersion",
|
name: "should pass on valid new AdmissionReviewVersion",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
AdmissionReviewVersions: []string{"v1beta1"},
|
AdmissionReviewVersions: []string{"v1beta1"},
|
||||||
},
|
},
|
||||||
}, true),
|
}, true),
|
||||||
oldconfig: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -724,14 +724,14 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should pass on invalid AdmissionReviewVersion with invalid previous versions",
|
name: "should pass on invalid AdmissionReviewVersion with invalid previous versions",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"},
|
AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"},
|
||||||
},
|
},
|
||||||
}, true),
|
}, true),
|
||||||
oldconfig: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -742,14 +742,14 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should fail on invalid AdmissionReviewVersion with valid previous versions",
|
name: "should fail on invalid AdmissionReviewVersion with valid previous versions",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
AdmissionReviewVersions: []string{"invalid-v1"},
|
AdmissionReviewVersions: []string{"invalid-v1"},
|
||||||
},
|
},
|
||||||
}, true),
|
}, true),
|
||||||
oldconfig: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
@ -760,14 +760,14 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "should fail on invalid AdmissionReviewVersion with missing previous versions",
|
name: "should fail on invalid AdmissionReviewVersion with missing previous versions",
|
||||||
config: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
AdmissionReviewVersions: []string{"invalid-v1"},
|
AdmissionReviewVersions: []string{"invalid-v1"},
|
||||||
},
|
},
|
||||||
}, true),
|
}, true),
|
||||||
oldconfig: newValidatingWebhookConfiguration([]admissionregistration.Webhook{
|
oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "webhook.k8s.io",
|
Name: "webhook.k8s.io",
|
||||||
ClientConfig: validClientConfig,
|
ClientConfig: validClientConfig,
|
||||||
|
@ -124,7 +124,7 @@ type ValidatingWebhookConfiguration struct {
|
|||||||
// +optional
|
// +optional
|
||||||
// +patchMergeKey=name
|
// +patchMergeKey=name
|
||||||
// +patchStrategy=merge
|
// +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
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
@ -154,7 +154,7 @@ type MutatingWebhookConfiguration struct {
|
|||||||
// +optional
|
// +optional
|
||||||
// +patchMergeKey=name
|
// +patchMergeKey=name
|
||||||
// +patchStrategy=merge
|
// +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
|
// +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"`
|
Items []MutatingWebhookConfiguration `json:"items" protobuf:"bytes,2,rep,name=items"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Webhook describes an admission webhook and the resources and operations it applies to.
|
// ValidatingWebhook describes an admission webhook and the resources and operations it applies to.
|
||||||
type Webhook struct {
|
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.
|
// The name of the admission webhook.
|
||||||
// Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where
|
// Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where
|
||||||
// "imagepolicy" is the name of the webhook, and kubernetes.io is the name
|
// "imagepolicy" is the name of the webhook, and kubernetes.io is the name
|
||||||
|
@ -80,18 +80,7 @@ filegroup(
|
|||||||
"//staging/src/k8s.io/apiserver/pkg/admission/initializer:all-srcs",
|
"//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/metrics:all-srcs",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle: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: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/testing:all-srcs",
|
"//staging/src/k8s.io/apiserver/pkg/admission/testing:all-srcs",
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
|
@ -39,6 +39,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_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/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait: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/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/informers:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/listers/admissionregistration/v1beta1:go_default_library",
|
"//staging/src/k8s.io/client-go/listers/admissionregistration/v1beta1:go_default_library",
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"k8s.io/api/admissionregistration/v1beta1"
|
"k8s.io/api/admissionregistration/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
|
admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
|
||||||
@ -48,7 +49,7 @@ func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) g
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start with an empty list
|
// Start with an empty list
|
||||||
manager.configuration.Store(&v1beta1.MutatingWebhookConfiguration{})
|
manager.configuration.Store([]webhook.WebhookAccessor{})
|
||||||
|
|
||||||
// On any change, rebuild the config
|
// On any change, rebuild the config
|
||||||
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||||
@ -61,8 +62,8 @@ func NewMutatingWebhookConfigurationManager(f informers.SharedInformerFactory) g
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Webhooks returns the merged MutatingWebhookConfiguration.
|
// Webhooks returns the merged MutatingWebhookConfiguration.
|
||||||
func (m *mutatingWebhookConfigurationManager) Webhooks() []v1beta1.Webhook {
|
func (m *mutatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccessor {
|
||||||
return m.configuration.Load().(*v1beta1.MutatingWebhookConfiguration).Webhooks
|
return m.configuration.Load().([]webhook.WebhookAccessor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mutatingWebhookConfigurationManager) HasSynced() bool {
|
func (m *mutatingWebhookConfigurationManager) HasSynced() bool {
|
||||||
@ -78,16 +79,18 @@ func (m *mutatingWebhookConfigurationManager) updateConfiguration() {
|
|||||||
m.configuration.Store(mergeMutatingWebhookConfigurations(configurations))
|
m.configuration.Store(mergeMutatingWebhookConfigurations(configurations))
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeMutatingWebhookConfigurations(configurations []*v1beta1.MutatingWebhookConfiguration) *v1beta1.MutatingWebhookConfiguration {
|
func mergeMutatingWebhookConfigurations(configurations []*v1beta1.MutatingWebhookConfiguration) []webhook.WebhookAccessor {
|
||||||
var ret v1beta1.MutatingWebhookConfiguration
|
|
||||||
// The internal order of webhooks for each configuration is provided by the user
|
// 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
|
// 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.
|
// webhooks in serial, they are sorted here to have a deterministic order.
|
||||||
sort.SliceStable(configurations, MutatingWebhookConfigurationSorter(configurations).ByName)
|
sort.SliceStable(configurations, MutatingWebhookConfigurationSorter(configurations).ByName)
|
||||||
|
accessors := []webhook.WebhookAccessor{}
|
||||||
for _, c := range configurations {
|
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
|
type MutatingWebhookConfigurationSorter []*v1beta1.MutatingWebhookConfiguration
|
||||||
|
@ -45,7 +45,7 @@ func TestGetMutatingWebhookConfig(t *testing.T) {
|
|||||||
|
|
||||||
webhookConfiguration := &v1beta1.MutatingWebhookConfiguration{
|
webhookConfiguration := &v1beta1.MutatingWebhookConfiguration{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "webhook1"},
|
ObjectMeta: metav1.ObjectMeta{Name: "webhook1"},
|
||||||
Webhooks: []v1beta1.Webhook{{Name: "webhook1.1"}},
|
Webhooks: []v1beta1.MutatingWebhook{{Name: "webhook1.1"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
mutatingInformer := informerFactory.Admissionregistration().V1beta1().MutatingWebhookConfigurations()
|
mutatingInformer := informerFactory.Admissionregistration().V1beta1().MutatingWebhookConfigurations()
|
||||||
@ -57,7 +57,14 @@ func TestGetMutatingWebhookConfig(t *testing.T) {
|
|||||||
if len(configurations) == 0 {
|
if len(configurations) == 0 {
|
||||||
t.Errorf("expected non empty webhooks")
|
t.Errorf("expected non empty webhooks")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(configurations, webhookConfiguration.Webhooks) {
|
for i := range configurations {
|
||||||
t.Errorf("Expected\n%#v\ngot\n%#v", webhookConfiguration.Webhooks, 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"k8s.io/api/admissionregistration/v1beta1"
|
"k8s.io/api/admissionregistration/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
|
admissionregistrationlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
|
||||||
@ -48,7 +49,7 @@ func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start with an empty list
|
// Start with an empty list
|
||||||
manager.configuration.Store(&v1beta1.ValidatingWebhookConfiguration{})
|
manager.configuration.Store([]webhook.WebhookAccessor{})
|
||||||
|
|
||||||
// On any change, rebuild the config
|
// On any change, rebuild the config
|
||||||
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||||
@ -61,8 +62,8 @@ func NewValidatingWebhookConfigurationManager(f informers.SharedInformerFactory)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Webhooks returns the merged ValidatingWebhookConfiguration.
|
// Webhooks returns the merged ValidatingWebhookConfiguration.
|
||||||
func (v *validatingWebhookConfigurationManager) Webhooks() []v1beta1.Webhook {
|
func (v *validatingWebhookConfigurationManager) Webhooks() []webhook.WebhookAccessor {
|
||||||
return v.configuration.Load().(*v1beta1.ValidatingWebhookConfiguration).Webhooks
|
return v.configuration.Load().([]webhook.WebhookAccessor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasSynced returns true if the shared informers have synced.
|
// HasSynced returns true if the shared informers have synced.
|
||||||
@ -79,15 +80,15 @@ func (v *validatingWebhookConfigurationManager) updateConfiguration() {
|
|||||||
v.configuration.Store(mergeValidatingWebhookConfigurations(configurations))
|
v.configuration.Store(mergeValidatingWebhookConfigurations(configurations))
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeValidatingWebhookConfigurations(
|
func mergeValidatingWebhookConfigurations(configurations []*v1beta1.ValidatingWebhookConfiguration) []webhook.WebhookAccessor {
|
||||||
configurations []*v1beta1.ValidatingWebhookConfiguration,
|
|
||||||
) *v1beta1.ValidatingWebhookConfiguration {
|
|
||||||
sort.SliceStable(configurations, ValidatingWebhookConfigurationSorter(configurations).ByName)
|
sort.SliceStable(configurations, ValidatingWebhookConfigurationSorter(configurations).ByName)
|
||||||
var ret v1beta1.ValidatingWebhookConfiguration
|
accessors := []webhook.WebhookAccessor{}
|
||||||
for _, c := range configurations {
|
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
|
type ValidatingWebhookConfigurationSorter []*v1beta1.ValidatingWebhookConfiguration
|
||||||
|
@ -46,7 +46,7 @@ func TestGetValidatingWebhookConfig(t *testing.T) {
|
|||||||
|
|
||||||
webhookConfiguration := &v1beta1.ValidatingWebhookConfiguration{
|
webhookConfiguration := &v1beta1.ValidatingWebhookConfiguration{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "webhook1"},
|
ObjectMeta: metav1.ObjectMeta{Name: "webhook1"},
|
||||||
Webhooks: []v1beta1.Webhook{{Name: "webhook1.1"}},
|
Webhooks: []v1beta1.ValidatingWebhook{{Name: "webhook1.1"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
validatingInformer := informerFactory.Admissionregistration().V1beta1().ValidatingWebhookConfigurations()
|
validatingInformer := informerFactory.Admissionregistration().V1beta1().ValidatingWebhookConfigurations()
|
||||||
@ -59,7 +59,14 @@ func TestGetValidatingWebhookConfig(t *testing.T) {
|
|||||||
if len(configurations) == 0 {
|
if len(configurations) == 0 {
|
||||||
t.Errorf("expected non empty webhooks")
|
t.Errorf("expected non empty webhooks")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(configurations, webhookConfiguration.Webhooks) {
|
for i := range configurations {
|
||||||
t.Errorf("Expected\n%#v\ngot\n%#v", webhookConfiguration.Webhooks, 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"],
|
||||||
|
)
|
@ -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
|
||||||
|
}
|
@ -18,6 +18,7 @@ go_library(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema: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:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/admission/initializer: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/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/namespace:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/rules: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/runtime/schema:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff: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: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/namespace:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/apis/example: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/example/v1:go_default_library",
|
||||||
|
@ -19,15 +19,15 @@ package generic
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"k8s.io/api/admissionregistration/v1beta1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source can list dynamic webhook plugins.
|
// Source can list dynamic webhook plugins.
|
||||||
type Source interface {
|
type Source interface {
|
||||||
Webhooks() []v1beta1.Webhook
|
Webhooks() []webhook.WebhookAccessor
|
||||||
HasSynced() bool
|
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,
|
// 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.
|
// and the kind that should be sent to the webhook.
|
||||||
type WebhookInvocation struct {
|
type WebhookInvocation struct {
|
||||||
Webhook *v1beta1.Webhook
|
Webhook webhook.WebhookAccessor
|
||||||
|
|
||||||
Resource schema.GroupVersionResource
|
Resource schema.GroupVersionResource
|
||||||
Subresource string
|
Subresource string
|
||||||
Kind schema.GroupVersionKind
|
Kind schema.GroupVersionKind
|
||||||
|
@ -27,10 +27,11 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
|
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/config"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/rules"
|
"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"
|
"k8s.io/client-go/informers"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
@ -42,7 +43,7 @@ type Webhook struct {
|
|||||||
sourceFactory sourceFactory
|
sourceFactory sourceFactory
|
||||||
|
|
||||||
hookSource Source
|
hookSource Source
|
||||||
clientManager *webhook.ClientManager
|
clientManager *webhookutil.ClientManager
|
||||||
namespaceMatcher *namespace.Matcher
|
namespaceMatcher *namespace.Matcher
|
||||||
dispatcher Dispatcher
|
dispatcher Dispatcher
|
||||||
}
|
}
|
||||||
@ -53,7 +54,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type sourceFactory func(f informers.SharedInformerFactory) Source
|
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.
|
// NewWebhook creates a new generic admission webhook.
|
||||||
func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory sourceFactory, dispatcherFactory dispatcherFactory) (*Webhook, error) {
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cm, err := webhook.NewClientManager(admissionv1beta1.SchemeGroupVersion, admissionv1beta1.AddToScheme)
|
cm, err := webhookutil.NewClientManager(admissionv1beta1.SchemeGroupVersion, admissionv1beta1.AddToScheme)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
authInfoResolver, err := webhook.NewDefaultAuthenticationInfoResolver(kubeconfigFile)
|
authInfoResolver, err := webhookutil.NewDefaultAuthenticationInfoResolver(kubeconfigFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Set defaults which may be overridden later.
|
// Set defaults which may be overridden later.
|
||||||
cm.SetAuthenticationInfoResolver(authInfoResolver)
|
cm.SetAuthenticationInfoResolver(authInfoResolver)
|
||||||
cm.SetServiceResolver(webhook.NewDefaultServiceResolver())
|
cm.SetServiceResolver(webhookutil.NewDefaultServiceResolver())
|
||||||
|
|
||||||
return &Webhook{
|
return &Webhook{
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
@ -86,13 +87,13 @@ func NewWebhook(handler *admission.Handler, configFile io.Reader, sourceFactory
|
|||||||
// SetAuthenticationInfoResolverWrapper sets the
|
// SetAuthenticationInfoResolverWrapper sets the
|
||||||
// AuthenticationInfoResolverWrapper.
|
// AuthenticationInfoResolverWrapper.
|
||||||
// TODO find a better way wire this, but keep this pull small for now.
|
// 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)
|
a.clientManager.SetAuthenticationInfoResolverWrapper(wrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetServiceResolver sets a service resolver for the webhook admission plugin.
|
// 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.
|
// 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)
|
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,
|
// 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.
|
// 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 err *apierrors.StatusError
|
||||||
var invocation *WebhookInvocation
|
var invocation *WebhookInvocation
|
||||||
for _, r := range h.Rules {
|
for _, r := range h.GetRules() {
|
||||||
m := rules.Matcher{Rule: r, Attr: attr}
|
m := rules.Matcher{Rule: r, Attr: attr}
|
||||||
if m.Matches() {
|
if m.Matches() {
|
||||||
invocation = &WebhookInvocation{
|
invocation = &WebhookInvocation{
|
||||||
@ -143,12 +144,12 @@ func (a *Webhook) shouldCallHook(h *v1beta1.Webhook, attr admission.Attributes,
|
|||||||
break
|
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}
|
attrWithOverride := &attrWithResourceOverride{Attributes: attr}
|
||||||
equivalents := o.GetEquivalentResourceMapper().EquivalentResourcesFor(attr.GetResource(), attr.GetSubresource())
|
equivalents := o.GetEquivalentResourceMapper().EquivalentResourcesFor(attr.GetResource(), attr.GetSubresource())
|
||||||
// honor earlier rules first
|
// honor earlier rules first
|
||||||
OuterLoop:
|
OuterLoop:
|
||||||
for _, r := range h.Rules {
|
for _, r := range h.GetRules() {
|
||||||
// see if the rule matches any of the equivalent resources
|
// see if the rule matches any of the equivalent resources
|
||||||
for _, equivalent := range equivalents {
|
for _, equivalent := range equivalents {
|
||||||
if equivalent == attr.GetResource() {
|
if equivalent == attr.GetResource() {
|
||||||
@ -207,7 +208,7 @@ func (a *Webhook) Dispatch(attr admission.Attributes, o admission.ObjectInterfac
|
|||||||
|
|
||||||
var relevantHooks []*WebhookInvocation
|
var relevantHooks []*WebhookInvocation
|
||||||
for i := range hooks {
|
for i := range hooks {
|
||||||
invocation, err := a.shouldCallHook(&hooks[i], attr, o)
|
invocation, err := a.shouldCallHook(hooks[i], attr, o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"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/namespace"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,7 +62,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
||||||
webhook *v1beta1.Webhook
|
webhook *v1beta1.ValidatingWebhook
|
||||||
attrs admission.Attributes
|
attrs admission.Attributes
|
||||||
|
|
||||||
expectCall bool
|
expectCall bool
|
||||||
@ -72,13 +73,13 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no rules (just write)",
|
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),
|
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,
|
expectCall: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid kind lookup",
|
name: "invalid kind lookup",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
MatchPolicy: &equivalentMatch,
|
MatchPolicy: &equivalentMatch,
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -91,7 +92,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wildcard rule, match as requested",
|
name: "wildcard rule, match as requested",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
Operations: []v1beta1.OperationType{"*"},
|
Operations: []v1beta1.OperationType{"*"},
|
||||||
@ -105,7 +106,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "specific rules, prefer exact match",
|
name: "specific rules, prefer exact match",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
Operations: []v1beta1.OperationType{"*"},
|
Operations: []v1beta1.OperationType{"*"},
|
||||||
@ -125,7 +126,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "specific rules, match miss",
|
name: "specific rules, match miss",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
Operations: []v1beta1.OperationType{"*"},
|
Operations: []v1beta1.OperationType{"*"},
|
||||||
@ -139,7 +140,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "specific rules, exact match miss",
|
name: "specific rules, exact match miss",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
MatchPolicy: &exactMatch,
|
MatchPolicy: &exactMatch,
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -154,7 +155,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "specific rules, equivalent match, prefer extensions",
|
name: "specific rules, equivalent match, prefer extensions",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
MatchPolicy: &equivalentMatch,
|
MatchPolicy: &equivalentMatch,
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -172,7 +173,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "specific rules, equivalent match, prefer apps",
|
name: "specific rules, equivalent match, prefer apps",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
MatchPolicy: &equivalentMatch,
|
MatchPolicy: &equivalentMatch,
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -191,7 +192,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
name: "specific rules, subresource prefer exact match",
|
name: "specific rules, subresource prefer exact match",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
Operations: []v1beta1.OperationType{"*"},
|
Operations: []v1beta1.OperationType{"*"},
|
||||||
@ -211,7 +212,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "specific rules, subresource match miss",
|
name: "specific rules, subresource match miss",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
Operations: []v1beta1.OperationType{"*"},
|
Operations: []v1beta1.OperationType{"*"},
|
||||||
@ -225,7 +226,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "specific rules, subresource exact match miss",
|
name: "specific rules, subresource exact match miss",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
MatchPolicy: &exactMatch,
|
MatchPolicy: &exactMatch,
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -240,7 +241,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "specific rules, subresource equivalent match, prefer extensions",
|
name: "specific rules, subresource equivalent match, prefer extensions",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
MatchPolicy: &equivalentMatch,
|
MatchPolicy: &equivalentMatch,
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -258,7 +259,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "specific rules, subresource equivalent match, prefer apps",
|
name: "specific rules, subresource equivalent match, prefer apps",
|
||||||
webhook: &v1beta1.Webhook{
|
webhook: &v1beta1.ValidatingWebhook{
|
||||||
MatchPolicy: &equivalentMatch,
|
MatchPolicy: &equivalentMatch,
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -278,7 +279,7 @@ func TestShouldCallHook(t *testing.T) {
|
|||||||
|
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
t.Run(testcase.name, func(t *testing.T) {
|
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 err != nil {
|
||||||
if len(testcase.expectErr) == 0 {
|
if len(testcase.expectErr) == 0 {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -39,16 +39,16 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/util"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/util"
|
||||||
"k8s.io/apiserver/pkg/util/webhook"
|
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mutatingDispatcher struct {
|
type mutatingDispatcher struct {
|
||||||
cm *webhook.ClientManager
|
cm *webhookutil.ClientManager
|
||||||
plugin *Plugin
|
plugin *Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMutatingDispatcher(p *Plugin) func(cm *webhook.ClientManager) generic.Dispatcher {
|
func newMutatingDispatcher(p *Plugin) func(cm *webhookutil.ClientManager) generic.Dispatcher {
|
||||||
return func(cm *webhook.ClientManager) generic.Dispatcher {
|
return func(cm *webhookutil.ClientManager) generic.Dispatcher {
|
||||||
return &mutatingDispatcher{cm, p}
|
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 {
|
func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces, relevantHooks []*generic.WebhookInvocation) error {
|
||||||
var versionedAttr *generic.VersionedAttributes
|
var versionedAttr *generic.VersionedAttributes
|
||||||
for _, invocation := range relevantHooks {
|
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 {
|
if versionedAttr == nil {
|
||||||
// First webhook, create versioned attributes
|
// First webhook, create versioned attributes
|
||||||
var err error
|
var err error
|
||||||
@ -73,14 +76,14 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib
|
|||||||
}
|
}
|
||||||
|
|
||||||
t := time.Now()
|
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)
|
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, versionedAttr.Attributes, "admit", hook.Name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
||||||
if callErr, ok := err.(*webhook.ErrCallingWebhook); ok {
|
if callErr, ok := err.(*webhookutil.ErrCallingWebhook); ok {
|
||||||
if ignoreClientCallFailures {
|
if ignoreClientCallFailures {
|
||||||
klog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
klog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
||||||
utilruntime.HandleError(callErr)
|
utilruntime.HandleError(callErr)
|
||||||
@ -100,11 +103,11 @@ func (a *mutatingDispatcher) Dispatch(ctx context.Context, attr admission.Attrib
|
|||||||
}
|
}
|
||||||
|
|
||||||
// note that callAttrMutatingHook updates attr
|
// 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 attr.Attributes.IsDryRun() {
|
||||||
if h.SideEffects == nil {
|
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) {
|
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
||||||
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
||||||
@ -113,15 +116,15 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, invocatio
|
|||||||
|
|
||||||
// Currently dispatcher only supports `v1beta1` AdmissionReview
|
// Currently dispatcher only supports `v1beta1` AdmissionReview
|
||||||
// TODO: Make the dispatcher capable of sending multiple AdmissionReview versions
|
// TODO: Make the dispatcher capable of sending multiple AdmissionReview versions
|
||||||
if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, h) {
|
if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, invocation.Webhook) {
|
||||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReview")}
|
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReview")}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the webhook request
|
// Make the webhook request
|
||||||
request := request.CreateAdmissionReview(attr, invocation)
|
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 {
|
if err != nil {
|
||||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||||
}
|
}
|
||||||
response := &admissionv1beta1.AdmissionReview{}
|
response := &admissionv1beta1.AdmissionReview{}
|
||||||
r := client.Post().Context(ctx).Body(&request)
|
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)
|
r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second)
|
||||||
}
|
}
|
||||||
if err := r.Do().Into(response); err != nil {
|
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 {
|
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 {
|
for k, v := range response.Response.AuditAnnotations {
|
||||||
|
@ -46,7 +46,7 @@ func TestAdmit(t *testing.T) {
|
|||||||
defer close(stopCh)
|
defer close(stopCh)
|
||||||
|
|
||||||
testCases := append(webhooktesting.NewMutatingTestCases(serverURL),
|
testCases := append(webhooktesting.NewMutatingTestCases(serverURL),
|
||||||
webhooktesting.NewNonMutatingTestCases(serverURL)...)
|
webhooktesting.ConvertToMutatingTestCases(webhooktesting.NewNonMutatingTestCases(serverURL))...)
|
||||||
|
|
||||||
for _, tt := range testCases {
|
for _, tt := range testCases {
|
||||||
wh, err := NewMutatingWebhook(nil)
|
wh, err := NewMutatingWebhook(nil)
|
||||||
@ -56,7 +56,7 @@ func TestAdmit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ns := "webhook-test"
|
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.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32))))
|
||||||
wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL))
|
wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL))
|
||||||
@ -136,7 +136,7 @@ func TestAdmitCachedClient(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) {
|
for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) {
|
||||||
ns := "webhook-test"
|
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.
|
// override the webhook source. The client cache will stay the same.
|
||||||
cacheMisses := new(int32)
|
cacheMisses := new(int32)
|
||||||
|
@ -10,13 +10,13 @@ go_library(
|
|||||||
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace",
|
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
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/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta: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/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/labels: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/apimachinery/pkg/util/errors:go_default_library",
|
||||||
"//staging/src/k8s.io/apiserver/pkg/admission: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/kubernetes:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/listers/core/v1: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/labels:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema: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:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@ package namespace
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/api/admissionregistration/v1beta1"
|
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
corelisters "k8s.io/client-go/listers/core/v1"
|
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
|
// MatchNamespaceSelector decideds whether the request matches the
|
||||||
// namespaceSelctor of the webhook. Only when they match, the webhook is called.
|
// 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()
|
namespaceName := attr.GetNamespace()
|
||||||
if len(namespaceName) == 0 && attr.GetResource().Resource != "namespaces" {
|
if len(namespaceName) == 0 && attr.GetResource().Resource != "namespaces" {
|
||||||
// If the request is about a cluster scoped resource, and it is not a
|
// 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
|
return true, nil
|
||||||
}
|
}
|
||||||
// TODO: adding an LRU cache to cache the translation
|
// TODO: adding an LRU cache to cache the translation
|
||||||
selector, err := metav1.LabelSelectorAsSelector(h.NamespaceSelector)
|
selector, err := metav1.LabelSelectorAsSelector(h.GetNamespaceSelector())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, apierrors.NewInternalError(err)
|
return false, apierrors.NewInternalError(err)
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeNamespaceLister struct {
|
type fakeNamespaceLister struct {
|
||||||
@ -114,12 +115,12 @@ func TestGetNamespaceLabels(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNotExemptClusterScopedResource(t *testing.T) {
|
func TestNotExemptClusterScopedResource(t *testing.T) {
|
||||||
hook := ®istrationv1beta1.Webhook{
|
hook := ®istrationv1beta1.ValidatingWebhook{
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
}
|
}
|
||||||
attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, &metav1.CreateOptions{}, false, nil)
|
attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, &metav1.CreateOptions{}, false, nil)
|
||||||
matcher := Matcher{}
|
matcher := Matcher{}
|
||||||
matches, err := matcher.MatchNamespaceSelector(hook, attr)
|
matches, err := matcher.MatchNamespaceSelector(webhook.NewValidatingWebhookAccessor(hook), attr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,8 @@ var sideEffectsNone = registrationv1beta1.SideEffectClassNone
|
|||||||
var sideEffectsSome = registrationv1beta1.SideEffectClassSome
|
var sideEffectsSome = registrationv1beta1.SideEffectClassSome
|
||||||
var sideEffectsNoneOnDryRun = registrationv1beta1.SideEffectClassNoneOnDryRun
|
var sideEffectsNoneOnDryRun = registrationv1beta1.SideEffectClassNoneOnDryRun
|
||||||
|
|
||||||
// NewFakeDataSource returns a mock client and informer returning the given webhooks.
|
// NewFakeValidatingDataSource 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) {
|
func NewFakeValidatingDataSource(name string, webhooks []registrationv1beta1.ValidatingWebhook, stopCh <-chan struct{}) (clientset kubernetes.Interface, factory informers.SharedInformerFactory) {
|
||||||
var objs = []runtime.Object{
|
var objs = []runtime.Object{
|
||||||
&corev1.Namespace{
|
&corev1.Namespace{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -61,22 +61,38 @@ func NewFakeDataSource(name string, webhooks []registrationv1beta1.Webhook, muta
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if mutating {
|
|
||||||
objs = append(objs, ®istrationv1beta1.MutatingWebhookConfiguration{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "test-webhooks",
|
|
||||||
},
|
|
||||||
Webhooks: webhooks,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
objs = append(objs, ®istrationv1beta1.ValidatingWebhookConfiguration{
|
objs = append(objs, ®istrationv1beta1.ValidatingWebhookConfiguration{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-webhooks",
|
Name: "test-webhooks",
|
||||||
},
|
},
|
||||||
Webhooks: 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: name,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"runlevel": "0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
objs = append(objs, ®istrationv1beta1.MutatingWebhookConfiguration{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-webhooks",
|
||||||
|
},
|
||||||
|
Webhooks: webhooks,
|
||||||
|
})
|
||||||
|
|
||||||
client := fakeclientset.NewSimpleClientset(objs...)
|
client := fakeclientset.NewSimpleClientset(objs...)
|
||||||
informerFactory := informers.NewSharedInformerFactory(client, 0)
|
informerFactory := informers.NewSharedInformerFactory(client, 0)
|
||||||
|
|
||||||
@ -181,10 +197,10 @@ func (c urlConfigGenerator) ccfgURL(urlPath string) registrationv1beta1.WebhookC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test is a webhook test case.
|
// ValidatingTest is a validating webhook test case.
|
||||||
type Test struct {
|
type ValidatingTest struct {
|
||||||
Name string
|
Name string
|
||||||
Webhooks []registrationv1beta1.Webhook
|
Webhooks []registrationv1beta1.ValidatingWebhook
|
||||||
Path string
|
Path string
|
||||||
IsCRD bool
|
IsCRD bool
|
||||||
IsDryRun bool
|
IsDryRun bool
|
||||||
@ -196,19 +212,52 @@ type Test struct {
|
|||||||
ExpectStatusCode int32
|
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.
|
// NewNonMutatingTestCases returns test cases with a given base url.
|
||||||
// All test cases in NewNonMutatingTestCases have no Patch set in
|
// All test cases in NewNonMutatingTestCases have no Patch set in
|
||||||
// AdmissionResponse. The test cases are used by both MutatingAdmissionWebhook
|
// AdmissionResponse. The test cases are used by both MutatingAdmissionWebhook
|
||||||
// and ValidatingAdmissionWebhook.
|
// and ValidatingAdmissionWebhook.
|
||||||
func NewNonMutatingTestCases(url *url.URL) []Test {
|
func NewNonMutatingTestCases(url *url.URL) []ValidatingTest {
|
||||||
policyFail := registrationv1beta1.Fail
|
policyFail := registrationv1beta1.Fail
|
||||||
policyIgnore := registrationv1beta1.Ignore
|
policyIgnore := registrationv1beta1.Ignore
|
||||||
ccfgURL := urlConfigGenerator{url}.ccfgURL
|
ccfgURL := urlConfigGenerator{url}.ccfgURL
|
||||||
|
|
||||||
return []Test{
|
return []ValidatingTest{
|
||||||
{
|
{
|
||||||
Name: "no match",
|
Name: "no match",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "nomatch",
|
Name: "nomatch",
|
||||||
ClientConfig: ccfgSVC("disallow"),
|
ClientConfig: ccfgSVC("disallow"),
|
||||||
Rules: []registrationv1beta1.RuleWithOperations{{
|
Rules: []registrationv1beta1.RuleWithOperations{{
|
||||||
@ -221,7 +270,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & allow",
|
Name: "match & allow",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "allow.example.com",
|
Name: "allow.example.com",
|
||||||
ClientConfig: ccfgSVC("allow"),
|
ClientConfig: ccfgSVC("allow"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -233,7 +282,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & disallow",
|
Name: "match & disallow",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "disallow",
|
Name: "disallow",
|
||||||
ClientConfig: ccfgSVC("disallow"),
|
ClientConfig: ccfgSVC("disallow"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -245,7 +294,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & disallow ii",
|
Name: "match & disallow ii",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "disallowReason",
|
Name: "disallowReason",
|
||||||
ClientConfig: ccfgSVC("disallowReason"),
|
ClientConfig: ccfgSVC("disallowReason"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -257,7 +306,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & disallow & but allowed because namespaceSelector exempt the ns",
|
Name: "match & disallow & but allowed because namespaceSelector exempt the ns",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "disallow",
|
Name: "disallow",
|
||||||
ClientConfig: ccfgSVC("disallow"),
|
ClientConfig: ccfgSVC("disallow"),
|
||||||
Rules: newMatchEverythingRules(),
|
Rules: newMatchEverythingRules(),
|
||||||
@ -275,7 +324,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & disallow & but allowed because namespaceSelector exempt the ns ii",
|
Name: "match & disallow & but allowed because namespaceSelector exempt the ns ii",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "disallow",
|
Name: "disallow",
|
||||||
ClientConfig: ccfgSVC("disallow"),
|
ClientConfig: ccfgSVC("disallow"),
|
||||||
Rules: newMatchEverythingRules(),
|
Rules: newMatchEverythingRules(),
|
||||||
@ -292,7 +341,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & fail (but allow because fail open)",
|
Name: "match & fail (but allow because fail open)",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "internalErr A",
|
Name: "internalErr A",
|
||||||
ClientConfig: ccfgSVC("internalErr"),
|
ClientConfig: ccfgSVC("internalErr"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -319,7 +368,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & fail (but disallow because fail close on nil FailurePolicy)",
|
Name: "match & fail (but disallow because fail close on nil FailurePolicy)",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "internalErr A",
|
Name: "internalErr A",
|
||||||
ClientConfig: ccfgSVC("internalErr"),
|
ClientConfig: ccfgSVC("internalErr"),
|
||||||
NamespaceSelector: &metav1.LabelSelector{},
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
@ -343,7 +392,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & fail (but fail because fail closed)",
|
Name: "match & fail (but fail because fail closed)",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "internalErr A",
|
Name: "internalErr A",
|
||||||
ClientConfig: ccfgSVC("internalErr"),
|
ClientConfig: ccfgSVC("internalErr"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -370,7 +419,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & allow (url)",
|
Name: "match & allow (url)",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "allow.example.com",
|
Name: "allow.example.com",
|
||||||
ClientConfig: ccfgURL("allow"),
|
ClientConfig: ccfgURL("allow"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -382,7 +431,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & disallow (url)",
|
Name: "match & disallow (url)",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "disallow",
|
Name: "disallow",
|
||||||
ClientConfig: ccfgURL("disallow"),
|
ClientConfig: ccfgURL("disallow"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -393,7 +442,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
ErrorContains: "without explanation",
|
ErrorContains: "without explanation",
|
||||||
}, {
|
}, {
|
||||||
Name: "absent response and fail open",
|
Name: "absent response and fail open",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "nilResponse",
|
Name: "nilResponse",
|
||||||
ClientConfig: ccfgURL("nilResponse"),
|
ClientConfig: ccfgURL("nilResponse"),
|
||||||
FailurePolicy: &policyIgnore,
|
FailurePolicy: &policyIgnore,
|
||||||
@ -405,7 +454,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "absent response and fail closed",
|
Name: "absent response and fail closed",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "nilResponse",
|
Name: "nilResponse",
|
||||||
ClientConfig: ccfgURL("nilResponse"),
|
ClientConfig: ccfgURL("nilResponse"),
|
||||||
FailurePolicy: &policyFail,
|
FailurePolicy: &policyFail,
|
||||||
@ -418,7 +467,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "no match dry run",
|
Name: "no match dry run",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "nomatch",
|
Name: "nomatch",
|
||||||
ClientConfig: ccfgSVC("allow"),
|
ClientConfig: ccfgSVC("allow"),
|
||||||
Rules: []registrationv1beta1.RuleWithOperations{{
|
Rules: []registrationv1beta1.RuleWithOperations{{
|
||||||
@ -433,7 +482,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match dry run side effects Unknown",
|
Name: "match dry run side effects Unknown",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "allow",
|
Name: "allow",
|
||||||
ClientConfig: ccfgSVC("allow"),
|
ClientConfig: ccfgSVC("allow"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -447,7 +496,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match dry run side effects None",
|
Name: "match dry run side effects None",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "allow",
|
Name: "allow",
|
||||||
ClientConfig: ccfgSVC("allow"),
|
ClientConfig: ccfgSVC("allow"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -461,7 +510,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match dry run side effects Some",
|
Name: "match dry run side effects Some",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "allow",
|
Name: "allow",
|
||||||
ClientConfig: ccfgSVC("allow"),
|
ClientConfig: ccfgSVC("allow"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -475,7 +524,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match dry run side effects NoneOnDryRun",
|
Name: "match dry run side effects NoneOnDryRun",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "allow",
|
Name: "allow",
|
||||||
ClientConfig: ccfgSVC("allow"),
|
ClientConfig: ccfgSVC("allow"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -489,7 +538,7 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "illegal annotation format",
|
Name: "illegal annotation format",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "invalidAnnotation",
|
Name: "invalidAnnotation",
|
||||||
ClientConfig: ccfgURL("invalidAnnotation"),
|
ClientConfig: ccfgURL("invalidAnnotation"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -506,11 +555,11 @@ func NewNonMutatingTestCases(url *url.URL) []Test {
|
|||||||
// NewMutatingTestCases returns test cases with a given base url.
|
// NewMutatingTestCases returns test cases with a given base url.
|
||||||
// All test cases in NewMutatingTestCases have Patch set in
|
// All test cases in NewMutatingTestCases have Patch set in
|
||||||
// AdmissionResponse. The test cases are only used by both MutatingAdmissionWebhook.
|
// AdmissionResponse. The test cases are only used by both MutatingAdmissionWebhook.
|
||||||
func NewMutatingTestCases(url *url.URL) []Test {
|
func NewMutatingTestCases(url *url.URL) []MutatingTest {
|
||||||
return []Test{
|
return []MutatingTest{
|
||||||
{
|
{
|
||||||
Name: "match & remove label",
|
Name: "match & remove label",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.MutatingWebhook{{
|
||||||
Name: "removelabel.example.com",
|
Name: "removelabel.example.com",
|
||||||
ClientConfig: ccfgSVC("removeLabel"),
|
ClientConfig: ccfgSVC("removeLabel"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -524,7 +573,7 @@ func NewMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & add label",
|
Name: "match & add label",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.MutatingWebhook{{
|
||||||
Name: "addLabel",
|
Name: "addLabel",
|
||||||
ClientConfig: ccfgSVC("addLabel"),
|
ClientConfig: ccfgSVC("addLabel"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -536,7 +585,7 @@ func NewMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match CRD & add label",
|
Name: "match CRD & add label",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.MutatingWebhook{{
|
||||||
Name: "addLabel",
|
Name: "addLabel",
|
||||||
ClientConfig: ccfgSVC("addLabel"),
|
ClientConfig: ccfgSVC("addLabel"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -549,7 +598,7 @@ func NewMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match CRD & remove label",
|
Name: "match CRD & remove label",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.MutatingWebhook{{
|
||||||
Name: "removelabel.example.com",
|
Name: "removelabel.example.com",
|
||||||
ClientConfig: ccfgSVC("removeLabel"),
|
ClientConfig: ccfgSVC("removeLabel"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -564,7 +613,7 @@ func NewMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & invalid mutation",
|
Name: "match & invalid mutation",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.MutatingWebhook{{
|
||||||
Name: "invalidMutation",
|
Name: "invalidMutation",
|
||||||
ClientConfig: ccfgSVC("invalidMutation"),
|
ClientConfig: ccfgSVC("invalidMutation"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -576,7 +625,7 @@ func NewMutatingTestCases(url *url.URL) []Test {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "match & remove label dry run unsupported",
|
Name: "match & remove label dry run unsupported",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.MutatingWebhook{{
|
||||||
Name: "removeLabel",
|
Name: "removeLabel",
|
||||||
ClientConfig: ccfgSVC("removeLabel"),
|
ClientConfig: ccfgSVC("removeLabel"),
|
||||||
Rules: matchEverythingRules,
|
Rules: matchEverythingRules,
|
||||||
@ -596,7 +645,7 @@ func NewMutatingTestCases(url *url.URL) []Test {
|
|||||||
// CachedTest is a test case for the client manager.
|
// CachedTest is a test case for the client manager.
|
||||||
type CachedTest struct {
|
type CachedTest struct {
|
||||||
Name string
|
Name string
|
||||||
Webhooks []registrationv1beta1.Webhook
|
Webhooks []registrationv1beta1.ValidatingWebhook
|
||||||
ExpectAllow bool
|
ExpectAllow bool
|
||||||
ExpectCacheMiss bool
|
ExpectCacheMiss bool
|
||||||
}
|
}
|
||||||
@ -609,7 +658,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest {
|
|||||||
return []CachedTest{
|
return []CachedTest{
|
||||||
{
|
{
|
||||||
Name: "uncached: service webhook, path 'allow'",
|
Name: "uncached: service webhook, path 'allow'",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "cache1",
|
Name: "cache1",
|
||||||
ClientConfig: ccfgSVC("allow"),
|
ClientConfig: ccfgSVC("allow"),
|
||||||
Rules: newMatchEverythingRules(),
|
Rules: newMatchEverythingRules(),
|
||||||
@ -622,7 +671,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "uncached: service webhook, path 'internalErr'",
|
Name: "uncached: service webhook, path 'internalErr'",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "cache2",
|
Name: "cache2",
|
||||||
ClientConfig: ccfgSVC("internalErr"),
|
ClientConfig: ccfgSVC("internalErr"),
|
||||||
Rules: newMatchEverythingRules(),
|
Rules: newMatchEverythingRules(),
|
||||||
@ -635,7 +684,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "cached: service webhook, path 'allow'",
|
Name: "cached: service webhook, path 'allow'",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "cache3",
|
Name: "cache3",
|
||||||
ClientConfig: ccfgSVC("allow"),
|
ClientConfig: ccfgSVC("allow"),
|
||||||
Rules: newMatchEverythingRules(),
|
Rules: newMatchEverythingRules(),
|
||||||
@ -648,7 +697,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "uncached: url webhook, path 'allow'",
|
Name: "uncached: url webhook, path 'allow'",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "cache4",
|
Name: "cache4",
|
||||||
ClientConfig: ccfgURL("allow"),
|
ClientConfig: ccfgURL("allow"),
|
||||||
Rules: newMatchEverythingRules(),
|
Rules: newMatchEverythingRules(),
|
||||||
@ -661,7 +710,7 @@ func NewCachedClientTestcases(url *url.URL) []CachedTest {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "cached: service webhook, path 'allow'",
|
Name: "cached: service webhook, path 'allow'",
|
||||||
Webhooks: []registrationv1beta1.Webhook{{
|
Webhooks: []registrationv1beta1.ValidatingWebhook{{
|
||||||
Name: "cache5",
|
Name: "cache5",
|
||||||
ClientConfig: ccfgURL("allow"),
|
ClientConfig: ccfgURL("allow"),
|
||||||
Rules: newMatchEverythingRules(),
|
Rules: newMatchEverythingRules(),
|
||||||
|
@ -7,7 +7,7 @@ go_library(
|
|||||||
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/util",
|
importpath = "k8s.io/apiserver/pkg/admission/plugin/webhook/util",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
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",
|
"//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -17,38 +17,39 @@ limitations under the License.
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"k8s.io/api/admissionregistration/v1beta1"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook"
|
||||||
"k8s.io/apiserver/pkg/util/webhook"
|
webhookutil "k8s.io/apiserver/pkg/util/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HookClientConfigForWebhook construct a webhook.ClientConfig using a v1beta1.Webhook API object.
|
// HookClientConfigForWebhook construct a webhookutil.ClientConfig using a WebhookAccessor to access
|
||||||
// webhook.ClientConfig is used to create a HookClient and the purpose of the config struct is to
|
// v1beta1.MutatingWebhook and v1beta1.ValidatingWebhook API objects. webhookutil.ClientConfig is used
|
||||||
// share that with other packages that need to create a HookClient.
|
// to create a HookClient and the purpose of the config struct is to share that with other packages
|
||||||
func HookClientConfigForWebhook(w *v1beta1.Webhook) webhook.ClientConfig {
|
// that need to create a HookClient.
|
||||||
ret := webhook.ClientConfig{Name: w.Name, CABundle: w.ClientConfig.CABundle}
|
func HookClientConfigForWebhook(w webhook.WebhookAccessor) webhookutil.ClientConfig {
|
||||||
if w.ClientConfig.URL != nil {
|
ret := webhookutil.ClientConfig{Name: w.GetName(), CABundle: w.GetClientConfig().CABundle}
|
||||||
ret.URL = *w.ClientConfig.URL
|
if w.GetClientConfig().URL != nil {
|
||||||
|
ret.URL = *w.GetClientConfig().URL
|
||||||
}
|
}
|
||||||
if w.ClientConfig.Service != nil {
|
if w.GetClientConfig().Service != nil {
|
||||||
ret.Service = &webhook.ClientConfigService{
|
ret.Service = &webhookutil.ClientConfigService{
|
||||||
Name: w.ClientConfig.Service.Name,
|
Name: w.GetClientConfig().Service.Name,
|
||||||
Namespace: w.ClientConfig.Service.Namespace,
|
Namespace: w.GetClientConfig().Service.Namespace,
|
||||||
}
|
}
|
||||||
if w.ClientConfig.Service.Port != nil {
|
if w.GetClientConfig().Service.Port != nil {
|
||||||
ret.Service.Port = *w.ClientConfig.Service.Port
|
ret.Service.Port = *w.GetClientConfig().Service.Port
|
||||||
} else {
|
} else {
|
||||||
ret.Service.Port = 443
|
ret.Service.Port = 443
|
||||||
}
|
}
|
||||||
if w.ClientConfig.Service.Path != nil {
|
if w.GetClientConfig().Service.Path != nil {
|
||||||
ret.Service.Path = *w.ClientConfig.Service.Path
|
ret.Service.Path = *w.GetClientConfig().Service.Path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAdmissionReviewVersion check whether a version is accepted by a given webhook.
|
// HasAdmissionReviewVersion check whether a version is accepted by a given webhook.
|
||||||
func HasAdmissionReviewVersion(a string, w *v1beta1.Webhook) bool {
|
func HasAdmissionReviewVersion(a string, w webhook.WebhookAccessor) bool {
|
||||||
for _, b := range w.AdmissionReviewVersions {
|
for _, b := range w.GetAdmissionReviewVersions() {
|
||||||
if b == a {
|
if b == a {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/klog"
|
|
||||||
|
|
||||||
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
admissionv1beta1 "k8s.io/api/admission/v1beta1"
|
||||||
"k8s.io/api/admissionregistration/v1beta1"
|
"k8s.io/api/admissionregistration/v1beta1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
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/generic"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/request"
|
||||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/util"
|
"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 {
|
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}
|
return &validatingDispatcher{cm}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,18 +68,21 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr
|
|||||||
for i := range relevantHooks {
|
for i := range relevantHooks {
|
||||||
go func(invocation *generic.WebhookInvocation) {
|
go func(invocation *generic.WebhookInvocation) {
|
||||||
defer wg.Done()
|
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]
|
versionedAttr := versionedAttrs[invocation.Kind]
|
||||||
|
|
||||||
t := time.Now()
|
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)
|
admissionmetrics.Metrics.ObserveWebhook(time.Since(t), err != nil, versionedAttr.Attributes, "validating", hook.Name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
ignoreClientCallFailures := hook.FailurePolicy != nil && *hook.FailurePolicy == v1beta1.Ignore
|
||||||
if callErr, ok := err.(*webhook.ErrCallingWebhook); ok {
|
if callErr, ok := err.(*webhookutil.ErrCallingWebhook); ok {
|
||||||
if ignoreClientCallFailures {
|
if ignoreClientCallFailures {
|
||||||
klog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
klog.Warningf("Failed calling webhook, failing open %v: %v", hook.Name, callErr)
|
||||||
utilruntime.HandleError(callErr)
|
utilruntime.HandleError(callErr)
|
||||||
@ -115,11 +117,10 @@ func (d *validatingDispatcher) Dispatch(ctx context.Context, attr admission.Attr
|
|||||||
return errs[0]
|
return errs[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *validatingDispatcher) callHook(ctx context.Context, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes) error {
|
func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.ValidatingWebhook, invocation *generic.WebhookInvocation, attr *generic.VersionedAttributes) error {
|
||||||
h := invocation.Webhook
|
|
||||||
if attr.Attributes.IsDryRun() {
|
if attr.Attributes.IsDryRun() {
|
||||||
if h.SideEffects == nil {
|
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) {
|
if !(*h.SideEffects == v1beta1.SideEffectClassNone || *h.SideEffects == v1beta1.SideEffectClassNoneOnDryRun) {
|
||||||
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
return webhookerrors.NewDryRunUnsupportedErr(h.Name)
|
||||||
@ -128,15 +129,15 @@ func (d *validatingDispatcher) callHook(ctx context.Context, invocation *generic
|
|||||||
|
|
||||||
// Currently dispatcher only supports `v1beta1` AdmissionReview
|
// Currently dispatcher only supports `v1beta1` AdmissionReview
|
||||||
// TODO: Make the dispatcher capable of sending multiple AdmissionReview versions
|
// TODO: Make the dispatcher capable of sending multiple AdmissionReview versions
|
||||||
if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, h) {
|
if !util.HasAdmissionReviewVersion(v1beta1.SchemeGroupVersion.Version, invocation.Webhook) {
|
||||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReviewRequest")}
|
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: fmt.Errorf("webhook does not accept v1beta1 AdmissionReviewRequest")}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the webhook request
|
// Make the webhook request
|
||||||
request := request.CreateAdmissionReview(attr, invocation)
|
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 {
|
if err != nil {
|
||||||
return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
return &webhookutil.ErrCallingWebhook{WebhookName: h.Name, Reason: err}
|
||||||
}
|
}
|
||||||
response := &admissionv1beta1.AdmissionReview{}
|
response := &admissionv1beta1.AdmissionReview{}
|
||||||
r := client.Post().Context(ctx).Body(&request)
|
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)
|
r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second)
|
||||||
}
|
}
|
||||||
if err := r.Do().Into(response); err != nil {
|
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 {
|
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 {
|
for k, v := range response.Response.AuditAnnotations {
|
||||||
key := h.Name + "/" + k
|
key := h.Name + "/" + k
|
||||||
|
@ -51,7 +51,7 @@ func TestValidate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ns := "webhook-test"
|
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.SetAuthenticationInfoResolverWrapper(webhooktesting.Wrapper(webhooktesting.NewAuthenticationInfoResolver(new(int32))))
|
||||||
wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL))
|
wh.SetServiceResolver(webhooktesting.NewServiceResolver(*serverURL))
|
||||||
@ -116,7 +116,7 @@ func TestValidateCachedClient(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) {
|
for _, tt := range webhooktesting.NewCachedClientTestcases(serverURL) {
|
||||||
ns := "webhook-test"
|
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.
|
// override the webhook source. The client cache will stay the same.
|
||||||
cacheMisses := new(int32)
|
cacheMisses := new(int32)
|
||||||
|
@ -436,7 +436,7 @@ func registerWebhook(f *framework.Framework, context *certContext) func() {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "deny-unwanted-pod-container-name-and-label.k8s.io",
|
Name: "deny-unwanted-pod-container-name-and-label.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -517,7 +517,7 @@ func registerWebhookForAttachingPod(f *framework.Framework, context *certContext
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "deny-attaching-pod.k8s.io",
|
Name: "deny-attaching-pod.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -561,7 +561,7 @@ func registerMutatingWebhookForConfigMap(f *framework.Framework, context *certCo
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.MutatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "adding-configmap-data-stage-1.k8s.io",
|
Name: "adding-configmap-data-stage-1.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -638,7 +638,7 @@ func registerMutatingWebhookForPod(f *framework.Framework, context *certContext)
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.MutatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "adding-init-container.k8s.io",
|
Name: "adding-init-container.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -841,8 +841,8 @@ func testAttachingPodWebhook(f *framework.Framework) {
|
|||||||
|
|
||||||
// failingWebhook returns a webhook with rule of create configmaps,
|
// failingWebhook returns a webhook with rule of create configmaps,
|
||||||
// but with an invalid client config so that server cannot communicate with it
|
// but with an invalid client config so that server cannot communicate with it
|
||||||
func failingWebhook(namespace, name string) v1beta1.Webhook {
|
func failingWebhook(namespace, name string) v1beta1.ValidatingWebhook {
|
||||||
return v1beta1.Webhook{
|
return v1beta1.ValidatingWebhook{
|
||||||
Name: name,
|
Name: name,
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
Operations: []v1beta1.OperationType{v1beta1.Create},
|
Operations: []v1beta1.OperationType{v1beta1.Create},
|
||||||
@ -889,7 +889,7 @@ func registerFailClosedWebhook(f *framework.Framework, context *certContext) fun
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.ValidatingWebhook{
|
||||||
// Server cannot talk to this webhook, so it always fails.
|
// 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.
|
// Because this webhook is configured fail-closed, request should be rejected after the call fails.
|
||||||
hook,
|
hook,
|
||||||
@ -945,7 +945,7 @@ func registerValidatingWebhookForWebhookConfigurations(f *framework.Framework, c
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "deny-webhook-configuration-deletions.k8s.io",
|
Name: "deny-webhook-configuration-deletions.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -998,7 +998,7 @@ func registerMutatingWebhookForWebhookConfigurations(f *framework.Framework, con
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.MutatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "add-label-to-webhook-configurations.k8s.io",
|
Name: "add-label-to-webhook-configurations.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -1050,7 +1050,7 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: dummyValidatingWebhookConfigName,
|
Name: dummyValidatingWebhookConfigName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "dummy-validating-webhook.k8s.io",
|
Name: "dummy-validating-webhook.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -1098,7 +1098,7 @@ func testWebhooksForWebhookConfigurations(f *framework.Framework) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: dummyMutatingWebhookConfigName,
|
Name: dummyMutatingWebhookConfigName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.MutatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "dummy-mutating-webhook.k8s.io",
|
Name: "dummy-mutating-webhook.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -1306,7 +1306,7 @@ func registerWebhookForCustomResource(f *framework.Framework, context *certConte
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "deny-unwanted-custom-resource-data.k8s.io",
|
Name: "deny-unwanted-custom-resource-data.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -1348,7 +1348,7 @@ func registerMutatingWebhookForCustomResource(f *framework.Framework, context *c
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.MutatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "mutate-custom-resource-data-stage-1.k8s.io",
|
Name: "mutate-custom-resource-data-stage-1.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -1543,7 +1543,7 @@ func registerValidatingWebhookForCRD(f *framework.Framework, context *certContex
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "deny-crd-with-unwanted-label.k8s.io",
|
Name: "deny-crd-with-unwanted-label.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
@ -1649,7 +1649,7 @@ func registerSlowWebhook(f *framework.Framework, context *certContext, policy *v
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: configName,
|
Name: configName,
|
||||||
},
|
},
|
||||||
Webhooks: []v1beta1.Webhook{
|
Webhooks: []v1beta1.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "allow-configmap-with-delay-webhook.k8s.io",
|
Name: "allow-configmap-with-delay-webhook.k8s.io",
|
||||||
Rules: []v1beta1.RuleWithOperations{{
|
Rules: []v1beta1.RuleWithOperations{{
|
||||||
|
@ -283,6 +283,9 @@ func (h *holder) record(phase string, converted bool, request *v1beta1.Admission
|
|||||||
return
|
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
|
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
|
// Attaching Admission webhook to API server
|
||||||
_, err := client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(&admissionv1beta1.ValidatingWebhookConfiguration{
|
_, err := client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(&admissionv1beta1.ValidatingWebhookConfiguration{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "admission.integration.test"},
|
ObjectMeta: metav1.ObjectMeta{Name: "admission.integration.test"},
|
||||||
Webhooks: []admissionv1beta1.Webhook{
|
Webhooks: []admissionv1beta1.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "admission.integration.test",
|
Name: "admission.integration.test",
|
||||||
ClientConfig: admissionv1beta1.WebhookClientConfig{
|
ClientConfig: admissionv1beta1.WebhookClientConfig{
|
||||||
@ -1323,7 +1326,7 @@ func createV1beta1MutationWebhook(client clientset.Interface, endpoint, converte
|
|||||||
// Attaching Mutation webhook to API server
|
// Attaching Mutation webhook to API server
|
||||||
_, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionv1beta1.MutatingWebhookConfiguration{
|
_, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionv1beta1.MutatingWebhookConfiguration{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "mutation.integration.test"},
|
ObjectMeta: metav1.ObjectMeta{Name: "mutation.integration.test"},
|
||||||
Webhooks: []admissionv1beta1.Webhook{
|
Webhooks: []admissionv1beta1.MutatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "mutation.integration.test",
|
Name: "mutation.integration.test",
|
||||||
ClientConfig: admissionv1beta1.WebhookClientConfig{
|
ClientConfig: admissionv1beta1.WebhookClientConfig{
|
||||||
|
@ -155,7 +155,7 @@ func brokenWebhookConfig(name string) *admissionregistrationv1beta1.ValidatingWe
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
},
|
},
|
||||||
Webhooks: []admissionregistrationv1beta1.Webhook{
|
Webhooks: []admissionregistrationv1beta1.ValidatingWebhook{
|
||||||
{
|
{
|
||||||
Name: "broken-webhook.k8s.io",
|
Name: "broken-webhook.k8s.io",
|
||||||
Rules: []admissionregistrationv1beta1.RuleWithOperations{{
|
Rules: []admissionregistrationv1beta1.RuleWithOperations{{
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
admissionv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
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"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||||
@ -65,7 +65,7 @@ func TestWebhookLoopback(t *testing.T) {
|
|||||||
fail := admissionv1beta1.Fail
|
fail := admissionv1beta1.Fail
|
||||||
_, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionv1beta1.MutatingWebhookConfiguration{
|
_, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionv1beta1.MutatingWebhookConfiguration{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "webhooktest.example.com"},
|
ObjectMeta: metav1.ObjectMeta{Name: "webhooktest.example.com"},
|
||||||
Webhooks: []admissionv1beta1.Webhook{{
|
Webhooks: []admissionv1beta1.MutatingWebhook{{
|
||||||
Name: "webhooktest.example.com",
|
Name: "webhooktest.example.com",
|
||||||
ClientConfig: admissionv1beta1.WebhookClientConfig{
|
ClientConfig: admissionv1beta1.WebhookClientConfig{
|
||||||
Service: &admissionv1beta1.ServiceReference{Namespace: "default", Name: "kubernetes", Path: &webhookPath},
|
Service: &admissionv1beta1.ServiceReference{Namespace: "default", Name: "kubernetes", Path: &webhookPath},
|
||||||
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -1184,6 +1184,7 @@ k8s.io/apiserver/pkg/admission/configuration
|
|||||||
k8s.io/apiserver/pkg/admission/initializer
|
k8s.io/apiserver/pkg/admission/initializer
|
||||||
k8s.io/apiserver/pkg/admission/metrics
|
k8s.io/apiserver/pkg/admission/metrics
|
||||||
k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle
|
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
|
||||||
k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission
|
k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission
|
||||||
k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1
|
k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1
|
||||||
|
Loading…
Reference in New Issue
Block a user