Add MutatingWebhookConfiguration type

This commit is contained in:
mbohlool 2017-11-07 12:49:19 -08:00
parent cb43840492
commit fc5a613c17
21 changed files with 1987 additions and 587 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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"}: {},

View File

@ -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"

View File

@ -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,
},

View File

@ -48,7 +48,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&InitializerConfigurationList{},
&ValidatingWebhookConfiguration{},
&ValidatingWebhookConfigurationList{},
// TODO add mutating configs here too
&MutatingWebhookConfiguration{},
&MutatingWebhookConfigurationList{},
)
return nil
}

View File

@ -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.

View File

@ -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"

View File

@ -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)
}

View File

@ -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",

View File

@ -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{

View File

@ -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"

View File

@ -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}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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...)
}

View File

@ -47,6 +47,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&InitializerConfigurationList{},
&ValidatingWebhookConfiguration{},
&ValidatingWebhookConfigurationList{},
&MutatingWebhookConfiguration{},
&MutatingWebhookConfigurationList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@ -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.

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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