mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-27 12:31:23 +00:00
api
This commit is contained in:
parent
f006dcc9e1
commit
6b5bd19566
@ -103,24 +103,28 @@ func TestDefaulting(t *testing.T) {
|
||||
// This object contains only int fields which currently breaks the defaulting test because
|
||||
// it's pretty stupid. Once we add non integer fields, we should uncomment this.
|
||||
// {Group: "kubeadm.k8s.io", Version: "v1alpha1", Kind: "NodeConfiguration"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}: {},
|
||||
{Group: "apps", Version: "v1beta1", Kind: "Deployment"}: {},
|
||||
{Group: "apps", Version: "v1beta1", Kind: "DeploymentList"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBindingList"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBinding"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBindingList"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBinding"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBindingList"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBinding"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBindingList"}: {},
|
||||
{Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPreset"}: {},
|
||||
{Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPresetList"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}: {},
|
||||
{Group: "apps", Version: "v1beta1", Kind: "Deployment"}: {},
|
||||
{Group: "apps", Version: "v1beta1", Kind: "DeploymentList"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}: {},
|
||||
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBindingList"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBinding"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBindingList"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBinding"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBindingList"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBinding"}: {},
|
||||
{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBindingList"}: {},
|
||||
{Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPreset"}: {},
|
||||
{Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPresetList"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "InitializerConfiguration"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "InitializerConfigurationList"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ExternalAdmissionHookConfiguration"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ExternalAdmissionHookConfigurationList"}: {},
|
||||
}
|
||||
|
||||
f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1))
|
||||
|
@ -36,6 +36,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
|
||||
"k8s.io/kubernetes/federation/apis/federation"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/authorization"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
@ -50,6 +51,7 @@ import (
|
||||
|
||||
_ "k8s.io/kubernetes/federation/apis/federation/install"
|
||||
_ "k8s.io/kubernetes/pkg/api/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/admissionregistration/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/apps/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/authentication/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/authorization/install"
|
||||
@ -278,7 +280,15 @@ func init() {
|
||||
externalTypes: api.Scheme.KnownTypes(externalGroupVersion),
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := Groups[admissionregistration.GroupName]; !ok {
|
||||
externalGroupVersion := schema.GroupVersion{Group: admissionregistration.GroupName, Version: api.Registry.GroupOrDie(admissionregistration.GroupName).GroupVersion.Version}
|
||||
Groups[admissionregistration.GroupName] = TestGroup{
|
||||
externalGroupVersion: externalGroupVersion,
|
||||
internalGroupVersion: admissionregistration.SchemeGroupVersion,
|
||||
internalTypes: api.Scheme.KnownTypes(admissionregistration.SchemeGroupVersion),
|
||||
externalTypes: api.Scheme.KnownTypes(externalGroupVersion),
|
||||
}
|
||||
}
|
||||
Default = Groups[api.GroupName]
|
||||
Autoscaling = Groups[autoscaling.GroupName]
|
||||
Batch = Groups[batch.GroupName]
|
||||
|
@ -36,6 +36,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
@ -738,6 +739,21 @@ func certificateFuncs(t apitesting.TestingCommon) []interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
func admissionregistrationFuncs(t apitesting.TestingCommon) []interface{} {
|
||||
return []interface{}{
|
||||
func(obj *admissionregistration.ExternalAdmissionHook, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(obj) // fuzz self without calling this function again
|
||||
p := admissionregistration.FailurePolicyType("Fail")
|
||||
obj.FailurePolicy = &p
|
||||
},
|
||||
func(obj *admissionregistration.Initializer, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(obj) // fuzz self without calling this function again
|
||||
p := admissionregistration.FailurePolicyType("Fail")
|
||||
obj.FailurePolicy = &p
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzerFuncs(t apitesting.TestingCommon, codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
return apitesting.MergeFuzzerFuncs(t,
|
||||
apitesting.GenericFuzzerFuncs(t, codecs),
|
||||
@ -751,6 +767,7 @@ func FuzzerFuncs(t apitesting.TestingCommon, codecs runtimeserializer.CodecFacto
|
||||
kubeadmfuzzer.KubeadmFuzzerFuncs(t),
|
||||
policyFuncs(t),
|
||||
certificateFuncs(t),
|
||||
admissionregistrationFuncs(t),
|
||||
)
|
||||
}
|
||||
|
||||
|
24
pkg/apis/admissionregistration/doc.go
Normal file
24
pkg/apis/admissionregistration/doc.go
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package,register
|
||||
|
||||
// Package admissionregistration is the internal version of the API.
|
||||
// AdmissionConfiguration and AdmissionPluginConfiguration are legacy static admission plugin configuration
|
||||
// InitializerConfiguration and ExternalAdmissionHookConfiguration is for the
|
||||
// new dynamic admission controller configuration.
|
||||
// +groupName=admissionregistration.k8s.io
|
||||
package admissionregistration // import "k8s.io/kubernetes/pkg/apis/admissionregistration"
|
44
pkg/apis/admissionregistration/install/install.go
Normal file
44
pkg/apis/admissionregistration/install/install.go
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 install
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apimachinery/announced"
|
||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration"
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1"
|
||||
)
|
||||
|
||||
// Install registers the API group and adds types to a scheme
|
||||
func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) {
|
||||
if err := announced.NewGroupMetaFactory(
|
||||
&announced.GroupMetaFactoryArgs{
|
||||
GroupName: admissionregistration.GroupName,
|
||||
RootScopedKinds: sets.NewString("InitializerConfiguration", "ExternalAdmissionHookConfiguration"),
|
||||
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
|
||||
ImportPrefix: "k8s.io/kubernetes/pkg/apis/admissionregistration",
|
||||
AddInternalObjectsToScheme: admissionregistration.AddToScheme,
|
||||
},
|
||||
announced.VersionToSchemeFunc{
|
||||
v1alpha1.SchemeGroupVersion.Version: v1alpha1.AddToScheme,
|
||||
},
|
||||
).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
53
pkg/apis/admissionregistration/register.go
Normal file
53
pkg/apis/admissionregistration/register.go
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
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 admissionregistration
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
const GroupName = "admissionregistration.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
|
||||
|
||||
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
|
||||
func Kind(kind string) schema.GroupKind {
|
||||
return SchemeGroupVersion.WithKind(kind).GroupKind()
|
||||
}
|
||||
|
||||
// Resource takes an unqualified resource and returns back a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
var (
|
||||
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
// Adds the list of known types to scheme.
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&InitializerConfiguration{},
|
||||
&InitializerConfigurationList{},
|
||||
&ExternalAdmissionHookConfiguration{},
|
||||
&ExternalAdmissionHookConfigurationList{},
|
||||
)
|
||||
return nil
|
||||
}
|
206
pkg/apis/admissionregistration/types.go
Normal file
206
pkg/apis/admissionregistration/types.go
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
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 admissionregistration
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// InitializerConfiguration describes the configuration of initializers.
|
||||
type InitializerConfiguration struct {
|
||||
metav1.TypeMeta
|
||||
// Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata.
|
||||
// +optional
|
||||
metav1.ObjectMeta
|
||||
|
||||
// Initializers is a list of resources and their default initializers
|
||||
// Order-sensitive.
|
||||
// When merging multiple InitializerConfigurations, we sort the initializers
|
||||
// from different InitializerConfigurations by the name of the
|
||||
// InitializerConfigurations; the order of the initializers from the same
|
||||
// InitializerConfiguration is preserved.
|
||||
// +optional
|
||||
Initializers []Initializer
|
||||
}
|
||||
|
||||
// InitializerConfigurationList is a list of InitializerConfiguration.
|
||||
type InitializerConfigurationList struct {
|
||||
metav1.TypeMeta
|
||||
// Standard list metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
|
||||
// +optional
|
||||
metav1.ListMeta
|
||||
|
||||
// List of InitializerConfiguration.
|
||||
Items []InitializerConfiguration
|
||||
}
|
||||
|
||||
// Initializer describes the name and the failure policy of an initializer, and
|
||||
// what resources it applies to.
|
||||
type Initializer struct {
|
||||
// Name is the identifier of the initializer. It will be added to the
|
||||
// object that needs to be initialized.
|
||||
// Name should be fully qualified, e.g., alwayspullimages.kubernetes.io, where
|
||||
// "alwayspullimages" is the name of the webhook, and kubernetes.io is the name
|
||||
// of the organization.
|
||||
// Required
|
||||
Name string
|
||||
|
||||
// Rules describes what resources/subresources the initializer cares about.
|
||||
// The initializer cares about an operation if it matches _any_ Rule.
|
||||
Rules []Rule
|
||||
|
||||
// FailurePolicy defines what happens if the responsible initializer controller
|
||||
// fails to takes action. Allowed values are Ignore, or Fail. If "Ignore" is
|
||||
// set, initializer is removed from the initializers list of an object if
|
||||
// the timeout is reached; If "Fail" is set, admissionregistration returns timeout error
|
||||
// if the timeout is reached.
|
||||
FailurePolicy *FailurePolicyType
|
||||
}
|
||||
|
||||
// Rule is a tuple of APIGroups, APIVersion, and Resources.It is recommended
|
||||
// to make sure that all the tuple expansions are valid.
|
||||
type Rule struct {
|
||||
// APIGroups is the API groups the resources belong to. '*' is all groups.
|
||||
// If '*' is present, the length of the slice must be one.
|
||||
// Required.
|
||||
APIGroups []string
|
||||
|
||||
// APIVersions is the API versions the resources belong to. '*' is all versions.
|
||||
// If '*' is present, the length of the slice must be one.
|
||||
// Required.
|
||||
APIVersions []string
|
||||
|
||||
// Resources is a list of resources this rule applies to.
|
||||
//
|
||||
// For example:
|
||||
// 'pods' means pods.
|
||||
// 'pods/log' means the log subresource of pods.
|
||||
// '*' means all resources, but not subresources.
|
||||
// 'pods/*' means all subresources of pods.
|
||||
// '*/scale' means all scale subresources.
|
||||
// '*/*' means all resources and their subresources.
|
||||
//
|
||||
// If '*' or '*/*' is present, the length of the slice must be one.
|
||||
// Required.
|
||||
Resources []string
|
||||
}
|
||||
|
||||
type FailurePolicyType string
|
||||
|
||||
const (
|
||||
// Ignore means the initilizer is removed from the initializers list of an
|
||||
// object if the initializer is timed out.
|
||||
Ignore FailurePolicyType = "Ignore"
|
||||
// For 1.7, only "Ignore" is allowed. "Fail" will be allowed when the
|
||||
// extensible admission feature is beta.
|
||||
Fail FailurePolicyType = "Fail"
|
||||
)
|
||||
|
||||
// ExternalAdmissionHookConfiguration describes the configuration of initializers.
|
||||
type ExternalAdmissionHookConfiguration struct {
|
||||
metav1.TypeMeta
|
||||
// Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata.
|
||||
// +optional
|
||||
metav1.ObjectMeta
|
||||
// ExternalAdmissionHooks is a list of external admission webhooks and the
|
||||
// affected resources and operations.
|
||||
// +optional
|
||||
ExternalAdmissionHooks []ExternalAdmissionHook
|
||||
}
|
||||
|
||||
// ExternalAdmissionHookConfigurationList is a list of ExternalAdmissionHookConfiguration.
|
||||
type ExternalAdmissionHookConfigurationList struct {
|
||||
metav1.TypeMeta
|
||||
// Standard list metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
|
||||
// +optional
|
||||
metav1.ListMeta
|
||||
// List of ExternalAdmissionHookConfiguration.
|
||||
Items []ExternalAdmissionHookConfiguration
|
||||
}
|
||||
|
||||
// ExternalAdmissionHook describes an external admission webhook and the
|
||||
// resources and operations it applies to.
|
||||
type ExternalAdmissionHook struct {
|
||||
// The name of the external admission webhook.
|
||||
// Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where
|
||||
// "imagepolicy" is the name of the webhook, and kubernetes.io is the name
|
||||
// of the organization.
|
||||
// Required.
|
||||
Name string
|
||||
|
||||
// ClientConfig defines how to communicate with the hook.
|
||||
// Required
|
||||
ClientConfig AdmissionHookClientConfig
|
||||
|
||||
// Rules describes what operations on what resources/subresources the webhook cares about.
|
||||
// The webhook cares about an operation if it matches _any_ Rule.
|
||||
Rules []RuleWithOperations
|
||||
|
||||
// FailurePolicy defines how unrecognized errors from the admission endpoint are handled -
|
||||
// allowed values are Ignore or Fail. Defaults to Ignore.
|
||||
// +optional
|
||||
FailurePolicy *FailurePolicyType
|
||||
}
|
||||
|
||||
// RuleWithOperations is a tuple of Operations and Resources. It is recommended to make
|
||||
// sure that all the tuple expansions are valid.
|
||||
type RuleWithOperations struct {
|
||||
// Operations is the operations the admission hook cares about - CREATE, UPDATE, or *
|
||||
// for all operations.
|
||||
// If '*' is present, the length of the slice must be one.
|
||||
// Required.
|
||||
Operations []OperationType
|
||||
// Rule is embedded, it describes other criteria of the rule, like
|
||||
// APIGroups, APIVersions, Resources, etc.
|
||||
Rule
|
||||
}
|
||||
|
||||
type OperationType string
|
||||
|
||||
// The constants should be kept in sync with those defined in k8s.io/kubernetes/pkg/admission/interface.go.
|
||||
const (
|
||||
OperationAll OperationType = "*"
|
||||
Create OperationType = "CREATE"
|
||||
Update OperationType = "UPDATE"
|
||||
Delete OperationType = "DELETE"
|
||||
Connect OperationType = "CONNECT"
|
||||
)
|
||||
|
||||
// AdmissionHookClientConfig contains the information to make a TLS
|
||||
// connection with the webhook
|
||||
type AdmissionHookClientConfig struct {
|
||||
// Service is a reference to the service for this webhook. If there is only
|
||||
// one port open for the service, that port will be used. If there are multiple
|
||||
// ports open, port 443 will be used if it is open, otherwise it is an error.
|
||||
// Required
|
||||
Service ServiceReference
|
||||
// CABundle is a PEM encoded CA bundle which will be used to validate webhook's server certificate.
|
||||
// Required
|
||||
CABundle []byte
|
||||
}
|
||||
|
||||
// ServiceReference holds a reference to Service.legacy.k8s.io
|
||||
type ServiceReference struct {
|
||||
// Namespace is the namespace of the service
|
||||
// Required
|
||||
Namespace string
|
||||
// Name is the name of the service
|
||||
// Required
|
||||
Name string
|
||||
}
|
39
pkg/apis/admissionregistration/v1alpha1/defaults.go
Normal file
39
pkg/apis/admissionregistration/v1alpha1/defaults.go
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
return RegisterDefaults(scheme)
|
||||
}
|
||||
|
||||
func SetDefaults_Initializer(obj *Initializer) {
|
||||
if obj.FailurePolicy == nil {
|
||||
policy := Ignore
|
||||
obj.FailurePolicy = &policy
|
||||
}
|
||||
}
|
||||
|
||||
func SetDefaults_ExternalAdmissionHook(obj *ExternalAdmissionHook) {
|
||||
if obj.FailurePolicy == nil {
|
||||
policy := Ignore
|
||||
obj.FailurePolicy = &policy
|
||||
}
|
||||
}
|
27
pkg/apis/admissionregistration/v1alpha1/doc.go
Normal file
27
pkg/apis/admissionregistration/v1alpha1/doc.go
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package,register
|
||||
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/admissionregistration
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
|
||||
// Package v1alpha1 is the v1alpha1 version of the API.
|
||||
// AdmissionConfiguration and AdmissionPluginConfiguration are legacy static admission plugin configuration
|
||||
// InitializerConfiguration and ExternalAdmissionHookConfiguration is for the
|
||||
// new dynamic admission controller configuration.
|
||||
// +groupName=admissionregistration.k8s.io
|
||||
package v1alpha1 // import "k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1"
|
60
pkg/apis/admissionregistration/v1alpha1/register.go
Normal file
60
pkg/apis/admissionregistration/v1alpha1/register.go
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
const GroupName = "admissionregistration.k8s.io"
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
var (
|
||||
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
|
||||
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
func init() {
|
||||
// We only register manually written functions here. The registration of the
|
||||
// generated functions takes place in the generated files. The separation
|
||||
// makes the code compile even when the generated files are missing.
|
||||
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
|
||||
}
|
||||
|
||||
// Adds the list of known types to scheme.
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&InitializerConfiguration{},
|
||||
&InitializerConfigurationList{},
|
||||
&ExternalAdmissionHookConfiguration{},
|
||||
&ExternalAdmissionHookConfigurationList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
}
|
210
pkg/apis/admissionregistration/v1alpha1/types.go
Normal file
210
pkg/apis/admissionregistration/v1alpha1/types.go
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// InitializerConfiguration describes the configuration of initializers.
|
||||
type InitializerConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata.
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// Initializers is a list of resources and their default initializers
|
||||
// Order-sensitive.
|
||||
// When merging multiple InitializerConfigurations, we sort the initializers
|
||||
// from different InitializerConfigurations by the name of the
|
||||
// InitializerConfigurations; the order of the initializers from the same
|
||||
// InitializerConfiguration is preserved.
|
||||
// +patchMergeKey=name
|
||||
// +patchStrategy=merge
|
||||
// +optional
|
||||
Initializers []Initializer `json:"initializers,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=initializers"`
|
||||
}
|
||||
|
||||
// InitializerConfigurationList is a list of InitializerConfiguration.
|
||||
type InitializerConfigurationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard list metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
|
||||
// List of InitializerConfiguration.
|
||||
Items []InitializerConfiguration `json:"items" protobuf:"bytes,2,rep,name=items"`
|
||||
}
|
||||
|
||||
// Initializer describes the name and the failure policy of an initializer, and
|
||||
// what resources it applies to.
|
||||
type Initializer struct {
|
||||
// Name is the identifier of the initializer. It will be added to the
|
||||
// object that needs to be initialized.
|
||||
// Name should be fully qualified, e.g., alwayspullimages.kubernetes.io, where
|
||||
// "alwayspullimages" is the name of the webhook, and kubernetes.io is the name
|
||||
// of the organization.
|
||||
// Required
|
||||
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
|
||||
|
||||
// Rules describes what resources/subresources the initializer cares about.
|
||||
// The initializer cares about an operation if it matches _any_ Rule.
|
||||
Rules []Rule `json:"rules,omitempty" protobuf:"bytes,2,rep,name=rules"`
|
||||
|
||||
// FailurePolicy defines what happens if the responsible initializer controller
|
||||
// fails to takes action. Allowed values are Ignore, or Fail. If "Ignore" is
|
||||
// set, initializer is removed from the initializers list of an object if
|
||||
// the timeout is reached; If "Fail" is set, admissionregistration returns timeout error
|
||||
// if the timeout is reached.
|
||||
FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" protobuf:"bytes,3,opt,name=failurePolicy,casttype=FailurePolicyType"`
|
||||
}
|
||||
|
||||
// Rule is a tuple of APIGroups, APIVersion, and Resources.It is recommended
|
||||
// to make sure that all the tuple expansions are valid.
|
||||
type Rule struct {
|
||||
// APIGroups is the API groups the resources belong to. '*' is all groups.
|
||||
// If '*' is present, the length of the slice must be one.
|
||||
// Required.
|
||||
APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,1,rep,name=apiGroups"`
|
||||
|
||||
// APIVersions is the API versions the resources belong to. '*' is all versions.
|
||||
// If '*' is present, the length of the slice must be one.
|
||||
// Required.
|
||||
APIVersions []string `json:"apiVersions,omitempty" protobuf:"bytes,2,rep,name=apiVersions"`
|
||||
|
||||
// Resources is a list of resources this rule applies to.
|
||||
//
|
||||
// For example:
|
||||
// 'pods' means pods.
|
||||
// 'pods/log' means the log subresource of pods.
|
||||
// '*' means all resources, but not subresources.
|
||||
// 'pods/*' means all subresources of pods.
|
||||
// '*/scale' means all scale subresources.
|
||||
// '*/*' means all resources and their subresources.
|
||||
//
|
||||
// If '*' or '*/*' is present, the length of the slice must be one.
|
||||
// Required.
|
||||
Resources []string `json:"resources,omitempty" protobuf:"bytes,3,rep,name=resources"`
|
||||
}
|
||||
|
||||
type FailurePolicyType string
|
||||
|
||||
const (
|
||||
// Ignore means the initilizer is removed from the initializers list of an
|
||||
// object if the initializer is timed out.
|
||||
Ignore FailurePolicyType = "Ignore"
|
||||
// For 1.7, only "Ignore" is allowed. "Fail" will be allowed when the
|
||||
// extensible admission feature is beta.
|
||||
Fail FailurePolicyType = "Fail"
|
||||
)
|
||||
|
||||
// ExternalAdmissionHookConfiguration describes the configuration of initializers.
|
||||
type ExternalAdmissionHookConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata.
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
// ExternalAdmissionHooks is a list of external admission webhooks and the
|
||||
// affected resources and operations.
|
||||
// +optional
|
||||
// +patchMergeKey=name
|
||||
// +patchStrategy=merge
|
||||
ExternalAdmissionHooks []ExternalAdmissionHook `json:"externalAdmissionHooks,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=externalAdmissionHooks"`
|
||||
}
|
||||
|
||||
// ExternalAdmissionHookConfigurationList is a list of ExternalAdmissionHookConfiguration.
|
||||
type ExternalAdmissionHookConfigurationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard list metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
// List of ExternalAdmissionHookConfiguration.
|
||||
Items []ExternalAdmissionHookConfiguration `json:"items" protobuf:"bytes,2,rep,name=items"`
|
||||
}
|
||||
|
||||
// ExternalAdmissionHook describes an external admission webhook and the
|
||||
// resources and operations it applies to.
|
||||
type ExternalAdmissionHook struct {
|
||||
// The name of the external admission webhook.
|
||||
// Name should be fully qualified, e.g., imagepolicy.kubernetes.io, where
|
||||
// "imagepolicy" is the name of the webhook, and kubernetes.io is the name
|
||||
// of the organization.
|
||||
// Required.
|
||||
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
|
||||
|
||||
// ClientConfig defines how to communicate with the hook.
|
||||
// Required
|
||||
ClientConfig AdmissionHookClientConfig `json:"clientConfig" protobuf:"bytes,2,opt,name=clientConfig"`
|
||||
|
||||
// Rules describes what operations on what resources/subresources the webhook cares about.
|
||||
// The webhook cares about an operation if it matches _any_ Rule.
|
||||
Rules []RuleWithOperations `json:"rules,omitempty" protobuf:"bytes,3,rep,name=rules"`
|
||||
|
||||
// FailurePolicy defines how unrecognized errors from the admission endpoint are handled -
|
||||
// allowed values are Ignore or Fail. Defaults to Ignore.
|
||||
// +optional
|
||||
FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" protobuf:"bytes,4,opt,name=failurePolicy,casttype=FailurePolicyType"`
|
||||
}
|
||||
|
||||
// RuleWithOperations is a tuple of Operations and Resources. It is recommended to make
|
||||
// sure that all the tuple expansions are valid.
|
||||
type RuleWithOperations struct {
|
||||
// Operations is the operations the admission hook cares about - CREATE, UPDATE, or *
|
||||
// for all operations.
|
||||
// If '*' is present, the length of the slice must be one.
|
||||
// Required.
|
||||
Operations []OperationType `json:"operations,omitempty" protobuf:"bytes,1,rep,name=operations,casttype=OperationType"`
|
||||
// Rule is embedded, it describes other criteria of the rule, like
|
||||
// APIGroups, APIVersions, Resources, etc.
|
||||
Rule `json:",inline" protobuf:"bytes,2,opt,name=rule"`
|
||||
}
|
||||
|
||||
type OperationType string
|
||||
|
||||
// The constants should be kept in sync with those defined in k8s.io/kubernetes/pkg/admission/interface.go.
|
||||
const (
|
||||
OperationAll OperationType = "*"
|
||||
Create OperationType = "CREATE"
|
||||
Update OperationType = "UPDATE"
|
||||
Delete OperationType = "DELETE"
|
||||
Connect OperationType = "CONNECT"
|
||||
)
|
||||
|
||||
// AdmissionHookClientConfig contains the information to make a TLS
|
||||
// connection with the webhook
|
||||
type AdmissionHookClientConfig struct {
|
||||
// Service is a reference to the service for this webhook. If there is only
|
||||
// one port open for the service, that port will be used. If there are multiple
|
||||
// ports open, port 443 will be used if it is open, otherwise it is an error.
|
||||
// Required
|
||||
Service ServiceReference `json:"service" protobuf:"bytes,1,opt,name=service"`
|
||||
// CABundle is a PEM encoded CA bundle which will be used to validate webhook's server certificate.
|
||||
// Required
|
||||
CABundle []byte `json:"caBundle" protobuf:"bytes,2,opt,name=caBundle"`
|
||||
}
|
||||
|
||||
// ServiceReference holds a reference to Service.legacy.k8s.io
|
||||
type ServiceReference struct {
|
||||
// Namespace is the namespace of the service
|
||||
// Required
|
||||
Namespace string `json:"namespace" protobuf:"bytes,1,opt,name=namespace"`
|
||||
// Name is the name of the service
|
||||
// Required
|
||||
Name string `json:"name" protobuf:"bytes,2,opt,name=name"`
|
||||
}
|
174
pkg/apis/admissionregistration/validation/validation.go
Normal file
174
pkg/apis/admissionregistration/validation/validation.go
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
validationutil "k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration"
|
||||
)
|
||||
|
||||
func ValidateInitializerConfiguration(ic *admissionregistration.InitializerConfiguration) field.ErrorList {
|
||||
allErrors := genericvalidation.ValidateObjectMeta(&ic.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
||||
for i, initializer := range ic.Initializers {
|
||||
allErrors = append(allErrors, validateInitializer(&initializer, field.NewPath("initializers").Index(i))...)
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func validateInitializer(initializer *admissionregistration.Initializer, fldPath *field.Path) field.ErrorList {
|
||||
var allErrors field.ErrorList
|
||||
// initlializer.Name must be fully qualified
|
||||
if len(initializer.Name) == 0 {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
if errs := validationutil.IsDNS1123Subdomain(initializer.Name); len(errs) > 0 {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), initializer.Name, strings.Join(errs, ",")))
|
||||
}
|
||||
if len(strings.Split(initializer.Name, ".")) < 3 {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), initializer.Name, "should be a domain with at least two dots"))
|
||||
}
|
||||
|
||||
for i, rule := range initializer.Rules {
|
||||
allErrors = append(allErrors, validateRule(&rule, fldPath.Child("rules").Index(i))...)
|
||||
}
|
||||
// TODO: relax the validation rule when admissionregistration is beta.
|
||||
if initializer.FailurePolicy != nil && *initializer.FailurePolicy != admissionregistration.Ignore {
|
||||
allErrors = append(allErrors, field.NotSupported(fldPath.Child("failurePolicy"), *initializer.FailurePolicy, []string{string(admissionregistration.Ignore)}))
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func hasWildcard(slice []string) bool {
|
||||
for _, s := range slice {
|
||||
if s == "*" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validateRule(rule *admissionregistration.Rule, fldPath *field.Path) field.ErrorList {
|
||||
var allErrors field.ErrorList
|
||||
if len(rule.APIGroups) == 0 {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("apiGroups"), ""))
|
||||
}
|
||||
if len(rule.APIGroups) > 1 && hasWildcard(rule.APIGroups) {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("apiGroups"), rule.APIGroups, "if '*' is present, must not specify other API groups"))
|
||||
}
|
||||
// Note: group could be empty, e.g., the legacy "v1" API
|
||||
if len(rule.APIVersions) == 0 {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("apiVersions"), ""))
|
||||
}
|
||||
if len(rule.APIVersions) > 1 && hasWildcard(rule.APIVersions) {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("apiVersions"), rule.APIVersions, "if '*' is present, must not specify other API versions"))
|
||||
}
|
||||
for i, version := range rule.APIVersions {
|
||||
if version == "" {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("apiVersions").Index(i), ""))
|
||||
}
|
||||
}
|
||||
if len(rule.Resources) == 0 {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("resources"), ""))
|
||||
}
|
||||
if len(rule.Resources) > 1 && hasWildcard(rule.Resources) {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("Resources"), rule.Resources, "if '*' is present, must not specify other resources"))
|
||||
}
|
||||
for i, resource := range rule.Resources {
|
||||
if resource == "" {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("resources").Index(i), ""))
|
||||
}
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func ValidateInitializerConfigurationUpdate(newIC, oldIC *admissionregistration.InitializerConfiguration) field.ErrorList {
|
||||
return ValidateInitializerConfiguration(newIC)
|
||||
}
|
||||
|
||||
func ValidateExternalAdmissionHookConfiguration(e *admissionregistration.ExternalAdmissionHookConfiguration) field.ErrorList {
|
||||
allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
|
||||
for i, hook := range e.ExternalAdmissionHooks {
|
||||
allErrors = append(allErrors, validateExternalAdmissionHook(&hook, field.NewPath("externalAdmissionHook").Index(i))...)
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func validateExternalAdmissionHook(hook *admissionregistration.ExternalAdmissionHook, fldPath *field.Path) field.ErrorList {
|
||||
var allErrors field.ErrorList
|
||||
// hook.Name must be fully qualified
|
||||
if len(hook.Name) == 0 {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
if errs := validationutil.IsDNS1123Subdomain(hook.Name); len(errs) > 0 {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), hook.Name, strings.Join(errs, ",")))
|
||||
}
|
||||
if len(strings.Split(hook.Name, ".")) < 3 {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), hook.Name, "should be a domain with at least two dots"))
|
||||
}
|
||||
|
||||
for i, rule := range hook.Rules {
|
||||
allErrors = append(allErrors, validateRuleWithOperations(&rule, fldPath.Child("rules").Index(i))...)
|
||||
}
|
||||
// TODO: relax the validation rule when admissionregistration is beta.
|
||||
if hook.FailurePolicy != nil && *hook.FailurePolicy != admissionregistration.Ignore {
|
||||
allErrors = append(allErrors, field.NotSupported(fldPath.Child("failurePolicy"), *hook.FailurePolicy, []string{string(admissionregistration.Ignore)}))
|
||||
}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
var supportedOperations = sets.NewString(
|
||||
string(admissionregistration.OperationAll),
|
||||
string(admissionregistration.Create),
|
||||
string(admissionregistration.Update),
|
||||
string(admissionregistration.Delete),
|
||||
string(admissionregistration.Connect),
|
||||
)
|
||||
|
||||
func hasWildcardOperation(operations []admissionregistration.OperationType) bool {
|
||||
for _, o := range operations {
|
||||
if o == admissionregistration.OperationAll {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validateRuleWithOperations(ruleWithOperations *admissionregistration.RuleWithOperations, fldPath *field.Path) field.ErrorList {
|
||||
var allErrors field.ErrorList
|
||||
if len(ruleWithOperations.Operations) == 0 {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("operations"), ""))
|
||||
}
|
||||
if len(ruleWithOperations.Operations) > 1 && hasWildcardOperation(ruleWithOperations.Operations) {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("operations"), ruleWithOperations.Operations, "if '*' is present, must not specify other operations"))
|
||||
}
|
||||
for i, operation := range ruleWithOperations.Operations {
|
||||
if !supportedOperations.Has(string(operation)) {
|
||||
allErrors = append(allErrors, field.NotSupported(fldPath.Child("operations").Index(i), operation, supportedOperations.List()))
|
||||
}
|
||||
}
|
||||
allErrors = append(allErrors, validateRule(&ruleWithOperations.Rule, fldPath)...)
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func ValidateExternalAdmissionHookConfigurationUpdate(newC, oldC *admissionregistration.ExternalAdmissionHookConfiguration) field.ErrorList {
|
||||
return ValidateExternalAdmissionHookConfiguration(newC)
|
||||
}
|
378
pkg/apis/admissionregistration/validation/validation_test.go
Normal file
378
pkg/apis/admissionregistration/validation/validation_test.go
Normal file
@ -0,0 +1,378 @@
|
||||
/*
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration"
|
||||
)
|
||||
|
||||
func getInitializerConfiguration(initializers []admissionregistration.Initializer) *admissionregistration.InitializerConfiguration {
|
||||
return &admissionregistration.InitializerConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "config",
|
||||
},
|
||||
Initializers: initializers,
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateInitializerConfiguration(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *admissionregistration.InitializerConfiguration
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "0 rule is valid",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "all initializers must have a fully qualified name",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
},
|
||||
{
|
||||
Name: "k8s.io",
|
||||
},
|
||||
{
|
||||
Name: "",
|
||||
},
|
||||
}),
|
||||
expectedError: `initializers[1].name: Invalid value: "k8s.io": should be a domain with at least two dots, initializers[2].name: Required value`,
|
||||
},
|
||||
{
|
||||
name: "APIGroups must not be empty or nil",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
Rules: []admissionregistration.Rule{
|
||||
{
|
||||
APIGroups: []string{},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
{
|
||||
APIGroups: nil,
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: `initializers[0].rules[0].apiGroups: Required value, initializers[0].rules[1].apiGroups: Required value`,
|
||||
},
|
||||
{
|
||||
name: "APIVersions must not be empty or nil",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
Rules: []admissionregistration.Rule{
|
||||
{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: nil,
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: `initializers[0].rules[0].apiVersions: Required value, initializers[0].rules[1].apiVersions: Required value`,
|
||||
},
|
||||
{
|
||||
name: "Resources must not be empty or nil",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
Rules: []admissionregistration.Rule{
|
||||
{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: `initializers[0].rules[0].resources: Required value, initializers[0].rules[1].resources: Required value`,
|
||||
},
|
||||
{
|
||||
name: "\"\" is a valid APIGroup",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
Rules: []admissionregistration.Rule{
|
||||
{
|
||||
APIGroups: []string{"a", ""},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "\"\" is NOT a valid APIVersion",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
Rules: []admissionregistration.Rule{
|
||||
{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a", ""},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: "apiVersions[1]: Required value",
|
||||
},
|
||||
{
|
||||
name: "\"\" is NOT a valid Resource",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
Rules: []admissionregistration.Rule{
|
||||
{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a", ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: "resources[1]: Required value",
|
||||
},
|
||||
{
|
||||
name: "wildcard cannot be mixed with other strings",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
Rules: []admissionregistration.Rule{
|
||||
{
|
||||
APIGroups: []string{"a", "*"},
|
||||
APIVersions: []string{"a", "*"},
|
||||
Resources: []string{"a", "*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: `initializers[0].rules[0].apiGroups: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API groups, initializers[0].rules[0].apiVersions: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API versions, initializers[0].rules[0].Resources: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other resources`,
|
||||
},
|
||||
{
|
||||
name: "FailurePolicy can only be \"Ignore\"",
|
||||
config: getInitializerConfiguration(
|
||||
[]admissionregistration.Initializer{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
FailurePolicy: func() *admissionregistration.FailurePolicyType {
|
||||
r := admissionregistration.Fail
|
||||
return &r
|
||||
}(),
|
||||
},
|
||||
}),
|
||||
expectedError: `failurePolicy: Unsupported value: "Fail": supported values: Ignore`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
errs := ValidateInitializerConfiguration(test.config)
|
||||
err := errs.ToAggregate()
|
||||
if err != nil {
|
||||
if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
|
||||
t.Errorf("test case %s, expected to contain %s, got %s", test.name, e, a)
|
||||
}
|
||||
} else {
|
||||
if test.expectedError != "" {
|
||||
t.Errorf("test case %s, unexpected no error, expected to contain %s", test.name, test.expectedError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getExternalAdmissionHookConfiguration(hooks []admissionregistration.ExternalAdmissionHook) *admissionregistration.ExternalAdmissionHookConfiguration {
|
||||
return &admissionregistration.ExternalAdmissionHookConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "config",
|
||||
},
|
||||
ExternalAdmissionHooks: hooks,
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateExternalAdmissionHookConfiguration(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *admissionregistration.ExternalAdmissionHookConfiguration
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "all ExternalAdmissionHook must have a fully qualified name",
|
||||
config: getExternalAdmissionHookConfiguration(
|
||||
[]admissionregistration.ExternalAdmissionHook{
|
||||
{
|
||||
Name: "webhook.k8s.io",
|
||||
},
|
||||
{
|
||||
Name: "k8s.io",
|
||||
},
|
||||
{
|
||||
Name: "",
|
||||
},
|
||||
}),
|
||||
expectedError: `externalAdmissionHook[1].name: Invalid value: "k8s.io": should be a domain with at least two dots, externalAdmissionHook[2].name: Required value`,
|
||||
},
|
||||
{
|
||||
name: "Operations must not be empty or nil",
|
||||
config: getExternalAdmissionHookConfiguration(
|
||||
[]admissionregistration.ExternalAdmissionHook{
|
||||
{
|
||||
Name: "webhook.k8s.io",
|
||||
Rules: []admissionregistration.RuleWithOperations{
|
||||
{
|
||||
Operations: []admissionregistration.OperationType{},
|
||||
Rule: admissionregistration.Rule{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Operations: nil,
|
||||
Rule: admissionregistration.Rule{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: `externalAdmissionHook[0].rules[0].operations: Required value, externalAdmissionHook[0].rules[1].operations: Required value`,
|
||||
},
|
||||
{
|
||||
name: "\"\" is NOT a valid operation",
|
||||
config: getExternalAdmissionHookConfiguration(
|
||||
[]admissionregistration.ExternalAdmissionHook{
|
||||
{
|
||||
Name: "webhook.k8s.io",
|
||||
Rules: []admissionregistration.RuleWithOperations{
|
||||
{
|
||||
Operations: []admissionregistration.OperationType{"CREATE", ""},
|
||||
Rule: admissionregistration.Rule{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: `Unsupported value: ""`,
|
||||
},
|
||||
{
|
||||
name: "operation must be either create/update/delete/connect",
|
||||
config: getExternalAdmissionHookConfiguration(
|
||||
[]admissionregistration.ExternalAdmissionHook{
|
||||
{
|
||||
Name: "webhook.k8s.io",
|
||||
Rules: []admissionregistration.RuleWithOperations{
|
||||
{
|
||||
Operations: []admissionregistration.OperationType{"PATCH"},
|
||||
Rule: admissionregistration.Rule{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: `Unsupported value: "PATCH"`,
|
||||
},
|
||||
{
|
||||
name: "wildcard cannot be mixed with other strings",
|
||||
config: getExternalAdmissionHookConfiguration(
|
||||
[]admissionregistration.ExternalAdmissionHook{
|
||||
{
|
||||
Name: "webhook.k8s.io",
|
||||
Rules: []admissionregistration.RuleWithOperations{
|
||||
{
|
||||
Operations: []admissionregistration.OperationType{"CREATE", "*"},
|
||||
Rule: admissionregistration.Rule{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
expectedError: `if '*' is present, must not specify other operations`,
|
||||
},
|
||||
{
|
||||
name: "FailurePolicy can only be \"Ignore\"",
|
||||
config: getExternalAdmissionHookConfiguration(
|
||||
[]admissionregistration.ExternalAdmissionHook{
|
||||
{
|
||||
Name: "initializer.k8s.io",
|
||||
FailurePolicy: func() *admissionregistration.FailurePolicyType {
|
||||
r := admissionregistration.Fail
|
||||
return &r
|
||||
}(),
|
||||
},
|
||||
}),
|
||||
expectedError: `failurePolicy: Unsupported value: "Fail": supported values: Ignore`,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
errs := ValidateExternalAdmissionHookConfiguration(test.config)
|
||||
err := errs.ToAggregate()
|
||||
if err != nil {
|
||||
if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
|
||||
t.Errorf("test case %s, expected to contain %s, got %s", test.name, e, a)
|
||||
}
|
||||
} else {
|
||||
if test.expectedError != "" {
|
||||
t.Errorf("test case %s, unexpected no error, expected to contain %s", test.name, test.expectedError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user