diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 36fc01a161a..3db50b806a3 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -223,6 +223,11 @@ "sideEffects": { "description": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", "type": "string" + }, + "timeoutSeconds": { + "description": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", + "format": "int32", + "type": "integer" } }, "required": [ diff --git a/pkg/apis/admissionregistration/fuzzer/fuzzer.go b/pkg/apis/admissionregistration/fuzzer/fuzzer.go index ba4f2c5e5bf..c68785abaa9 100644 --- a/pkg/apis/admissionregistration/fuzzer/fuzzer.go +++ b/pkg/apis/admissionregistration/fuzzer/fuzzer.go @@ -32,6 +32,10 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { obj.FailurePolicy = &p s := admissionregistration.SideEffectClassUnknown obj.SideEffects = &s + if obj.TimeoutSeconds == nil { + i := int32(30) + obj.TimeoutSeconds = &i + } }, } } diff --git a/pkg/apis/admissionregistration/types.go b/pkg/apis/admissionregistration/types.go index 9d4eb3813c7..b95a14b854a 100644 --- a/pkg/apis/admissionregistration/types.go +++ b/pkg/apis/admissionregistration/types.go @@ -208,6 +208,13 @@ type Webhook struct { // 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 } // RuleWithOperations is a tuple of Operations and Resources. It is recommended to make diff --git a/pkg/apis/admissionregistration/v1beta1/defaults.go b/pkg/apis/admissionregistration/v1beta1/defaults.go index fa35267624d..81decaae253 100644 --- a/pkg/apis/admissionregistration/v1beta1/defaults.go +++ b/pkg/apis/admissionregistration/v1beta1/defaults.go @@ -40,4 +40,8 @@ func SetDefaults_Webhook(obj *admissionregistrationv1beta1.Webhook) { unknown := admissionregistrationv1beta1.SideEffectClassUnknown obj.SideEffects = &unknown } + if obj.TimeoutSeconds == nil { + obj.TimeoutSeconds = new(int32) + *obj.TimeoutSeconds = 30 + } } diff --git a/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go b/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go index 3d1d1be7177..bbc12e8d9e3 100644 --- a/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/admissionregistration/v1beta1/zz_generated.conversion.go @@ -301,6 +301,7 @@ func autoConvert_v1beta1_Webhook_To_admissionregistration_Webhook(in *v1beta1.We out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) out.SideEffects = (*admissionregistration.SideEffectClass)(unsafe.Pointer(in.SideEffects)) + out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) return nil } @@ -318,6 +319,7 @@ func autoConvert_admissionregistration_Webhook_To_v1beta1_Webhook(in *admissionr out.FailurePolicy = (*v1beta1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy)) out.NamespaceSelector = (*v1.LabelSelector)(unsafe.Pointer(in.NamespaceSelector)) out.SideEffects = (*v1beta1.SideEffectClass)(unsafe.Pointer(in.SideEffects)) + out.TimeoutSeconds = (*int32)(unsafe.Pointer(in.TimeoutSeconds)) return nil } diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go index 09200fb2bc7..9cd0da0d9ee 100644 --- a/pkg/apis/admissionregistration/validation/validation.go +++ b/pkg/apis/admissionregistration/validation/validation.go @@ -171,6 +171,9 @@ func validateWebhook(hook *admissionregistration.Webhook, fldPath *field.Path) f 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"))...) diff --git a/pkg/apis/admissionregistration/validation/validation_test.go b/pkg/apis/admissionregistration/validation/validation_test.go index 9a51b6631b3..90d31fd4199 100644 --- a/pkg/apis/admissionregistration/validation/validation_test.go +++ b/pkg/apis/admissionregistration/validation/validation_test.go @@ -26,6 +26,8 @@ import ( func strPtr(s string) *string { return &s } +func int32Ptr(i int32) *int32 { return &i } + func newValidatingWebhookConfiguration(hooks []admissionregistration.Webhook) *admissionregistration.ValidatingWebhookConfiguration { return &admissionregistration.ValidatingWebhookConfiguration{ ObjectMeta: metav1.ObjectMeta{ @@ -544,6 +546,63 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { }), expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a DNS-1123 subdomain`, }, + { + name: "timeout seconds cannot be greater than 30", + config: newValidatingWebhookConfiguration( + []admissionregistration.Webhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + TimeoutSeconds: int32Ptr(31), + }, + }), + expectedError: `webhooks[0].timeoutSeconds: Invalid value: 31: the timeout value must be between 1 and 30 seconds`, + }, + { + name: "timeout seconds cannot be smaller than 1", + config: newValidatingWebhookConfiguration( + []admissionregistration.Webhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + TimeoutSeconds: int32Ptr(0), + }, + }), + expectedError: `webhooks[0].timeoutSeconds: Invalid value: 0: the timeout value must be between 1 and 30 seconds`, + }, + { + name: "timeout seconds must be positive", + config: newValidatingWebhookConfiguration( + []admissionregistration.Webhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + TimeoutSeconds: int32Ptr(-1), + }, + }), + expectedError: `webhooks[0].timeoutSeconds: Invalid value: -1: the timeout value must be between 1 and 30 seconds`, + }, + { + name: "valid timeout seconds", + config: newValidatingWebhookConfiguration( + []admissionregistration.Webhook{ + { + Name: "webhook.k8s.io", + ClientConfig: validClientConfig, + TimeoutSeconds: int32Ptr(1), + }, + { + Name: "webhook2.k8s.io", + ClientConfig: validClientConfig, + TimeoutSeconds: int32Ptr(15), + }, + { + Name: "webhook3.k8s.io", + ClientConfig: validClientConfig, + TimeoutSeconds: int32Ptr(30), + }, + }), + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/pkg/apis/admissionregistration/zz_generated.deepcopy.go b/pkg/apis/admissionregistration/zz_generated.deepcopy.go index e5d2a436674..1f9b365c5f7 100644 --- a/pkg/apis/admissionregistration/zz_generated.deepcopy.go +++ b/pkg/apis/admissionregistration/zz_generated.deepcopy.go @@ -257,6 +257,11 @@ func (in *Webhook) DeepCopyInto(out *Webhook) { *out = new(SideEffectClass) **out = **in } + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } return } diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go index 2ca3fa6524f..9eddc3c2acf 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.pb.go @@ -462,6 +462,11 @@ func (m *Webhook) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintGenerated(dAtA, i, uint64(len(*m.SideEffects))) i += copy(dAtA[i:], *m.SideEffects) } + if m.TimeoutSeconds != nil { + dAtA[i] = 0x38 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(*m.TimeoutSeconds)) + } return i, nil } @@ -647,6 +652,9 @@ func (m *Webhook) Size() (n int) { l = len(*m.SideEffects) n += 1 + l + sovGenerated(uint64(l)) } + if m.TimeoutSeconds != nil { + n += 1 + sovGenerated(uint64(*m.TimeoutSeconds)) + } return n } @@ -771,6 +779,7 @@ func (this *Webhook) String() string { `FailurePolicy:` + valueToStringGenerated(this.FailurePolicy) + `,`, `NamespaceSelector:` + strings.Replace(fmt.Sprintf("%v", this.NamespaceSelector), "LabelSelector", "k8s_io_apimachinery_pkg_apis_meta_v1.LabelSelector", 1) + `,`, `SideEffects:` + valueToStringGenerated(this.SideEffects) + `,`, + `TimeoutSeconds:` + valueToStringGenerated(this.TimeoutSeconds) + `,`, `}`, }, "") return s @@ -1835,6 +1844,26 @@ func (m *Webhook) Unmarshal(dAtA []byte) error { s := SideEffectClass(dAtA[iNdEx:postIndex]) m.SideEffects = &s iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeoutSeconds", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.TimeoutSeconds = &v default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -2110,62 +2139,64 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 906 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x54, 0xcf, 0x6f, 0xe3, 0x44, - 0x14, 0x8e, 0x37, 0x29, 0x49, 0x26, 0x89, 0x76, 0x3b, 0x80, 0x14, 0xaa, 0x95, 0x1d, 0xe5, 0x80, - 0x22, 0xa1, 0xb5, 0x49, 0x41, 0x08, 0x21, 0x10, 0xaa, 0x0b, 0x0b, 0x95, 0xba, 0xbb, 0x61, 0x0a, - 0xbb, 0x12, 0xe2, 0xc0, 0xc4, 0x79, 0x49, 0x86, 0xf8, 0x97, 0x66, 0xc6, 0x59, 0x7a, 0x43, 0xe2, - 0x1f, 0x40, 0x42, 0xfc, 0x0d, 0xfc, 0x15, 0xdc, 0x7b, 0xdc, 0x0b, 0x62, 0x4f, 0x16, 0x35, 0x67, - 0x0e, 0x5c, 0x7b, 0x42, 0x63, 0x3b, 0x71, 0xd2, 0x6c, 0xbb, 0xe9, 0x85, 0x03, 0x37, 0xcf, 0xf7, - 0xe6, 0xfb, 0xde, 0xfb, 0x9e, 0xdf, 0x1b, 0xf4, 0xc5, 0xec, 0x7d, 0x61, 0xb2, 0xc0, 0x9a, 0x45, - 0x43, 0xe0, 0x3e, 0x48, 0x10, 0xd6, 0x1c, 0xfc, 0x51, 0xc0, 0xad, 0x3c, 0x40, 0x43, 0x66, 0xd1, - 0x91, 0xc7, 0x84, 0x60, 0x81, 0xcf, 0x61, 0xc2, 0x84, 0xe4, 0x54, 0xb2, 0xc0, 0xb7, 0xe6, 0xfd, - 0x21, 0x48, 0xda, 0xb7, 0x26, 0xe0, 0x03, 0xa7, 0x12, 0x46, 0x66, 0xc8, 0x03, 0x19, 0xe0, 0x5e, - 0xc6, 0x34, 0x69, 0xc8, 0xcc, 0x17, 0x32, 0xcd, 0x9c, 0xb9, 0x77, 0x6f, 0xc2, 0xe4, 0x34, 0x1a, - 0x9a, 0x4e, 0xe0, 0x59, 0x93, 0x60, 0x12, 0x58, 0xa9, 0xc0, 0x30, 0x1a, 0xa7, 0xa7, 0xf4, 0x90, - 0x7e, 0x65, 0xc2, 0x7b, 0xef, 0x16, 0x25, 0x79, 0xd4, 0x99, 0x32, 0x1f, 0xf8, 0xa9, 0x15, 0xce, - 0x26, 0x0a, 0x10, 0x96, 0x07, 0x92, 0x5a, 0xf3, 0x8d, 0x72, 0xf6, 0xac, 0xab, 0x58, 0x3c, 0xf2, - 0x25, 0xf3, 0x60, 0x83, 0xf0, 0xde, 0xcb, 0x08, 0xc2, 0x99, 0x82, 0x47, 0x2f, 0xf3, 0xba, 0xbf, - 0x6b, 0xe8, 0xee, 0x83, 0x48, 0x52, 0xc9, 0xfc, 0xc9, 0x13, 0x18, 0x4e, 0x83, 0x60, 0x76, 0x18, - 0xf8, 0x63, 0x36, 0x89, 0x32, 0xdb, 0xf8, 0x5b, 0x54, 0x53, 0x45, 0x8e, 0xa8, 0xa4, 0x6d, 0xad, - 0xa3, 0xf5, 0x1a, 0xfb, 0x6f, 0x9b, 0x45, 0xaf, 0x96, 0xb9, 0xcc, 0x70, 0x36, 0x51, 0x80, 0x30, - 0xd5, 0x6d, 0x73, 0xde, 0x37, 0x1f, 0x0d, 0xbf, 0x03, 0x47, 0x3e, 0x00, 0x49, 0x6d, 0x7c, 0x16, - 0x1b, 0xa5, 0x24, 0x36, 0x50, 0x81, 0x91, 0xa5, 0x2a, 0x3e, 0x41, 0xb5, 0x3c, 0xb3, 0x68, 0xdf, - 0xea, 0x94, 0x7b, 0x8d, 0xfd, 0xbe, 0xb9, 0xed, 0xdf, 0x30, 0x73, 0xa6, 0x5d, 0x51, 0x29, 0x48, - 0xed, 0x69, 0x2e, 0xd4, 0xfd, 0x5b, 0x43, 0x9d, 0xeb, 0x7c, 0x1d, 0x33, 0x21, 0xf1, 0x37, 0x1b, - 0xde, 0xcc, 0xed, 0xbc, 0x29, 0x76, 0xea, 0xec, 0x4e, 0xee, 0xac, 0xb6, 0x40, 0x56, 0x7c, 0xcd, - 0xd0, 0x0e, 0x93, 0xe0, 0x2d, 0x4c, 0xdd, 0xdf, 0xde, 0xd4, 0x75, 0x85, 0xdb, 0xad, 0x3c, 0xe5, - 0xce, 0x91, 0x12, 0x27, 0x59, 0x8e, 0xee, 0xcf, 0x1a, 0xaa, 0x90, 0xc8, 0x05, 0xfc, 0x16, 0xaa, - 0xd3, 0x90, 0x7d, 0xc6, 0x83, 0x28, 0x14, 0x6d, 0xad, 0x53, 0xee, 0xd5, 0xed, 0x56, 0x12, 0x1b, - 0xf5, 0x83, 0xc1, 0x51, 0x06, 0x92, 0x22, 0x8e, 0xfb, 0xa8, 0x41, 0x43, 0xf6, 0x18, 0xb8, 0x2a, - 0x25, 0x2b, 0xb4, 0x6e, 0xdf, 0x4e, 0x62, 0xa3, 0x71, 0x30, 0x38, 0x5a, 0xc0, 0x64, 0xf5, 0x8e, - 0xd2, 0xe7, 0x20, 0x82, 0x88, 0x3b, 0x20, 0xda, 0xe5, 0x42, 0x9f, 0x2c, 0x40, 0x52, 0xc4, 0xbb, - 0xbf, 0x6a, 0x08, 0xab, 0xaa, 0x9e, 0x30, 0x39, 0x7d, 0x14, 0x42, 0xe6, 0x40, 0xe0, 0x8f, 0x11, - 0x0a, 0x96, 0xa7, 0xbc, 0x48, 0x23, 0x9d, 0x8f, 0x25, 0x7a, 0x11, 0x1b, 0xad, 0xe5, 0xe9, 0xcb, - 0xd3, 0x10, 0xc8, 0x0a, 0x05, 0x0f, 0x50, 0x85, 0x47, 0x2e, 0xb4, 0x6f, 0x6d, 0xfc, 0xb4, 0x97, - 0x74, 0x56, 0x15, 0x63, 0x37, 0xf3, 0x0e, 0xa6, 0x0d, 0x23, 0xa9, 0x52, 0xf7, 0x47, 0x0d, 0xdd, - 0x39, 0x01, 0x3e, 0x67, 0x0e, 0x10, 0x18, 0x03, 0x07, 0xdf, 0x01, 0x6c, 0xa1, 0xba, 0x4f, 0x3d, - 0x10, 0x21, 0x75, 0x20, 0x1d, 0x90, 0xba, 0xbd, 0x9b, 0x73, 0xeb, 0x0f, 0x17, 0x01, 0x52, 0xdc, - 0xc1, 0x1d, 0x54, 0x51, 0x87, 0xb4, 0xae, 0x7a, 0x91, 0x47, 0xdd, 0x25, 0x69, 0x04, 0xdf, 0x45, - 0x95, 0x90, 0xca, 0x69, 0xbb, 0x9c, 0xde, 0xa8, 0xa9, 0xe8, 0x80, 0xca, 0x29, 0x49, 0xd1, 0xee, - 0x1f, 0x1a, 0xd2, 0x1f, 0x53, 0x97, 0x8d, 0xfe, 0x77, 0xfb, 0xf8, 0x8f, 0x86, 0xba, 0xd7, 0x3b, - 0xfb, 0x0f, 0x36, 0xd2, 0x5b, 0xdf, 0xc8, 0xcf, 0xb7, 0xb7, 0x75, 0x7d, 0xe9, 0x57, 0xec, 0xe4, - 0x2f, 0x15, 0x54, 0xcd, 0xaf, 0x2f, 0x27, 0x43, 0xbb, 0x72, 0x32, 0x9e, 0xa2, 0xa6, 0xe3, 0x32, - 0xf0, 0x65, 0x26, 0x9d, 0xcf, 0xf6, 0x47, 0x37, 0x6e, 0xfd, 0xe1, 0x8a, 0x88, 0xfd, 0x5a, 0x9e, - 0xa8, 0xb9, 0x8a, 0x92, 0xb5, 0x44, 0x98, 0xa2, 0x1d, 0xb5, 0x02, 0xd9, 0x36, 0x37, 0xf6, 0x3f, - 0xbc, 0xd9, 0x36, 0xad, 0xaf, 0x76, 0xd1, 0x09, 0x15, 0x13, 0x24, 0x53, 0xc6, 0xc7, 0xa8, 0x35, - 0xa6, 0xcc, 0x8d, 0x38, 0x0c, 0x02, 0x97, 0x39, 0xa7, 0xed, 0x4a, 0xda, 0x86, 0x37, 0x93, 0xd8, - 0x68, 0xdd, 0x5f, 0x0d, 0x5c, 0xc4, 0xc6, 0xee, 0x1a, 0x90, 0xae, 0xfe, 0x3a, 0x19, 0x7f, 0x8f, - 0x76, 0x97, 0x2b, 0x77, 0x02, 0x2e, 0x38, 0x32, 0xe0, 0xed, 0x9d, 0xb4, 0x5d, 0xef, 0x6c, 0x39, - 0x2d, 0x74, 0x08, 0xee, 0x82, 0x6a, 0xbf, 0x9e, 0xc4, 0xc6, 0xee, 0xc3, 0xcb, 0x8a, 0x64, 0x33, - 0x09, 0xfe, 0x04, 0x35, 0x04, 0x1b, 0xc1, 0xa7, 0xe3, 0x31, 0x38, 0x52, 0xb4, 0x5f, 0x49, 0x5d, - 0x74, 0xd5, 0x7b, 0x79, 0x52, 0xc0, 0x17, 0xb1, 0x71, 0xbb, 0x38, 0x1e, 0xba, 0x54, 0x08, 0xb2, - 0x4a, 0xeb, 0xfe, 0xa6, 0xa1, 0x57, 0x5f, 0xf0, 0xb3, 0x30, 0x45, 0x55, 0x91, 0x3d, 0x41, 0xf9, - 0xec, 0x7f, 0xb0, 0xfd, 0xaf, 0xb8, 0xfc, 0x76, 0xd9, 0x8d, 0x24, 0x36, 0xaa, 0x0b, 0x74, 0xa1, - 0x8b, 0x7b, 0xa8, 0xe6, 0x50, 0x3b, 0xf2, 0x47, 0xf9, 0xe3, 0xd9, 0xb4, 0x9b, 0x6a, 0x57, 0x0e, - 0x0f, 0x32, 0x8c, 0x2c, 0xa3, 0xf8, 0x0d, 0x54, 0x8e, 0xb8, 0x9b, 0xbf, 0x53, 0xd5, 0x24, 0x36, - 0xca, 0x5f, 0x91, 0x63, 0xa2, 0x30, 0xfb, 0xde, 0xd9, 0xb9, 0x5e, 0x7a, 0x76, 0xae, 0x97, 0x9e, - 0x9f, 0xeb, 0xa5, 0x1f, 0x12, 0x5d, 0x3b, 0x4b, 0x74, 0xed, 0x59, 0xa2, 0x6b, 0xcf, 0x13, 0x5d, - 0xfb, 0x33, 0xd1, 0xb5, 0x9f, 0xfe, 0xd2, 0x4b, 0x5f, 0x57, 0xf3, 0xd2, 0xfe, 0x0d, 0x00, 0x00, - 0xff, 0xff, 0x85, 0x06, 0x8c, 0x7f, 0xae, 0x09, 0x00, 0x00, + // 936 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x55, 0x41, 0x8f, 0xdb, 0x44, + 0x14, 0x5e, 0x37, 0x59, 0x92, 0x4c, 0x92, 0xb6, 0x3b, 0x80, 0x64, 0xaa, 0xca, 0x8e, 0x72, 0x40, + 0x91, 0x50, 0x6d, 0xb2, 0x20, 0x84, 0x2a, 0x10, 0x5a, 0x2f, 0x14, 0x56, 0xda, 0xb6, 0x61, 0x52, + 0x5a, 0x09, 0x71, 0x60, 0xe2, 0xbc, 0x24, 0x43, 0x1c, 0x8f, 0xe5, 0x19, 0xa7, 0xec, 0x0d, 0x89, + 0x3f, 0x80, 0xc4, 0x8f, 0xe0, 0x57, 0x70, 0xdf, 0x63, 0x39, 0x20, 0x7a, 0xb2, 0x58, 0x73, 0xe6, + 0xc0, 0x75, 0x4f, 0x68, 0x6c, 0x27, 0x4e, 0x36, 0xdd, 0x6d, 0x7a, 0xe1, 0xc0, 0x2d, 0xf3, 0xbd, + 0xf7, 0x7d, 0xef, 0x7d, 0xcf, 0xf3, 0x26, 0xe8, 0xcb, 0xe9, 0x87, 0xc2, 0x62, 0xdc, 0x9e, 0x46, + 0x03, 0x08, 0x7d, 0x90, 0x20, 0xec, 0x39, 0xf8, 0x43, 0x1e, 0xda, 0x79, 0x80, 0x06, 0xcc, 0xa6, + 0xc3, 0x19, 0x13, 0x82, 0x71, 0x3f, 0x84, 0x31, 0x13, 0x32, 0xa4, 0x92, 0x71, 0xdf, 0x9e, 0x77, + 0x07, 0x20, 0x69, 0xd7, 0x1e, 0x83, 0x0f, 0x21, 0x95, 0x30, 0xb4, 0x82, 0x90, 0x4b, 0x8e, 0x3b, + 0x19, 0xd3, 0xa2, 0x01, 0xb3, 0x5e, 0xc8, 0xb4, 0x72, 0xe6, 0xad, 0x3b, 0x63, 0x26, 0x27, 0xd1, + 0xc0, 0x72, 0xf9, 0xcc, 0x1e, 0xf3, 0x31, 0xb7, 0x53, 0x81, 0x41, 0x34, 0x4a, 0x4f, 0xe9, 0x21, + 0xfd, 0x95, 0x09, 0xdf, 0x7a, 0xbf, 0x68, 0x69, 0x46, 0xdd, 0x09, 0xf3, 0x21, 0x3c, 0xb1, 0x83, + 0xe9, 0x58, 0x01, 0xc2, 0x9e, 0x81, 0xa4, 0xf6, 0x7c, 0xa3, 0x9d, 0x5b, 0xf6, 0x65, 0xac, 0x30, + 0xf2, 0x25, 0x9b, 0xc1, 0x06, 0xe1, 0x83, 0x97, 0x11, 0x84, 0x3b, 0x81, 0x19, 0xbd, 0xc8, 0x6b, + 0xff, 0xae, 0xa1, 0xdb, 0xf7, 0x23, 0x49, 0x25, 0xf3, 0xc7, 0x4f, 0x60, 0x30, 0xe1, 0x7c, 0x7a, + 0xc8, 0xfd, 0x11, 0x1b, 0x47, 0x99, 0x6d, 0xfc, 0x2d, 0xaa, 0xaa, 0x26, 0x87, 0x54, 0x52, 0x5d, + 0x6b, 0x69, 0x9d, 0xfa, 0xfe, 0xbb, 0x56, 0x31, 0xab, 0x65, 0x2d, 0x2b, 0x98, 0x8e, 0x15, 0x20, + 0x2c, 0x95, 0x6d, 0xcd, 0xbb, 0xd6, 0xc3, 0xc1, 0x77, 0xe0, 0xca, 0xfb, 0x20, 0xa9, 0x83, 0x4f, + 0x63, 0x73, 0x27, 0x89, 0x4d, 0x54, 0x60, 0x64, 0xa9, 0x8a, 0xfb, 0xa8, 0x9a, 0x57, 0x16, 0xfa, + 0xb5, 0x56, 0xa9, 0x53, 0xdf, 0xef, 0x5a, 0xdb, 0x7e, 0x0d, 0x2b, 0x67, 0x3a, 0x65, 0x55, 0x82, + 0x54, 0x9f, 0xe6, 0x42, 0xed, 0xbf, 0x35, 0xd4, 0xba, 0xca, 0xd7, 0x31, 0x13, 0x12, 0x7f, 0xb3, + 0xe1, 0xcd, 0xda, 0xce, 0x9b, 0x62, 0xa7, 0xce, 0x6e, 0xe6, 0xce, 0xaa, 0x0b, 0x64, 0xc5, 0xd7, + 0x14, 0xed, 0x32, 0x09, 0xb3, 0x85, 0xa9, 0x7b, 0xdb, 0x9b, 0xba, 0xaa, 0x71, 0xa7, 0x99, 0x97, + 0xdc, 0x3d, 0x52, 0xe2, 0x24, 0xab, 0xd1, 0xfe, 0x59, 0x43, 0x65, 0x12, 0x79, 0x80, 0xdf, 0x41, + 0x35, 0x1a, 0xb0, 0xcf, 0x43, 0x1e, 0x05, 0x42, 0xd7, 0x5a, 0xa5, 0x4e, 0xcd, 0x69, 0x26, 0xb1, + 0x59, 0x3b, 0xe8, 0x1d, 0x65, 0x20, 0x29, 0xe2, 0xb8, 0x8b, 0xea, 0x34, 0x60, 0x8f, 0x21, 0x54, + 0xad, 0x64, 0x8d, 0xd6, 0x9c, 0x1b, 0x49, 0x6c, 0xd6, 0x0f, 0x7a, 0x47, 0x0b, 0x98, 0xac, 0xe6, + 0x28, 0xfd, 0x10, 0x04, 0x8f, 0x42, 0x17, 0x84, 0x5e, 0x2a, 0xf4, 0xc9, 0x02, 0x24, 0x45, 0xbc, + 0xfd, 0x8b, 0x86, 0xb0, 0xea, 0xea, 0x09, 0x93, 0x93, 0x87, 0x01, 0x64, 0x0e, 0x04, 0xfe, 0x04, + 0x21, 0xbe, 0x3c, 0xe5, 0x4d, 0x9a, 0xe9, 0xfd, 0x58, 0xa2, 0xe7, 0xb1, 0xd9, 0x5c, 0x9e, 0x1e, + 0x9d, 0x04, 0x40, 0x56, 0x28, 0xb8, 0x87, 0xca, 0x61, 0xe4, 0x81, 0x7e, 0x6d, 0xe3, 0xa3, 0xbd, + 0x64, 0xb2, 0xaa, 0x19, 0xa7, 0x91, 0x4f, 0x30, 0x1d, 0x18, 0x49, 0x95, 0xda, 0x3f, 0x6a, 0xe8, + 0x66, 0x1f, 0xc2, 0x39, 0x73, 0x81, 0xc0, 0x08, 0x42, 0xf0, 0x5d, 0xc0, 0x36, 0xaa, 0xf9, 0x74, + 0x06, 0x22, 0xa0, 0x2e, 0xa4, 0x17, 0xa4, 0xe6, 0xec, 0xe5, 0xdc, 0xda, 0x83, 0x45, 0x80, 0x14, + 0x39, 0xb8, 0x85, 0xca, 0xea, 0x90, 0xf6, 0x55, 0x2b, 0xea, 0xa8, 0x5c, 0x92, 0x46, 0xf0, 0x6d, + 0x54, 0x0e, 0xa8, 0x9c, 0xe8, 0xa5, 0x34, 0xa3, 0xaa, 0xa2, 0x3d, 0x2a, 0x27, 0x24, 0x45, 0xdb, + 0x7f, 0x68, 0xc8, 0x78, 0x4c, 0x3d, 0x36, 0xfc, 0xdf, 0xed, 0xe3, 0x3f, 0x1a, 0x6a, 0x5f, 0xed, + 0xec, 0x3f, 0xd8, 0xc8, 0xd9, 0xfa, 0x46, 0x7e, 0xb1, 0xbd, 0xad, 0xab, 0x5b, 0xbf, 0x64, 0x27, + 0x7f, 0x2b, 0xa3, 0x4a, 0x9e, 0xbe, 0xbc, 0x19, 0xda, 0xa5, 0x37, 0xe3, 0x29, 0x6a, 0xb8, 0x1e, + 0x03, 0x5f, 0x66, 0xd2, 0xf9, 0xdd, 0xfe, 0xf8, 0x95, 0x47, 0x7f, 0xb8, 0x22, 0xe2, 0xbc, 0x91, + 0x17, 0x6a, 0xac, 0xa2, 0x64, 0xad, 0x10, 0xa6, 0x68, 0x57, 0xad, 0x40, 0xb6, 0xcd, 0xf5, 0xfd, + 0x8f, 0x5e, 0x6d, 0x9b, 0xd6, 0x57, 0xbb, 0x98, 0x84, 0x8a, 0x09, 0x92, 0x29, 0xe3, 0x63, 0xd4, + 0x1c, 0x51, 0xe6, 0x45, 0x21, 0xf4, 0xb8, 0xc7, 0xdc, 0x13, 0xbd, 0x9c, 0x8e, 0xe1, 0xed, 0x24, + 0x36, 0x9b, 0xf7, 0x56, 0x03, 0xe7, 0xb1, 0xb9, 0xb7, 0x06, 0xa4, 0xab, 0xbf, 0x4e, 0xc6, 0xdf, + 0xa3, 0xbd, 0xe5, 0xca, 0xf5, 0xc1, 0x03, 0x57, 0xf2, 0x50, 0xdf, 0x4d, 0xc7, 0xf5, 0xde, 0x96, + 0xb7, 0x85, 0x0e, 0xc0, 0x5b, 0x50, 0x9d, 0x37, 0x93, 0xd8, 0xdc, 0x7b, 0x70, 0x51, 0x91, 0x6c, + 0x16, 0xc1, 0x9f, 0xa2, 0xba, 0x60, 0x43, 0xf8, 0x6c, 0x34, 0x02, 0x57, 0x0a, 0xfd, 0xb5, 0xd4, + 0x45, 0x5b, 0xbd, 0x97, 0xfd, 0x02, 0x3e, 0x8f, 0xcd, 0x1b, 0xc5, 0xf1, 0xd0, 0xa3, 0x42, 0x90, + 0x55, 0x1a, 0xbe, 0x8b, 0xae, 0xab, 0xbf, 0x64, 0x1e, 0xc9, 0x3e, 0xb8, 0xdc, 0x1f, 0x0a, 0xbd, + 0xd2, 0xd2, 0x3a, 0xbb, 0x0e, 0x4e, 0x62, 0xf3, 0xfa, 0xa3, 0xb5, 0x08, 0xb9, 0x90, 0xd9, 0xfe, + 0x55, 0x43, 0xaf, 0xbf, 0xe0, 0x43, 0x63, 0x8a, 0x2a, 0x22, 0x7b, 0xbe, 0xf2, 0xbd, 0xb9, 0xbb, + 0xfd, 0x67, 0xbc, 0xf8, 0xee, 0x39, 0xf5, 0x24, 0x36, 0x2b, 0x0b, 0x74, 0xa1, 0x8b, 0x3b, 0xa8, + 0xea, 0x52, 0x27, 0xf2, 0x87, 0xf9, 0xc3, 0xdb, 0x70, 0x1a, 0x6a, 0xcf, 0x0e, 0x0f, 0x32, 0x8c, + 0x2c, 0xa3, 0xf8, 0x2d, 0x54, 0x8a, 0x42, 0x2f, 0x7f, 0xe3, 0x2a, 0x49, 0x6c, 0x96, 0xbe, 0x22, + 0xc7, 0x44, 0x61, 0xce, 0x9d, 0xd3, 0x33, 0x63, 0xe7, 0xd9, 0x99, 0xb1, 0xf3, 0xfc, 0xcc, 0xd8, + 0xf9, 0x21, 0x31, 0xb4, 0xd3, 0xc4, 0xd0, 0x9e, 0x25, 0x86, 0xf6, 0x3c, 0x31, 0xb4, 0x3f, 0x13, + 0x43, 0xfb, 0xe9, 0x2f, 0x63, 0xe7, 0xeb, 0x4a, 0xde, 0xda, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, + 0x85, 0xc4, 0x5b, 0xa6, 0xea, 0x09, 0x00, 0x00, } diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto index 1c40ae530de..a0278cee700 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/generated.proto @@ -217,6 +217,14 @@ message Webhook { // sideEffects == Unknown or Some. Defaults to Unknown. // +optional optional string sideEffects = 6; + + // TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, + // the webhook call will be ignored or the API call will fail based on the + // failure policy. + // The timeout value must be between 1 and 30 seconds. + // Default to 30 seconds. + // +optional + optional int32 timeoutSeconds = 7; } // WebhookClientConfig contains the information to make a TLS diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go index 49d94ec0eb7..7968372b39b 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types.go @@ -216,6 +216,14 @@ type Webhook struct { // 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"` } // RuleWithOperations is a tuple of Operations and Resources. It is recommended to make diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go index e97628aab7c..07df6477ea1 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go @@ -106,6 +106,7 @@ var map_Webhook = map[string]string{ "failurePolicy": "FailurePolicy defines how unrecognized errors from the admission endpoint are handled - allowed values are Ignore or Fail. Defaults to Ignore.", "namespaceSelector": "NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. If the object is another cluster scoped resource, it never skips the webhook.\n\nFor example, to run the webhook on any objects whose namespace is not associated with \"runlevel\" of \"0\" or \"1\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"runlevel\",\n \"operator\": \"NotIn\",\n \"values\": [\n \"0\",\n \"1\"\n ]\n }\n ]\n}\n\nIf instead you want to only run the webhook on any objects whose namespace is associated with the \"environment\" of \"prod\" or \"staging\"; you will set the selector as follows: \"namespaceSelector\": {\n \"matchExpressions\": [\n {\n \"key\": \"environment\",\n \"operator\": \"In\",\n \"values\": [\n \"prod\",\n \"staging\"\n ]\n }\n ]\n}\n\nSee https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more examples of label selectors.\n\nDefault to the empty LabelSelector, which matches everything.", "sideEffects": "SideEffects states whether this webhookk has side effects. Acceptable values are: Unknown, None, Some, NoneOnDryRun Webhooks with side effects MUST implement a reconciliation system, since a request may be rejected by a future step in the admission change and the side effects therefore need to be undone. Requests with the dryRun attribute will be auto-rejected if they match a webhook with sideEffects == Unknown or Some. Defaults to Unknown.", + "timeoutSeconds": "TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored or the API call will fail based on the failure policy. The timeout value must be between 1 and 30 seconds. Default to 30 seconds.", } func (Webhook) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go index c6867be1221..b955e4b56af 100644 --- a/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go @@ -257,6 +257,11 @@ func (in *Webhook) DeepCopyInto(out *Webhook) { *out = new(SideEffectClass) **out = **in } + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } return } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go index 44490d1b4f0..a2b36749557 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating/dispatcher.go @@ -101,7 +101,11 @@ func (a *mutatingDispatcher) callAttrMutatingHook(ctx context.Context, h *v1beta return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } response := &admissionv1beta1.AdmissionReview{} - if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil { + r := client.Post().Context(ctx).Body(&request) + if h.TimeoutSeconds != nil { + r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second) + } + if err := r.Do().Into(response); err != nil { return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go index d779be7b835..2a70e4e64e5 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating/dispatcher.go @@ -115,7 +115,11 @@ func (d *validatingDispatcher) callHook(ctx context.Context, h *v1beta1.Webhook, return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } response := &admissionv1beta1.AdmissionReview{} - if err := client.Post().Context(ctx).Body(&request).Do().Into(response); err != nil { + r := client.Post().Context(ctx).Body(&request) + if h.TimeoutSeconds != nil { + r = r.Timeout(time.Duration(*h.TimeoutSeconds) * time.Second) + } + if err := r.Do().Into(response); err != nil { return &webhook.ErrCallingWebhook{WebhookName: h.Name, Reason: err} } diff --git a/test/e2e/apimachinery/webhook.go b/test/e2e/apimachinery/webhook.go index 92de1b41096..09b5d2c1652 100644 --- a/test/e2e/apimachinery/webhook.go +++ b/test/e2e/apimachinery/webhook.go @@ -64,6 +64,7 @@ const ( dummyValidatingWebhookConfigName = "e2e-test-dummy-validating-webhook-config" dummyMutatingWebhookConfigName = "e2e-test-dummy-mutating-webhook-config" crdWebhookConfigName = "e2e-test-webhook-config-crd" + slowWebhookConfigName = "e2e-test-webhook-config-slow" skipNamespaceLabelKey = "skip-webhook-admission" skipNamespaceLabelValue = "yes" @@ -201,6 +202,31 @@ var _ = SIGDescribe("AdmissionWebhook", func() { testCRDDenyWebhook(f) }) + It("Should honor timeout", func() { + policyFail := v1beta1.Fail + policyIgnore := v1beta1.Ignore + + By("Setting timeout (1s) shorter than webhook latency (5s)") + slowWebhookCleanup := registerSlowWebhook(f, context, &policyFail, int32Ptr(1)) + testSlowWebhookTimeoutFailEarly(f) + slowWebhookCleanup() + + By("Having no error when timeout is shorter than webhook latency and failure policy is ignore") + slowWebhookCleanup = registerSlowWebhook(f, context, &policyIgnore, int32Ptr(1)) + testSlowWebhookTimeoutNoError(f) + slowWebhookCleanup() + + By("Having no error when timeout is longer than webhook latency") + slowWebhookCleanup = registerSlowWebhook(f, context, &policyFail, int32Ptr(10)) + testSlowWebhookTimeoutNoError(f) + slowWebhookCleanup() + + By("Having no error when timeout is empty (defaulted to 10s in v1beta1)") + slowWebhookCleanup = registerSlowWebhook(f, context, &policyFail, nil) + testSlowWebhookTimeoutNoError(f) + slowWebhookCleanup() + }) + // TODO: add more e2e tests for mutating webhooks // 1. mutating webhook that mutates pod // 2. mutating webhook that sends empty patch @@ -357,6 +383,8 @@ func deployWebhookAndService(f *framework.Framework, image string, context *cert func strPtr(s string) *string { return &s } +func int32Ptr(i int32) *int32 { return &i } + func registerWebhook(f *framework.Framework, context *certContext) func() { client := f.ClientSet By("Registering the webhook via the AdmissionRegistration API") @@ -1445,3 +1473,69 @@ func testCRDDenyWebhook(f *framework.Framework) { framework.Failf("expect error contains %q, got %q", expectedErrMsg, err.Error()) } } + +func registerSlowWebhook(f *framework.Framework, context *certContext, policy *v1beta1.FailurePolicyType, timeout *int32) func() { + client := f.ClientSet + By("Registering slow webhook via the AdmissionRegistration API") + + namespace := f.Namespace.Name + configName := slowWebhookConfigName + + _, err := client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(&v1beta1.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: configName, + }, + Webhooks: []v1beta1.Webhook{ + { + Name: "allow-configmap-with-delay-webhook.k8s.io", + Rules: []v1beta1.RuleWithOperations{{ + Operations: []v1beta1.OperationType{v1beta1.Create}, + Rule: v1beta1.Rule{ + APIGroups: []string{""}, + APIVersions: []string{"v1"}, + Resources: []string{"configmaps"}, + }, + }}, + ClientConfig: v1beta1.WebhookClientConfig{ + Service: &v1beta1.ServiceReference{ + Namespace: namespace, + Name: serviceName, + Path: strPtr("/always-allow-delay-5s"), + }, + CABundle: context.signingCert, + }, + FailurePolicy: policy, + TimeoutSeconds: timeout, + }, + }, + }) + framework.ExpectNoError(err, "registering slow webhook config %s with namespace %s", configName, namespace) + + // The webhook configuration is honored in 10s. + time.Sleep(10 * time.Second) + + return func() { + client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(configName, nil) + } +} + +func testSlowWebhookTimeoutFailEarly(f *framework.Framework) { + By("Request fails when timeout (1s) is shorter than slow webhook latency (5s)") + client := f.ClientSet + name := "e2e-test-slow-webhook-configmap" + _, err := client.CoreV1().ConfigMaps(f.Namespace.Name).Create(&v1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: name}}) + Expect(err).To(HaveOccurred(), "create configmap in namespace %s should have timed-out reaching slow webhook", f.Namespace.Name) + expectedErrMsg := `/always-allow-delay-5s?timeout=1s: context deadline exceeded` + if !strings.Contains(err.Error(), expectedErrMsg) { + framework.Failf("expect error contains %q, got %q", expectedErrMsg, err.Error()) + } +} + +func testSlowWebhookTimeoutNoError(f *framework.Framework) { + client := f.ClientSet + name := "e2e-test-slow-webhook-configmap" + _, err := client.CoreV1().ConfigMaps(f.Namespace.Name).Create(&v1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: name}}) + Expect(err).To(BeNil()) + err = client.CoreV1().ConfigMaps(f.Namespace.Name).Delete(name, &metav1.DeleteOptions{}) + Expect(err).To(BeNil()) +} diff --git a/test/images/webhook/BUILD b/test/images/webhook/BUILD index 17f9f049256..80a980253dc 100644 --- a/test/images/webhook/BUILD +++ b/test/images/webhook/BUILD @@ -4,6 +4,7 @@ go_library( name = "go_default_library", srcs = [ "addlabel.go", + "alwaysallow.go", "alwaysdeny.go", "config.go", "configmap.go", diff --git a/test/images/webhook/VERSION b/test/images/webhook/VERSION index fe9e4faa6b2..42e1b6f2255 100644 --- a/test/images/webhook/VERSION +++ b/test/images/webhook/VERSION @@ -1 +1 @@ -1.13v1 +1.14v1 diff --git a/test/images/webhook/alwaysallow.go b/test/images/webhook/alwaysallow.go new file mode 100644 index 00000000000..f9a4aa9a89f --- /dev/null +++ b/test/images/webhook/alwaysallow.go @@ -0,0 +1,36 @@ +/* +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 main + +import ( + "time" + + "k8s.io/api/admission/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog" +) + +// alwaysAllowDelayFiveSeconds sleeps for five seconds and allows all requests made to this function. +func alwaysAllowDelayFiveSeconds(ar v1beta1.AdmissionReview) *v1beta1.AdmissionResponse { + klog.V(2).Info("always-allow-with-delay sleeping for 5 seconds") + time.Sleep(5 * time.Second) + klog.V(2).Info("calling always-allow") + reviewResponse := v1beta1.AdmissionResponse{} + reviewResponse.Allowed = true + reviewResponse.Result = &metav1.Status{Message: "this webhook allows all requests"} + return &reviewResponse +} diff --git a/test/images/webhook/main.go b/test/images/webhook/main.go index 0d9460ca8ba..87484b99eaa 100644 --- a/test/images/webhook/main.go +++ b/test/images/webhook/main.go @@ -91,6 +91,10 @@ func serve(w http.ResponseWriter, r *http.Request, admit admitFunc) { } } +func serveAlwaysAllowDelayFiveSeconds(w http.ResponseWriter, r *http.Request) { + serve(w, r, alwaysAllowDelayFiveSeconds) +} + func serveAlwaysDeny(w http.ResponseWriter, r *http.Request) { serve(w, r, alwaysDeny) } @@ -132,10 +136,12 @@ func serveCRD(w http.ResponseWriter, r *http.Request) { } func main() { + klog.InitFlags(nil) var config Config config.addFlags() flag.Parse() + http.HandleFunc("/always-allow-delay-5s", serveAlwaysAllowDelayFiveSeconds) http.HandleFunc("/always-deny", serveAlwaysDeny) http.HandleFunc("/add-label", serveAddLabel) http.HandleFunc("/pods", servePods) diff --git a/test/utils/image/manifest.go b/test/utils/image/manifest.go index 266945a4bb8..a4ac5dfcd3a 100644 --- a/test/utils/image/manifest.go +++ b/test/utils/image/manifest.go @@ -93,7 +93,7 @@ var ( // Preconfigured image configs var ( CRDConversionWebhook = Config{e2eRegistry, "crd-conversion-webhook", "1.13rev2"} - AdmissionWebhook = Config{e2eRegistry, "webhook", "1.13v1"} + AdmissionWebhook = Config{e2eRegistry, "webhook", "1.14v1"} APIServer = Config{e2eRegistry, "sample-apiserver", "1.10"} AppArmorLoader = Config{e2eRegistry, "apparmor-loader", "1.0"} BusyBox = Config{dockerLibraryRegistry, "busybox", "1.29"}