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 | cluster/images/etcd-version-monitor | ||||||
| cmd/gke-certificates-controller/app | cmd/gke-certificates-controller/app | ||||||
| cmd/hyperkube | cmd/hyperkube | ||||||
| @@ -236,9 +237,10 @@ pkg/proxy/util | |||||||
| pkg/proxy/winkernel | pkg/proxy/winkernel | ||||||
| pkg/proxy/winuserspace | pkg/proxy/winuserspace | ||||||
| pkg/quota/evaluator/core | pkg/quota/evaluator/core | ||||||
| pkg/registry/admissionregistration/externaladmissionhookconfiguration/storage |  | ||||||
| pkg/registry/admissionregistration/initializerconfiguration/storage | pkg/registry/admissionregistration/initializerconfiguration/storage | ||||||
|  | pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage | ||||||
| pkg/registry/admissionregistration/rest | pkg/registry/admissionregistration/rest | ||||||
|  | pkg/registry/admissionregistration/validatingwebhookconfiguration/storage | ||||||
| pkg/registry/apps/rest | pkg/registry/apps/rest | ||||||
| pkg/registry/apps/statefulset | pkg/registry/apps/statefulset | ||||||
| pkg/registry/apps/statefulset/storage | pkg/registry/apps/statefulset/storage | ||||||
|   | |||||||
| @@ -137,6 +137,8 @@ func TestDefaulting(t *testing.T) { | |||||||
| 		{Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPresetList"}:                                   {}, | 		{Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPresetList"}:                                   {}, | ||||||
| 		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingWebhookConfiguration"}:     {}, | 		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingWebhookConfiguration"}:     {}, | ||||||
| 		{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingWebhookConfigurationList"}: {}, | 		{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: "NetworkPolicy"}:                                       {}, | ||||||
| 		{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicyList"}:                                   {}, | 		{Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicyList"}:                                   {}, | ||||||
| 		{Group: "storage.k8s.io", Version: "v1beta1", Kind: "StorageClass"}:                                      {}, | 		{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. | // Package admissionregistration is the internal version of the API. | ||||||
| // AdmissionConfiguration and AdmissionPluginConfiguration are legacy static admission plugin configuration | // 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. | // new dynamic admission controller configuration. | ||||||
| // +groupName=admissionregistration.k8s.io | // +groupName=admissionregistration.k8s.io | ||||||
| package admissionregistration // import "k8s.io/kubernetes/pkg/apis/admissionregistration" | package admissionregistration // import "k8s.io/kubernetes/pkg/apis/admissionregistration" | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *r | |||||||
| 	if err := announced.NewGroupMetaFactory( | 	if err := announced.NewGroupMetaFactory( | ||||||
| 		&announced.GroupMetaFactoryArgs{ | 		&announced.GroupMetaFactoryArgs{ | ||||||
| 			GroupName:                  admissionregistration.GroupName, | 			GroupName:                  admissionregistration.GroupName, | ||||||
| 			RootScopedKinds:            sets.NewString("InitializerConfiguration", "ValidatingWebhookConfiguration"), | 			RootScopedKinds:            sets.NewString("InitializerConfiguration", "ValidatingWebhookConfiguration", "MutatingWebhookConfiguration"), | ||||||
| 			VersionPreferenceOrder:     []string{v1alpha1.SchemeGroupVersion.Version}, | 			VersionPreferenceOrder:     []string{v1alpha1.SchemeGroupVersion.Version}, | ||||||
| 			AddInternalObjectsToScheme: admissionregistration.AddToScheme, | 			AddInternalObjectsToScheme: admissionregistration.AddToScheme, | ||||||
| 		}, | 		}, | ||||||
|   | |||||||
| @@ -48,7 +48,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { | |||||||
| 		&InitializerConfigurationList{}, | 		&InitializerConfigurationList{}, | ||||||
| 		&ValidatingWebhookConfiguration{}, | 		&ValidatingWebhookConfiguration{}, | ||||||
| 		&ValidatingWebhookConfigurationList{}, | 		&ValidatingWebhookConfigurationList{}, | ||||||
| 		// TODO add mutating configs here too | 		&MutatingWebhookConfiguration{}, | ||||||
|  | 		&MutatingWebhookConfigurationList{}, | ||||||
| 	) | 	) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|   | |||||||
| @@ -118,7 +118,7 @@ const ( | |||||||
| // +genclient:nonNamespaced | // +genclient:nonNamespaced | ||||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | // +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 { | type ValidatingWebhookConfiguration struct { | ||||||
| 	metav1.TypeMeta | 	metav1.TypeMeta | ||||||
| 	// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. | 	// 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 | 	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds | ||||||
| 	// +optional | 	// +optional | ||||||
| 	metav1.ListMeta | 	metav1.ListMeta | ||||||
| 	// List of ValidatingWebhookConfiguration. | 	// List of ValidatingWebhookConfigurations. | ||||||
| 	Items []ValidatingWebhookConfiguration | 	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. | // Webhook describes an admission webhook and the resources and operations it applies to. | ||||||
| type Webhook struct { | type Webhook struct { | ||||||
| 	// The name of the admission webhook. | 	// The name of the admission webhook. | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ limitations under the License. | |||||||
|  |  | ||||||
| // Package v1alpha1 is the v1alpha1 version of the API. | // Package v1alpha1 is the v1alpha1 version of the API. | ||||||
| // AdmissionConfiguration and AdmissionPluginConfiguration are legacy static admission plugin configuration | // 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. | // new dynamic admission controller configuration. | ||||||
| // +groupName=admissionregistration.k8s.io | // +groupName=admissionregistration.k8s.io | ||||||
| package v1alpha1 // import "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1" | 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 { | func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList { | ||||||
| 	allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata")) | 	allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata")) | ||||||
| 	for i, hook := range e.Webhooks { | 	for i, hook := range e.Webhooks { | ||||||
| 		allErrors = append(allErrors, validateWebhook(&hook, field.NewPath("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 | 	return allErrors | ||||||
| } | } | ||||||
| @@ -263,6 +271,10 @@ func validateRuleWithOperations(ruleWithOperations *admissionregistration.RuleWi | |||||||
| 	return allErrors | 	return allErrors | ||||||
| } | } | ||||||
|  |  | ||||||
| func ValidateValidatingwebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList { | func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList { | ||||||
| 	return ValidateValidatingWebhookConfiguration(newC) | 	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{ | 	return &admissionregistration.ValidatingWebhookConfiguration{ | ||||||
| 		ObjectMeta: metav1.ObjectMeta{ | 		ObjectMeta: metav1.ObjectMeta{ | ||||||
| 			Name: "config", | 			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) { | func TestValidateValidatingWebhookConfiguration(t *testing.T) { | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
| 		name          string | 		name          string | ||||||
| @@ -248,7 +250,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			name: "all Webhooks must have a fully qualified name", | 			name: "all Webhooks must have a fully qualified name", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -260,11 +262,11 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 						Name: "", | 						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", | 			name: "Operations must not be empty or nil", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						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", | 			name: "\"\" is NOT a valid operation", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -312,7 +314,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "operation must be either create/update/delete/connect", | 			name: "operation must be either create/update/delete/connect", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -332,7 +334,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "wildcard operation cannot be mixed with other strings", | 			name: "wildcard operation cannot be mixed with other strings", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -352,7 +354,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: `resource "*" can co-exist with resources that have subresources`, | 			name: `resource "*" can co-exist with resources that have subresources`, | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -371,7 +373,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: `resource "*" cannot mix with resources that don't have subresources`, | 			name: `resource "*" cannot mix with resources that don't have subresources`, | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -391,7 +393,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "resource a/* cannot mix with a/x", | 			name: "resource a/* cannot mix with a/x", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						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", | 			name: "resource a/* can mix with a", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -430,7 +432,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "resource */a cannot mix with x/a", | 			name: "resource */a cannot mix with x/a", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						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", | 			name: "resource */* cannot mix with other resources", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						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\"", | 			name: "FailurePolicy can only be \"Ignore\" or \"Fail\"", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						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", | 			name: "URLPath must start with slash", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -497,7 +499,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "URLPath accepts slash", | 			name: "URLPath accepts slash", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -510,7 +512,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "URLPath accepts no trailing slash", | 			name: "URLPath accepts no trailing slash", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -523,7 +525,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "URLPath fails //", | 			name: "URLPath fails //", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -536,7 +538,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "URLPath no empty step", | 			name: "URLPath no empty step", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						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`, | 			expectedError: `clientConfig.urlPath: Invalid value: "/foo//bar/": segment[1] may not be empty`, | ||||||
| 		}, { | 		}, { | ||||||
| 			name: "URLPath no empty step 2", | 			name: "URLPath no empty step 2", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
| @@ -561,7 +563,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "URLPath no non-subdomain", | 			name: "URLPath no non-subdomain", | ||||||
| 			config: getValidatingWebhookConfiguration( | 			config: newValidatingWebhookConfiguration( | ||||||
| 				[]admissionregistration.Webhook{ | 				[]admissionregistration.Webhook{ | ||||||
| 					{ | 					{ | ||||||
| 						Name: "webhook.k8s.io", | 						Name: "webhook.k8s.io", | ||||||
|   | |||||||
| @@ -106,7 +106,7 @@ func init() { | |||||||
| 	if err := announced.NewGroupMetaFactory( | 	if err := announced.NewGroupMetaFactory( | ||||||
| 		&announced.GroupMetaFactoryArgs{ | 		&announced.GroupMetaFactoryArgs{ | ||||||
| 			GroupName:              admissionregistrationv1alpha1.GroupName, | 			GroupName:              admissionregistrationv1alpha1.GroupName, | ||||||
| 			RootScopedKinds:        sets.NewString("InitializerConfiguration", "ValidatingWebhookConfiguration"), | 			RootScopedKinds:        sets.NewString("InitializerConfiguration", "ValidatingWebhookConfiguration", "MutatingWebhookConfiguration"), | ||||||
| 			VersionPreferenceOrder: []string{admissionregistrationv1alpha1.SchemeGroupVersion.Version}, | 			VersionPreferenceOrder: []string{admissionregistrationv1alpha1.SchemeGroupVersion.Version}, | ||||||
| 		}, | 		}, | ||||||
| 		announced.VersionToSchemeFunc{ | 		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/api/legacyscheme" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/admissionregistration" | 	"k8s.io/kubernetes/pkg/apis/admissionregistration" | ||||||
| 	initializerconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/initializerconfiguration/storage" | 	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" | 	validatingwebhookconfigurationstorage "k8s.io/kubernetes/pkg/registry/admissionregistration/validatingwebhookconfiguration/storage" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -53,6 +54,10 @@ func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstora | |||||||
| 		s := validatingwebhookconfigurationstorage.NewREST(restOptionsGetter) | 		s := validatingwebhookconfigurationstorage.NewREST(restOptionsGetter) | ||||||
| 		storage["validatingwebhookconfigurations"] = s | 		storage["validatingwebhookconfigurations"] = s | ||||||
| 	} | 	} | ||||||
|  | 	if apiResourceConfigSource.ResourceEnabled(version.WithResource("mutatingwebhookconfigurations")) { | ||||||
|  | 		s := mutatingwebhookconfigurationstorage.NewREST(restOptionsGetter) | ||||||
|  | 		storage["mutatingwebhookconfigurations"] = s | ||||||
|  | 	} | ||||||
| 	return storage | 	return storage | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -79,7 +79,7 @@ func (validatingWebhookConfigurationStrategy) AllowCreateOnUpdate() bool { | |||||||
| // ValidateUpdate is the default update validation for an end user. | // ValidateUpdate is the default update validation for an end user. | ||||||
| func (validatingWebhookConfigurationStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { | func (validatingWebhookConfigurationStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { | ||||||
| 	validationErrorList := validation.ValidateValidatingWebhookConfiguration(obj.(*admissionregistration.ValidatingWebhookConfiguration)) | 	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...) | 	return append(validationErrorList, updateErrorList...) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -47,6 +47,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { | |||||||
| 		&InitializerConfigurationList{}, | 		&InitializerConfigurationList{}, | ||||||
| 		&ValidatingWebhookConfiguration{}, | 		&ValidatingWebhookConfiguration{}, | ||||||
| 		&ValidatingWebhookConfigurationList{}, | 		&ValidatingWebhookConfigurationList{}, | ||||||
|  | 		&MutatingWebhookConfiguration{}, | ||||||
|  | 		&MutatingWebhookConfigurationList{}, | ||||||
| 	) | 	) | ||||||
| 	metav1.AddToGroupVersion(scheme, SchemeGroupVersion) | 	metav1.AddToGroupVersion(scheme, SchemeGroupVersion) | ||||||
| 	return nil | 	return nil | ||||||
|   | |||||||
| @@ -130,7 +130,7 @@ type ValidatingWebhookConfiguration struct { | |||||||
| 	// +optional | 	// +optional | ||||||
| 	// +patchMergeKey=name | 	// +patchMergeKey=name | ||||||
| 	// +patchStrategy=merge | 	// +patchStrategy=merge | ||||||
| 	Webhooks []Webhook `json:"Webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"` | 	Webhooks []Webhook `json:"webhooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=Webhooks"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||||||
| @@ -146,6 +146,36 @@ type ValidatingWebhookConfigurationList struct { | |||||||
| 	Items []ValidatingWebhookConfiguration `json:"items" protobuf:"bytes,2,rep,name=items"` | 	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. | // Webhook describes an admission webhook and the resources and operations it applies to. | ||||||
| type Webhook struct { | type Webhook struct { | ||||||
| 	// The name of the admission webhook. | 	// 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"}]}`, | 		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", | 		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 | 	// k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user