mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	Add MutatingWebhookConfiguration type
This commit is contained in:
		
							
								
								
									
										2059
									
								
								api/openapi-spec/swagger.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2059
									
								
								api/openapi-spec/swagger.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,3 +1,4 @@ | ||||
|  | ||||
| cluster/images/etcd-version-monitor | ||||
| cmd/gke-certificates-controller/app | ||||
| cmd/hyperkube | ||||
| @@ -236,9 +237,10 @@ pkg/proxy/util | ||||
| pkg/proxy/winkernel | ||||
| pkg/proxy/winuserspace | ||||
| pkg/quota/evaluator/core | ||||
| pkg/registry/admissionregistration/externaladmissionhookconfiguration/storage | ||||
| pkg/registry/admissionregistration/initializerconfiguration/storage | ||||
| pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage | ||||
| pkg/registry/admissionregistration/rest | ||||
| pkg/registry/admissionregistration/validatingwebhookconfiguration/storage | ||||
| pkg/registry/apps/rest | ||||
| pkg/registry/apps/statefulset | ||||
| pkg/registry/apps/statefulset/storage | ||||
|   | ||||
| @@ -137,6 +137,8 @@ func TestDefaulting(t *testing.T) { | ||||
| 		{Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPresetList"}:                                   {}, | ||||
| 		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingWebhookConfiguration"}:     {}, | ||||
| 		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingWebhookConfigurationList"}: {}, | ||||
| 		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "MutatingWebhookConfiguration"}:       {}, | ||||
| 		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "MutatingWebhookConfigurationList"}:   {}, | ||||
| 		{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"}:                                       {}, | ||||
| 		{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicyList"}:                                   {}, | ||||
| 		{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClass"}:                                      {}, | ||||
|   | ||||
| @@ -18,7 +18,7 @@ limitations under the License. | ||||
|  | ||||
| // Package admissionregistration is the internal version of the API. | ||||
| // AdmissionConfiguration and AdmissionPluginConfiguration are legacy static admission plugin configuration | ||||
| // InitializerConfiguration and ValidatingWebhookConfiguration is for the | ||||
| // InitializerConfiguration, ValidatingWebhookConfiguration, and MutatingWebhookConfiguration are for the | ||||
| // new dynamic admission controller configuration. | ||||
| // +groupName=admissionregistration.k8s.io | ||||
| package admissionregistration // import "k8s.io/kubernetes/pkg/apis/admissionregistration" | ||||
|   | ||||
| @@ -35,7 +35,7 @@ func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *r | ||||
| 	if err := announced.NewGroupMetaFactory( | ||||
| 		&announced.GroupMetaFactoryArgs{ | ||||
| 			GroupName:                  admissionregistration.GroupName, | ||||
| 			RootScopedKinds:            sets.NewString("InitializerConfiguration", "ValidatingWebhookConfiguration"), | ||||
| 			RootScopedKinds:            sets.NewString("InitializerConfiguration", "ValidatingWebhookConfiguration", "MutatingWebhookConfiguration"), | ||||
| 			VersionPreferenceOrder:     []string{v1alpha1.SchemeGroupVersion.Version}, | ||||
| 			AddInternalObjectsToScheme: admissionregistration.AddToScheme, | ||||
| 		}, | ||||
|   | ||||
| @@ -48,7 +48,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { | ||||
| 		&InitializerConfigurationList{}, | ||||
| 		&ValidatingWebhookConfiguration{}, | ||||
| 		&ValidatingWebhookConfigurationList{}, | ||||
| 		// TODO add mutating configs here too | ||||
| 		&MutatingWebhookConfiguration{}, | ||||
| 		&MutatingWebhookConfigurationList{}, | ||||
| 	) | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -118,7 +118,7 @@ const ( | ||||
| // +genclient:nonNamespaced | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
|  | ||||
| // ValidatingWebhookConfiguration describes the configuration of an admission webhook that accept or reject and object without changing it. | ||||
| // ValidatingWebhookConfiguration describes the configuration of an admission webhook that accepts or rejects and object without changing it. | ||||
| type ValidatingWebhookConfiguration struct { | ||||
| 	metav1.TypeMeta | ||||
| 	// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. | ||||
| @@ -138,10 +138,38 @@ type ValidatingWebhookConfigurationList struct { | ||||
| 	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds | ||||
| 	// +optional | ||||
| 	metav1.ListMeta | ||||
| 	// List of ValidatingWebhookConfiguration. | ||||
| 	// List of ValidatingWebhookConfigurations. | ||||
| 	Items []ValidatingWebhookConfiguration | ||||
| } | ||||
|  | ||||
| // +genclient | ||||
| // +genclient:nonNamespaced | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
|  | ||||
| // MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object. | ||||
| type MutatingWebhookConfiguration struct { | ||||
| 	metav1.TypeMeta | ||||
| 	// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. | ||||
| 	// +optional | ||||
| 	metav1.ObjectMeta | ||||
| 	// Webhooks is a list of webhooks and the affected resources and operations. | ||||
| 	// +optional | ||||
| 	Webhooks []Webhook | ||||
| } | ||||
|  | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
|  | ||||
| // MutatingWebhookConfigurationList is a list of MutatingWebhookConfiguration. | ||||
| type MutatingWebhookConfigurationList struct { | ||||
| 	metav1.TypeMeta | ||||
| 	// Standard list metadata. | ||||
| 	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds | ||||
| 	// +optional | ||||
| 	metav1.ListMeta | ||||
| 	// List of MutatingWebhookConfiguration. | ||||
| 	Items []MutatingWebhookConfiguration | ||||
| } | ||||
|  | ||||
| // Webhook describes an admission webhook and the resources and operations it applies to. | ||||
| type Webhook struct { | ||||
| 	// The name of the admission webhook. | ||||
|   | ||||
| @@ -21,7 +21,7 @@ limitations under the License. | ||||
|  | ||||
| // Package v1alpha1 is the v1alpha1 version of the API. | ||||
| // AdmissionConfiguration and AdmissionPluginConfiguration are legacy static admission plugin configuration | ||||
| // admissionregistrationv1alpha1.InitializerConfiguration and admissionregistrationv1alpha1.ValidatingWebhookConfiguration is for the | ||||
| // InitializerConfiguration, ValidatingWebhookConfiguration, and MutatingWebhookConfiguration are for the | ||||
| // new dynamic admission controller configuration. | ||||
| // +groupName=admissionregistration.k8s.io | ||||
| package v1alpha1 // import "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1" | ||||
|   | ||||
| @@ -166,7 +166,15 @@ func ValidateInitializerConfigurationUpdate(newIC, oldIC *admissionregistration. | ||||
| func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList { | ||||
| 	allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata")) | ||||
| 	for i, hook := range e.Webhooks { | ||||
| 		allErrors = append(allErrors, validateWebhook(&hook, field.NewPath("validatingWebhooks").Index(i))...) | ||||
| 		allErrors = append(allErrors, validateWebhook(&hook, field.NewPath("webhooks").Index(i))...) | ||||
| 	} | ||||
| 	return allErrors | ||||
| } | ||||
|  | ||||
| func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration) field.ErrorList { | ||||
| 	allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata")) | ||||
| 	for i, hook := range e.Webhooks { | ||||
| 		allErrors = append(allErrors, validateWebhook(&hook, field.NewPath("webhooks").Index(i))...) | ||||
| 	} | ||||
| 	return allErrors | ||||
| } | ||||
| @@ -263,6 +271,10 @@ func validateRuleWithOperations(ruleWithOperations *admissionregistration.RuleWi | ||||
| 	return allErrors | ||||
| } | ||||
|  | ||||
| func ValidateValidatingwebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList { | ||||
| func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList { | ||||
| 	return ValidateValidatingWebhookConfiguration(newC) | ||||
| } | ||||
|  | ||||
| func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.MutatingWebhookConfiguration) field.ErrorList { | ||||
| 	return ValidateMutatingWebhookConfiguration(newC) | ||||
| } | ||||
|   | ||||
| @@ -231,7 +231,7 @@ func TestValidateInitializerConfiguration(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func getValidatingWebhookConfiguration(hooks []admissionregistration.Webhook) *admissionregistration.ValidatingWebhookConfiguration { | ||||
| func newValidatingWebhookConfiguration(hooks []admissionregistration.Webhook) *admissionregistration.ValidatingWebhookConfiguration { | ||||
| 	return &admissionregistration.ValidatingWebhookConfiguration{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name: "config", | ||||
| @@ -240,6 +240,8 @@ func getValidatingWebhookConfiguration(hooks []admissionregistration.Webhook) *a | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TODO: Add TestValidateMutatingWebhookConfiguration to test validation for mutating webhooks. | ||||
|  | ||||
| func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name          string | ||||
| @@ -248,7 +250,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "all Webhooks must have a fully qualified name", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -260,11 +262,11 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 						Name: "", | ||||
| 					}, | ||||
| 				}), | ||||
| 			expectedError: `validatingWebhooks[1].name: Invalid value: "k8s.io": should be a domain with at least three segments separated by dots, validatingWebhooks[2].name: Required value`, | ||||
| 			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`, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Operations must not be empty or nil", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -288,11 +290,11 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 						}, | ||||
| 					}, | ||||
| 				}), | ||||
| 			expectedError: `validatingWebhooks[0].rules[0].operations: Required value, validatingWebhooks[0].rules[1].operations: Required value`, | ||||
| 			expectedError: `webhooks[0].rules[0].operations: Required value, webhooks[0].rules[1].operations: Required value`, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "\"\" is NOT a valid operation", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -312,7 +314,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "operation must be either create/update/delete/connect", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -332,7 +334,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "wildcard operation cannot be mixed with other strings", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -352,7 +354,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: `resource "*" can co-exist with resources that have subresources`, | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -371,7 +373,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: `resource "*" cannot mix with resources that don't have subresources`, | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -391,7 +393,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "resource a/* cannot mix with a/x", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -407,11 +409,11 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 						}, | ||||
| 					}, | ||||
| 				}), | ||||
| 			expectedError: `validatingWebhooks[0].rules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`, | ||||
| 			expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "resource a/* can mix with a", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -430,7 +432,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "resource */a cannot mix with x/a", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -446,11 +448,11 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 						}, | ||||
| 					}, | ||||
| 				}), | ||||
| 			expectedError: `validatingWebhooks[0].rules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`, | ||||
| 			expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "resource */* cannot mix with other resources", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -466,11 +468,11 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 						}, | ||||
| 					}, | ||||
| 				}), | ||||
| 			expectedError: `validatingWebhooks[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`, | ||||
| 			expectedError: `webhooks[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "FailurePolicy can only be \"Ignore\" or \"Fail\"", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -480,11 +482,11 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 						}(), | ||||
| 					}, | ||||
| 				}), | ||||
| 			expectedError: `validatingWebhooks[0].failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`, | ||||
| 			expectedError: `webhooks[0].failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "URLPath must start with slash", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -497,7 +499,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "URLPath accepts slash", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -510,7 +512,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "URLPath accepts no trailing slash", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -523,7 +525,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "URLPath fails //", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -536,7 +538,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "URLPath no empty step", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -548,7 +550,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 			expectedError: `clientConfig.urlPath: Invalid value: "/foo//bar/": segment[1] may not be empty`, | ||||
| 		}, { | ||||
| 			name: "URLPath no empty step 2", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
| @@ -561,7 +563,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "URLPath no non-subdomain", | ||||
| 			config: getValidatingWebhookConfiguration( | ||||
| 			config: newValidatingWebhookConfiguration( | ||||
| 				[]admissionregistration.Webhook{ | ||||
| 					{ | ||||
| 						Name: "webhook.k8s.io", | ||||
|   | ||||
| @@ -106,7 +106,7 @@ func init() { | ||||
| 	if err := announced.NewGroupMetaFactory( | ||||
| 		&announced.GroupMetaFactoryArgs{ | ||||
| 			GroupName:              admissionregistrationv1alpha1.GroupName, | ||||
| 			RootScopedKinds:        sets.NewString("InitializerConfiguration", "ValidatingWebhookConfiguration"), | ||||
| 			RootScopedKinds:        sets.NewString("InitializerConfiguration", "ValidatingWebhookConfiguration", "MutatingWebhookConfiguration"), | ||||
| 			VersionPreferenceOrder: []string{admissionregistrationv1alpha1.SchemeGroupVersion.Version}, | ||||
| 		}, | ||||
| 		announced.VersionToSchemeFunc{ | ||||
|   | ||||
| @@ -0,0 +1,17 @@ | ||||
| /* | ||||
| Copyright 2015 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 mutatingwebhookconfiguration // import "k8s.io/kubernetes/pkg/registry/admissionregistration/mutatingwebhookconfiguration" | ||||
| @@ -0,0 +1,51 @@ | ||||
| /* | ||||
| Copyright 2015 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 storage | ||||
|  | ||||
| import ( | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apiserver/pkg/registry/generic" | ||||
| 	genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" | ||||
| 	"k8s.io/kubernetes/pkg/apis/admissionregistration" | ||||
| 	"k8s.io/kubernetes/pkg/registry/admissionregistration/mutatingwebhookconfiguration" | ||||
| ) | ||||
|  | ||||
| // rest implements a RESTStorage for pod disruption budgets against etcd | ||||
| type REST struct { | ||||
| 	*genericregistry.Store | ||||
| } | ||||
|  | ||||
| // NewREST returns a RESTStorage object that will work against pod disruption budgets. | ||||
| func NewREST(optsGetter generic.RESTOptionsGetter) *REST { | ||||
| 	store := &genericregistry.Store{ | ||||
| 		NewFunc:     func() runtime.Object { return &admissionregistration.MutatingWebhookConfiguration{} }, | ||||
| 		NewListFunc: func() runtime.Object { return &admissionregistration.MutatingWebhookConfigurationList{} }, | ||||
| 		ObjectNameFunc: func(obj runtime.Object) (string, error) { | ||||
| 			return obj.(*admissionregistration.MutatingWebhookConfiguration).Name, nil | ||||
| 		}, | ||||
| 		DefaultQualifiedResource: admissionregistration.Resource("mutatingwebhookconfigurations"), | ||||
|  | ||||
| 		CreateStrategy: mutatingwebhookconfiguration.Strategy, | ||||
| 		UpdateStrategy: mutatingwebhookconfiguration.Strategy, | ||||
| 		DeleteStrategy: mutatingwebhookconfiguration.Strategy, | ||||
| 	} | ||||
| 	options := &generic.StoreOptions{RESTOptions: optsGetter} | ||||
| 	if err := store.CompleteWithOptions(options); err != nil { | ||||
| 		panic(err) // TODO: Propagate error up | ||||
| 	} | ||||
| 	return &REST{store} | ||||
| } | ||||
| @@ -0,0 +1,90 @@ | ||||
| /* | ||||
| Copyright 2014 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 mutatingwebhookconfiguration | ||||
|  | ||||
| import ( | ||||
| 	"reflect" | ||||
|  | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apimachinery/pkg/util/validation/field" | ||||
| 	genericapirequest "k8s.io/apiserver/pkg/endpoints/request" | ||||
| 	"k8s.io/apiserver/pkg/storage/names" | ||||
| 	"k8s.io/kubernetes/pkg/api/legacyscheme" | ||||
| 	"k8s.io/kubernetes/pkg/apis/admissionregistration" | ||||
| 	"k8s.io/kubernetes/pkg/apis/admissionregistration/validation" | ||||
| ) | ||||
|  | ||||
| // mutatingWebhookConfigurationStrategy implements verification logic for mutatingWebhookConfiguration. | ||||
| type mutatingWebhookConfigurationStrategy struct { | ||||
| 	runtime.ObjectTyper | ||||
| 	names.NameGenerator | ||||
| } | ||||
|  | ||||
| // Strategy is the default logic that applies when creating and updating mutatingWebhookConfiguration objects. | ||||
| var Strategy = mutatingWebhookConfigurationStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} | ||||
|  | ||||
| // NamespaceScoped returns true because all mutatingWebhookConfiguration' need to be within a namespace. | ||||
| func (mutatingWebhookConfigurationStrategy) NamespaceScoped() bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // PrepareForCreate clears the status of an mutatingWebhookConfiguration before creation. | ||||
| func (mutatingWebhookConfigurationStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) { | ||||
| 	ic := obj.(*admissionregistration.MutatingWebhookConfiguration) | ||||
| 	ic.Generation = 1 | ||||
| } | ||||
|  | ||||
| // PrepareForUpdate clears fields that are not allowed to be set by end users on update. | ||||
| func (mutatingWebhookConfigurationStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) { | ||||
| 	newIC := obj.(*admissionregistration.MutatingWebhookConfiguration) | ||||
| 	oldIC := old.(*admissionregistration.MutatingWebhookConfiguration) | ||||
|  | ||||
| 	// Any changes to the spec increment the generation number, any changes to the | ||||
| 	// status should reflect the generation number of the corresponding object. | ||||
| 	// See metav1.ObjectMeta description for more information on Generation. | ||||
| 	if !reflect.DeepEqual(oldIC.Webhooks, newIC.Webhooks) { | ||||
| 		newIC.Generation = oldIC.Generation + 1 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Validate validates a new mutatingWebhookConfiguration. | ||||
| func (mutatingWebhookConfigurationStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList { | ||||
| 	ic := obj.(*admissionregistration.MutatingWebhookConfiguration) | ||||
| 	return validation.ValidateMutatingWebhookConfiguration(ic) | ||||
| } | ||||
|  | ||||
| // Canonicalize normalizes the object after validation. | ||||
| func (mutatingWebhookConfigurationStrategy) Canonicalize(obj runtime.Object) { | ||||
| } | ||||
|  | ||||
| // AllowCreateOnUpdate is true for mutatingWebhookConfiguration; this means you may create one with a PUT request. | ||||
| func (mutatingWebhookConfigurationStrategy) AllowCreateOnUpdate() bool { | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // ValidateUpdate is the default update validation for an end user. | ||||
| func (mutatingWebhookConfigurationStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { | ||||
| 	validationErrorList := validation.ValidateMutatingWebhookConfiguration(obj.(*admissionregistration.MutatingWebhookConfiguration)) | ||||
| 	updateErrorList := validation.ValidateMutatingWebhookConfigurationUpdate(obj.(*admissionregistration.MutatingWebhookConfiguration), old.(*admissionregistration.MutatingWebhookConfiguration)) | ||||
| 	return append(validationErrorList, updateErrorList...) | ||||
| } | ||||
|  | ||||
| // AllowUnconditionalUpdate is the default update policy for mutatingWebhookConfiguration objects. Status update should | ||||
| // only be allowed if version match. | ||||
| func (mutatingWebhookConfigurationStrategy) AllowUnconditionalUpdate() bool { | ||||
| 	return false | ||||
| } | ||||
| @@ -25,6 +25,7 @@ import ( | ||||
| 	"k8s.io/kubernetes/pkg/api/legacyscheme" | ||||
| 	"k8s.io/kubernetes/pkg/apis/admissionregistration" | ||||
| 	initializerconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/initializerconfiguration/storage" | ||||
| 	mutatingwebhookconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage" | ||||
| 	validatingwebhookconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingwebhookconfiguration/storage" | ||||
| ) | ||||
|  | ||||
| @@ -53,6 +54,10 @@ func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstora | ||||
| 		s := validatingwebhookconfigurationstorage.NewREST(restOptionsGetter) | ||||
| 		storage["validatingwebhookconfigurations"] = s | ||||
| 	} | ||||
| 	if apiResourceConfigSource.ResourceEnabled(version.WithResource("mutatingwebhookconfigurations")) { | ||||
| 		s := mutatingwebhookconfigurationstorage.NewREST(restOptionsGetter) | ||||
| 		storage["mutatingwebhookconfigurations"] = s | ||||
| 	} | ||||
| 	return storage | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -79,7 +79,7 @@ func (validatingWebhookConfigurationStrategy) AllowCreateOnUpdate() bool { | ||||
| // ValidateUpdate is the default update validation for an end user. | ||||
| func (validatingWebhookConfigurationStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { | ||||
| 	validationErrorList := validation.ValidateValidatingWebhookConfiguration(obj.(*admissionregistration.ValidatingWebhookConfiguration)) | ||||
| 	updateErrorList := validation.ValidateValidatingwebhookConfigurationUpdate(obj.(*admissionregistration.ValidatingWebhookConfiguration), old.(*admissionregistration.ValidatingWebhookConfiguration)) | ||||
| 	updateErrorList := validation.ValidateValidatingWebhookConfigurationUpdate(obj.(*admissionregistration.ValidatingWebhookConfiguration), old.(*admissionregistration.ValidatingWebhookConfiguration)) | ||||
| 	return append(validationErrorList, updateErrorList...) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -47,6 +47,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { | ||||
| 		&InitializerConfigurationList{}, | ||||
| 		&ValidatingWebhookConfiguration{}, | ||||
| 		&ValidatingWebhookConfigurationList{}, | ||||
| 		&MutatingWebhookConfiguration{}, | ||||
| 		&MutatingWebhookConfigurationList{}, | ||||
| 	) | ||||
| 	metav1.AddToGroupVersion(scheme, SchemeGroupVersion) | ||||
| 	return nil | ||||
|   | ||||
| @@ -130,7 +130,7 @@ type ValidatingWebhookConfiguration struct { | ||||
| 	// +optional | ||||
| 	// +patchMergeKey=name | ||||
| 	// +patchStrategy=merge | ||||
| 	Webhooks []Webhook `json:"Webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"` | ||||
| 	Webhooks []Webhook `json:"webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"` | ||||
| } | ||||
|  | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
| @@ -146,6 +146,36 @@ type ValidatingWebhookConfigurationList struct { | ||||
| 	Items []ValidatingWebhookConfiguration `json:"items" protobuf:"bytes,2,rep,name=items"` | ||||
| } | ||||
|  | ||||
| // +genclient | ||||
| // +genclient:nonNamespaced | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
|  | ||||
| // MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object. | ||||
| type MutatingWebhookConfiguration struct { | ||||
| 	metav1.TypeMeta `json:",inline"` | ||||
| 	// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. | ||||
| 	// +optional | ||||
| 	metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` | ||||
| 	// Webhooks is a list of webhooks and the affected resources and operations. | ||||
| 	// +optional | ||||
| 	// +patchMergeKey=name | ||||
| 	// +patchStrategy=merge | ||||
| 	Webhooks []Webhook `json:"webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"` | ||||
| } | ||||
|  | ||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||
|  | ||||
| // MutatingWebhookConfigurationList is a list of MutatingWebhookConfiguration. | ||||
| type MutatingWebhookConfigurationList struct { | ||||
| 	metav1.TypeMeta `json:",inline"` | ||||
| 	// Standard list metadata. | ||||
| 	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds | ||||
| 	// +optional | ||||
| 	metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` | ||||
| 	// List of MutatingWebhookConfiguration. | ||||
| 	Items []MutatingWebhookConfiguration `json:"items" protobuf:"bytes,2,rep,name=items"` | ||||
| } | ||||
|  | ||||
| // Webhook describes an admission webhook and the resources and operations it applies to. | ||||
| type Webhook struct { | ||||
| 	// The name of the admission webhook. | ||||
|   | ||||
| @@ -0,0 +1,101 @@ | ||||
| /* | ||||
| Copyright 2017 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 configuration | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"sort" | ||||
|  | ||||
| 	"github.com/golang/glog" | ||||
|  | ||||
| 	"k8s.io/api/admissionregistration/v1alpha1" | ||||
| 	"k8s.io/apimachinery/pkg/api/errors" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| ) | ||||
|  | ||||
| type MutatingWebhookConfigurationLister interface { | ||||
| 	List(opts metav1.ListOptions) (*v1alpha1.MutatingWebhookConfigurationList, error) | ||||
| } | ||||
|  | ||||
| // MutatingWebhookConfigurationManager collects the mutating webhook objects so that they can be called. | ||||
| type MutatingWebhookConfigurationManager struct { | ||||
| 	*poller | ||||
| } | ||||
|  | ||||
| func NewMutatingWebhookConfigurationManager(c MutatingWebhookConfigurationLister) *MutatingWebhookConfigurationManager { | ||||
| 	getFn := func() (runtime.Object, error) { | ||||
| 		list, err := c.List(metav1.ListOptions{}) | ||||
| 		if err != nil { | ||||
| 			if errors.IsNotFound(err) || errors.IsForbidden(err) { | ||||
| 				glog.V(5).Infof("MutatingWebhookConfiguration are disabled due to an error: %v", err) | ||||
| 				return nil, ErrDisabled | ||||
| 			} | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return mergeMutatingWebhookConfigurations(list), nil | ||||
| 	} | ||||
|  | ||||
| 	return &MutatingWebhookConfigurationManager{ | ||||
| 		newPoller(getFn), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Webhooks returns the merged MutatingWebhookConfiguration. | ||||
| func (im *MutatingWebhookConfigurationManager) Webhooks() (*v1alpha1.MutatingWebhookConfiguration, error) { | ||||
| 	configuration, err := im.poller.configuration() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	mutatingWebhookConfiguration, ok := configuration.(*v1alpha1.MutatingWebhookConfiguration) | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("expected type %v, got type %v", reflect.TypeOf(mutatingWebhookConfiguration), reflect.TypeOf(configuration)) | ||||
| 	} | ||||
| 	return mutatingWebhookConfiguration, nil | ||||
| } | ||||
|  | ||||
| func (im *MutatingWebhookConfigurationManager) Run(stopCh <-chan struct{}) { | ||||
| 	im.poller.Run(stopCh) | ||||
| } | ||||
|  | ||||
| func mergeMutatingWebhookConfigurations( | ||||
| 	list *v1alpha1.MutatingWebhookConfigurationList, | ||||
| ) *v1alpha1.MutatingWebhookConfiguration { | ||||
| 	configurations := append([]v1alpha1.MutatingWebhookConfiguration{}, list.Items...) | ||||
| 	var ret v1alpha1.MutatingWebhookConfiguration | ||||
| 	// The internal order of webhooks for each configuration is provided by the user | ||||
| 	// but configurations themselves can be in any order. As we are going to run these | ||||
| 	// webhooks in serial, they are sorted here to have a deterministic order. | ||||
| 	sort.Sort(byName(configurations)) | ||||
| 	for _, c := range configurations { | ||||
| 		ret.Webhooks = append(ret.Webhooks, c.Webhooks...) | ||||
| 	} | ||||
| 	return &ret | ||||
| } | ||||
|  | ||||
| // byName sorts MutatingWebhookConfiguration by name. These objects are all in | ||||
| // cluster namespace (aka no namespace) thus they all have unique names. | ||||
| type byName []v1alpha1.MutatingWebhookConfiguration | ||||
|  | ||||
| func (x byName) Len() int { return len(x) } | ||||
|  | ||||
| func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } | ||||
|  | ||||
| func (x byName) Less(i, j int) bool { | ||||
| 	return x[i].ObjectMeta.Name < x[j].ObjectMeta.Name | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
| Copyright 2017 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 configuration | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"k8s.io/api/admissionregistration/v1alpha1" | ||||
| 	"k8s.io/apimachinery/pkg/api/errors" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||
| ) | ||||
|  | ||||
| type disabledMutatingWebhookConfigLister struct{} | ||||
|  | ||||
| func (l *disabledMutatingWebhookConfigLister) List(options metav1.ListOptions) (*v1alpha1.MutatingWebhookConfigurationList, error) { | ||||
| 	return nil, errors.NewNotFound(schema.GroupResource{Group: "admissionregistration", Resource: "MutatingWebhookConfigurations"}, "") | ||||
| } | ||||
| func TestMutatingWebhookConfigDisabled(t *testing.T) { | ||||
| 	manager := NewMutatingWebhookConfigurationManager(&disabledMutatingWebhookConfigLister{}) | ||||
| 	manager.sync() | ||||
| 	_, err := manager.Webhooks() | ||||
| 	if err.Error() != ErrDisabled.Error() { | ||||
| 		t.Errorf("expected %v, got %v", ErrDisabled, err) | ||||
| 	} | ||||
| } | ||||
| @@ -385,6 +385,10 @@ var etcdStorageData = map[schema.GroupVersionResource]struct { | ||||
| 		stub:             `{"metadata":{"name":"hook1","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"","name":""},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`, | ||||
| 		expectedEtcdPath: "/registry/validatingwebhookconfigurations/hook1", | ||||
| 	}, | ||||
| 	gvr("admissionregistration.k8s.io", "v1alpha1", "mutatingwebhookconfigurations"): { | ||||
| 		stub:             `{"metadata":{"name":"hook1","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"","name":""},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`, | ||||
| 		expectedEtcdPath: "/registry/mutatingwebhookconfigurations/hook1", | ||||
| 	}, | ||||
| 	// -- | ||||
|  | ||||
| 	// k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user