diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go index 8bfb006b7aa..d2313d55d84 100644 --- a/pkg/apis/admissionregistration/validation/validation.go +++ b/pkg/apis/admissionregistration/validation/validation.go @@ -167,7 +167,7 @@ func isAcceptedAdmissionReviewVersion(v string) bool { return false } -func validateAdmissionReviewVersions(versions []string, requireRecognizedVersion bool, fldPath *field.Path) field.ErrorList { +func validateAdmissionReviewVersions(versions []string, requireRecognizedAdmissionReviewVersion bool, fldPath *field.Path) field.ErrorList { allErrors := field.ErrorList{} // Currently only v1beta1 accepted in AdmissionReviewVersions @@ -189,7 +189,7 @@ func validateAdmissionReviewVersions(versions []string, requireRecognizedVersion hasAcceptedVersion = true } } - if requireRecognizedVersion && !hasAcceptedVersion { + if requireRecognizedAdmissionReviewVersion && !hasAcceptedVersion { allErrors = append(allErrors, field.Invalid( fldPath, versions, fmt.Sprintf("must include at least one of %v", @@ -201,16 +201,20 @@ func validateAdmissionReviewVersions(versions []string, requireRecognizedVersion // ValidateValidatingWebhookConfiguration validates a webhook before creation. func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration, requestGV schema.GroupVersion) field.ErrorList { - return validateValidatingWebhookConfiguration(e, true, requireUniqueWebhookNames(requestGV)) + return validateValidatingWebhookConfiguration(e, validationOptions{ + requireNoSideEffects: requireNoSideEffects(requestGV), + requireRecognizedAdmissionReviewVersion: true, + requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV), + }) } -func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration, requireRecognizedVersion, requireUniqueWebhookNames bool) field.ErrorList { +func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration, opts validationOptions) field.ErrorList { allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata")) hookNames := sets.NewString() for i, hook := range e.Webhooks { - allErrors = append(allErrors, validateValidatingWebhook(&hook, field.NewPath("webhooks").Index(i))...) - allErrors = append(allErrors, validateAdmissionReviewVersions(hook.AdmissionReviewVersions, requireRecognizedVersion, field.NewPath("webhooks").Index(i).Child("admissionReviewVersions"))...) - if requireUniqueWebhookNames && len(hook.Name) > 0 { + allErrors = append(allErrors, validateValidatingWebhook(&hook, opts, field.NewPath("webhooks").Index(i))...) + allErrors = append(allErrors, validateAdmissionReviewVersions(hook.AdmissionReviewVersions, opts.requireRecognizedAdmissionReviewVersion, field.NewPath("webhooks").Index(i).Child("admissionReviewVersions"))...) + if opts.requireUniqueWebhookNames && len(hook.Name) > 0 { if hookNames.Has(hook.Name) { allErrors = append(allErrors, field.Duplicate(field.NewPath("webhooks").Index(i).Child("name"), hook.Name)) } @@ -222,16 +226,26 @@ func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingW // ValidateMutatingWebhookConfiguration validates a webhook before creation. func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration, requestGV schema.GroupVersion) field.ErrorList { - return validateMutatingWebhookConfiguration(e, true, requireUniqueWebhookNames(requestGV)) + return validateMutatingWebhookConfiguration(e, validationOptions{ + requireNoSideEffects: requireNoSideEffects(requestGV), + requireRecognizedAdmissionReviewVersion: true, + requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV), + }) } -func validateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration, requireRecognizedVersion, requireUniqueWebhookNames bool) field.ErrorList { +type validationOptions struct { + requireNoSideEffects bool + requireRecognizedAdmissionReviewVersion bool + requireUniqueWebhookNames bool +} + +func validateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration, opts validationOptions) field.ErrorList { allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata")) hookNames := sets.NewString() for i, hook := range e.Webhooks { - allErrors = append(allErrors, validateMutatingWebhook(&hook, field.NewPath("webhooks").Index(i))...) - allErrors = append(allErrors, validateAdmissionReviewVersions(hook.AdmissionReviewVersions, requireRecognizedVersion, field.NewPath("webhooks").Index(i).Child("admissionReviewVersions"))...) - if requireUniqueWebhookNames && len(hook.Name) > 0 { + allErrors = append(allErrors, validateMutatingWebhook(&hook, opts, field.NewPath("webhooks").Index(i))...) + allErrors = append(allErrors, validateAdmissionReviewVersions(hook.AdmissionReviewVersions, opts.requireRecognizedAdmissionReviewVersion, field.NewPath("webhooks").Index(i).Child("admissionReviewVersions"))...) + if opts.requireUniqueWebhookNames && len(hook.Name) > 0 { if hookNames.Has(hook.Name) { allErrors = append(allErrors, field.Duplicate(field.NewPath("webhooks").Index(i).Child("name"), hook.Name)) } @@ -241,7 +255,7 @@ func validateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebho return allErrors } -func validateValidatingWebhook(hook *admissionregistration.ValidatingWebhook, fldPath *field.Path) field.ErrorList { +func validateValidatingWebhook(hook *admissionregistration.ValidatingWebhook, opts validationOptions, 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)...) @@ -255,11 +269,15 @@ func validateValidatingWebhook(hook *admissionregistration.ValidatingWebhook, fl 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 { - allErrors = append(allErrors, field.Required(fldPath.Child("sideEffects"), fmt.Sprintf("must specify one of %v", strings.Join(supportedSideEffectClasses.List(), ", ")))) + allowedSideEffects := supportedSideEffectClasses + if opts.requireNoSideEffects { + allowedSideEffects = noSideEffectClasses } - if hook.SideEffects != nil && !supportedSideEffectClasses.Has(string(*hook.SideEffects)) { - allErrors = append(allErrors, field.NotSupported(fldPath.Child("sideEffects"), *hook.SideEffects, supportedSideEffectClasses.List())) + if hook.SideEffects == nil { + allErrors = append(allErrors, field.Required(fldPath.Child("sideEffects"), fmt.Sprintf("must specify one of %v", strings.Join(allowedSideEffects.List(), ", ")))) + } + if hook.SideEffects != nil && !allowedSideEffects.Has(string(*hook.SideEffects)) { + allErrors = append(allErrors, field.NotSupported(fldPath.Child("sideEffects"), *hook.SideEffects, allowedSideEffects.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")) @@ -285,7 +303,7 @@ func validateValidatingWebhook(hook *admissionregistration.ValidatingWebhook, fl return allErrors } -func validateMutatingWebhook(hook *admissionregistration.MutatingWebhook, fldPath *field.Path) field.ErrorList { +func validateMutatingWebhook(hook *admissionregistration.MutatingWebhook, opts validationOptions, 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)...) @@ -299,11 +317,15 @@ func validateMutatingWebhook(hook *admissionregistration.MutatingWebhook, fldPat 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 { - allErrors = append(allErrors, field.Required(fldPath.Child("sideEffects"), fmt.Sprintf("must specify one of %v", strings.Join(supportedSideEffectClasses.List(), ", ")))) + allowedSideEffects := supportedSideEffectClasses + if opts.requireNoSideEffects { + allowedSideEffects = noSideEffectClasses } - if hook.SideEffects != nil && !supportedSideEffectClasses.Has(string(*hook.SideEffects)) { - allErrors = append(allErrors, field.NotSupported(fldPath.Child("sideEffects"), *hook.SideEffects, supportedSideEffectClasses.List())) + if hook.SideEffects == nil { + allErrors = append(allErrors, field.Required(fldPath.Child("sideEffects"), fmt.Sprintf("must specify one of %v", strings.Join(allowedSideEffects.List(), ", ")))) + } + if hook.SideEffects != nil && !allowedSideEffects.Has(string(*hook.SideEffects)) { + allErrors = append(allErrors, field.NotSupported(fldPath.Child("sideEffects"), *hook.SideEffects, allowedSideEffects.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")) @@ -348,6 +370,11 @@ var supportedSideEffectClasses = sets.NewString( string(admissionregistration.SideEffectClassNoneOnDryRun), ) +var noSideEffectClasses = sets.NewString( + string(admissionregistration.SideEffectClassNone), + string(admissionregistration.SideEffectClassNoneOnDryRun), +) + var supportedOperations = sets.NewString( string(admissionregistration.OperationAll), string(admissionregistration.Create), @@ -448,23 +475,48 @@ func validatingHasUniqueWebhookNames(webhooks []admissionregistration.Validating return true } +// mutatingHasNoSideEffects returns true if all webhooks have no side effects +func mutatingHasNoSideEffects(webhooks []admissionregistration.MutatingWebhook) bool { + for _, hook := range webhooks { + if hook.SideEffects == nil || !noSideEffectClasses.Has(string(*hook.SideEffects)) { + return false + } + } + return true +} + +// validatingHasNoSideEffects returns true if all webhooks have no side effects +func validatingHasNoSideEffects(webhooks []admissionregistration.ValidatingWebhook) bool { + for _, hook := range webhooks { + if hook.SideEffects == nil || !noSideEffectClasses.Has(string(*hook.SideEffects)) { + return false + } + } + return true +} + func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration, requestGV schema.GroupVersion) field.ErrorList { - return validateValidatingWebhookConfiguration( - newC, - validatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks), - requireUniqueWebhookNames(requestGV) && validatingHasUniqueWebhookNames(oldC.Webhooks), - ) + return validateValidatingWebhookConfiguration(newC, validationOptions{ + requireNoSideEffects: requireNoSideEffects(requestGV) && validatingHasNoSideEffects(oldC.Webhooks), + requireRecognizedAdmissionReviewVersion: validatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks), + requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV) && validatingHasUniqueWebhookNames(oldC.Webhooks), + }) } func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.MutatingWebhookConfiguration, requestGV schema.GroupVersion) field.ErrorList { - return validateMutatingWebhookConfiguration( - newC, - mutatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks), - requireUniqueWebhookNames(requestGV) && mutatingHasUniqueWebhookNames(oldC.Webhooks), - ) + return validateMutatingWebhookConfiguration(newC, validationOptions{ + requireNoSideEffects: requireNoSideEffects(requestGV) && mutatingHasNoSideEffects(oldC.Webhooks), + requireRecognizedAdmissionReviewVersion: mutatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks), + requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV) && mutatingHasUniqueWebhookNames(oldC.Webhooks), + }) } // requireUniqueWebhookNames returns true for all requests except v1beta1 (for backwards compatibility) func requireUniqueWebhookNames(requestGV schema.GroupVersion) bool { return requestGV != (schema.GroupVersion{Group: admissionregistration.GroupName, Version: "v1beta1"}) } + +// requireNoSideEffects returns true for all requests except v1beta1 (for backwards compatibility) +func requireNoSideEffects(requestGV schema.GroupVersion) bool { + return requestGV != (schema.GroupVersion{Group: admissionregistration.GroupName, Version: "v1beta1"}) +} diff --git a/pkg/apis/admissionregistration/validation/validation_test.go b/pkg/apis/admissionregistration/validation/validation_test.go index b7b574a6343..a7af132f4d1 100644 --- a/pkg/apis/admissionregistration/validation/validation_test.go +++ b/pkg/apis/admissionregistration/validation/validation_test.go @@ -47,7 +47,6 @@ func newValidatingWebhookConfiguration(hooks []admissionregistration.ValidatingW func TestValidateValidatingWebhookConfiguration(t *testing.T) { unknownSideEffect := admissionregistration.SideEffectClassUnknown - validSideEffect := &unknownSideEffect validClientConfig := admissionregistration.WebhookClientConfig{ URL: strPtr("https://example.com"), } @@ -63,7 +62,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, false), expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1beta1`, @@ -84,10 +83,11 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"v1beta1"}, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: ``, }, { @@ -96,10 +96,11 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"v1beta1", "invalid-version"}, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: ``, }, { @@ -130,19 +131,20 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: `webhooks[1].name: Invalid value: "k8s.io": should be a domain with at least three segments separated by dots, webhooks[2].name: Required value`, }, { @@ -151,12 +153,12 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), gv: schema.GroupVersion{Group: "foo", Version: "bar"}, @@ -168,12 +170,12 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, @@ -269,7 +271,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -282,6 +284,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, }, { name: `resource "*" cannot mix with resources that don't have subresources`, @@ -289,7 +292,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -310,7 +313,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -331,7 +334,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -344,6 +347,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, }, { name: "resource */a cannot mix with x/a", @@ -351,7 +355,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -372,7 +376,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -393,7 +397,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, FailurePolicy: func() *admissionregistration.FailurePolicyType { r := admissionregistration.FailurePolicyType("other") return &r @@ -408,7 +412,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, false), expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1beta1`, @@ -422,10 +426,10 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { SideEffects: nil, }, }, true), - expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun, Some, Unknown`, + expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`, }, { - name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\"", + name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\" via v1beta1", config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", @@ -436,8 +440,24 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }(), }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun", "Some", "Unknown"`, }, + { + name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" via v1", + config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + SideEffects: func() *admissionregistration.SideEffectClass { + r := admissionregistration.SideEffectClass("other") + return &r + }(), + }, + }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1"}, + expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`, + }, { name: "both service and URL missing", config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ @@ -579,9 +599,10 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: ``, }, { @@ -597,9 +618,10 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: ``, }, { @@ -615,7 +637,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`, @@ -633,7 +655,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`, @@ -650,7 +672,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `clientConfig.service.path: Invalid value: "/foo/bar//": segment[2] may not be empty`, @@ -668,7 +690,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a DNS-1123 subdomain`, @@ -687,7 +709,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { Port: 0, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `Invalid value: 0: port is not valid: must be between 1 and 65535, inclusive`, @@ -706,7 +728,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { Port: 65536, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `Invalid value: 65536: port is not valid: must be between 1 and 65535, inclusive`, @@ -717,7 +739,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(31), }, }, true), @@ -729,7 +751,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(0), }, }, true), @@ -741,7 +763,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(-1), }, }, true), @@ -753,22 +775,23 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(1), }, { Name: "webhook2.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(15), }, { Name: "webhook3.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(30), }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, }, } for _, test := range tests { @@ -791,7 +814,6 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { unknownSideEffect := admissionregistration.SideEffectClassUnknown - validSideEffect := &unknownSideEffect validClientConfig := admissionregistration.WebhookClientConfig{ URL: strPtr("https://example.com"), } @@ -808,7 +830,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"v1beta1"}, }, }, true), @@ -816,7 +838,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: ``, @@ -827,7 +849,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"}, }, }, true), @@ -835,7 +857,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"invalid-v0"}, }, }, true), @@ -847,7 +869,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"invalid-v1"}, }, }, true), @@ -855,7 +877,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"v1beta1", "invalid-v1"}, }, }, true), @@ -867,7 +889,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"invalid-v1"}, }, }, true), @@ -875,7 +897,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, false), expectedError: `Invalid value: []string{"invalid-v1"}`, @@ -886,19 +908,19 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, false), gv: schema.GroupVersion{Group: "foo", Version: "bar"}, @@ -910,24 +932,24 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), gv: schema.GroupVersion{Group: "foo", Version: "bar"}, @@ -939,19 +961,19 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, false), gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, @@ -994,7 +1016,6 @@ func newMutatingWebhookConfiguration(hooks []admissionregistration.MutatingWebho func TestValidateMutatingWebhookConfiguration(t *testing.T) { unknownSideEffect := admissionregistration.SideEffectClassUnknown - validSideEffect := &unknownSideEffect validClientConfig := admissionregistration.WebhookClientConfig{ URL: strPtr("https://example.com"), } @@ -1010,7 +1031,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, false), expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1beta1`, @@ -1031,10 +1052,11 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"v1beta1"}, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: ``, }, { @@ -1043,10 +1065,11 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"v1beta1", "invalid-version"}, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: ``, }, { @@ -1077,19 +1100,20 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: `webhooks[1].name: Invalid value: "k8s.io": should be a domain with at least three segments separated by dots, webhooks[2].name: Required value`, }, { @@ -1098,12 +1122,12 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), gv: schema.GroupVersion{Group: "foo", Version: "bar"}, @@ -1115,12 +1139,12 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, @@ -1216,7 +1240,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -1229,6 +1253,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { }, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, }, { name: `resource "*" cannot mix with resources that don't have subresources`, @@ -1236,7 +1261,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -1257,7 +1282,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -1278,7 +1303,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -1291,6 +1316,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { }, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, }, { name: "resource */a cannot mix with x/a", @@ -1298,7 +1324,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -1319,7 +1345,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, Rules: []admissionregistration.RuleWithOperations{ { Operations: []admissionregistration.OperationType{"CREATE"}, @@ -1340,7 +1366,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, FailurePolicy: func() *admissionregistration.FailurePolicyType { r := admissionregistration.FailurePolicyType("other") return &r @@ -1355,7 +1381,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, false), expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1beta1`, @@ -1369,10 +1395,10 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { SideEffects: nil, }, }, true), - expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun, Some, Unknown`, + expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`, }, { - name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\"", + name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\" via v1beta1", config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ { Name: "webhook.k8s.io", @@ -1383,8 +1409,24 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { }(), }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun", "Some", "Unknown"`, }, + { + name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" via v1", + config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + SideEffects: func() *admissionregistration.SideEffectClass { + r := admissionregistration.SideEffectClass("other") + return &r + }(), + }, + }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1"}, + expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`, + }, { name: "both service and URL missing", config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ @@ -1526,9 +1568,10 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: ``, }, { @@ -1544,9 +1587,10 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, expectedError: ``, }, { @@ -1562,7 +1606,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`, @@ -1580,7 +1624,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`, @@ -1597,7 +1641,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `clientConfig.service.path: Invalid value: "/foo/bar//": segment[2] may not be empty`, @@ -1615,7 +1659,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { Port: 443, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a DNS-1123 subdomain`, @@ -1634,7 +1678,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { Port: 0, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `Invalid value: 0: port is not valid: must be between 1 and 65535, inclusive`, @@ -1653,7 +1697,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { Port: 65536, }, }, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: `Invalid value: 65536: port is not valid: must be between 1 and 65535, inclusive`, @@ -1664,7 +1708,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(31), }, }, true), @@ -1676,7 +1720,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(0), }, }, true), @@ -1688,7 +1732,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(-1), }, }, true), @@ -1700,22 +1744,23 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(1), }, { Name: "webhook2.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(15), }, { Name: "webhook3.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, TimeoutSeconds: int32Ptr(30), }, }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, }, } for _, test := range tests { @@ -1738,7 +1783,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) { func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { unknownSideEffect := admissionregistration.SideEffectClassUnknown - validSideEffect := &unknownSideEffect + noSideEffect := admissionregistration.SideEffectClassNone validClientConfig := admissionregistration.WebhookClientConfig{ URL: strPtr("https://example.com"), } @@ -1755,7 +1800,7 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"v1beta1"}, }, }, true), @@ -1763,7 +1808,7 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), expectedError: ``, @@ -1774,7 +1819,7 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"}, }, }, true), @@ -1782,7 +1827,7 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"invalid-v0"}, }, }, true), @@ -1794,7 +1839,7 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"invalid-v1"}, }, }, true), @@ -1802,7 +1847,7 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"v1beta1", "invalid-v1"}, }, }, true), @@ -1814,7 +1859,7 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, AdmissionReviewVersions: []string{"invalid-v1"}, }, }, true), @@ -1822,7 +1867,7 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, false), expectedError: `Invalid value: []string{"invalid-v1"}`, @@ -1833,24 +1878,24 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), gv: schema.GroupVersion{Group: "foo", Version: "bar"}, @@ -1862,19 +1907,76 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) { { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, }, }, true), oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ { Name: "webhook.k8s.io", ClientConfig: validClientConfig, - SideEffects: validSideEffect, + SideEffects: &unknownSideEffect, + }, + }, false), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, + expectedError: ``, + }, + { + name: "Webhooks can't have side effects when old config has no side effects via v1", + config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + SideEffects: &unknownSideEffect, + }, + }, true), + oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + SideEffects: &noSideEffect, + }, + }, true), + gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1"}, + expectedError: `Unsupported value: "Unknown": supported values: "None", "NoneOnDryRun"`, + }, + { + name: "Webhooks can have side effects when old config has side effects", + config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + SideEffects: &unknownSideEffect, + }, + }, true), + oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + SideEffects: &unknownSideEffect, + }, + }, true), + gv: schema.GroupVersion{Group: "foo", Version: "bar"}, + expectedError: ``, + }, + { + name: "Webhooks can have side effects when updated via v1beta1", + config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + SideEffects: &unknownSideEffect, + }, + }, true), + oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + SideEffects: &noSideEffect, }, }, false), gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"}, diff --git a/staging/src/k8s.io/api/admissionregistration/v1/types.go b/staging/src/k8s.io/api/admissionregistration/v1/types.go index b9b40f26928..fb9152f8e57 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1/types.go +++ b/staging/src/k8s.io/api/admissionregistration/v1/types.go @@ -273,8 +273,8 @@ type ValidatingWebhook struct { // +optional ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty" protobuf:"bytes,10,opt,name=objectSelector"` - // SideEffects states whether this webhookk has side effects. - // Acceptable values are: Unknown, None, Some, NoneOnDryRun + // SideEffects states whether this webhook has side effects. + // Acceptable values are: None, NoneOnDryRun (webhooks created via v1beta1 may also specify Some or Unknown). // 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 @@ -402,8 +402,8 @@ type MutatingWebhook struct { // +optional ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty" protobuf:"bytes,11,opt,name=objectSelector"` - // SideEffects states whether this webhookk has side effects. - // Acceptable values are: Unknown, None, Some, NoneOnDryRun + // SideEffects states whether this webhook has side effects. + // Acceptable values are: None, NoneOnDryRun (webhooks created via v1beta1 may also specify Some or Unknown). // 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 diff --git a/test/integration/etcd/data.go b/test/integration/etcd/data.go index eafd44abe15..d7f383547e3 100644 --- a/test/integration/etcd/data.go +++ b/test/integration/etcd/data.go @@ -415,12 +415,12 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes // k8s.io/kubernetes/pkg/apis/admissionregistration/v1 gvr("admissionregistration.k8s.io", "v1", "validatingwebhookconfigurations"): { - Stub: `{"metadata":{"name":"hook2","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore","sideEffects":"Unknown","admissionReviewVersions":["v1beta1"]}]}`, + Stub: `{"metadata":{"name":"hook2","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore","sideEffects":"None","admissionReviewVersions":["v1beta1"]}]}`, ExpectedEtcdPath: "/registry/validatingwebhookconfigurations/hook2", ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "ValidatingWebhookConfiguration"), }, gvr("admissionregistration.k8s.io", "v1", "mutatingwebhookconfigurations"): { - Stub: `{"metadata":{"name":"hook2","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore","sideEffects":"Unknown","admissionReviewVersions":["v1beta1"]}]}`, + Stub: `{"metadata":{"name":"hook2","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore","sideEffects":"None","admissionReviewVersions":["v1beta1"]}]}`, ExpectedEtcdPath: "/registry/mutatingwebhookconfigurations/hook2", ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "MutatingWebhookConfiguration"), },