diff --git a/pkg/kubeapiserver/options/plugins.go b/pkg/kubeapiserver/options/plugins.go index 1ccd6e2d5d..cc0407dd73 100644 --- a/pkg/kubeapiserver/options/plugins.go +++ b/pkg/kubeapiserver/options/plugins.go @@ -21,92 +21,50 @@ package options // given binary target. import ( // Admission policies - "k8s.io/kubernetes/plugin/pkg/admission/admit" - "k8s.io/kubernetes/plugin/pkg/admission/alwayspullimages" - "k8s.io/kubernetes/plugin/pkg/admission/antiaffinity" "k8s.io/kubernetes/plugin/pkg/admission/defaulttolerationseconds" - "k8s.io/kubernetes/plugin/pkg/admission/deny" - "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit" - "k8s.io/kubernetes/plugin/pkg/admission/exec" - "k8s.io/kubernetes/plugin/pkg/admission/extendedresourcetoleration" - "k8s.io/kubernetes/plugin/pkg/admission/gc" "k8s.io/kubernetes/plugin/pkg/admission/limitranger" - "k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision" - "k8s.io/kubernetes/plugin/pkg/admission/namespace/exists" "k8s.io/kubernetes/plugin/pkg/admission/nodetaint" - "k8s.io/kubernetes/plugin/pkg/admission/podnodeselector" - "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction" podpriority "k8s.io/kubernetes/plugin/pkg/admission/priority" "k8s.io/kubernetes/plugin/pkg/admission/resourcequota" - "k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy" - "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny" "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount" "k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/resize" "k8s.io/kubernetes/plugin/pkg/admission/storage/storageclass/setdefault" - "k8s.io/kubernetes/plugin/pkg/admission/storage/storageobjectinuseprotection" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle" + mutatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating" + validatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/validating" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/features" ) // AllOrderedPlugins is the list of all the plugins in order. var AllOrderedPlugins = []string{ - admit.PluginName, // AlwaysAdmit - autoprovision.PluginName, // NamespaceAutoProvision - lifecycle.PluginName, // NamespaceLifecycle - exists.PluginName, // NamespaceExists - scdeny.PluginName, // SecurityContextDeny - antiaffinity.PluginName, // LimitPodHardAntiAffinityTopology - limitranger.PluginName, // LimitRanger - serviceaccount.PluginName, // ServiceAccount - nodetaint.PluginName, // TaintNodesByCondition - alwayspullimages.PluginName, // AlwaysPullImages - podsecuritypolicy.PluginName, // PodSecurityPolicy - podnodeselector.PluginName, // PodNodeSelector - podpriority.PluginName, // Priority - defaulttolerationseconds.PluginName, // DefaultTolerationSeconds - podtolerationrestriction.PluginName, // PodTolerationRestriction - exec.DenyEscalatingExec, // DenyEscalatingExec - exec.DenyExecOnPrivileged, // DenyExecOnPrivileged - eventratelimit.PluginName, // EventRateLimit - extendedresourcetoleration.PluginName, // ExtendedResourceToleration - setdefault.PluginName, // DefaultStorageClass - storageobjectinuseprotection.PluginName, // StorageObjectInUseProtection - gc.PluginName, // OwnerReferencesPermissionEnforcement - resize.PluginName, // PersistentVolumeClaimResize - resourcequota.PluginName, // ResourceQuota - deny.PluginName, // AlwaysDeny + lifecycle.PluginName, // NamespaceLifecycle + limitranger.PluginName, // LimitRanger + serviceaccount.PluginName, // ServiceAccount + nodetaint.PluginName, // TaintNodesByCondition + podpriority.PluginName, // Priority + defaulttolerationseconds.PluginName, // DefaultTolerationSeconds + setdefault.PluginName, // DefaultStorageClass + resize.PluginName, // PersistentVolumeClaimResize + mutatingwebhook.PluginName, // MutatingAdmissionWebhook + validatingwebhook.PluginName, // ValidatingAdmissionWebhook + resourcequota.PluginName, // ResourceQuota } // RegisterAllAdmissionPlugins registers all admission plugins and // sets the recommended plugins order. func RegisterAllAdmissionPlugins(plugins *admission.Plugins) { - admit.Register(plugins) // DEPRECATED as no real meaning - alwayspullimages.Register(plugins) - antiaffinity.Register(plugins) defaulttolerationseconds.Register(plugins) - deny.Register(plugins) // DEPRECATED as no real meaning - eventratelimit.Register(plugins) - exec.Register(plugins) - extendedresourcetoleration.Register(plugins) - gc.Register(plugins) limitranger.Register(plugins) - autoprovision.Register(plugins) - exists.Register(plugins) nodetaint.Register(plugins) - podnodeselector.Register(plugins) - podtolerationrestriction.Register(plugins) resourcequota.Register(plugins) - podsecuritypolicy.Register(plugins) podpriority.Register(plugins) - scdeny.Register(plugins) serviceaccount.Register(plugins) setdefault.Register(plugins) resize.Register(plugins) - storageobjectinuseprotection.Register(plugins) } // DefaultOffAdmissionPlugins get admission plugins off by default for kube-apiserver. @@ -118,6 +76,8 @@ func DefaultOffAdmissionPlugins() sets.String { setdefault.PluginName, //DefaultStorageClass resize.PluginName, //PersistentVolumeClaimResize defaulttolerationseconds.PluginName, //DefaultTolerationSeconds + mutatingwebhook.PluginName, //MutatingAdmissionWebhook + validatingwebhook.PluginName, //ValidatingAdmissionWebhook resourcequota.PluginName, //ResourceQuota ) diff --git a/plugin/pkg/admission/admit/BUILD b/plugin/pkg/admission/admit/BUILD deleted file mode 100644 index bd93027e33..0000000000 --- a/plugin/pkg/admission/admit/BUILD +++ /dev/null @@ -1,40 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/admit", - deps = [ - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/admit/admission.go b/plugin/pkg/admission/admit/admission.go deleted file mode 100644 index 867bbcdae4..0000000000 --- a/plugin/pkg/admission/admit/admission.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -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 admit - -import ( - "io" - - "k8s.io/apiserver/pkg/admission" - "k8s.io/klog" -) - -// PluginName indicates name of admission plugin. -const PluginName = "AlwaysAdmit" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - return NewAlwaysAdmit(), nil - }) -} - -// alwaysAdmit is an implementation of admission.Interface which always says yes to an admit request. -type alwaysAdmit struct{} - -var _ admission.MutationInterface = alwaysAdmit{} -var _ admission.ValidationInterface = alwaysAdmit{} - -// Admit makes an admission decision based on the request attributes -func (alwaysAdmit) Admit(a admission.Attributes) (err error) { - return nil -} - -// Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate. -func (alwaysAdmit) Validate(a admission.Attributes) (err error) { - return nil -} - -// Handles returns true if this admission controller can handle the given operation -// where operation can be one of CREATE, UPDATE, DELETE, or CONNECT -func (alwaysAdmit) Handles(operation admission.Operation) bool { - return true -} - -// NewAlwaysAdmit creates a new always admit admission handler -func NewAlwaysAdmit() admission.Interface { - // DEPRECATED: AlwaysAdmit admit all admission request, it is no use. - klog.Warningf("%s admission controller is deprecated. "+ - "Please remove this controller from your configuration files and scripts.", PluginName) - return new(alwaysAdmit) -} diff --git a/plugin/pkg/admission/admit/admission_test.go b/plugin/pkg/admission/admit/admission_test.go deleted file mode 100644 index cabd9cfc71..0000000000 --- a/plugin/pkg/admission/admit/admission_test.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -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 admit - -import ( - "testing" - - "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" -) - -func TestAdmissionNonNilAttribute(t *testing.T) { - handler := NewAlwaysAdmit() - err := handler.(*alwaysAdmit).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, false, nil)) - if err != nil { - t.Errorf("Unexpected error returned from admission handler") - } -} - -func TestAdmissionNilAttribute(t *testing.T) { - handler := NewAlwaysAdmit() - err := handler.(*alwaysAdmit).Admit(nil) - if err != nil { - t.Errorf("Unexpected error returned from admission handler") - } -} - -func TestHandles(t *testing.T) { - handler := NewAlwaysAdmit() - tests := []admission.Operation{admission.Create, admission.Connect, admission.Update, admission.Delete} - - for _, test := range tests { - if !handler.Handles(test) { - t.Errorf("Expected handling all operations, including: %v", test) - } - } -} diff --git a/plugin/pkg/admission/alwayspullimages/BUILD b/plugin/pkg/admission/alwayspullimages/BUILD deleted file mode 100644 index 4613472879..0000000000 --- a/plugin/pkg/admission/alwayspullimages/BUILD +++ /dev/null @@ -1,44 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/alwayspullimages", - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/alwayspullimages/admission.go b/plugin/pkg/admission/alwayspullimages/admission.go deleted file mode 100644 index 3024c98562..0000000000 --- a/plugin/pkg/admission/alwayspullimages/admission.go +++ /dev/null @@ -1,124 +0,0 @@ -/* -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 alwayspullimages contains an admission controller that modifies every new Pod to force -// the image pull policy to Always. This is useful in a multitenant cluster so that users can be -// assured that their private images can only be used by those who have the credentials to pull -// them. Without this admission controller, once an image has been pulled to a node, any pod from -// any user can use it simply by knowing the image's name (assuming the Pod is scheduled onto the -// right node), without any authorization check against the image. With this admission controller -// enabled, images are always pulled prior to starting containers, which means valid credentials are -// required. -package alwayspullimages - -import ( - "io" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// PluginName indicates name of admission plugin. -const PluginName = "AlwaysPullImages" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - return NewAlwaysPullImages(), nil - }) -} - -// AlwaysPullImages is an implementation of admission.Interface. -// It looks at all new pods and overrides each container's image pull policy to Always. -type AlwaysPullImages struct { - *admission.Handler -} - -var _ admission.MutationInterface = &AlwaysPullImages{} -var _ admission.ValidationInterface = &AlwaysPullImages{} - -// Admit makes an admission decision based on the request attributes -func (a *AlwaysPullImages) Admit(attributes admission.Attributes) (err error) { - // Ignore all calls to subresources or resources other than pods. - if shouldIgnore(attributes) { - return nil - } - pod, ok := attributes.GetObject().(*api.Pod) - if !ok { - return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted") - } - - for i := range pod.Spec.InitContainers { - pod.Spec.InitContainers[i].ImagePullPolicy = api.PullAlways - } - - for i := range pod.Spec.Containers { - pod.Spec.Containers[i].ImagePullPolicy = api.PullAlways - } - - return nil -} - -// Validate makes sure that all containers are set to always pull images -func (*AlwaysPullImages) Validate(attributes admission.Attributes) (err error) { - if shouldIgnore(attributes) { - return nil - } - - pod, ok := attributes.GetObject().(*api.Pod) - if !ok { - return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted") - } - - for i := range pod.Spec.InitContainers { - if pod.Spec.InitContainers[i].ImagePullPolicy != api.PullAlways { - return admission.NewForbidden(attributes, - field.NotSupported(field.NewPath("spec", "initContainers").Index(i).Child("imagePullPolicy"), - pod.Spec.InitContainers[i].ImagePullPolicy, []string{string(api.PullAlways)}, - ), - ) - } - } - for i := range pod.Spec.Containers { - if pod.Spec.Containers[i].ImagePullPolicy != api.PullAlways { - return admission.NewForbidden(attributes, - field.NotSupported(field.NewPath("spec", "containers").Index(i).Child("imagePullPolicy"), - pod.Spec.Containers[i].ImagePullPolicy, []string{string(api.PullAlways)}, - ), - ) - } - } - - return nil -} - -func shouldIgnore(attributes admission.Attributes) bool { - // Ignore all calls to subresources or resources other than pods. - if len(attributes.GetSubresource()) != 0 || attributes.GetResource().GroupResource() != api.Resource("pods") { - return true - } - - return false -} - -// NewAlwaysPullImages creates a new always pull images admission control handler -func NewAlwaysPullImages() *AlwaysPullImages { - return &AlwaysPullImages{ - Handler: admission.NewHandler(admission.Create, admission.Update), - } -} diff --git a/plugin/pkg/admission/alwayspullimages/admission_test.go b/plugin/pkg/admission/alwayspullimages/admission_test.go deleted file mode 100644 index 9d5ebbd6f4..0000000000 --- a/plugin/pkg/admission/alwayspullimages/admission_test.go +++ /dev/null @@ -1,160 +0,0 @@ -/* -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 alwayspullimages - -import ( - "testing" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// TestAdmission verifies all create requests for pods result in every container's image pull policy -// set to Always -func TestAdmission(t *testing.T) { - namespace := "test" - handler := &AlwaysPullImages{} - pod := api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: namespace}, - Spec: api.PodSpec{ - InitContainers: []api.Container{ - {Name: "init1", Image: "image"}, - {Name: "init2", Image: "image", ImagePullPolicy: api.PullNever}, - {Name: "init3", Image: "image", ImagePullPolicy: api.PullIfNotPresent}, - {Name: "init4", Image: "image", ImagePullPolicy: api.PullAlways}, - }, - Containers: []api.Container{ - {Name: "ctr1", Image: "image"}, - {Name: "ctr2", Image: "image", ImagePullPolicy: api.PullNever}, - {Name: "ctr3", Image: "image", ImagePullPolicy: api.PullIfNotPresent}, - {Name: "ctr4", Image: "image", ImagePullPolicy: api.PullAlways}, - }, - }, - } - err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if err != nil { - t.Errorf("Unexpected error returned from admission handler") - } - for _, c := range pod.Spec.InitContainers { - if c.ImagePullPolicy != api.PullAlways { - t.Errorf("Container %v: expected pull always, got %v", c, c.ImagePullPolicy) - } - } - for _, c := range pod.Spec.Containers { - if c.ImagePullPolicy != api.PullAlways { - t.Errorf("Container %v: expected pull always, got %v", c, c.ImagePullPolicy) - } - } -} - -func TestValidate(t *testing.T) { - namespace := "test" - handler := &AlwaysPullImages{} - pod := api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: namespace}, - Spec: api.PodSpec{ - InitContainers: []api.Container{ - {Name: "init1", Image: "image"}, - {Name: "init2", Image: "image", ImagePullPolicy: api.PullNever}, - {Name: "init3", Image: "image", ImagePullPolicy: api.PullIfNotPresent}, - {Name: "init4", Image: "image", ImagePullPolicy: api.PullAlways}, - }, - Containers: []api.Container{ - {Name: "ctr1", Image: "image"}, - {Name: "ctr2", Image: "image", ImagePullPolicy: api.PullNever}, - {Name: "ctr3", Image: "image", ImagePullPolicy: api.PullIfNotPresent}, - {Name: "ctr4", Image: "image", ImagePullPolicy: api.PullAlways}, - }, - }, - } - expectedError := `pods "123" is forbidden: spec.initContainers[0].imagePullPolicy: Unsupported value: "": supported values: "Always"` - err := handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if err == nil { - t.Fatal("missing expected error") - } - if err.Error() != expectedError { - t.Fatal(err) - } -} - -// TestOtherResources ensures that this admission controller is a no-op for other resources, -// subresources, and non-pods. -func TestOtherResources(t *testing.T) { - namespace := "testnamespace" - name := "testname" - pod := &api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, - Spec: api.PodSpec{ - Containers: []api.Container{ - {Name: "ctr2", Image: "image", ImagePullPolicy: api.PullNever}, - }, - }, - } - tests := []struct { - name string - kind string - resource string - subresource string - object runtime.Object - expectError bool - }{ - { - name: "non-pod resource", - kind: "Foo", - resource: "foos", - object: pod, - }, - { - name: "pod subresource", - kind: "Pod", - resource: "pods", - subresource: "exec", - object: pod, - }, - { - name: "non-pod object", - kind: "Pod", - resource: "pods", - object: &api.Service{}, - expectError: true, - }, - } - - for _, tc := range tests { - handler := &AlwaysPullImages{} - - err := handler.Admit(admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, false, nil)) - - if tc.expectError { - if err == nil { - t.Errorf("%s: unexpected nil error", tc.name) - } - continue - } - - if err != nil { - t.Errorf("%s: unexpected error: %v", tc.name, err) - continue - } - - if e, a := api.PullNever, pod.Spec.Containers[0].ImagePullPolicy; e != a { - t.Errorf("%s: image pull policy was changed to %s", tc.name, a) - } - } -} diff --git a/plugin/pkg/admission/antiaffinity/BUILD b/plugin/pkg/admission/antiaffinity/BUILD deleted file mode 100644 index a9dcc98505..0000000000 --- a/plugin/pkg/admission/antiaffinity/BUILD +++ /dev/null @@ -1,48 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = [ - "admission.go", - "doc.go", - ], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/antiaffinity", - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/kubelet/apis:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/kubelet/apis:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/antiaffinity/admission.go b/plugin/pkg/admission/antiaffinity/admission.go deleted file mode 100644 index b337697397..0000000000 --- a/plugin/pkg/admission/antiaffinity/admission.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2016 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 antiaffinity - -import ( - "fmt" - "io" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" - kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" -) - -const PluginName = "LimitPodHardAntiAffinityTopology" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - return NewInterPodAntiAffinity(), nil - }) -} - -// Plugin contains the client used by the admission controller -type Plugin struct { - *admission.Handler -} - -var _ admission.ValidationInterface = &Plugin{} - -// NewInterPodAntiAffinity creates a new instance of the LimitPodHardAntiAffinityTopology admission controller -func NewInterPodAntiAffinity() *Plugin { - return &Plugin{ - Handler: admission.NewHandler(admission.Create, admission.Update), - } -} - -// Validate will deny any pod that defines AntiAffinity topology key other than kubeletapis.LabelHostname i.e. "kubernetes.io/hostname" -// in requiredDuringSchedulingRequiredDuringExecution and requiredDuringSchedulingIgnoredDuringExecution. -func (p *Plugin) Validate(attributes admission.Attributes) (err error) { - // Ignore all calls to subresources or resources other than pods. - if len(attributes.GetSubresource()) != 0 || attributes.GetResource().GroupResource() != api.Resource("pods") { - return nil - } - pod, ok := attributes.GetObject().(*api.Pod) - if !ok { - return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted") - } - affinity := pod.Spec.Affinity - if affinity != nil && affinity.PodAntiAffinity != nil { - var podAntiAffinityTerms []api.PodAffinityTerm - if len(affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution) != 0 { - podAntiAffinityTerms = affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution - } - // TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution. - //if len(affinity.PodAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution) != 0 { - // podAntiAffinityTerms = append(podAntiAffinityTerms, affinity.PodAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution...) - //} - for _, v := range podAntiAffinityTerms { - if v.TopologyKey != kubeletapis.LabelHostname { - return apierrors.NewForbidden(attributes.GetResource().GroupResource(), pod.Name, fmt.Errorf("affinity.PodAntiAffinity.RequiredDuringScheduling has TopologyKey %v but only key %v is allowed", v.TopologyKey, kubeletapis.LabelHostname)) - } - } - } - return nil -} diff --git a/plugin/pkg/admission/antiaffinity/admission_test.go b/plugin/pkg/admission/antiaffinity/admission_test.go deleted file mode 100644 index 6d59059a95..0000000000 --- a/plugin/pkg/admission/antiaffinity/admission_test.go +++ /dev/null @@ -1,284 +0,0 @@ -/* -Copyright 2016 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 antiaffinity - -import ( - "testing" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" - kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" -) - -// ensures the hard PodAntiAffinity is denied if it defines TopologyKey other than kubernetes.io/hostname. -// TODO: Add test case "invalid topologyKey in requiredDuringSchedulingRequiredDuringExecution then admission fails" -// after RequiredDuringSchedulingRequiredDuringExecution is implemented. -func TestInterPodAffinityAdmission(t *testing.T) { - handler := NewInterPodAntiAffinity() - pod := api.Pod{ - Spec: api.PodSpec{}, - } - tests := []struct { - affinity *api.Affinity - errorExpected bool - }{ - // empty affinity its success. - { - affinity: &api.Affinity{}, - errorExpected: false, - }, - // what ever topologyKey in preferredDuringSchedulingIgnoredDuringExecution, the admission should success. - { - affinity: &api.Affinity{ - PodAntiAffinity: &api.PodAntiAffinity{ - PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{ - { - Weight: 5, - PodAffinityTerm: api.PodAffinityTerm{ - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "security", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"S2"}, - }, - }, - }, - TopologyKey: "az", - }, - }, - }, - }, - }, - errorExpected: false, - }, - // valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution, - // plus any topologyKey in preferredDuringSchedulingIgnoredDuringExecution, then admission success. - { - affinity: &api.Affinity{ - PodAntiAffinity: &api.PodAntiAffinity{ - PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{ - { - Weight: 5, - PodAffinityTerm: api.PodAffinityTerm{ - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "security", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"S2"}, - }, - }, - }, - TopologyKey: "az", - }, - }, - }, - RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "security", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"S2"}, - }, - }, - }, - TopologyKey: kubeletapis.LabelHostname, - }, - }, - }, - }, - errorExpected: false, - }, - // valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission success. - { - affinity: &api.Affinity{ - PodAntiAffinity: &api.PodAntiAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "security", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"S2"}, - }, - }, - }, - TopologyKey: kubeletapis.LabelHostname, - }, - }, - }, - }, - errorExpected: false, - }, - // invalid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission fails. - { - affinity: &api.Affinity{ - PodAntiAffinity: &api.PodAntiAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "security", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"S2"}, - }, - }, - }, - TopologyKey: " zone ", - }, - }, - }, - }, - errorExpected: true, - }, - // list of requiredDuringSchedulingIgnoredDuringExecution middle element topologyKey is not valid. - { - affinity: &api.Affinity{ - PodAntiAffinity: &api.PodAntiAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "security", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"S2"}, - }, - }, - }, - TopologyKey: kubeletapis.LabelHostname, - }, { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "security", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"S2"}, - }, - }, - }, - TopologyKey: " zone ", - }, { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "security", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"S2"}, - }, - }, - }, - TopologyKey: kubeletapis.LabelHostname, - }, - }, - }, - }, - errorExpected: true, - }, - } - for _, test := range tests { - pod.Spec.Affinity = test.affinity - err := handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", false, nil)) - - if test.errorExpected && err == nil { - t.Errorf("Expected error for Anti Affinity %+v but did not get an error", test.affinity) - } - - if !test.errorExpected && err != nil { - t.Errorf("Unexpected error %v for AntiAffinity %+v", err, test.affinity) - } - } -} -func TestHandles(t *testing.T) { - handler := NewInterPodAntiAffinity() - tests := map[admission.Operation]bool{ - admission.Update: true, - admission.Create: true, - admission.Delete: false, - admission.Connect: false, - } - for op, expected := range tests { - result := handler.Handles(op) - if result != expected { - t.Errorf("Unexpected result for operation %s: %v\n", op, result) - } - } -} - -// TestOtherResources ensures that this admission controller is a no-op for other resources, -// subresources, and non-pods. -func TestOtherResources(t *testing.T) { - namespace := "testnamespace" - name := "testname" - pod := &api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, - } - tests := []struct { - name string - kind string - resource string - subresource string - object runtime.Object - expectError bool - }{ - { - name: "non-pod resource", - kind: "Foo", - resource: "foos", - object: pod, - }, - { - name: "pod subresource", - kind: "Pod", - resource: "pods", - subresource: "eviction", - object: pod, - }, - { - name: "non-pod object", - kind: "Pod", - resource: "pods", - object: &api.Service{}, - expectError: true, - }, - } - - for _, tc := range tests { - handler := &Plugin{} - - err := handler.Validate(admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, false, nil)) - - if tc.expectError { - if err == nil { - t.Errorf("%s: unexpected nil error", tc.name) - } - continue - } - - if err != nil { - t.Errorf("%s: unexpected error: %v", tc.name, err) - continue - } - } -} diff --git a/plugin/pkg/admission/antiaffinity/doc.go b/plugin/pkg/admission/antiaffinity/doc.go deleted file mode 100644 index d8a9b87ede..0000000000 --- a/plugin/pkg/admission/antiaffinity/doc.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright 2016 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. -*/ - -// LimitPodHardAntiAffinityTopology admission controller rejects any pod -// that specifies "hard" (RequiredDuringScheduling) anti-affinity -// with a TopologyKey other than kubeletapis.LabelHostname. -// Because anti-affinity is symmetric, without this admission controller, -// a user could maliciously or accidentally specify that their pod (once it has scheduled) -// should block other pods from scheduling into the same zone or some other large topology, -// essentially DoSing the cluster. -// In the future we will address this problem more fully by using quota and priority, -// but for now this admission controller provides a simple protection, -// on the assumption that the only legitimate use of hard pod anti-affinity -// is to exclude other pods from the same node. -package antiaffinity // import "k8s.io/kubernetes/plugin/pkg/admission/antiaffinity" diff --git a/plugin/pkg/admission/deny/BUILD b/plugin/pkg/admission/deny/BUILD deleted file mode 100644 index cdbe0e929a..0000000000 --- a/plugin/pkg/admission/deny/BUILD +++ /dev/null @@ -1,40 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/deny", - deps = [ - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/deny/admission.go b/plugin/pkg/admission/deny/admission.go deleted file mode 100644 index bf484590cb..0000000000 --- a/plugin/pkg/admission/deny/admission.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -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 deny - -import ( - "errors" - "io" - - "k8s.io/klog" - - "k8s.io/apiserver/pkg/admission" -) - -// PluginName indicates name of admission plugin. -const PluginName = "AlwaysDeny" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - return NewAlwaysDeny(), nil - }) -} - -// alwaysDeny is an implementation of admission.Interface which always says no to an admission request. -type alwaysDeny struct{} - -var _ admission.MutationInterface = alwaysDeny{} -var _ admission.ValidationInterface = alwaysDeny{} - -// Admit makes an admission decision based on the request attributes. -func (alwaysDeny) Admit(a admission.Attributes) (err error) { - return admission.NewForbidden(a, errors.New("admission control is denying all modifications")) -} - -// Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate. -func (alwaysDeny) Validate(a admission.Attributes) (err error) { - return admission.NewForbidden(a, errors.New("admission control is denying all modifications")) -} - -// Handles returns true if this admission controller can handle the given operation -// where operation can be one of CREATE, UPDATE, DELETE, or CONNECT -func (alwaysDeny) Handles(operation admission.Operation) bool { - return true -} - -// NewAlwaysDeny creates an always deny admission handler -func NewAlwaysDeny() admission.Interface { - // DEPRECATED: AlwaysDeny denys all admission request, it is no use. - klog.Warningf("%s admission controller is deprecated. "+ - "Please remove this controller from your configuration files and scripts.", PluginName) - return new(alwaysDeny) -} diff --git a/plugin/pkg/admission/deny/admission_test.go b/plugin/pkg/admission/deny/admission_test.go deleted file mode 100644 index 1573a7f793..0000000000 --- a/plugin/pkg/admission/deny/admission_test.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -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 deny - -import ( - "testing" - - "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" -) - -func TestAdmission(t *testing.T) { - handler := NewAlwaysDeny() - err := handler.(*alwaysDeny).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, false, nil)) - if err == nil { - t.Error("Expected error returned from admission handler") - } -} - -func TestHandles(t *testing.T) { - handler := NewAlwaysDeny() - tests := []admission.Operation{admission.Create, admission.Connect, admission.Update, admission.Delete} - - for _, test := range tests { - if !handler.Handles(test) { - t.Errorf("Expected handling all operations, including: %v", test) - } - } -} diff --git a/plugin/pkg/admission/eventratelimit/BUILD b/plugin/pkg/admission/eventratelimit/BUILD deleted file mode 100644 index 97b33f9bbe..0000000000 --- a/plugin/pkg/admission/eventratelimit/BUILD +++ /dev/null @@ -1,70 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_test( - name = "go_default_test", - srcs = [ - "admission_test.go", - "cache_test.go", - ], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/clock:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", - "//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library", - "//vendor/github.com/hashicorp/golang-lru:go_default_library", - ], -) - -go_library( - name = "go_default_library", - srcs = [ - "admission.go", - "cache.go", - "clock.go", - "config.go", - "doc.go", - "limitenforcer.go", - ], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit", - deps = [ - "//pkg/apis/core:go_default_library", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit/install:go_default_library", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1:go_default_library", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library", - "//vendor/github.com/hashicorp/golang-lru:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [ - ":package-srcs", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit:all-srcs", - ], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/eventratelimit/admission.go b/plugin/pkg/admission/eventratelimit/admission.go deleted file mode 100644 index 84e92db09d..0000000000 --- a/plugin/pkg/admission/eventratelimit/admission.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -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 eventratelimit - -import ( - "io" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apiserver/pkg/admission" - "k8s.io/client-go/util/flowcontrol" - api "k8s.io/kubernetes/pkg/apis/core" - eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" - "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation" -) - -// PluginName indicates name of admission plugin. -const PluginName = "EventRateLimit" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, - func(config io.Reader) (admission.Interface, error) { - // load the configuration provided (if any) - configuration, err := LoadConfiguration(config) - if err != nil { - return nil, err - } - // validate the configuration (if any) - if configuration != nil { - if errs := validation.ValidateConfiguration(configuration); len(errs) != 0 { - return nil, errs.ToAggregate() - } - } - return newEventRateLimit(configuration, realClock{}) - }) -} - -// Plugin implements an admission controller that can enforce event rate limits -type Plugin struct { - *admission.Handler - // limitEnforcers is the collection of limit enforcers. There is one limit enforcer for each - // active limit type. As there are 4 limit types, the length of the array will be at most 4. - // The array is read-only after construction. - limitEnforcers []*limitEnforcer -} - -var _ admission.ValidationInterface = &Plugin{} - -// newEventRateLimit configures an admission controller that can enforce event rate limits -func newEventRateLimit(config *eventratelimitapi.Configuration, clock flowcontrol.Clock) (*Plugin, error) { - limitEnforcers := make([]*limitEnforcer, 0, len(config.Limits)) - for _, limitConfig := range config.Limits { - enforcer, err := newLimitEnforcer(limitConfig, clock) - if err != nil { - return nil, err - } - limitEnforcers = append(limitEnforcers, enforcer) - } - - eventRateLimitAdmission := &Plugin{ - Handler: admission.NewHandler(admission.Create, admission.Update), - limitEnforcers: limitEnforcers, - } - - return eventRateLimitAdmission, nil -} - -// Validate makes admission decisions while enforcing event rate limits -func (a *Plugin) Validate(attr admission.Attributes) (err error) { - // ignore all operations that do not correspond to an Event kind - if attr.GetKind().GroupKind() != api.Kind("Event") { - return nil - } - - // ignore all requests that specify dry-run - // because they don't correspond to any calls to etcd, - // they should not be affected by the ratelimit - if attr.IsDryRun() { - return nil - } - - var errors []error - // give each limit enforcer a chance to reject the event - for _, enforcer := range a.limitEnforcers { - if err := enforcer.accept(attr); err != nil { - errors = append(errors, err) - } - } - - if aggregatedErr := utilerrors.NewAggregate(errors); aggregatedErr != nil { - return apierrors.NewTooManyRequestsError(aggregatedErr.Error()) - } - - return nil -} diff --git a/plugin/pkg/admission/eventratelimit/admission_test.go b/plugin/pkg/admission/eventratelimit/admission_test.go deleted file mode 100644 index f684852046..0000000000 --- a/plugin/pkg/admission/eventratelimit/admission_test.go +++ /dev/null @@ -1,524 +0,0 @@ -/* -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 eventratelimit - -import ( - "testing" - "time" - - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/clock" - "k8s.io/apiserver/pkg/admission" - "k8s.io/apiserver/pkg/authentication/user" - api "k8s.io/kubernetes/pkg/apis/core" - eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" -) - -const ( - qps = 1 - eventKind = "Event" - nonEventKind = "NonEvent" -) - -// attributesForRequest generates the admission.Attributes that for the specified request -func attributesForRequest(rq request) admission.Attributes { - return admission.NewAttributesRecord( - rq.event, - nil, - api.Kind(rq.kind).WithVersion("version"), - rq.namespace, - "name", - api.Resource("resource").WithVersion("version"), - "", - admission.Create, - rq.dryRun, - &user.DefaultInfo{Name: rq.username}) -} - -type request struct { - kind string - namespace string - username string - event *api.Event - delay time.Duration - accepted bool - dryRun bool -} - -func newRequest(kind string) request { - return request{ - kind: kind, - accepted: true, - } -} - -func newEventRequest() request { - return newRequest(eventKind) -} - -func newNonEventRequest() request { - return newRequest(nonEventKind) -} - -func (r request) withNamespace(namespace string) request { - r.namespace = namespace - return r -} - -func (r request) withEvent(event *api.Event) request { - r.event = event - return r -} - -func (r request) withEventComponent(component string) request { - return r.withEvent(&api.Event{ - Source: api.EventSource{ - Component: component, - }, - }) -} - -func (r request) withDryRun(dryRun bool) request { - r.dryRun = dryRun - return r -} - -func (r request) withUser(name string) request { - r.username = name - return r -} - -func (r request) blocked() request { - r.accepted = false - return r -} - -// withDelay will adjust the clock to simulate the specified delay, in seconds -func (r request) withDelay(delayInSeconds int) request { - r.delay = time.Duration(delayInSeconds) * time.Second - return r -} - -// createSourceAndObjectKeyInclusionRequests creates a series of requests that can be used -// to test that a particular part of the event is included in the source+object key -func createSourceAndObjectKeyInclusionRequests(eventFactory func(label string) *api.Event) []request { - return []request{ - newEventRequest().withEvent(eventFactory("A")), - newEventRequest().withEvent(eventFactory("A")).blocked(), - newEventRequest().withEvent(eventFactory("B")), - } -} - -func TestEventRateLimiting(t *testing.T) { - cases := []struct { - name string - serverBurst int32 - namespaceBurst int32 - namespaceCacheSize int32 - sourceAndObjectBurst int32 - sourceAndObjectCacheSize int32 - userBurst int32 - userCacheSize int32 - requests []request - }{ - { - name: "event not blocked when tokens available", - serverBurst: 3, - requests: []request{ - newEventRequest(), - }, - }, - { - name: "non-event not blocked", - serverBurst: 3, - requests: []request{ - newNonEventRequest(), - }, - }, - { - name: "event blocked after tokens exhausted", - serverBurst: 3, - requests: []request{ - newEventRequest(), - newEventRequest(), - newEventRequest(), - newEventRequest().blocked(), - }, - }, - { - name: "event not blocked by dry-run requests", - serverBurst: 3, - requests: []request{ - newEventRequest(), - newEventRequest(), - newEventRequest().withDryRun(true), - newEventRequest().withDryRun(true), - newEventRequest().withDryRun(true), - newEventRequest().withDryRun(true), - newEventRequest(), - newEventRequest().blocked(), - newEventRequest().withDryRun(true), - }, - }, - { - name: "non-event not blocked after tokens exhausted", - serverBurst: 3, - requests: []request{ - newEventRequest(), - newEventRequest(), - newEventRequest(), - newNonEventRequest(), - }, - }, - { - name: "non-events should not count against limit", - serverBurst: 3, - requests: []request{ - newEventRequest(), - newEventRequest(), - newNonEventRequest(), - newEventRequest(), - }, - }, - { - name: "event accepted after token refill", - serverBurst: 3, - requests: []request{ - newEventRequest(), - newEventRequest(), - newEventRequest(), - newEventRequest().blocked(), - newEventRequest().withDelay(1), - }, - }, - { - name: "event blocked by namespace limits", - serverBurst: 100, - namespaceBurst: 3, - namespaceCacheSize: 10, - requests: []request{ - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A").blocked(), - }, - }, - { - name: "event from other namespace not blocked", - serverBurst: 100, - namespaceBurst: 3, - namespaceCacheSize: 10, - requests: []request{ - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("B"), - }, - }, - { - name: "events from other namespaces should not count against limit", - serverBurst: 100, - namespaceBurst: 3, - namespaceCacheSize: 10, - requests: []request{ - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("B"), - newEventRequest().withNamespace("A"), - }, - }, - { - name: "event accepted after namespace token refill", - serverBurst: 100, - namespaceBurst: 3, - namespaceCacheSize: 10, - requests: []request{ - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A").blocked(), - newEventRequest().withNamespace("A").withDelay(1), - }, - }, - { - name: "event from other namespaces should not clear namespace limits", - serverBurst: 100, - namespaceBurst: 3, - namespaceCacheSize: 10, - requests: []request{ - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("B"), - newEventRequest().withNamespace("A").blocked(), - }, - }, - { - name: "namespace limits from lru namespace should clear when cache size exceeded", - serverBurst: 100, - namespaceBurst: 3, - namespaceCacheSize: 2, - requests: []request{ - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("B"), - newEventRequest().withNamespace("B"), - newEventRequest().withNamespace("B"), - newEventRequest().withNamespace("A"), - newEventRequest().withNamespace("B").blocked(), - newEventRequest().withNamespace("A").blocked(), - // This should clear out namespace B from the lru cache - newEventRequest().withNamespace("C"), - newEventRequest().withNamespace("A").blocked(), - newEventRequest().withNamespace("B"), - }, - }, - { - name: "event blocked by source+object limits", - serverBurst: 100, - sourceAndObjectBurst: 3, - sourceAndObjectCacheSize: 10, - requests: []request{ - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A").blocked(), - }, - }, - { - name: "event from other source+object not blocked", - serverBurst: 100, - sourceAndObjectBurst: 3, - sourceAndObjectCacheSize: 10, - requests: []request{ - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("B"), - }, - }, - { - name: "events from other source+object should not count against limit", - serverBurst: 100, - sourceAndObjectBurst: 3, - sourceAndObjectCacheSize: 10, - requests: []request{ - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("B"), - newEventRequest().withEventComponent("A"), - }, - }, - { - name: "event accepted after source+object token refill", - serverBurst: 100, - sourceAndObjectBurst: 3, - sourceAndObjectCacheSize: 10, - requests: []request{ - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A").blocked(), - newEventRequest().withEventComponent("A").withDelay(1), - }, - }, - { - name: "event from other source+object should not clear source+object limits", - serverBurst: 100, - sourceAndObjectBurst: 3, - sourceAndObjectCacheSize: 10, - requests: []request{ - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("B"), - newEventRequest().withEventComponent("A").blocked(), - }, - }, - { - name: "source+object limits from lru source+object should clear when cache size exceeded", - serverBurst: 100, - sourceAndObjectBurst: 3, - sourceAndObjectCacheSize: 2, - requests: []request{ - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("B"), - newEventRequest().withEventComponent("B"), - newEventRequest().withEventComponent("B"), - newEventRequest().withEventComponent("A"), - newEventRequest().withEventComponent("B").blocked(), - newEventRequest().withEventComponent("A").blocked(), - // This should clear out component B from the lru cache - newEventRequest().withEventComponent("C"), - newEventRequest().withEventComponent("A").blocked(), - newEventRequest().withEventComponent("B"), - }, - }, - { - name: "source host should be included in source+object key", - serverBurst: 100, - sourceAndObjectBurst: 1, - sourceAndObjectCacheSize: 10, - requests: createSourceAndObjectKeyInclusionRequests(func(label string) *api.Event { - return &api.Event{Source: api.EventSource{Host: label}} - }), - }, - { - name: "involved object kind should be included in source+object key", - serverBurst: 100, - sourceAndObjectBurst: 1, - sourceAndObjectCacheSize: 10, - requests: createSourceAndObjectKeyInclusionRequests(func(label string) *api.Event { - return &api.Event{InvolvedObject: api.ObjectReference{Kind: label}} - }), - }, - { - name: "involved object namespace should be included in source+object key", - serverBurst: 100, - sourceAndObjectBurst: 1, - sourceAndObjectCacheSize: 10, - requests: createSourceAndObjectKeyInclusionRequests(func(label string) *api.Event { - return &api.Event{InvolvedObject: api.ObjectReference{Namespace: label}} - }), - }, - { - name: "involved object name should be included in source+object key", - serverBurst: 100, - sourceAndObjectBurst: 1, - sourceAndObjectCacheSize: 10, - requests: createSourceAndObjectKeyInclusionRequests(func(label string) *api.Event { - return &api.Event{InvolvedObject: api.ObjectReference{Name: label}} - }), - }, - { - name: "involved object UID should be included in source+object key", - serverBurst: 100, - sourceAndObjectBurst: 1, - sourceAndObjectCacheSize: 10, - requests: createSourceAndObjectKeyInclusionRequests(func(label string) *api.Event { - return &api.Event{InvolvedObject: api.ObjectReference{UID: types.UID(label)}} - }), - }, - { - name: "involved object APIVersion should be included in source+object key", - serverBurst: 100, - sourceAndObjectBurst: 1, - sourceAndObjectCacheSize: 10, - requests: createSourceAndObjectKeyInclusionRequests(func(label string) *api.Event { - return &api.Event{InvolvedObject: api.ObjectReference{APIVersion: label}} - }), - }, - { - name: "event blocked by user limits", - userBurst: 3, - userCacheSize: 10, - requests: []request{ - newEventRequest().withUser("A"), - newEventRequest().withUser("A"), - newEventRequest().withUser("A"), - newEventRequest().withUser("A").blocked(), - }, - }, - { - name: "event from other user not blocked", - requests: []request{ - newEventRequest().withUser("A"), - newEventRequest().withUser("A"), - newEventRequest().withUser("A"), - newEventRequest().withUser("B"), - }, - }, - { - name: "events from other user should not count against limit", - requests: []request{ - newEventRequest().withUser("A"), - newEventRequest().withUser("A"), - newEventRequest().withUser("B"), - newEventRequest().withUser("A"), - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clock := clock.NewFakeClock(time.Now()) - config := &eventratelimitapi.Configuration{} - if tc.serverBurst > 0 { - serverLimit := eventratelimitapi.Limit{ - Type: eventratelimitapi.ServerLimitType, - QPS: qps, - Burst: tc.serverBurst, - } - config.Limits = append(config.Limits, serverLimit) - } - if tc.namespaceBurst > 0 { - namespaceLimit := eventratelimitapi.Limit{ - Type: eventratelimitapi.NamespaceLimitType, - Burst: tc.namespaceBurst, - QPS: qps, - CacheSize: tc.namespaceCacheSize, - } - config.Limits = append(config.Limits, namespaceLimit) - } - if tc.userBurst > 0 { - userLimit := eventratelimitapi.Limit{ - Type: eventratelimitapi.UserLimitType, - Burst: tc.userBurst, - QPS: qps, - CacheSize: tc.userCacheSize, - } - config.Limits = append(config.Limits, userLimit) - } - if tc.sourceAndObjectBurst > 0 { - sourceAndObjectLimit := eventratelimitapi.Limit{ - Type: eventratelimitapi.SourceAndObjectLimitType, - Burst: tc.sourceAndObjectBurst, - QPS: qps, - CacheSize: tc.sourceAndObjectCacheSize, - } - config.Limits = append(config.Limits, sourceAndObjectLimit) - } - eventratelimit, err := newEventRateLimit(config, clock) - if err != nil { - t.Fatalf("%v: Could not create EventRateLimit: %v", tc.name, err) - } - - for rqIndex, rq := range tc.requests { - if rq.delay > 0 { - clock.Step(rq.delay) - } - attributes := attributesForRequest(rq) - err = eventratelimit.Validate(attributes) - if rq.accepted != (err == nil) { - expectedAction := "admitted" - if !rq.accepted { - expectedAction = "blocked" - } - t.Fatalf("%v: Request %v should have been %v: %v", tc.name, rqIndex, expectedAction, err) - } - if err != nil { - statusErr, ok := err.(*errors.StatusError) - if ok && statusErr.ErrStatus.Code != errors.StatusTooManyRequests { - t.Fatalf("%v: Request %v should yield a 429 response: %v", tc.name, rqIndex, err) - } - } - } - }) - } -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/BUILD b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/BUILD deleted file mode 100644 index 61a31c743a..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/BUILD +++ /dev/null @@ -1,40 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = [ - "doc.go", - "register.go", - "types.go", - "zz_generated.deepcopy.go", - ], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit", - deps = [ - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [ - ":package-srcs", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit/install:all-srcs", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1:all-srcs", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation:all-srcs", - ], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/OWNERS b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/OWNERS deleted file mode 100755 index 6c48a1a83e..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/OWNERS +++ /dev/null @@ -1,7 +0,0 @@ -reviewers: -- deads2k -- derekwaynecarr -approvers: -- deads2k -- derekwaynecarr -- smarterclayton \ No newline at end of file diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/doc.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/doc.go deleted file mode 100644 index 56d99bee29..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2016 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 - -package eventratelimit // import "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/install/BUILD b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/install/BUILD deleted file mode 100644 index 588ee73688..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/install/BUILD +++ /dev/null @@ -1,31 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = ["install.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/install", - deps = [ - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library", - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/install/install.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/install/install.go deleted file mode 100644 index 413cd56b8d..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -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 installs the experimental API group, making it available as -// an option to all of the API encoding/decoding machinery. -package install - -import ( - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - internalapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" - versionedapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1" -) - -// Install registers the API group and adds types to a scheme -func Install(scheme *runtime.Scheme) { - utilruntime.Must(internalapi.AddToScheme(scheme)) - utilruntime.Must(versionedapi.AddToScheme(scheme)) - utilruntime.Must(scheme.SetVersionPriority(versionedapi.SchemeGroupVersion)) -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/register.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/register.go deleted file mode 100644 index d763c55cb2..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/register.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -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 eventratelimit - -import ( - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme -) - -// GroupName is the group name use in this package -const GroupName = "eventratelimit.admission.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 a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -func addKnownTypes(scheme *runtime.Scheme) error { - // TODO this will get cleaned up with the scheme types are fixed - scheme.AddKnownTypes(SchemeGroupVersion, - &Configuration{}, - ) - return nil -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/types.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/types.go deleted file mode 100644 index 397027c249..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/types.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -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 eventratelimit - -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - -// LimitType is the type of the limit (e.g., per-namespace) -type LimitType string - -const ( - // ServerLimitType is a type of limit where there is one bucket shared by - // all of the event queries received by the API Server. - ServerLimitType LimitType = "Server" - // NamespaceLimitType is a type of limit where there is one bucket used by - // each namespace - NamespaceLimitType LimitType = "Namespace" - // UserLimitType is a type of limit where there is one bucket used by each - // user - UserLimitType LimitType = "User" - // SourceAndObjectLimitType is a type of limit where there is one bucket used - // by each combination of source and involved object of the event. - SourceAndObjectLimitType LimitType = "SourceAndObject" -) - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Configuration provides configuration for the EventRateLimit admission -// controller. -type Configuration struct { - metav1.TypeMeta `json:",inline"` - - // limits are the limits to place on event queries received. - // Limits can be placed on events received server-wide, per namespace, - // per user, and per source+object. - // At least one limit is required. - Limits []Limit `json:"limits"` -} - -// Limit is the configuration for a particular limit type -type Limit struct { - // type is the type of limit to which this configuration applies - Type LimitType `json:"type"` - - // qps is the number of event queries per second that are allowed for this - // type of limit. The qps and burst fields are used together to determine if - // a particular event query is accepted. The qps determines how many queries - // are accepted once the burst amount of queries has been exhausted. - QPS int32 `json:"qps"` - - // burst is the burst number of event queries that are allowed for this type - // of limit. The qps and burst fields are used together to determine if a - // particular event query is accepted. The burst determines the maximum size - // of the allowance granted for a particular bucket. For example, if the burst - // is 10 and the qps is 3, then the admission control will accept 10 queries - // before blocking any queries. Every second, 3 more queries will be allowed. - // If some of that allowance is not used, then it will roll over to the next - // second, until the maximum allowance of 10 is reached. - Burst int32 `json:"burst"` - - // cacheSize is the size of the LRU cache for this type of limit. If a bucket - // is evicted from the cache, then the allowance for that bucket is reset. If - // more queries are later received for an evicted bucket, then that bucket - // will re-enter the cache with a clean slate, giving that bucket a full - // allowance of burst queries. - // - // The default cache size is 4096. - // - // If limitType is 'server', then cacheSize is ignored. - // +optional - CacheSize int32 `json:"cacheSize,omitempty"` -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/BUILD b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/BUILD deleted file mode 100644 index 3f3116c90a..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/BUILD +++ /dev/null @@ -1,40 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = [ - "defaults.go", - "doc.go", - "register.go", - "types.go", - "zz_generated.conversion.go", - "zz_generated.deepcopy.go", - "zz_generated.defaults.go", - ], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1", - deps = [ - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/defaults.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/defaults.go deleted file mode 100644 index ebade2de23..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/defaults.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -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 kruntime "k8s.io/apimachinery/pkg/runtime" - -func addDefaultingFuncs(scheme *kruntime.Scheme) error { - return RegisterDefaults(scheme) -} - -func SetDefaults_Configuration(obj *Configuration) {} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/doc.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/doc.go deleted file mode 100644 index 09fdc9fc39..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2016 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 -// +k8s:conversion-gen=k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit -// +k8s:defaulter-gen=TypeMeta -// +groupName=eventratelimit.admission.k8s.io - -// Package v1alpha1 is the v1alpha1 version of the API. -package v1alpha1 // import "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1" diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/register.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/register.go deleted file mode 100644 index 74c039344d..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/register.go +++ /dev/null @@ -1,50 +0,0 @@ -/* -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" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// GroupName is the group name use in this package -const GroupName = "eventratelimit.admission.k8s.io" - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} - -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) -} - -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Configuration{}, - ) - return nil -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/types.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/types.go deleted file mode 100644 index 2f9d482c4e..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/types.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -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" - -// LimitType is the type of the limit (e.g., per-namespace) -type LimitType string - -const ( - // ServerLimitType is a type of limit where there is one bucket shared by - // all of the event queries received by the API Server. - ServerLimitType LimitType = "Server" - // NamespaceLimitType is a type of limit where there is one bucket used by - // each namespace - NamespaceLimitType LimitType = "Namespace" - // UserLimitType is a type of limit where there is one bucket used by each - // user - UserLimitType LimitType = "User" - // SourceAndObjectLimitType is a type of limit where there is one bucket used - // by each combination of source and involved object of the event. - SourceAndObjectLimitType LimitType = "SourceAndObject" -) - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Configuration provides configuration for the EventRateLimit admission -// controller. -type Configuration struct { - metav1.TypeMeta `json:",inline"` - - // limits are the limits to place on event queries received. - // Limits can be placed on events received server-wide, per namespace, - // per user, and per source+object. - // At least one limit is required. - Limits []Limit `json:"limits"` -} - -// Limit is the configuration for a particular limit type -type Limit struct { - // type is the type of limit to which this configuration applies - Type LimitType `json:"type"` - - // qps is the number of event queries per second that are allowed for this - // type of limit. The qps and burst fields are used together to determine if - // a particular event query is accepted. The qps determines how many queries - // are accepted once the burst amount of queries has been exhausted. - QPS int32 `json:"qps"` - - // burst is the burst number of event queries that are allowed for this type - // of limit. The qps and burst fields are used together to determine if a - // particular event query is accepted. The burst determines the maximum size - // of the allowance granted for a particular bucket. For example, if the burst - // is 10 and the qps is 3, then the admission control will accept 10 queries - // before blocking any queries. Every second, 3 more queries will be allowed. - // If some of that allowance is not used, then it will roll over to the next - // second, until the maximum allowance of 10 is reached. - Burst int32 `json:"burst"` - - // cacheSize is the size of the LRU cache for this type of limit. If a bucket - // is evicted from the cache, then the allowance for that bucket is reset. If - // more queries are later received for an evicted bucket, then that bucket - // will re-enter the cache with a clean slate, giving that bucket a full - // allowance of burst queries. - // - // The default cache size is 4096. - // - // If limitType is 'server', then cacheSize is ignored. - // +optional - CacheSize int32 `json:"cacheSize,omitempty"` -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/zz_generated.conversion.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/zz_generated.conversion.go deleted file mode 100644 index 1591501d11..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/zz_generated.conversion.go +++ /dev/null @@ -1,105 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 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. -*/ - -// Code generated by conversion-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - unsafe "unsafe" - - conversion "k8s.io/apimachinery/pkg/conversion" - runtime "k8s.io/apimachinery/pkg/runtime" - eventratelimit "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" -) - -func init() { - localSchemeBuilder.Register(RegisterConversions) -} - -// RegisterConversions adds conversion functions to the given scheme. -// Public to allow building arbitrary schemes. -func RegisterConversions(s *runtime.Scheme) error { - if err := s.AddGeneratedConversionFunc((*Configuration)(nil), (*eventratelimit.Configuration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_Configuration_To_eventratelimit_Configuration(a.(*Configuration), b.(*eventratelimit.Configuration), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*eventratelimit.Configuration)(nil), (*Configuration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_eventratelimit_Configuration_To_v1alpha1_Configuration(a.(*eventratelimit.Configuration), b.(*Configuration), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*Limit)(nil), (*eventratelimit.Limit)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_Limit_To_eventratelimit_Limit(a.(*Limit), b.(*eventratelimit.Limit), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*eventratelimit.Limit)(nil), (*Limit)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_eventratelimit_Limit_To_v1alpha1_Limit(a.(*eventratelimit.Limit), b.(*Limit), scope) - }); err != nil { - return err - } - return nil -} - -func autoConvert_v1alpha1_Configuration_To_eventratelimit_Configuration(in *Configuration, out *eventratelimit.Configuration, s conversion.Scope) error { - out.Limits = *(*[]eventratelimit.Limit)(unsafe.Pointer(&in.Limits)) - return nil -} - -// Convert_v1alpha1_Configuration_To_eventratelimit_Configuration is an autogenerated conversion function. -func Convert_v1alpha1_Configuration_To_eventratelimit_Configuration(in *Configuration, out *eventratelimit.Configuration, s conversion.Scope) error { - return autoConvert_v1alpha1_Configuration_To_eventratelimit_Configuration(in, out, s) -} - -func autoConvert_eventratelimit_Configuration_To_v1alpha1_Configuration(in *eventratelimit.Configuration, out *Configuration, s conversion.Scope) error { - out.Limits = *(*[]Limit)(unsafe.Pointer(&in.Limits)) - return nil -} - -// Convert_eventratelimit_Configuration_To_v1alpha1_Configuration is an autogenerated conversion function. -func Convert_eventratelimit_Configuration_To_v1alpha1_Configuration(in *eventratelimit.Configuration, out *Configuration, s conversion.Scope) error { - return autoConvert_eventratelimit_Configuration_To_v1alpha1_Configuration(in, out, s) -} - -func autoConvert_v1alpha1_Limit_To_eventratelimit_Limit(in *Limit, out *eventratelimit.Limit, s conversion.Scope) error { - out.Type = eventratelimit.LimitType(in.Type) - out.QPS = in.QPS - out.Burst = in.Burst - out.CacheSize = in.CacheSize - return nil -} - -// Convert_v1alpha1_Limit_To_eventratelimit_Limit is an autogenerated conversion function. -func Convert_v1alpha1_Limit_To_eventratelimit_Limit(in *Limit, out *eventratelimit.Limit, s conversion.Scope) error { - return autoConvert_v1alpha1_Limit_To_eventratelimit_Limit(in, out, s) -} - -func autoConvert_eventratelimit_Limit_To_v1alpha1_Limit(in *eventratelimit.Limit, out *Limit, s conversion.Scope) error { - out.Type = LimitType(in.Type) - out.QPS = in.QPS - out.Burst = in.Burst - out.CacheSize = in.CacheSize - return nil -} - -// Convert_eventratelimit_Limit_To_v1alpha1_Limit is an autogenerated conversion function. -func Convert_eventratelimit_Limit_To_v1alpha1_Limit(in *eventratelimit.Limit, out *Limit, s conversion.Scope) error { - return autoConvert_eventratelimit_Limit_To_v1alpha1_Limit(in, out, s) -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/zz_generated.deepcopy.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index d7a182db1a..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,71 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 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. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Configuration) DeepCopyInto(out *Configuration) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Limits != nil { - in, out := &in.Limits, &out.Limits - *out = make([]Limit, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Configuration. -func (in *Configuration) DeepCopy() *Configuration { - if in == nil { - return nil - } - out := new(Configuration) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Configuration) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Limit) DeepCopyInto(out *Limit) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Limit. -func (in *Limit) DeepCopy() *Limit { - if in == nil { - return nil - } - out := new(Limit) - in.DeepCopyInto(out) - return out -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/zz_generated.defaults.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/zz_generated.defaults.go deleted file mode 100644 index fbf617bbf1..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1/zz_generated.defaults.go +++ /dev/null @@ -1,37 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 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. -*/ - -// Code generated by defaulter-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// RegisterDefaults adds defaulters functions to the given scheme. -// Public to allow building arbitrary schemes. -// All generated defaulters are covering - they call all nested defaulters. -func RegisterDefaults(scheme *runtime.Scheme) error { - scheme.AddTypeDefaultingFunc(&Configuration{}, func(obj interface{}) { SetObjectDefaults_Configuration(obj.(*Configuration)) }) - return nil -} - -func SetObjectDefaults_Configuration(in *Configuration) { - SetDefaults_Configuration(in) -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/BUILD b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/BUILD deleted file mode 100644 index 58ff16e138..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/BUILD +++ /dev/null @@ -1,37 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["validation.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation", - deps = [ - "//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) - -go_test( - name = "go_default_test", - srcs = ["validation_test.go"], - embed = [":go_default_library"], - deps = ["//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library"], -) diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation.go deleted file mode 100644 index f09acfd132..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -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 ( - "k8s.io/apimachinery/pkg/util/validation/field" - - eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" -) - -var limitTypes = map[eventratelimitapi.LimitType]bool{ - eventratelimitapi.ServerLimitType: true, - eventratelimitapi.NamespaceLimitType: true, - eventratelimitapi.UserLimitType: true, - eventratelimitapi.SourceAndObjectLimitType: true, -} - -// ValidateConfiguration validates the configuration. -func ValidateConfiguration(config *eventratelimitapi.Configuration) field.ErrorList { - allErrs := field.ErrorList{} - limitsPath := field.NewPath("limits") - if len(config.Limits) == 0 { - allErrs = append(allErrs, field.Invalid(limitsPath, config.Limits, "must not be empty")) - } - for i, limit := range config.Limits { - idxPath := limitsPath.Index(i) - if !limitTypes[limit.Type] { - allowedValues := make([]string, len(limitTypes)) - i := 0 - for limitType := range limitTypes { - allowedValues[i] = string(limitType) - i++ - } - allErrs = append(allErrs, field.NotSupported(idxPath.Child("type"), limit.Type, allowedValues)) - } - if limit.Burst <= 0 { - allErrs = append(allErrs, field.Invalid(idxPath.Child("burst"), limit.Burst, "must be positive")) - } - if limit.QPS <= 0 { - allErrs = append(allErrs, field.Invalid(idxPath.Child("qps"), limit.QPS, "must be positive")) - } - if limit.Type != eventratelimitapi.ServerLimitType { - if limit.CacheSize < 0 { - allErrs = append(allErrs, field.Invalid(idxPath.Child("cacheSize"), limit.CacheSize, "must not be negative")) - } - } - } - return allErrs -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation_test.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation_test.go deleted file mode 100644 index 03415d0449..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation_test.go +++ /dev/null @@ -1,192 +0,0 @@ -/* -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 ( - "testing" - - eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" -) - -func TestValidateConfiguration(t *testing.T) { - cases := []struct { - name string - config eventratelimitapi.Configuration - expectedResult bool - }{ - { - name: "valid server", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Type: "Server", - Burst: 5, - QPS: 1, - }, - }, - }, - expectedResult: true, - }, - { - name: "valid namespace", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Type: "Namespace", - Burst: 10, - QPS: 2, - CacheSize: 100, - }, - }, - }, - expectedResult: true, - }, - { - name: "valid user", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Type: "User", - Burst: 10, - QPS: 2, - CacheSize: 100, - }, - }, - }, - expectedResult: true, - }, - { - name: "valid source+object", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Type: "SourceAndObject", - Burst: 5, - QPS: 1, - CacheSize: 1000, - }, - }, - }, - expectedResult: true, - }, - { - name: "valid multiple", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Type: "Server", - Burst: 5, - QPS: 1, - }, - { - Type: "Namespace", - Burst: 10, - QPS: 2, - CacheSize: 100, - }, - { - Type: "SourceAndObject", - Burst: 25, - QPS: 10, - CacheSize: 1000, - }, - }, - }, - expectedResult: true, - }, - { - name: "missing limits", - config: eventratelimitapi.Configuration{}, - expectedResult: false, - }, - { - name: "missing type", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Burst: 25, - QPS: 10, - CacheSize: 1000, - }, - }, - }, - expectedResult: false, - }, - { - name: "invalid type", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Type: "unknown-type", - Burst: 25, - QPS: 10, - CacheSize: 1000, - }, - }, - }, - expectedResult: false, - }, - { - name: "missing burst", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Type: "Server", - QPS: 1, - }, - }, - }, - expectedResult: false, - }, - { - name: "missing qps", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Type: "Server", - Burst: 5, - }, - }, - }, - expectedResult: false, - }, - { - name: "negative cache size", - config: eventratelimitapi.Configuration{ - Limits: []eventratelimitapi.Limit{ - { - Type: "Namespace", - Burst: 10, - QPS: 2, - CacheSize: -1, - }, - }, - }, - expectedResult: false, - }, - } - for _, tc := range cases { - errs := ValidateConfiguration(&tc.config) - if e, a := tc.expectedResult, len(errs) == 0; e != a { - if e { - t.Errorf("%v: expected success: %v", tc.name, errs) - } else { - t.Errorf("%v: expected failure", tc.name) - } - } - } -} diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/zz_generated.deepcopy.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/zz_generated.deepcopy.go deleted file mode 100644 index 56614f05b9..0000000000 --- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/zz_generated.deepcopy.go +++ /dev/null @@ -1,71 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 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. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package eventratelimit - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Configuration) DeepCopyInto(out *Configuration) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Limits != nil { - in, out := &in.Limits, &out.Limits - *out = make([]Limit, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Configuration. -func (in *Configuration) DeepCopy() *Configuration { - if in == nil { - return nil - } - out := new(Configuration) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Configuration) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Limit) DeepCopyInto(out *Limit) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Limit. -func (in *Limit) DeepCopy() *Limit { - if in == nil { - return nil - } - out := new(Limit) - in.DeepCopyInto(out) - return out -} diff --git a/plugin/pkg/admission/eventratelimit/cache.go b/plugin/pkg/admission/eventratelimit/cache.go deleted file mode 100644 index 7e1016372e..0000000000 --- a/plugin/pkg/admission/eventratelimit/cache.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -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 eventratelimit - -import ( - "github.com/hashicorp/golang-lru" - - "k8s.io/client-go/util/flowcontrol" -) - -// cache is an interface for caching the limits of a particular type -type cache interface { - // get the rate limiter associated with the specified key - get(key interface{}) flowcontrol.RateLimiter -} - -// singleCache is a cache that only stores a single, constant item -type singleCache struct { - // the single rate limiter held by the cache - rateLimiter flowcontrol.RateLimiter -} - -func (c *singleCache) get(key interface{}) flowcontrol.RateLimiter { - return c.rateLimiter -} - -// lruCache is a least-recently-used cache -type lruCache struct { - // factory to use to create new rate limiters - rateLimiterFactory func() flowcontrol.RateLimiter - // the actual LRU cache - cache *lru.Cache -} - -func (c *lruCache) get(key interface{}) flowcontrol.RateLimiter { - value, found := c.cache.Get(key) - if !found { - rateLimter := c.rateLimiterFactory() - c.cache.Add(key, rateLimter) - return rateLimter - } - return value.(flowcontrol.RateLimiter) -} diff --git a/plugin/pkg/admission/eventratelimit/cache_test.go b/plugin/pkg/admission/eventratelimit/cache_test.go deleted file mode 100644 index bced5f7d34..0000000000 --- a/plugin/pkg/admission/eventratelimit/cache_test.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -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 eventratelimit - -import ( - "testing" - - "github.com/hashicorp/golang-lru" - - "k8s.io/client-go/util/flowcontrol" -) - -func TestSingleCache(t *testing.T) { - rateLimiter := flowcontrol.NewTokenBucketRateLimiter(1., 1) - cache := singleCache{ - rateLimiter: rateLimiter, - } - cases := []interface{}{nil, "key1", "key2"} - for _, tc := range cases { - actual := cache.get(tc) - if e, a := rateLimiter, actual; e != a { - t.Errorf("unexpected entry in cache for key %v: expected %v, got %v", tc, e, a) - } - } -} - -func TestLRUCache(t *testing.T) { - rateLimiters := []flowcontrol.RateLimiter{ - flowcontrol.NewTokenBucketRateLimiter(1., 1), - flowcontrol.NewTokenBucketRateLimiter(2., 2), - flowcontrol.NewTokenBucketRateLimiter(3., 3), - flowcontrol.NewTokenBucketRateLimiter(4., 4), - } - nextRateLimiter := 0 - rateLimiterFactory := func() flowcontrol.RateLimiter { - rateLimiter := rateLimiters[nextRateLimiter] - nextRateLimiter++ - return rateLimiter - } - underlyingCache, err := lru.New(2) - if err != nil { - t.Fatalf("Could not create LRU cache: %v", err) - } - cache := lruCache{ - rateLimiterFactory: rateLimiterFactory, - cache: underlyingCache, - } - cases := []struct { - name string - key int - expected flowcontrol.RateLimiter - }{ - { - name: "first added", - key: 0, - expected: rateLimiters[0], - }, - { - name: "first obtained", - key: 0, - expected: rateLimiters[0], - }, - { - name: "second added", - key: 1, - expected: rateLimiters[1], - }, - { - name: "second obtained", - key: 1, - expected: rateLimiters[1], - }, - { - name: "first obtained second time", - key: 0, - expected: rateLimiters[0], - }, - { - name: "third added", - key: 2, - expected: rateLimiters[2], - }, - { - name: "third obtained", - key: 2, - expected: rateLimiters[2], - }, - { - name: "first obtained third time", - key: 0, - expected: rateLimiters[0], - }, - { - name: "second re-added after eviction", - key: 1, - expected: rateLimiters[3], - }, - } - for _, tc := range cases { - actual := cache.get(tc.key) - if e, a := tc.expected, actual; e != a { - t.Errorf("%v: unexpected entry in cache for key %v: expected %v, got %v", tc.name, tc.key, e, a) - } - } -} diff --git a/plugin/pkg/admission/eventratelimit/clock.go b/plugin/pkg/admission/eventratelimit/clock.go deleted file mode 100644 index 507d57a750..0000000000 --- a/plugin/pkg/admission/eventratelimit/clock.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -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 eventratelimit - -import ( - "time" -) - -// realClock implements flowcontrol.Clock in terms of standard time functions. -type realClock struct{} - -// Now is identical to time.Now. -func (realClock) Now() time.Time { - return time.Now() -} - -// Sleep is identical to time.Sleep. -func (realClock) Sleep(d time.Duration) { - time.Sleep(d) -} diff --git a/plugin/pkg/admission/eventratelimit/config.go b/plugin/pkg/admission/eventratelimit/config.go deleted file mode 100644 index 34610391fa..0000000000 --- a/plugin/pkg/admission/eventratelimit/config.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -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 eventratelimit - -import ( - "fmt" - "io" - "io/ioutil" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" - "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/install" - eventratelimitv1alpha1 "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1" -) - -var ( - scheme = runtime.NewScheme() - codecs = serializer.NewCodecFactory(scheme) -) - -func init() { - install.Install(scheme) -} - -// LoadConfiguration loads the provided configuration. -func LoadConfiguration(config io.Reader) (*eventratelimitapi.Configuration, error) { - // if no config is provided, return a default configuration - if config == nil { - externalConfig := &eventratelimitv1alpha1.Configuration{} - scheme.Default(externalConfig) - internalConfig := &eventratelimitapi.Configuration{} - if err := scheme.Convert(externalConfig, internalConfig, nil); err != nil { - return nil, err - } - return internalConfig, nil - } - // we have a config so parse it. - data, err := ioutil.ReadAll(config) - if err != nil { - return nil, err - } - decoder := codecs.UniversalDecoder() - decodedObj, err := runtime.Decode(decoder, data) - if err != nil { - return nil, err - } - resourceQuotaConfiguration, ok := decodedObj.(*eventratelimitapi.Configuration) - if !ok { - return nil, fmt.Errorf("unexpected type: %T", decodedObj) - } - return resourceQuotaConfiguration, nil -} diff --git a/plugin/pkg/admission/eventratelimit/doc.go b/plugin/pkg/admission/eventratelimit/doc.go deleted file mode 100644 index d51d2379b6..0000000000 --- a/plugin/pkg/admission/eventratelimit/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -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 eventratelimit contains an admission controller that enforces a rate limit on events -package eventratelimit // import "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit" diff --git a/plugin/pkg/admission/eventratelimit/limitenforcer.go b/plugin/pkg/admission/eventratelimit/limitenforcer.go deleted file mode 100644 index c65a1715ed..0000000000 --- a/plugin/pkg/admission/eventratelimit/limitenforcer.go +++ /dev/null @@ -1,144 +0,0 @@ -/* -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 eventratelimit - -import ( - "fmt" - "strings" - - "github.com/hashicorp/golang-lru" - - "k8s.io/apiserver/pkg/admission" - "k8s.io/client-go/util/flowcontrol" - api "k8s.io/kubernetes/pkg/apis/core" - eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" -) - -const ( - // cache size to use if the user did not specify a cache size - defaultCacheSize = 4096 -) - -// limitEnforcer enforces a single type of event rate limit, such as server, namespace, or source+object -type limitEnforcer struct { - // type of this limit - limitType eventratelimitapi.LimitType - // cache for holding the rate limiters - cache cache - // a keyFunc which is responsible for computing a single key based on input - keyFunc func(admission.Attributes) string -} - -func newLimitEnforcer(config eventratelimitapi.Limit, clock flowcontrol.Clock) (*limitEnforcer, error) { - rateLimiterFactory := func() flowcontrol.RateLimiter { - return flowcontrol.NewTokenBucketRateLimiterWithClock(float32(config.QPS), int(config.Burst), clock) - } - - if config.Type == eventratelimitapi.ServerLimitType { - return &limitEnforcer{ - limitType: config.Type, - cache: &singleCache{ - rateLimiter: rateLimiterFactory(), - }, - keyFunc: getServerKey, - }, nil - } - - cacheSize := int(config.CacheSize) - if cacheSize == 0 { - cacheSize = defaultCacheSize - } - underlyingCache, err := lru.New(cacheSize) - if err != nil { - return nil, fmt.Errorf("could not create lru cache: %v", err) - } - cache := &lruCache{ - rateLimiterFactory: rateLimiterFactory, - cache: underlyingCache, - } - - var keyFunc func(admission.Attributes) string - switch t := config.Type; t { - case eventratelimitapi.NamespaceLimitType: - keyFunc = getNamespaceKey - case eventratelimitapi.UserLimitType: - keyFunc = getUserKey - case eventratelimitapi.SourceAndObjectLimitType: - keyFunc = getSourceAndObjectKey - default: - return nil, fmt.Errorf("unknown event rate limit type: %v", t) - } - - return &limitEnforcer{ - limitType: config.Type, - cache: cache, - keyFunc: keyFunc, - }, nil -} - -func (enforcer *limitEnforcer) accept(attr admission.Attributes) error { - key := enforcer.keyFunc(attr) - rateLimiter := enforcer.cache.get(key) - - // ensure we have available rate - allow := rateLimiter.TryAccept() - - if !allow { - return fmt.Errorf("limit reached on type %v for key %v", enforcer.limitType, key) - } - - return nil -} - -func getServerKey(attr admission.Attributes) string { - return "" -} - -// getNamespaceKey returns a cache key that is based on the namespace of the event request -func getNamespaceKey(attr admission.Attributes) string { - return attr.GetNamespace() -} - -// getUserKey returns a cache key that is based on the user of the event request -func getUserKey(attr admission.Attributes) string { - userInfo := attr.GetUserInfo() - if userInfo == nil { - return "" - } - return userInfo.GetName() -} - -// getSourceAndObjectKey returns a cache key that is based on the source+object of the event -func getSourceAndObjectKey(attr admission.Attributes) string { - object := attr.GetObject() - if object == nil { - return "" - } - event, ok := object.(*api.Event) - if !ok { - return "" - } - return strings.Join([]string{ - event.Source.Component, - event.Source.Host, - event.InvolvedObject.Kind, - event.InvolvedObject.Namespace, - event.InvolvedObject.Name, - string(event.InvolvedObject.UID), - event.InvolvedObject.APIVersion, - }, "") -} diff --git a/plugin/pkg/admission/exec/BUILD b/plugin/pkg/admission/exec/BUILD deleted file mode 100644 index 4d571efc75..0000000000 --- a/plugin/pkg/admission/exec/BUILD +++ /dev/null @@ -1,48 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/exec", - deps = [ - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", - "//staging/src/k8s.io/client-go/testing:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/exec/admission.go b/plugin/pkg/admission/exec/admission.go deleted file mode 100644 index b6cbf94bc3..0000000000 --- a/plugin/pkg/admission/exec/admission.go +++ /dev/null @@ -1,158 +0,0 @@ -/* -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 exec - -import ( - "fmt" - "io" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apiserver/pkg/admission" - genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - "k8s.io/client-go/kubernetes" -) - -const ( - // DenyEscalatingExec indicates name of admission plugin. - DenyEscalatingExec = "DenyEscalatingExec" - // DenyExecOnPrivileged indicates name of admission plugin. - // Deprecated, should use DenyEscalatingExec instead. - DenyExecOnPrivileged = "DenyExecOnPrivileged" -) - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(DenyEscalatingExec, func(config io.Reader) (admission.Interface, error) { - return NewDenyEscalatingExec(), nil - }) - - // This is for legacy support of the DenyExecOnPrivileged admission controller. Most - // of the time DenyEscalatingExec should be preferred. - plugins.Register(DenyExecOnPrivileged, func(config io.Reader) (admission.Interface, error) { - return NewDenyExecOnPrivileged(), nil - }) -} - -// DenyExec is an implementation of admission.Interface which says no to a pod/exec on -// a pod using host based configurations. -type DenyExec struct { - *admission.Handler - client kubernetes.Interface - - // these flags control which items will be checked to deny exec/attach - hostNetwork bool - hostIPC bool - hostPID bool - privileged bool -} - -var _ admission.ValidationInterface = &DenyExec{} -var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&DenyExec{}) - -// NewDenyExecOnPrivileged creates a new admission controller that is only checking the privileged -// option. This is for legacy support of the DenyExecOnPrivileged admission controller. -// Most of the time NewDenyEscalatingExec should be preferred. -func NewDenyExecOnPrivileged() *DenyExec { - return &DenyExec{ - Handler: admission.NewHandler(admission.Connect), - hostNetwork: false, - hostIPC: false, - hostPID: false, - privileged: true, - } -} - -// NewDenyEscalatingExec creates a new admission controller that denies an exec operation on a pod -// using host based configurations. -func NewDenyEscalatingExec() *DenyExec { - return &DenyExec{ - Handler: admission.NewHandler(admission.Connect), - hostNetwork: true, - hostIPC: true, - hostPID: true, - privileged: true, - } -} - -// SetExternalKubeClientSet implements the WantsInternalKubeClientSet interface. -func (d *DenyExec) SetExternalKubeClientSet(client kubernetes.Interface) { - d.client = client -} - -// ValidateInitialization implements the InitializationValidator interface. -func (d *DenyExec) ValidateInitialization() error { - if d.client == nil { - return fmt.Errorf("missing client") - } - return nil -} - -// Validate makes an admission decision based on the request attributes -func (d *DenyExec) Validate(a admission.Attributes) (err error) { - path := a.GetResource().Resource - if subresource := a.GetSubresource(); subresource != "" { - path = path + "/" + subresource - } - // Only handle exec or attach requests on pods - if path != "pods/exec" && path != "pods/attach" { - return nil - } - pod, err := d.client.CoreV1().Pods(a.GetNamespace()).Get(a.GetName(), metav1.GetOptions{}) - if err != nil { - return admission.NewForbidden(a, err) - } - - if d.hostNetwork && pod.Spec.HostNetwork { - return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host network")) - } - - if d.hostPID && pod.Spec.HostPID { - return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host pid")) - } - - if d.hostIPC && pod.Spec.HostIPC { - return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host ipc")) - } - - if d.privileged && isPrivileged(pod) { - return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a privileged container")) - } - - return nil -} - -// isPrivileged will return true a pod has any privileged containers -func isPrivileged(pod *corev1.Pod) bool { - for _, c := range pod.Spec.InitContainers { - if c.SecurityContext == nil || c.SecurityContext.Privileged == nil { - continue - } - if *c.SecurityContext.Privileged { - return true - } - } - for _, c := range pod.Spec.Containers { - if c.SecurityContext == nil || c.SecurityContext.Privileged == nil { - continue - } - if *c.SecurityContext.Privileged { - return true - } - } - return false -} diff --git a/plugin/pkg/admission/exec/admission_test.go b/plugin/pkg/admission/exec/admission_test.go deleted file mode 100644 index c8d0b665c1..0000000000 --- a/plugin/pkg/admission/exec/admission_test.go +++ /dev/null @@ -1,205 +0,0 @@ -/* -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 exec - -import ( - "testing" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/admission" - "k8s.io/client-go/kubernetes/fake" - core "k8s.io/client-go/testing" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// newAllowEscalatingExec returns `admission.Interface` that allows execution on -// "hostIPC", "hostPID" and "privileged". -func newAllowEscalatingExec() *DenyExec { - return &DenyExec{ - Handler: admission.NewHandler(admission.Connect), - hostIPC: false, - hostPID: false, - privileged: false, - } -} - -func TestAdmission(t *testing.T) { - privPod := validPod("privileged") - priv := true - privPod.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{ - Privileged: &priv, - } - - hostPIDPod := validPod("hostPID") - hostPIDPod.Spec.HostPID = true - - hostIPCPod := validPod("hostIPC") - hostIPCPod.Spec.HostIPC = true - - testCases := map[string]struct { - pod *corev1.Pod - shouldAccept bool - }{ - "priv": { - shouldAccept: false, - pod: privPod, - }, - "hostPID": { - shouldAccept: false, - pod: hostPIDPod, - }, - "hostIPC": { - shouldAccept: false, - pod: hostIPCPod, - }, - "non privileged": { - shouldAccept: true, - pod: validPod("nonPrivileged"), - }, - } - - // Get the direct object though to allow testAdmission to inject the client - handler := NewDenyEscalatingExec() - - for _, tc := range testCases { - testAdmission(t, tc.pod, handler, tc.shouldAccept) - } - - // run with a permissive config and all cases should pass - handler = newAllowEscalatingExec() - - for _, tc := range testCases { - testAdmission(t, tc.pod, handler, true) - } - - // run against an init container - handler = NewDenyEscalatingExec() - - for _, tc := range testCases { - tc.pod.Spec.InitContainers = tc.pod.Spec.Containers - tc.pod.Spec.Containers = nil - testAdmission(t, tc.pod, handler, tc.shouldAccept) - } - - // run with a permissive config and all cases should pass - handler = newAllowEscalatingExec() - - for _, tc := range testCases { - testAdmission(t, tc.pod, handler, true) - } -} - -func testAdmission(t *testing.T, pod *corev1.Pod, handler *DenyExec, shouldAccept bool) { - mockClient := &fake.Clientset{} - mockClient.AddReactor("get", "pods", func(action core.Action) (bool, runtime.Object, error) { - if action.(core.GetAction).GetName() == pod.Name { - return true, pod, nil - } - t.Errorf("Unexpected API call: %#v", action) - return true, nil, nil - }) - - handler.SetExternalKubeClientSet(mockClient) - admission.ValidateInitialization(handler) - - // pods/exec - { - err := handler.Validate(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "exec", admission.Connect, false, nil)) - if shouldAccept && err != nil { - t.Errorf("Unexpected error returned from admission handler: %v", err) - } - if !shouldAccept && err == nil { - t.Errorf("An error was expected from the admission handler. Received nil") - } - } - - // pods/attach - { - err := handler.Validate(admission.NewAttributesRecord(nil, nil, api.Kind("Pod").WithVersion("version"), "test", pod.Name, api.Resource("pods").WithVersion("version"), "attach", admission.Connect, false, nil)) - if shouldAccept && err != nil { - t.Errorf("Unexpected error returned from admission handler: %v", err) - } - if !shouldAccept && err == nil { - t.Errorf("An error was expected from the admission handler. Received nil") - } - } -} - -// Test to ensure legacy admission controller works as expected. -func TestDenyExecOnPrivileged(t *testing.T) { - privPod := validPod("privileged") - priv := true - privPod.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{ - Privileged: &priv, - } - - hostPIDPod := validPod("hostPID") - hostPIDPod.Spec.HostPID = true - - hostIPCPod := validPod("hostIPC") - hostIPCPod.Spec.HostIPC = true - - testCases := map[string]struct { - pod *corev1.Pod - shouldAccept bool - }{ - "priv": { - shouldAccept: false, - pod: privPod, - }, - "hostPID": { - shouldAccept: true, - pod: hostPIDPod, - }, - "hostIPC": { - shouldAccept: true, - pod: hostIPCPod, - }, - "non privileged": { - shouldAccept: true, - pod: validPod("nonPrivileged"), - }, - } - - // Get the direct object though to allow testAdmission to inject the client - handler := NewDenyExecOnPrivileged() - - for _, tc := range testCases { - testAdmission(t, tc.pod, handler, tc.shouldAccept) - } - - // test init containers - for _, tc := range testCases { - tc.pod.Spec.InitContainers = tc.pod.Spec.Containers - tc.pod.Spec.Containers = nil - testAdmission(t, tc.pod, handler, tc.shouldAccept) - } -} - -func validPod(name string) *corev1.Pod { - return &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "test"}, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - {Name: "ctr1", Image: "image"}, - {Name: "ctr2", Image: "image2"}, - }, - }, - } -} diff --git a/plugin/pkg/admission/extendedresourcetoleration/BUILD b/plugin/pkg/admission/extendedresourcetoleration/BUILD deleted file mode 100644 index e2a39bf37a..0000000000 --- a/plugin/pkg/admission/extendedresourcetoleration/BUILD +++ /dev/null @@ -1,41 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/extendedresourcetoleration", - visibility = ["//visibility:public"], - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/apis/core/helper:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/apis/core/helper:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], - visibility = ["//visibility:public"], -) diff --git a/plugin/pkg/admission/extendedresourcetoleration/admission.go b/plugin/pkg/admission/extendedresourcetoleration/admission.go deleted file mode 100644 index 207922a478..0000000000 --- a/plugin/pkg/admission/extendedresourcetoleration/admission.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -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 extendedresourcetoleration - -import ( - "fmt" - "io" - - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apiserver/pkg/admission" - "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/apis/core/helper" -) - -// PluginName indicates name of admission plugin. -const PluginName = "ExtendedResourceToleration" - -// Register is called by the apiserver to register the plugin factory. -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - return newExtendedResourceToleration(), nil - }) -} - -// newExtendedResourceToleration creates a new instance of the ExtendedResourceToleration admission controller. -func newExtendedResourceToleration() *plugin { - return &plugin{ - Handler: admission.NewHandler(admission.Create, admission.Update), - } -} - -// Make sure we are implementing the interface. -var _ admission.MutationInterface = &plugin{} - -type plugin struct { - *admission.Handler -} - -// Admit updates the toleration of a pod based on the resources requested by it. -// If an extended resource of name "example.com/device" is requested, it adds -// a toleration with key "example.com/device", operator "Exists" and effect "NoSchedule". -// The rationale for this is described in: -// https://github.com/kubernetes/kubernetes/issues/55080 -func (p *plugin) Admit(attributes admission.Attributes) error { - // Ignore all calls to subresources or resources other than pods. - if len(attributes.GetSubresource()) != 0 || attributes.GetResource().GroupResource() != core.Resource("pods") { - return nil - } - - pod, ok := attributes.GetObject().(*core.Pod) - if !ok { - return errors.NewBadRequest(fmt.Sprintf("expected *core.Pod but got %T", attributes.GetObject())) - } - - resources := sets.String{} - for _, container := range pod.Spec.Containers { - for resourceName := range container.Resources.Requests { - if helper.IsExtendedResourceName(resourceName) { - resources.Insert(string(resourceName)) - } - } - } - for _, container := range pod.Spec.InitContainers { - for resourceName := range container.Resources.Requests { - if helper.IsExtendedResourceName(resourceName) { - resources.Insert(string(resourceName)) - } - } - } - - // Doing .List() so that we get a stable sorted list. - // This allows us to test adding tolerations for multiple extended resources. - for _, resource := range resources.List() { - helper.AddOrUpdateTolerationInPod(pod, &core.Toleration{ - Key: resource, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }) - } - - return nil -} diff --git a/plugin/pkg/admission/extendedresourcetoleration/admission_test.go b/plugin/pkg/admission/extendedresourcetoleration/admission_test.go deleted file mode 100644 index 7c8f645034..0000000000 --- a/plugin/pkg/admission/extendedresourcetoleration/admission_test.go +++ /dev/null @@ -1,382 +0,0 @@ -/* -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 extendedresourcetoleration - -import ( - "testing" - - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/apiserver/pkg/admission" - "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/apis/core/helper" -) - -func TestAdmit(t *testing.T) { - - plugin := newExtendedResourceToleration() - - containerRequestingCPU := core.Container{ - Resources: core.ResourceRequirements{ - Requests: core.ResourceList{ - core.ResourceCPU: *resource.NewQuantity(2, resource.DecimalSI), - }, - }, - } - - containerRequestingMemory := core.Container{ - Resources: core.ResourceRequirements{ - Requests: core.ResourceList{ - core.ResourceMemory: *resource.NewQuantity(2048, resource.DecimalSI), - }, - }, - } - - extendedResource1 := "example.com/device-ek" - extendedResource2 := "example.com/device-do" - - containerRequestingExtendedResource1 := core.Container{ - Resources: core.ResourceRequirements{ - Requests: core.ResourceList{ - core.ResourceName(extendedResource1): *resource.NewQuantity(1, resource.DecimalSI), - }, - }, - } - containerRequestingExtendedResource2 := core.Container{ - Resources: core.ResourceRequirements{ - Requests: core.ResourceList{ - core.ResourceName(extendedResource2): *resource.NewQuantity(2, resource.DecimalSI), - }, - }, - } - - tests := []struct { - description string - requestedPod core.Pod - expectedPod core.Pod - }{ - { - description: "empty pod without any extended resources, expect no change in tolerations", - requestedPod: core.Pod{ - Spec: core.PodSpec{}, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{}, - }, - }, - { - description: "pod with container without any extended resources, expect no change in tolerations", - requestedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - }, - }, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - }, - }, - }, - }, - { - description: "pod with init container without any extended resources, expect no change in tolerations", - requestedPod: core.Pod{ - Spec: core.PodSpec{ - InitContainers: []core.Container{ - containerRequestingMemory, - }, - }, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{ - InitContainers: []core.Container{ - containerRequestingMemory, - }, - }, - }, - }, - { - description: "pod with container with extended resource, expect toleration to be added", - requestedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingExtendedResource1, - }, - }, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingExtendedResource1, - }, - Tolerations: []core.Toleration{ - { - Key: extendedResource1, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }, - }, - }, - }, - }, - { - description: "pod with init container with extended resource, expect toleration to be added", - requestedPod: core.Pod{ - Spec: core.PodSpec{ - InitContainers: []core.Container{ - containerRequestingExtendedResource2, - }, - }, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{ - InitContainers: []core.Container{ - containerRequestingExtendedResource2, - }, - Tolerations: []core.Toleration{ - { - Key: extendedResource2, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }, - }, - }, - }, - }, - { - description: "pod with existing tolerations and container with extended resource, expect existing tolerations to be preserved and new toleration to be added", - requestedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - containerRequestingExtendedResource1, - }, - Tolerations: []core.Toleration{ - { - Key: "foo", - Operator: core.TolerationOpEqual, - Value: "bar", - Effect: core.TaintEffectNoSchedule, - }, - }, - }, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - containerRequestingExtendedResource1, - }, - Tolerations: []core.Toleration{ - { - Key: "foo", - Operator: core.TolerationOpEqual, - Value: "bar", - Effect: core.TaintEffectNoSchedule, - }, - { - Key: extendedResource1, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }, - }, - }, - }, - }, - { - description: "pod with multiple extended resources, expect multiple tolerations to be added", - requestedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingMemory, - containerRequestingExtendedResource1, - }, - InitContainers: []core.Container{ - containerRequestingCPU, - containerRequestingExtendedResource2, - }, - }, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingMemory, - containerRequestingExtendedResource1, - }, - InitContainers: []core.Container{ - containerRequestingCPU, - containerRequestingExtendedResource2, - }, - Tolerations: []core.Toleration{ - // Note the order, it's sorted by the Key - { - Key: extendedResource2, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }, - { - Key: extendedResource1, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }, - }, - }, - }, - }, - { - description: "pod with container requesting extended resource and existing correct toleration, expect no change in tolerations", - requestedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - containerRequestingMemory, - containerRequestingExtendedResource1, - }, - Tolerations: []core.Toleration{ - { - Key: extendedResource1, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }, - }, - }, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - containerRequestingMemory, - containerRequestingExtendedResource1, - }, - Tolerations: []core.Toleration{ - { - Key: extendedResource1, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }, - }, - }, - }, - }, - { - description: "pod with container requesting extended resource and existing toleration with the same key but different effect and value, expect existing tolerations to be preserved and new toleration to be added", - requestedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - containerRequestingMemory, - containerRequestingExtendedResource1, - }, - Tolerations: []core.Toleration{ - { - Key: extendedResource1, - Operator: core.TolerationOpEqual, - Value: "foo", - Effect: core.TaintEffectNoExecute, - }, - }, - }, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - containerRequestingMemory, - containerRequestingExtendedResource1, - }, - Tolerations: []core.Toleration{ - { - Key: extendedResource1, - Operator: core.TolerationOpEqual, - Value: "foo", - Effect: core.TaintEffectNoExecute, - }, - { - Key: extendedResource1, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }, - }, - }, - }, - }, - { - description: "pod with wildcard toleration and container requesting extended resource, expect existing tolerations to be preserved and new toleration to be added", - requestedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - containerRequestingMemory, - containerRequestingExtendedResource1, - }, - Tolerations: []core.Toleration{ - { - Operator: core.TolerationOpExists, - }, - }, - }, - }, - expectedPod: core.Pod{ - Spec: core.PodSpec{ - Containers: []core.Container{ - containerRequestingCPU, - containerRequestingMemory, - containerRequestingExtendedResource1, - }, - Tolerations: []core.Toleration{ - { - Operator: core.TolerationOpExists, - }, - { - Key: extendedResource1, - Operator: core.TolerationOpExists, - Effect: core.TaintEffectNoSchedule, - }, - }, - }, - }, - }, - } - for i, test := range tests { - err := plugin.Admit(admission.NewAttributesRecord(&test.requestedPod, nil, core.Kind("Pod").WithVersion("version"), "foo", "name", core.Resource("pods").WithVersion("version"), "", "ignored", false, nil)) - if err != nil { - t.Errorf("[%d: %s] unexpected error %v for pod %+v", i, test.description, err, test.requestedPod) - } - - if !helper.Semantic.DeepEqual(test.expectedPod.Spec.Tolerations, test.requestedPod.Spec.Tolerations) { - t.Errorf("[%d: %s] expected %#v got %#v", i, test.description, test.expectedPod.Spec.Tolerations, test.requestedPod.Spec.Tolerations) - } - } -} - -func TestHandles(t *testing.T) { - plugin := newExtendedResourceToleration() - tests := map[admission.Operation]bool{ - admission.Create: true, - admission.Update: true, - admission.Delete: false, - admission.Connect: false, - } - for op, expected := range tests { - result := plugin.Handles(op) - if result != expected { - t.Errorf("Unexpected result for operation %s: %v\n", op, result) - } - } -} diff --git a/plugin/pkg/admission/gc/BUILD b/plugin/pkg/admission/gc/BUILD deleted file mode 100644 index 354e386505..0000000000 --- a/plugin/pkg/admission/gc/BUILD +++ /dev/null @@ -1,58 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["gc_admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/gc", - deps = [ - "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["gc_admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/kubeapiserver/admission:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/api/extensions/v1beta1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", - "//staging/src/k8s.io/client-go/discovery/fake:go_default_library", - "//staging/src/k8s.io/client-go/restmapper:go_default_library", - "//staging/src/k8s.io/client-go/testing:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/gc/gc_admission.go b/plugin/pkg/admission/gc/gc_admission.go deleted file mode 100644 index 89122da5a6..0000000000 --- a/plugin/pkg/admission/gc/gc_admission.go +++ /dev/null @@ -1,284 +0,0 @@ -/* -Copyright 2016 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 gc - -import ( - "fmt" - "io" - - apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apiserver/pkg/admission" - "k8s.io/apiserver/pkg/authorization/authorizer" -) - -// PluginName indicates name of admission plugin. -const PluginName = "OwnerReferencesPermissionEnforcement" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - // the pods/status endpoint is ignored by this plugin since old kubelets - // corrupt them. the pod status strategy ensures status updates cannot mutate - // ownerRef. - whiteList := []whiteListItem{ - { - groupResource: schema.GroupResource{Resource: "pods"}, - subresource: "status", - }, - } - return &gcPermissionsEnforcement{ - Handler: admission.NewHandler(admission.Create, admission.Update), - whiteList: whiteList, - }, nil - }) -} - -// gcPermissionsEnforcement is an implementation of admission.Interface. -type gcPermissionsEnforcement struct { - *admission.Handler - - authorizer authorizer.Authorizer - - restMapper meta.RESTMapper - - // items in this whitelist are ignored upon admission. - // any item in this list must protect against ownerRef mutations - // via strategy enforcement. - whiteList []whiteListItem -} - -var _ admission.ValidationInterface = &gcPermissionsEnforcement{} - -// whiteListItem describes an entry in a whitelist ignored by gc permission enforcement. -type whiteListItem struct { - groupResource schema.GroupResource - subresource string -} - -// isWhiteListed returns true if the specified item is in the whitelist. -func (a *gcPermissionsEnforcement) isWhiteListed(groupResource schema.GroupResource, subresource string) bool { - for _, item := range a.whiteList { - if item.groupResource == groupResource && item.subresource == subresource { - return true - } - } - return false -} - -func (a *gcPermissionsEnforcement) Validate(attributes admission.Attributes) (err error) { - // // if the request is in the whitelist, we skip mutation checks for this resource. - if a.isWhiteListed(attributes.GetResource().GroupResource(), attributes.GetSubresource()) { - return nil - } - - // if we aren't changing owner references, then the edit is always allowed - if !isChangingOwnerReference(attributes.GetObject(), attributes.GetOldObject()) { - return nil - } - - // if you are creating a thing, you should always be allowed to set an owner ref since you logically had the power - // to never create it. We still need to check block owner deletion below, because the power to delete does not - // imply the power to prevent deletion on other resources. - if attributes.GetOperation() != admission.Create { - deleteAttributes := authorizer.AttributesRecord{ - User: attributes.GetUserInfo(), - Verb: "delete", - Namespace: attributes.GetNamespace(), - APIGroup: attributes.GetResource().Group, - APIVersion: attributes.GetResource().Version, - Resource: attributes.GetResource().Resource, - Subresource: attributes.GetSubresource(), - Name: attributes.GetName(), - ResourceRequest: true, - Path: "", - } - decision, reason, err := a.authorizer.Authorize(deleteAttributes) - if decision != authorizer.DecisionAllow { - return admission.NewForbidden(attributes, fmt.Errorf("cannot set an ownerRef on a resource you can't delete: %v, %v", reason, err)) - } - } - - // Further check if the user is setting ownerReference.blockOwnerDeletion to - // true. If so, only allows the change if the user has delete permission of - // the _OWNER_ - newBlockingRefs := newBlockingOwnerDeletionRefs(attributes.GetObject(), attributes.GetOldObject()) - for _, ref := range newBlockingRefs { - records, err := a.ownerRefToDeleteAttributeRecords(ref, attributes) - if err != nil { - return admission.NewForbidden(attributes, fmt.Errorf("cannot set blockOwnerDeletion in this case because cannot find RESTMapping for APIVersion %s Kind %s: %v", ref.APIVersion, ref.Kind, err)) - } - // Multiple records are returned if ref.Kind could map to multiple - // resources. User needs to have delete permission on all the - // matched Resources. - for _, record := range records { - decision, reason, err := a.authorizer.Authorize(record) - if decision != authorizer.DecisionAllow { - return admission.NewForbidden(attributes, fmt.Errorf("cannot set blockOwnerDeletion if an ownerReference refers to a resource you can't set finalizers on: %v, %v", reason, err)) - } - } - } - - return nil - -} - -func isChangingOwnerReference(newObj, oldObj runtime.Object) bool { - newMeta, err := meta.Accessor(newObj) - if err != nil { - // if we don't have objectmeta, we don't have the object reference - return false - } - - if oldObj == nil { - return len(newMeta.GetOwnerReferences()) > 0 - } - oldMeta, err := meta.Accessor(oldObj) - if err != nil { - // if we don't have objectmeta, we don't have the object reference - return false - } - - // compare the old and new. If they aren't the same, then we're trying to change an ownerRef - oldOwners := oldMeta.GetOwnerReferences() - newOwners := newMeta.GetOwnerReferences() - if len(oldOwners) != len(newOwners) { - return true - } - for i := range oldOwners { - if !apiequality.Semantic.DeepEqual(oldOwners[i], newOwners[i]) { - return true - } - } - - return false -} - -// Translates ref to a DeleteAttribute deleting the object referred by the ref. -// OwnerReference only records the object kind, which might map to multiple -// resources, so multiple DeleteAttribute might be returned. -func (a *gcPermissionsEnforcement) ownerRefToDeleteAttributeRecords(ref metav1.OwnerReference, attributes admission.Attributes) ([]authorizer.AttributesRecord, error) { - var ret []authorizer.AttributesRecord - groupVersion, err := schema.ParseGroupVersion(ref.APIVersion) - if err != nil { - return ret, err - } - mappings, err := a.restMapper.RESTMappings(schema.GroupKind{Group: groupVersion.Group, Kind: ref.Kind}, groupVersion.Version) - if err != nil { - return ret, err - } - for _, mapping := range mappings { - ar := authorizer.AttributesRecord{ - User: attributes.GetUserInfo(), - Verb: "update", - APIGroup: mapping.Resource.Group, - APIVersion: mapping.Resource.Version, - Resource: mapping.Resource.Resource, - Subresource: "finalizers", - Name: ref.Name, - ResourceRequest: true, - Path: "", - } - if mapping.Scope.Name() == meta.RESTScopeNameNamespace { - // if the owner is namespaced, it must be in the same namespace as the dependent is. - ar.Namespace = attributes.GetNamespace() - } - ret = append(ret, ar) - } - return ret, nil -} - -// only keeps the blocking refs -func blockingOwnerRefs(refs []metav1.OwnerReference) []metav1.OwnerReference { - var ret []metav1.OwnerReference - for _, ref := range refs { - if ref.BlockOwnerDeletion != nil && *ref.BlockOwnerDeletion == true { - ret = append(ret, ref) - } - } - return ret -} - -func indexByUID(refs []metav1.OwnerReference) map[types.UID]metav1.OwnerReference { - ret := make(map[types.UID]metav1.OwnerReference) - for _, ref := range refs { - ret[ref.UID] = ref - } - return ret -} - -// Returns new blocking ownerReferences, and references whose blockOwnerDeletion -// field is changed from nil or false to true. -func newBlockingOwnerDeletionRefs(newObj, oldObj runtime.Object) []metav1.OwnerReference { - newMeta, err := meta.Accessor(newObj) - if err != nil { - // if we don't have objectmeta, we don't have the object reference - return nil - } - newRefs := newMeta.GetOwnerReferences() - blockingNewRefs := blockingOwnerRefs(newRefs) - if len(blockingNewRefs) == 0 { - return nil - } - - if oldObj == nil { - return blockingNewRefs - } - oldMeta, err := meta.Accessor(oldObj) - if err != nil { - // if we don't have objectmeta, treat it as if all the ownerReference are newly created - return blockingNewRefs - } - - var ret []metav1.OwnerReference - indexedOldRefs := indexByUID(oldMeta.GetOwnerReferences()) - for _, ref := range blockingNewRefs { - oldRef, ok := indexedOldRefs[ref.UID] - if !ok { - // if ref is newly added, and it's blocking, then returns it. - ret = append(ret, ref) - continue - } - wasNotBlocking := oldRef.BlockOwnerDeletion == nil || *oldRef.BlockOwnerDeletion == false - if wasNotBlocking { - ret = append(ret, ref) - } - } - return ret -} - -func (a *gcPermissionsEnforcement) SetAuthorizer(authorizer authorizer.Authorizer) { - a.authorizer = authorizer -} - -func (a *gcPermissionsEnforcement) SetRESTMapper(restMapper meta.RESTMapper) { - a.restMapper = restMapper -} - -func (a *gcPermissionsEnforcement) ValidateInitialization() error { - if a.authorizer == nil { - return fmt.Errorf("missing authorizer") - } - if a.restMapper == nil { - return fmt.Errorf("missing restMapper") - } - return nil -} diff --git a/plugin/pkg/admission/gc/gc_admission_test.go b/plugin/pkg/admission/gc/gc_admission_test.go deleted file mode 100644 index 034e5179e4..0000000000 --- a/plugin/pkg/admission/gc/gc_admission_test.go +++ /dev/null @@ -1,619 +0,0 @@ -/* -Copyright 2016 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 gc - -import ( - "fmt" - "strings" - "testing" - - corev1 "k8s.io/api/core/v1" - extensionv1beta1 "k8s.io/api/extensions/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apiserver/pkg/admission" - "k8s.io/apiserver/pkg/admission/initializer" - "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/apiserver/pkg/authorization/authorizer" - fakediscovery "k8s.io/client-go/discovery/fake" - "k8s.io/client-go/restmapper" - coretesting "k8s.io/client-go/testing" - api "k8s.io/kubernetes/pkg/apis/core" - kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" -) - -type fakeAuthorizer struct{} - -func (fakeAuthorizer) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) { - username := a.GetUser().GetName() - - if username == "non-deleter" { - if a.GetVerb() == "delete" { - return authorizer.DecisionNoOpinion, "", nil - } - if a.GetVerb() == "update" && a.GetSubresource() == "finalizers" { - return authorizer.DecisionNoOpinion, "", nil - } - return authorizer.DecisionAllow, "", nil - } - - if username == "non-pod-deleter" { - if a.GetVerb() == "delete" && a.GetResource() == "pods" { - return authorizer.DecisionNoOpinion, "", nil - } - if a.GetVerb() == "update" && a.GetResource() == "pods" && a.GetSubresource() == "finalizers" { - return authorizer.DecisionNoOpinion, "", nil - } - return authorizer.DecisionAllow, "", nil - } - - if username == "non-rc-deleter" { - if a.GetVerb() == "delete" && a.GetResource() == "replicationcontrollers" { - return authorizer.DecisionNoOpinion, "", nil - } - if a.GetVerb() == "update" && a.GetResource() == "replicationcontrollers" && a.GetSubresource() == "finalizers" { - return authorizer.DecisionNoOpinion, "", nil - } - return authorizer.DecisionAllow, "", nil - } - - if username == "non-node-deleter" { - if a.GetVerb() == "delete" && a.GetResource() == "nodes" { - return authorizer.DecisionNoOpinion, "", nil - } - if a.GetVerb() == "update" && a.GetResource() == "nodes" && a.GetSubresource() == "finalizers" { - return authorizer.DecisionNoOpinion, "", nil - } - return authorizer.DecisionAllow, "", nil - } - return authorizer.DecisionAllow, "", nil -} - -// newGCPermissionsEnforcement returns the admission controller configured for testing. -func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) { - // the pods/status endpoint is ignored by this plugin since old kubelets - // corrupt them. the pod status strategy ensures status updates cannot mutate - // ownerRef. - whiteList := []whiteListItem{ - { - groupResource: schema.GroupResource{Resource: "pods"}, - subresource: "status", - }, - } - gcAdmit := &gcPermissionsEnforcement{ - Handler: admission.NewHandler(admission.Create, admission.Update), - whiteList: whiteList, - } - - genericPluginInitializer := initializer.New(nil, nil, fakeAuthorizer{}, nil) - fakeDiscoveryClient := &fakediscovery.FakeDiscovery{Fake: &coretesting.Fake{}} - fakeDiscoveryClient.Resources = []*metav1.APIResourceList{ - { - GroupVersion: corev1.SchemeGroupVersion.String(), - APIResources: []metav1.APIResource{ - {Name: "nodes", Namespaced: false, Kind: "Node"}, - {Name: "pods", Namespaced: true, Kind: "Pod"}, - {Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"}, - }, - }, - { - GroupVersion: extensionv1beta1.SchemeGroupVersion.String(), - APIResources: []metav1.APIResource{ - {Name: "daemonsets", Namespaced: true, Kind: "DaemonSet"}, - }, - }, - } - - restMapperRes, err := restmapper.GetAPIGroupResources(fakeDiscoveryClient) - if err != nil { - return nil, fmt.Errorf("unexpected error while constructing resource list from fake discovery client: %v", err) - } - restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes) - pluginInitializer := kubeadmission.NewPluginInitializer(nil, restMapper, nil) - initializersChain := admission.PluginInitializers{} - initializersChain = append(initializersChain, genericPluginInitializer) - initializersChain = append(initializersChain, pluginInitializer) - - initializersChain.Initialize(gcAdmit) - return gcAdmit, nil -} - -func TestGCAdmission(t *testing.T) { - expectNoError := func(err error) bool { - return err == nil - } - expectCantSetOwnerRefError := func(err error) bool { - if err == nil { - return false - } - return strings.Contains(err.Error(), "cannot set an ownerRef on a resource you can't delete") - } - tests := []struct { - name string - username string - resource schema.GroupVersionResource - subresource string - oldObj runtime.Object - newObj runtime.Object - - checkError func(error) bool - }{ - { - name: "super-user, create, no objectref change", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: &api.Pod{}, - checkError: expectNoError, - }, - { - name: "super-user, create, objectref change", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectNoError, - }, - { - name: "non-deleter, create, no objectref change", - username: "non-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: &api.Pod{}, - checkError: expectNoError, - }, - { - name: "non-deleter, create, objectref change", - username: "non-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectNoError, - }, - { - name: "non-pod-deleter, create, no objectref change", - username: "non-pod-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: &api.Pod{}, - checkError: expectNoError, - }, - { - name: "non-pod-deleter, create, objectref change", - username: "non-pod-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectNoError, - }, - { - name: "non-pod-deleter, create, objectref change, but not a pod", - username: "non-pod-deleter", - resource: api.SchemeGroupVersion.WithResource("not-pods"), - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectNoError, - }, - - { - name: "super-user, update, no objectref change", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: &api.Pod{}, - newObj: &api.Pod{}, - checkError: expectNoError, - }, - { - name: "super-user, update, no objectref change two", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectNoError, - }, - { - name: "super-user, update, objectref change", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: &api.Pod{}, - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectNoError, - }, - { - name: "non-deleter, update, no objectref change", - username: "non-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: &api.Pod{}, - newObj: &api.Pod{}, - checkError: expectNoError, - }, - { - name: "non-deleter, update, no objectref change two", - username: "non-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectNoError, - }, - { - name: "non-deleter, update, objectref change", - username: "non-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: &api.Pod{}, - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectCantSetOwnerRefError, - }, - { - name: "non-deleter, update, objectref change two", - username: "non-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}, {Name: "second"}}}}, - checkError: expectCantSetOwnerRefError, - }, - { - name: "non-pod-deleter, update, no objectref change", - username: "non-pod-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: &api.Pod{}, - newObj: &api.Pod{}, - checkError: expectNoError, - }, - { - name: "non-pod-deleter, update status, objectref change", - username: "non-pod-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - subresource: "status", - oldObj: &api.Pod{}, - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectNoError, - }, - { - name: "non-pod-deleter, update, objectref change", - username: "non-pod-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: &api.Pod{}, - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectCantSetOwnerRefError, - }, - { - name: "non-pod-deleter, update, objectref change, but not a pod", - username: "non-pod-deleter", - resource: api.SchemeGroupVersion.WithResource("not-pods"), - oldObj: &api.Pod{}, - newObj: &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}}, - checkError: expectNoError, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - gcAdmit, err := newGCPermissionsEnforcement() - if err != nil { - t.Error(err) - } - - operation := admission.Create - if tc.oldObj != nil { - operation = admission.Update - } - user := &user.DefaultInfo{Name: tc.username} - attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, false, user) - - err = gcAdmit.Validate(attributes) - if !tc.checkError(err) { - t.Errorf("unexpected err: %v", err) - } - }) - } -} - -func TestBlockOwnerDeletionAdmission(t *testing.T) { - podWithOwnerRefs := func(refs ...metav1.OwnerReference) *api.Pod { - var refSlice []metav1.OwnerReference - refSlice = append(refSlice, refs...) - - return &api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - OwnerReferences: refSlice, - }, - } - } - - getTrueVar := func() *bool { - ret := true - return &ret - } - - getFalseVar := func() *bool { - ret := false - return &ret - } - blockRC1 := metav1.OwnerReference{ - APIVersion: "v1", - Kind: "ReplicationController", - Name: "rc1", - BlockOwnerDeletion: getTrueVar(), - } - blockRC2 := metav1.OwnerReference{ - APIVersion: "v1", - Kind: "ReplicationController", - Name: "rc2", - BlockOwnerDeletion: getTrueVar(), - } - notBlockRC1 := metav1.OwnerReference{ - APIVersion: "v1", - Kind: "ReplicationController", - Name: "rc1", - BlockOwnerDeletion: getFalseVar(), - } - notBlockRC2 := metav1.OwnerReference{ - APIVersion: "v1", - Kind: "ReplicationController", - Name: "rc2", - BlockOwnerDeletion: getFalseVar(), - } - nilBlockRC1 := metav1.OwnerReference{ - APIVersion: "v1", - Kind: "ReplicationController", - Name: "rc1", - } - nilBlockRC2 := metav1.OwnerReference{ - APIVersion: "v1", - Kind: "ReplicationController", - Name: "rc2", - } - blockDS1 := metav1.OwnerReference{ - APIVersion: "extensions/v1beta1", - Kind: "DaemonSet", - Name: "ds1", - BlockOwnerDeletion: getTrueVar(), - } - notBlockDS1 := metav1.OwnerReference{ - APIVersion: "extensions/v1beta1", - Kind: "DaemonSet", - Name: "ds1", - BlockOwnerDeletion: getFalseVar(), - } - blockNode := metav1.OwnerReference{ - APIVersion: "v1", - Kind: "Node", - Name: "node1", - BlockOwnerDeletion: getTrueVar(), - } - notBlockNode := metav1.OwnerReference{ - APIVersion: "v1", - Kind: "Node", - Name: "node", - BlockOwnerDeletion: getFalseVar(), - } - nilBlockNode := metav1.OwnerReference{ - APIVersion: "v1", - Kind: "Node", - Name: "node", - } - - expectNoError := func(err error) bool { - return err == nil - } - expectCantSetBlockOwnerDeletionError := func(err error) bool { - if err == nil { - return false - } - return strings.Contains(err.Error(), "cannot set blockOwnerDeletion if an ownerReference refers to a resource you can't set finalizers on") - } - tests := []struct { - name string - username string - resource schema.GroupVersionResource - subresource string - oldObj runtime.Object - newObj runtime.Object - - checkError func(error) bool - }{ - // cases for create - { - name: "super-user, create, no ownerReferences", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: podWithOwnerRefs(), - checkError: expectNoError, - }, - { - name: "super-user, create, all ownerReferences have blockOwnerDeletion=false", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: podWithOwnerRefs(notBlockRC1, notBlockRC2), - checkError: expectNoError, - }, - { - name: "super-user, create, some ownerReferences have blockOwnerDeletion=true", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: podWithOwnerRefs(blockRC1, blockRC2, blockNode), - checkError: expectNoError, - }, - { - name: "non-rc-deleter, create, no ownerReferences", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: podWithOwnerRefs(), - checkError: expectNoError, - }, - { - name: "non-rc-deleter, create, all ownerReferences have blockOwnerDeletion=false or nil", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: podWithOwnerRefs(notBlockRC1, nilBlockRC2), - checkError: expectNoError, - }, - { - name: "non-node-deleter, create, all ownerReferences have blockOwnerDeletion=false", - username: "non-node-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: podWithOwnerRefs(notBlockNode), - checkError: expectNoError, - }, - { - name: "non-rc-deleter, create, some ownerReferences have blockOwnerDeletion=true", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: podWithOwnerRefs(blockRC1, notBlockRC2), - checkError: expectCantSetBlockOwnerDeletionError, - }, - { - name: "non-rc-deleter, create, some ownerReferences have blockOwnerDeletion=true, but are pointing to daemonset", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: podWithOwnerRefs(blockDS1), - checkError: expectNoError, - }, - { - name: "non-node-deleter, create, some ownerReferences have blockOwnerDeletion=true", - username: "non-node-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - newObj: podWithOwnerRefs(blockNode), - checkError: expectCantSetBlockOwnerDeletionError, - }, - // cases are for update - { - name: "super-user, update, no ownerReferences change blockOwnerDeletion", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(nilBlockRC1, nilBlockNode), - newObj: podWithOwnerRefs(notBlockRC1, notBlockNode), - checkError: expectNoError, - }, - { - name: "super-user, update, some ownerReferences change to blockOwnerDeletion=true", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(notBlockRC1, notBlockNode), - newObj: podWithOwnerRefs(blockRC1, blockNode), - checkError: expectNoError, - }, - { - name: "super-user, update, add new ownerReferences with blockOwnerDeletion=true", - username: "super", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(), - newObj: podWithOwnerRefs(blockRC1, blockNode), - checkError: expectNoError, - }, - { - name: "non-rc-deleter, update, no ownerReferences change blockOwnerDeletion", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(nilBlockRC1), - newObj: podWithOwnerRefs(notBlockRC1), - checkError: expectNoError, - }, - { - name: "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=false to true", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(notBlockRC1), - newObj: podWithOwnerRefs(blockRC1), - checkError: expectCantSetBlockOwnerDeletionError, - }, - { - name: "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=nil to true", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(nilBlockRC1), - newObj: podWithOwnerRefs(blockRC1), - checkError: expectCantSetBlockOwnerDeletionError, - }, - { - name: "non-node-deleter, update, some ownerReferences change from blockOwnerDeletion=nil to true", - username: "non-node-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(nilBlockNode), - newObj: podWithOwnerRefs(blockNode), - checkError: expectCantSetBlockOwnerDeletionError, - }, - { - name: "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=true to false", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(blockRC1), - newObj: podWithOwnerRefs(notBlockRC1), - checkError: expectNoError, - }, - { - name: "non-node-deleter, update, some ownerReferences change from blockOwnerDeletion=true to false", - username: "non-node-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(blockNode), - newObj: podWithOwnerRefs(notBlockNode), - checkError: expectNoError, - }, - { - name: "non-rc-deleter, update, some ownerReferences change blockOwnerDeletion, but all such references are to daemonset", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(notBlockDS1), - newObj: podWithOwnerRefs(blockDS1), - checkError: expectNoError, - }, - { - name: "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=nil or false", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(), - newObj: podWithOwnerRefs(notBlockRC1, nilBlockRC2), - checkError: expectNoError, - }, - { - name: "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=true", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(), - newObj: podWithOwnerRefs(blockRC1), - checkError: expectCantSetBlockOwnerDeletionError, - }, - { - name: "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=true, but the references are to daemonset", - username: "non-rc-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(), - newObj: podWithOwnerRefs(blockDS1), - checkError: expectNoError, - }, - { - name: "non-node-deleter, update, add ownerReferences with blockOwnerDeletion=true", - username: "non-node-deleter", - resource: api.SchemeGroupVersion.WithResource("pods"), - oldObj: podWithOwnerRefs(), - newObj: podWithOwnerRefs(blockNode), - checkError: expectCantSetBlockOwnerDeletionError, - }, - } - gcAdmit, err := newGCPermissionsEnforcement() - if err != nil { - t.Error(err) - } - - for _, tc := range tests { - operation := admission.Create - if tc.oldObj != nil { - operation = admission.Update - } - user := &user.DefaultInfo{Name: tc.username} - attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, false, user) - - err := gcAdmit.Validate(attributes) - if !tc.checkError(err) { - t.Errorf("%v: unexpected err: %v", tc.name, err) - } - } -} diff --git a/plugin/pkg/admission/namespace/autoprovision/BUILD b/plugin/pkg/admission/namespace/autoprovision/BUILD deleted file mode 100644 index bf14b7465c..0000000000 --- a/plugin/pkg/admission/namespace/autoprovision/BUILD +++ /dev/null @@ -1,57 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision", - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/listers/core/v1:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", - "//staging/src/k8s.io/client-go/testing:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/namespace/autoprovision/admission.go b/plugin/pkg/admission/namespace/autoprovision/admission.go deleted file mode 100644 index 07bb104fcc..0000000000 --- a/plugin/pkg/admission/namespace/autoprovision/admission.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -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 autoprovision - -import ( - "fmt" - "io" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apiserver/pkg/admission" - genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - corev1listers "k8s.io/client-go/listers/core/v1" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// PluginName indicates name of admission plugin. -const PluginName = "NamespaceAutoProvision" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - return NewProvision(), nil - }) -} - -// Provision is an implementation of admission.Interface. -// It looks at all incoming requests in a namespace context, and if the namespace does not exist, it creates one. -// It is useful in deployments that do not want to restrict creation of a namespace prior to its usage. -type Provision struct { - *admission.Handler - client kubernetes.Interface - namespaceLister corev1listers.NamespaceLister -} - -var _ admission.MutationInterface = &Provision{} -var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&Provision{}) -var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&Provision{}) - -// Admit makes an admission decision based on the request attributes -func (p *Provision) Admit(a admission.Attributes) error { - // Don't create a namespace if the request is for a dry-run. - if a.IsDryRun() { - return nil - } - - // if we're here, then we've already passed authentication, so we're allowed to do what we're trying to do - // if we're here, then the API server has found a route, which means that if we have a non-empty namespace - // its a namespaced resource. - if len(a.GetNamespace()) == 0 || a.GetKind().GroupKind() == api.Kind("Namespace") { - return nil - } - // we need to wait for our caches to warm - if !p.WaitForReady() { - return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) - } - - _, err := p.namespaceLister.Get(a.GetNamespace()) - if err == nil { - return nil - } - - if !errors.IsNotFound(err) { - return admission.NewForbidden(a, err) - } - - namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: a.GetNamespace(), - Namespace: "", - }, - Status: corev1.NamespaceStatus{}, - } - - _, err = p.client.Core().Namespaces().Create(namespace) - if err != nil && !errors.IsAlreadyExists(err) { - return admission.NewForbidden(a, err) - } - - return nil -} - -// NewProvision creates a new namespace provision admission control handler -func NewProvision() *Provision { - return &Provision{ - Handler: admission.NewHandler(admission.Create), - } -} - -// SetExternalKubeClientSet implements the WantsExternalKubeClientSet interface. -func (p *Provision) SetExternalKubeClientSet(client kubernetes.Interface) { - p.client = client -} - -// SetExternalKubeInformerFactory implements the WantsExternalKubeInformerFactory interface. -func (p *Provision) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { - namespaceInformer := f.Core().V1().Namespaces() - p.namespaceLister = namespaceInformer.Lister() - p.SetReadyFunc(namespaceInformer.Informer().HasSynced) -} - -// ValidateInitialization implements the InitializationValidator interface. -func (p *Provision) ValidateInitialization() error { - if p.namespaceLister == nil { - return fmt.Errorf("missing namespaceLister") - } - if p.client == nil { - return fmt.Errorf("missing client") - } - return nil -} diff --git a/plugin/pkg/admission/namespace/autoprovision/admission_test.go b/plugin/pkg/admission/namespace/autoprovision/admission_test.go deleted file mode 100644 index 8e5e0dca5f..0000000000 --- a/plugin/pkg/admission/namespace/autoprovision/admission_test.go +++ /dev/null @@ -1,193 +0,0 @@ -/* -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 autoprovision - -import ( - "fmt" - "testing" - "time" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/apiserver/pkg/admission" - genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - "k8s.io/client-go/informers" - clientset "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - core "k8s.io/client-go/testing" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// newHandlerForTest returns the admission controller configured for testing. -func newHandlerForTest(c clientset.Interface) (admission.MutationInterface, informers.SharedInformerFactory, error) { - f := informers.NewSharedInformerFactory(c, 5*time.Minute) - handler := NewProvision() - pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil) - pluginInitializer.Initialize(handler) - err := admission.ValidateInitialization(handler) - return handler, f, err -} - -// newMockClientForTest creates a mock client that returns a client configured for the specified list of namespaces. -func newMockClientForTest(namespaces []string) *fake.Clientset { - mockClient := &fake.Clientset{} - mockClient.AddReactor("list", "namespaces", func(action core.Action) (bool, runtime.Object, error) { - namespaceList := &corev1.NamespaceList{ - ListMeta: metav1.ListMeta{ - ResourceVersion: fmt.Sprintf("%d", len(namespaces)), - }, - } - for i, ns := range namespaces { - namespaceList.Items = append(namespaceList.Items, corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: ns, - ResourceVersion: fmt.Sprintf("%d", i), - }, - }) - } - return true, namespaceList, nil - }) - return mockClient -} - -// newPod returns a new pod for the specified namespace -func newPod(namespace string) api.Pod { - return api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: namespace}, - Spec: api.PodSpec{ - Volumes: []api.Volume{{Name: "vol"}}, - Containers: []api.Container{{Name: "ctr", Image: "image"}}, - }, - } -} - -// hasCreateNamespaceAction returns true if it has the create namespace action -func hasCreateNamespaceAction(mockClient *fake.Clientset) bool { - for _, action := range mockClient.Actions() { - if action.GetVerb() == "create" && action.GetResource().Resource == "namespaces" { - return true - } - } - return false -} - -// TestAdmission verifies a namespace is created on create requests for namespace managed resources -func TestAdmission(t *testing.T) { - namespace := "test" - mockClient := newMockClientForTest([]string{}) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - informerFactory.Start(wait.NeverStop) - - pod := newPod(namespace) - err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if err != nil { - t.Errorf("unexpected error returned from admission handler") - } - if !hasCreateNamespaceAction(mockClient) { - t.Errorf("expected create namespace action") - } -} - -// TestAdmissionNamespaceExists verifies that no client call is made when a namespace already exists -func TestAdmissionNamespaceExists(t *testing.T) { - namespace := "test" - mockClient := newMockClientForTest([]string{namespace}) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - informerFactory.Start(wait.NeverStop) - - pod := newPod(namespace) - err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if err != nil { - t.Errorf("unexpected error returned from admission handler") - } - if hasCreateNamespaceAction(mockClient) { - t.Errorf("unexpected create namespace action") - } -} - -// TestAdmissionDryRun verifies that no client call is made on a dry run request -func TestAdmissionDryRun(t *testing.T) { - namespace := "test" - mockClient := newMockClientForTest([]string{}) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - informerFactory.Start(wait.NeverStop) - - pod := newPod(namespace) - err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, true, nil)) - if err != nil { - t.Errorf("unexpected error returned from admission handler") - } - if hasCreateNamespaceAction(mockClient) { - t.Errorf("unexpected create namespace action") - } -} - -// TestIgnoreAdmission validates that a request is ignored if its not a create -func TestIgnoreAdmission(t *testing.T) { - namespace := "test" - mockClient := newMockClientForTest([]string{}) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - informerFactory.Start(wait.NeverStop) - chainHandler := admission.NewChainHandler(handler) - - pod := newPod(namespace) - err = chainHandler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil)) - if err != nil { - t.Errorf("unexpected error returned from admission handler") - } - if hasCreateNamespaceAction(mockClient) { - t.Errorf("unexpected create namespace action") - } -} - -func TestAdmissionWithLatentCache(t *testing.T) { - namespace := "test" - mockClient := newMockClientForTest([]string{}) - mockClient.AddReactor("create", "namespaces", func(action core.Action) (bool, runtime.Object, error) { - return true, nil, errors.NewAlreadyExists(api.Resource("namespaces"), namespace) - }) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - informerFactory.Start(wait.NeverStop) - - pod := newPod(namespace) - err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if err != nil { - t.Errorf("unexpected error returned from admission handler") - } - - if !hasCreateNamespaceAction(mockClient) { - t.Errorf("expected create namespace action") - } -} diff --git a/plugin/pkg/admission/namespace/exists/BUILD b/plugin/pkg/admission/namespace/exists/BUILD deleted file mode 100644 index cb572441cc..0000000000 --- a/plugin/pkg/admission/namespace/exists/BUILD +++ /dev/null @@ -1,55 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/namespace/exists", - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/listers/core/v1:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", - "//staging/src/k8s.io/client-go/testing:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/namespace/exists/admission.go b/plugin/pkg/admission/namespace/exists/admission.go deleted file mode 100644 index 40f76a4733..0000000000 --- a/plugin/pkg/admission/namespace/exists/admission.go +++ /dev/null @@ -1,117 +0,0 @@ -/* -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 exists - -import ( - "fmt" - "io" - - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apiserver/pkg/admission" - genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - informers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - corev1listers "k8s.io/client-go/listers/core/v1" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// PluginName indicates name of admission plugin. -const PluginName = "NamespaceExists" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - return NewExists(), nil - }) -} - -// Exists is an implementation of admission.Interface. -// It rejects all incoming requests in a namespace context if the namespace does not exist. -// It is useful in deployments that want to enforce pre-declaration of a Namespace resource. -type Exists struct { - *admission.Handler - client kubernetes.Interface - namespaceLister corev1listers.NamespaceLister -} - -var _ admission.ValidationInterface = &Exists{} -var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&Exists{}) -var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&Exists{}) - -// Validate makes an admission decision based on the request attributes -func (e *Exists) Validate(a admission.Attributes) error { - // if we're here, then we've already passed authentication, so we're allowed to do what we're trying to do - // if we're here, then the API server has found a route, which means that if we have a non-empty namespace - // its a namespaced resource. - if len(a.GetNamespace()) == 0 || a.GetKind().GroupKind() == api.Kind("Namespace") { - return nil - } - - // we need to wait for our caches to warm - if !e.WaitForReady() { - return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) - } - _, err := e.namespaceLister.Get(a.GetNamespace()) - if err == nil { - return nil - } - if !errors.IsNotFound(err) { - return errors.NewInternalError(err) - } - - // in case of latency in our caches, make a call direct to storage to verify that it truly exists or not - _, err = e.client.Core().Namespaces().Get(a.GetNamespace(), metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return err - } - return errors.NewInternalError(err) - } - - return nil -} - -// NewExists creates a new namespace exists admission control handler -func NewExists() *Exists { - return &Exists{ - Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete), - } -} - -// SetExternalKubeClientSet implements the WantsExternalKubeClientSet interface. -func (e *Exists) SetExternalKubeClientSet(client kubernetes.Interface) { - e.client = client -} - -// SetExternalKubeInformerFactory implements the WantsExternalKubeInformerFactory interface. -func (e *Exists) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { - namespaceInformer := f.Core().V1().Namespaces() - e.namespaceLister = namespaceInformer.Lister() - e.SetReadyFunc(namespaceInformer.Informer().HasSynced) -} - -// ValidateInitialization implements the InitializationValidator interface. -func (e *Exists) ValidateInitialization() error { - if e.namespaceLister == nil { - return fmt.Errorf("missing namespaceLister") - } - if e.client == nil { - return fmt.Errorf("missing client") - } - return nil -} diff --git a/plugin/pkg/admission/namespace/exists/admission_test.go b/plugin/pkg/admission/namespace/exists/admission_test.go deleted file mode 100644 index 2aa72bc89c..0000000000 --- a/plugin/pkg/admission/namespace/exists/admission_test.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -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 exists - -import ( - "fmt" - "testing" - "time" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/apiserver/pkg/admission" - genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - informers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - core "k8s.io/client-go/testing" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// newHandlerForTest returns the admission controller configured for testing. -func newHandlerForTest(c kubernetes.Interface) (admission.ValidationInterface, informers.SharedInformerFactory, error) { - f := informers.NewSharedInformerFactory(c, 5*time.Minute) - handler := NewExists() - pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil) - pluginInitializer.Initialize(handler) - err := admission.ValidateInitialization(handler) - return handler, f, err -} - -// newMockClientForTest creates a mock client that returns a client configured for the specified list of namespaces. -func newMockClientForTest(namespaces []string) *fake.Clientset { - mockClient := &fake.Clientset{} - mockClient.AddReactor("list", "namespaces", func(action core.Action) (bool, runtime.Object, error) { - namespaceList := &corev1.NamespaceList{ - ListMeta: metav1.ListMeta{ - ResourceVersion: fmt.Sprintf("%d", len(namespaces)), - }, - } - for i, ns := range namespaces { - namespaceList.Items = append(namespaceList.Items, corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: ns, - ResourceVersion: fmt.Sprintf("%d", i), - }, - }) - } - return true, namespaceList, nil - }) - return mockClient -} - -// newPod returns a new pod for the specified namespace -func newPod(namespace string) api.Pod { - return api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: namespace}, - Spec: api.PodSpec{ - Volumes: []api.Volume{{Name: "vol"}}, - Containers: []api.Container{{Name: "ctr", Image: "image"}}, - }, - } -} - -// TestAdmissionNamespaceExists verifies pod is admitted only if namespace exists. -func TestAdmissionNamespaceExists(t *testing.T) { - namespace := "test" - mockClient := newMockClientForTest([]string{namespace}) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - informerFactory.Start(wait.NeverStop) - - pod := newPod(namespace) - err = handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if err != nil { - t.Errorf("unexpected error returned from admission handler") - } -} - -// TestAdmissionNamespaceDoesNotExist verifies pod is not admitted if namespace does not exist. -func TestAdmissionNamespaceDoesNotExist(t *testing.T) { - namespace := "test" - mockClient := newMockClientForTest([]string{}) - mockClient.AddReactor("get", "namespaces", func(action core.Action) (bool, runtime.Object, error) { - return true, nil, fmt.Errorf("nope, out of luck") - }) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - informerFactory.Start(wait.NeverStop) - - pod := newPod(namespace) - err = handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if err == nil { - actions := "" - for _, action := range mockClient.Actions() { - actions = actions + action.GetVerb() + ":" + action.GetResource().Resource + ":" + action.GetSubresource() + ", " - } - t.Errorf("expected error returned from admission handler: %v", actions) - } -} diff --git a/plugin/pkg/admission/podnodeselector/BUILD b/plugin/pkg/admission/podnodeselector/BUILD deleted file mode 100644 index 0144ccd207..0000000000 --- a/plugin/pkg/admission/podnodeselector/BUILD +++ /dev/null @@ -1,58 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/podnodeselector", - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/kubeapiserver/admission/util:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/listers/core/v1:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/podnodeselector/admission.go b/plugin/pkg/admission/podnodeselector/admission.go deleted file mode 100644 index 1d502a0de1..0000000000 --- a/plugin/pkg/admission/podnodeselector/admission.go +++ /dev/null @@ -1,264 +0,0 @@ -/* -Copyright 2016 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 podnodeselector - -import ( - "fmt" - "io" - "reflect" - - "k8s.io/klog" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/apiserver/pkg/admission" - genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - corev1listers "k8s.io/client-go/listers/core/v1" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/kubeapiserver/admission/util" -) - -// The annotation key scheduler.alpha.kubernetes.io/node-selector is for assigning -// node selectors labels to namespaces -var NamespaceNodeSelectors = []string{"scheduler.alpha.kubernetes.io/node-selector"} - -const PluginName = "PodNodeSelector" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - // TODO move this to a versioned configuration file format. - pluginConfig := readConfig(config) - plugin := NewPodNodeSelector(pluginConfig.PodNodeSelectorPluginConfig) - return plugin, nil - }) -} - -// podNodeSelector is an implementation of admission.Interface. -type podNodeSelector struct { - *admission.Handler - client kubernetes.Interface - namespaceLister corev1listers.NamespaceLister - // global default node selector and namespace whitelists in a cluster. - clusterNodeSelectors map[string]string -} - -var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&podNodeSelector{}) -var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&podNodeSelector{}) - -type pluginConfig struct { - PodNodeSelectorPluginConfig map[string]string -} - -// readConfig reads default value of clusterDefaultNodeSelector -// from the file provided with --admission-control-config-file -// If the file is not supplied, it defaults to "" -// The format in a file: -// podNodeSelectorPluginConfig: -// clusterDefaultNodeSelector: -// namespace1: -// namespace2: -func readConfig(config io.Reader) *pluginConfig { - defaultConfig := &pluginConfig{} - if config == nil || reflect.ValueOf(config).IsNil() { - return defaultConfig - } - d := yaml.NewYAMLOrJSONDecoder(config, 4096) - for { - if err := d.Decode(defaultConfig); err != nil { - if err != io.EOF { - continue - } - } - break - } - return defaultConfig -} - -// Admit enforces that pod and its namespace node label selectors matches at least a node in the cluster. -func (p *podNodeSelector) Admit(a admission.Attributes) error { - if shouldIgnore(a) { - return nil - } - updateInitialized, err := util.IsUpdatingInitializedObject(a) - if err != nil { - return err - } - if updateInitialized { - // node selector of an initialized pod is immutable - return nil - } - if !p.WaitForReady() { - return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) - } - - resource := a.GetResource().GroupResource() - pod := a.GetObject().(*api.Pod) - namespaceNodeSelector, err := p.getNamespaceNodeSelectorMap(a.GetNamespace()) - if err != nil { - return err - } - - if labels.Conflicts(namespaceNodeSelector, labels.Set(pod.Spec.NodeSelector)) { - return errors.NewForbidden(resource, pod.Name, fmt.Errorf("pod node label selector conflicts with its namespace node label selector")) - } - - // Merge pod node selector = namespace node selector + current pod node selector - // second selector wins - podNodeSelectorLabels := labels.Merge(namespaceNodeSelector, pod.Spec.NodeSelector) - pod.Spec.NodeSelector = map[string]string(podNodeSelectorLabels) - return p.Validate(a) -} - -// Validate ensures that the pod node selector is allowed -func (p *podNodeSelector) Validate(a admission.Attributes) error { - if shouldIgnore(a) { - return nil - } - if !p.WaitForReady() { - return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) - } - - resource := a.GetResource().GroupResource() - pod := a.GetObject().(*api.Pod) - - namespaceNodeSelector, err := p.getNamespaceNodeSelectorMap(a.GetNamespace()) - if err != nil { - return err - } - if labels.Conflicts(namespaceNodeSelector, labels.Set(pod.Spec.NodeSelector)) { - return errors.NewForbidden(resource, pod.Name, fmt.Errorf("pod node label selector conflicts with its namespace node label selector")) - } - - // whitelist verification - whitelist, err := labels.ConvertSelectorToLabelsMap(p.clusterNodeSelectors[a.GetNamespace()]) - if err != nil { - return err - } - if !labels.AreLabelsInWhiteList(pod.Spec.NodeSelector, whitelist) { - return errors.NewForbidden(resource, pod.Name, fmt.Errorf("pod node label selector labels conflict with its namespace whitelist")) - } - - return nil -} - -func (p *podNodeSelector) getNamespaceNodeSelectorMap(namespaceName string) (labels.Set, error) { - namespace, err := p.namespaceLister.Get(namespaceName) - if errors.IsNotFound(err) { - namespace, err = p.defaultGetNamespace(namespaceName) - if err != nil { - if errors.IsNotFound(err) { - return nil, err - } - return nil, errors.NewInternalError(err) - } - } else if err != nil { - return nil, errors.NewInternalError(err) - } - - return p.getNodeSelectorMap(namespace) -} - -func shouldIgnore(a admission.Attributes) bool { - resource := a.GetResource().GroupResource() - if resource != api.Resource("pods") { - return true - } - if a.GetSubresource() != "" { - // only run the checks below on pods proper and not subresources - return true - } - - _, ok := a.GetObject().(*api.Pod) - if !ok { - klog.Errorf("expected pod but got %s", a.GetKind().Kind) - return true - } - - return false -} - -func NewPodNodeSelector(clusterNodeSelectors map[string]string) *podNodeSelector { - return &podNodeSelector{ - Handler: admission.NewHandler(admission.Create, admission.Update), - clusterNodeSelectors: clusterNodeSelectors, - } -} - -func (a *podNodeSelector) SetExternalKubeClientSet(client kubernetes.Interface) { - a.client = client -} - -func (p *podNodeSelector) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { - namespaceInformer := f.Core().V1().Namespaces() - p.namespaceLister = namespaceInformer.Lister() - p.SetReadyFunc(namespaceInformer.Informer().HasSynced) -} - -func (p *podNodeSelector) ValidateInitialization() error { - if p.namespaceLister == nil { - return fmt.Errorf("missing namespaceLister") - } - if p.client == nil { - return fmt.Errorf("missing client") - } - return nil -} - -func (p *podNodeSelector) defaultGetNamespace(name string) (*corev1.Namespace, error) { - namespace, err := p.client.Core().Namespaces().Get(name, metav1.GetOptions{}) - if err != nil { - return nil, fmt.Errorf("namespace %s does not exist", name) - } - return namespace, nil -} - -func (p *podNodeSelector) getNodeSelectorMap(namespace *corev1.Namespace) (labels.Set, error) { - selector := labels.Set{} - labelsMap := labels.Set{} - var err error - found := false - if len(namespace.ObjectMeta.Annotations) > 0 { - for _, annotation := range NamespaceNodeSelectors { - if ns, ok := namespace.ObjectMeta.Annotations[annotation]; ok { - labelsMap, err = labels.ConvertSelectorToLabelsMap(ns) - if err != nil { - return labels.Set{}, err - } - - if labels.Conflicts(selector, labelsMap) { - nsName := namespace.ObjectMeta.Name - return labels.Set{}, fmt.Errorf("%s annotations' node label selectors conflict", nsName) - } - selector = labels.Merge(selector, labelsMap) - found = true - } - } - } - if !found { - selector, err = labels.ConvertSelectorToLabelsMap(p.clusterNodeSelectors["clusterDefaultNodeSelector"]) - if err != nil { - return labels.Set{}, err - } - } - return selector, nil -} diff --git a/plugin/pkg/admission/podnodeselector/admission_test.go b/plugin/pkg/admission/podnodeselector/admission_test.go deleted file mode 100644 index ba1d3f16a5..0000000000 --- a/plugin/pkg/admission/podnodeselector/admission_test.go +++ /dev/null @@ -1,261 +0,0 @@ -/* -Copyright 2016 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 podnodeselector - -import ( - "testing" - "time" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apiserver/pkg/admission" - genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// TestPodAdmission verifies various scenarios involving pod/namespace/global node label selectors -func TestPodAdmission(t *testing.T) { - namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testNamespace", - Namespace: "", - }, - } - - mockClient := fake.NewSimpleClientset(namespace) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - stopCh := make(chan struct{}) - defer close(stopCh) - informerFactory.Start(stopCh) - - pod := &api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "testPod", Namespace: "testNamespace"}, - } - - oldPod := *pod - oldPod.Initializers = &metav1.Initializers{Pending: []metav1.Initializer{{Name: "init"}}} - oldPod.Spec.NodeSelector = map[string]string{ - "old": "true", - } - - tests := []struct { - defaultNodeSelector string - namespaceNodeSelector string - whitelist string - podNodeSelector map[string]string - mergedNodeSelector labels.Set - ignoreTestNamespaceNodeSelector bool - admit bool - testName string - }{ - { - defaultNodeSelector: "", - podNodeSelector: map[string]string{}, - mergedNodeSelector: labels.Set{}, - ignoreTestNamespaceNodeSelector: true, - admit: true, - testName: "No node selectors", - }, - { - defaultNodeSelector: "infra = false", - podNodeSelector: map[string]string{}, - mergedNodeSelector: labels.Set{"infra": "false"}, - ignoreTestNamespaceNodeSelector: true, - admit: true, - testName: "Default node selector and no conflicts", - }, - { - defaultNodeSelector: "", - namespaceNodeSelector: " infra = false ", - podNodeSelector: map[string]string{}, - mergedNodeSelector: labels.Set{"infra": "false"}, - admit: true, - testName: "TestNamespace node selector with whitespaces and no conflicts", - }, - { - defaultNodeSelector: "infra = false", - namespaceNodeSelector: "infra=true", - podNodeSelector: map[string]string{}, - mergedNodeSelector: labels.Set{"infra": "true"}, - admit: true, - testName: "Default and namespace node selector, no conflicts", - }, - { - defaultNodeSelector: "infra = false", - namespaceNodeSelector: "", - podNodeSelector: map[string]string{}, - mergedNodeSelector: labels.Set{}, - admit: true, - testName: "Empty namespace node selector and no conflicts", - }, - { - defaultNodeSelector: "infra = false", - namespaceNodeSelector: "infra=true", - podNodeSelector: map[string]string{"env": "test"}, - mergedNodeSelector: labels.Set{"infra": "true", "env": "test"}, - admit: true, - testName: "TestNamespace and pod node selector, no conflicts", - }, - { - defaultNodeSelector: "env = test", - namespaceNodeSelector: "infra=true", - podNodeSelector: map[string]string{"infra": "false"}, - admit: false, - testName: "Conflicting pod and namespace node selector, one label", - }, - { - defaultNodeSelector: "env=dev", - namespaceNodeSelector: "infra=false, env = test", - podNodeSelector: map[string]string{"env": "dev", "color": "blue"}, - admit: false, - testName: "Conflicting pod and namespace node selector, multiple labels", - }, - { - defaultNodeSelector: "env=dev", - namespaceNodeSelector: "infra=false, env = dev", - whitelist: "env=dev, infra=false, color=blue", - podNodeSelector: map[string]string{"env": "dev", "color": "blue"}, - mergedNodeSelector: labels.Set{"infra": "false", "env": "dev", "color": "blue"}, - admit: true, - testName: "Merged pod node selectors satisfy the whitelist", - }, - { - defaultNodeSelector: "env=dev", - namespaceNodeSelector: "infra=false, env = dev", - whitelist: "env=dev, infra=true, color=blue", - podNodeSelector: map[string]string{"env": "dev", "color": "blue"}, - admit: false, - testName: "Merged pod node selectors conflict with the whitelist", - }, - { - defaultNodeSelector: "env=dev", - ignoreTestNamespaceNodeSelector: true, - whitelist: "env=prd", - podNodeSelector: map[string]string{}, - admit: false, - testName: "Default node selector conflict with the whitelist", - }, - } - for _, test := range tests { - if !test.ignoreTestNamespaceNodeSelector { - namespace.ObjectMeta.Annotations = map[string]string{"scheduler.alpha.kubernetes.io/node-selector": test.namespaceNodeSelector} - informerFactory.Core().V1().Namespaces().Informer().GetStore().Update(namespace) - } - handler.clusterNodeSelectors = make(map[string]string) - handler.clusterNodeSelectors["clusterDefaultNodeSelector"] = test.defaultNodeSelector - handler.clusterNodeSelectors[namespace.Name] = test.whitelist - pod.Spec = api.PodSpec{NodeSelector: test.podNodeSelector} - - err := handler.Admit(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if test.admit && err != nil { - t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) - } else if !test.admit && err == nil { - t.Errorf("Test: %s, expected an error", test.testName) - } - if test.admit && !labels.Equals(test.mergedNodeSelector, labels.Set(pod.Spec.NodeSelector)) { - t.Errorf("Test: %s, expected: %s but got: %s", test.testName, test.mergedNodeSelector, pod.Spec.NodeSelector) - } - err = handler.Validate(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if test.admit && err != nil { - t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) - } else if !test.admit && err == nil { - t.Errorf("Test: %s, expected an error", test.testName) - } - - // handles update of uninitialized pod like it's newly created. - err = handler.Admit(admission.NewAttributesRecord(pod, &oldPod, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil)) - if test.admit && err != nil { - t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) - } else if !test.admit && err == nil { - t.Errorf("Test: %s, expected an error", test.testName) - } - if test.admit && !labels.Equals(test.mergedNodeSelector, labels.Set(pod.Spec.NodeSelector)) { - t.Errorf("Test: %s, expected: %s but got: %s", test.testName, test.mergedNodeSelector, pod.Spec.NodeSelector) - } - err = handler.Validate(admission.NewAttributesRecord(pod, &oldPod, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil)) - if test.admit && err != nil { - t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) - } else if !test.admit && err == nil { - t.Errorf("Test: %s, expected an error", test.testName) - } - } -} - -func TestHandles(t *testing.T) { - for op, shouldHandle := range map[admission.Operation]bool{ - admission.Create: true, - admission.Update: true, - admission.Connect: false, - admission.Delete: false, - } { - nodeEnvionment := NewPodNodeSelector(nil) - if e, a := shouldHandle, nodeEnvionment.Handles(op); e != a { - t.Errorf("%v: shouldHandle=%t, handles=%t", op, e, a) - } - } -} - -func TestIgnoreUpdatingInitializedPod(t *testing.T) { - namespaceNodeSelector := "infra=true" - namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testNamespace", - Namespace: "", - Annotations: map[string]string{"scheduler.alpha.kubernetes.io/node-selector": namespaceNodeSelector}, - }, - } - mockClient := fake.NewSimpleClientset(namespace) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - handler.SetReadyFunc(func() bool { return true }) - - podNodeSelector := map[string]string{"infra": "false"} - pod := &api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "testPod", Namespace: "testNamespace"}, - Spec: api.PodSpec{NodeSelector: podNodeSelector}, - } - // this conflicts with podNodeSelector - err = informerFactory.Core().V1().Namespaces().Informer().GetStore().Update(namespace) - if err != nil { - t.Fatal(err) - } - - // if the update of initialized pod is not ignored, an error will be returned because the pod's nodeSelector conflicts with namespace's nodeSelector. - err = handler.Admit(admission.NewAttributesRecord(pod, pod, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil)) - if err != nil { - t.Errorf("expected no error, got: %v", err) - } -} - -// newHandlerForTest returns the admission controller configured for testing. -func newHandlerForTest(c kubernetes.Interface) (*podNodeSelector, informers.SharedInformerFactory, error) { - f := informers.NewSharedInformerFactory(c, 5*time.Minute) - handler := NewPodNodeSelector(nil) - pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil) - pluginInitializer.Initialize(handler) - err := admission.ValidateInitialization(handler) - return handler, f, err -} diff --git a/plugin/pkg/admission/podtolerationrestriction/BUILD b/plugin/pkg/admission/podtolerationrestriction/BUILD deleted file mode 100644 index b519bf07ee..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/BUILD +++ /dev/null @@ -1,81 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_test( - name = "go_default_test", - srcs = [ - "admission_test.go", - "main_test.go", - ], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/features:go_default_library", - "//pkg/scheduler/api:go_default_library", - "//pkg/util/tolerations:go_default_library", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library", - ], -) - -go_library( - name = "go_default_library", - srcs = [ - "admission.go", - "config.go", - ], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction", - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/apis/core/helper/qos:go_default_library", - "//pkg/apis/core/v1:go_default_library", - "//pkg/kubeapiserver/admission/util:go_default_library", - "//pkg/scheduler/api:go_default_library", - "//pkg/util/tolerations:go_default_library", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install:go_default_library", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1:go_default_library", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/listers/core/v1:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [ - ":package-srcs", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:all-srcs", - ], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/podtolerationrestriction/admission.go b/plugin/pkg/admission/podtolerationrestriction/admission.go deleted file mode 100644 index 35823f5a05..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/admission.go +++ /dev/null @@ -1,297 +0,0 @@ -/* -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 podtolerationrestriction - -import ( - "encoding/json" - "fmt" - "io" - - "k8s.io/klog" - - "k8s.io/api/core/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apiserver/pkg/admission" - genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - corev1listers "k8s.io/client-go/listers/core/v1" - api "k8s.io/kubernetes/pkg/apis/core" - qoshelper "k8s.io/kubernetes/pkg/apis/core/helper/qos" - k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1" - "k8s.io/kubernetes/pkg/kubeapiserver/admission/util" - schedulerapi "k8s.io/kubernetes/pkg/scheduler/api" - "k8s.io/kubernetes/pkg/util/tolerations" - pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" -) - -const PluginName = "PodTolerationRestriction" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - pluginConfig, err := loadConfiguration(config) - if err != nil { - return nil, err - } - return NewPodTolerationsPlugin(pluginConfig), nil - }) -} - -// The annotation keys for default and whitelist of tolerations -const ( - NSDefaultTolerations string = "scheduler.alpha.kubernetes.io/defaultTolerations" - NSWLTolerations string = "scheduler.alpha.kubernetes.io/tolerationsWhitelist" -) - -var _ admission.MutationInterface = &podTolerationsPlugin{} -var _ admission.ValidationInterface = &podTolerationsPlugin{} -var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&podTolerationsPlugin{}) -var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&podTolerationsPlugin{}) - -type podTolerationsPlugin struct { - *admission.Handler - client kubernetes.Interface - namespaceLister corev1listers.NamespaceLister - pluginConfig *pluginapi.Configuration -} - -// This plugin first verifies any conflict between a pod's tolerations and -// its namespace's tolerations, and rejects the pod if there's a conflict. -// If there's no conflict, the pod's tolerations are merged with its namespace's -// toleration. Resulting pod's tolerations are verified against its namespace's -// whitelist of tolerations. If the verification is successful, the pod is admitted -// otherwise rejected. If a namespace does not have associated default or whitelist -// of tolerations, then cluster level default or whitelist of tolerations are used -// instead if specified. Tolerations to a namespace are assigned via -// scheduler.alpha.kubernetes.io/defaultTolerations and scheduler.alpha.kubernetes.io/tolerationsWhitelist -// annotations keys. -func (p *podTolerationsPlugin) Admit(a admission.Attributes) error { - if shouldIgnore(a) { - return nil - } - - if !p.WaitForReady() { - return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) - } - - pod := a.GetObject().(*api.Pod) - var finalTolerations []api.Toleration - updateUninitialized, err := util.IsUpdatingUninitializedObject(a) - if err != nil { - return err - } - if a.GetOperation() == admission.Create || updateUninitialized { - ts, err := p.getNamespaceDefaultTolerations(a.GetNamespace()) - if err != nil { - return err - } - - // If the namespace has not specified its default tolerations, - // fall back to cluster's default tolerations. - if ts == nil { - ts = p.pluginConfig.Default - } - - if len(ts) > 0 { - if len(pod.Spec.Tolerations) > 0 { - if tolerations.IsConflict(ts, pod.Spec.Tolerations) { - return fmt.Errorf("namespace tolerations and pod tolerations conflict") - } - - // modified pod tolerations = namespace tolerations + current pod tolerations - finalTolerations = tolerations.MergeTolerations(ts, pod.Spec.Tolerations) - } else { - finalTolerations = ts - - } - } else { - finalTolerations = pod.Spec.Tolerations - } - } else { - finalTolerations = pod.Spec.Tolerations - } - - if qoshelper.GetPodQOS(pod) != api.PodQOSBestEffort { - finalTolerations = tolerations.MergeTolerations(finalTolerations, []api.Toleration{ - { - Key: schedulerapi.TaintNodeMemoryPressure, - Operator: api.TolerationOpExists, - Effect: api.TaintEffectNoSchedule, - }, - }) - } - pod.Spec.Tolerations = finalTolerations - - return p.Validate(a) -} -func (p *podTolerationsPlugin) Validate(a admission.Attributes) error { - if shouldIgnore(a) { - return nil - } - - if !p.WaitForReady() { - return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request")) - } - - // whitelist verification. - pod := a.GetObject().(*api.Pod) - if len(pod.Spec.Tolerations) > 0 { - whitelist, err := p.getNamespaceTolerationsWhitelist(a.GetNamespace()) - if err != nil { - return err - } - - // If the namespace has not specified its tolerations whitelist, - // fall back to cluster's whitelist of tolerations. - if whitelist == nil { - whitelist = p.pluginConfig.Whitelist - } - - if len(whitelist) > 0 { - // check if the merged pod tolerations satisfy its namespace whitelist - if !tolerations.VerifyAgainstWhitelist(pod.Spec.Tolerations, whitelist) { - return fmt.Errorf("pod tolerations (possibly merged with namespace default tolerations) conflict with its namespace whitelist") - } - } - } - - return nil -} - -func shouldIgnore(a admission.Attributes) bool { - resource := a.GetResource().GroupResource() - if resource != api.Resource("pods") { - return true - } - if a.GetSubresource() != "" { - // only run the checks below on pods proper and not subresources - return true - } - - obj := a.GetObject() - _, ok := obj.(*api.Pod) - if !ok { - klog.Errorf("expected pod but got %s", a.GetKind().Kind) - return true - } - - return false -} - -func NewPodTolerationsPlugin(pluginConfig *pluginapi.Configuration) *podTolerationsPlugin { - return &podTolerationsPlugin{ - Handler: admission.NewHandler(admission.Create, admission.Update), - pluginConfig: pluginConfig, - } -} - -func (a *podTolerationsPlugin) SetExternalKubeClientSet(client kubernetes.Interface) { - a.client = client -} - -func (p *podTolerationsPlugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { - namespaceInformer := f.Core().V1().Namespaces() - p.namespaceLister = namespaceInformer.Lister() - p.SetReadyFunc(namespaceInformer.Informer().HasSynced) - -} - -func (p *podTolerationsPlugin) ValidateInitialization() error { - if p.namespaceLister == nil { - return fmt.Errorf("missing namespaceLister") - } - if p.client == nil { - return fmt.Errorf("missing client") - } - return nil -} - -// in exceptional cases, this can result in two live calls, but once the cache catches up, that will stop. -func (p *podTolerationsPlugin) getNamespace(nsName string) (*corev1.Namespace, error) { - namespace, err := p.namespaceLister.Get(nsName) - if errors.IsNotFound(err) { - // in case of latency in our caches, make a call direct to storage to verify that it truly exists or not - namespace, err = p.client.CoreV1().Namespaces().Get(nsName, metav1.GetOptions{}) - if err != nil { - if errors.IsNotFound(err) { - return nil, err - } - return nil, errors.NewInternalError(err) - } - } else if err != nil { - return nil, errors.NewInternalError(err) - } - - return namespace, nil -} - -func (p *podTolerationsPlugin) getNamespaceDefaultTolerations(nsName string) ([]api.Toleration, error) { - ns, err := p.getNamespace(nsName) - if err != nil { - return nil, err - } - return extractNSTolerations(ns, NSDefaultTolerations) -} - -func (p *podTolerationsPlugin) getNamespaceTolerationsWhitelist(nsName string) ([]api.Toleration, error) { - ns, err := p.getNamespace(nsName) - if err != nil { - return nil, err - } - return extractNSTolerations(ns, NSWLTolerations) -} - -// extractNSTolerations extracts default or whitelist of tolerations from -// following namespace annotations keys: "scheduler.alpha.kubernetes.io/defaultTolerations" -// and "scheduler.alpha.kubernetes.io/tolerationsWhitelist". If these keys are -// unset (nil), extractNSTolerations returns nil. If the value to these -// keys are set to empty, an empty toleration is returned, otherwise -// configured tolerations are returned. -func extractNSTolerations(ns *corev1.Namespace, key string) ([]api.Toleration, error) { - // if a namespace does not have any annotations - if len(ns.Annotations) == 0 { - return nil, nil - } - - // if NSWLTolerations or NSDefaultTolerations does not exist - if _, ok := ns.Annotations[key]; !ok { - return nil, nil - } - - // if value is set to empty - if len(ns.Annotations[key]) == 0 { - return []api.Toleration{}, nil - } - - var v1Tolerations []v1.Toleration - err := json.Unmarshal([]byte(ns.Annotations[key]), &v1Tolerations) - if err != nil { - return nil, err - } - - ts := make([]api.Toleration, len(v1Tolerations)) - for i := range v1Tolerations { - if err := k8s_api_v1.Convert_v1_Toleration_To_core_Toleration(&v1Tolerations[i], &ts[i], nil); err != nil { - return nil, err - } - } - - return ts, nil -} diff --git a/plugin/pkg/admission/podtolerationrestriction/admission_test.go b/plugin/pkg/admission/podtolerationrestriction/admission_test.go deleted file mode 100644 index 10bfa2e6a6..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/admission_test.go +++ /dev/null @@ -1,371 +0,0 @@ -/* -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 podtolerationrestriction - -import ( - "encoding/json" - "testing" - "time" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apiserver/pkg/admission" - genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - utilfeature "k8s.io/apiserver/pkg/util/feature" - utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/features" - schedulerapi "k8s.io/kubernetes/pkg/scheduler/api" - "k8s.io/kubernetes/pkg/util/tolerations" - pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" -) - -// TestPodAdmission verifies various scenarios involving pod/namespace tolerations -func TestPodAdmission(t *testing.T) { - - CPU1000m := resource.MustParse("1000m") - CPU500m := resource.MustParse("500m") - - burstablePod := &api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "testPod", Namespace: "testNamespace"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "test", - Resources: api.ResourceRequirements{ - Limits: api.ResourceList{api.ResourceCPU: CPU1000m}, - Requests: api.ResourceList{api.ResourceCPU: CPU500m}, - }, - }, - }, - }, - } - - guaranteedPod := &api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "testPod", Namespace: "testNamespace"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "test", - Resources: api.ResourceRequirements{ - Limits: api.ResourceList{api.ResourceCPU: CPU1000m}, - Requests: api.ResourceList{api.ResourceCPU: CPU1000m}, - }, - }, - }, - }, - } - - bestEffortPod := &api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "testPod", Namespace: "testNamespace"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "test", - }, - }, - }, - } - - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TaintNodesByCondition, true)() - - tests := []struct { - pod *api.Pod - defaultClusterTolerations []api.Toleration - namespaceTolerations []api.Toleration - whitelist []api.Toleration - clusterWhitelist []api.Toleration - podTolerations []api.Toleration - mergedTolerations []api.Toleration - admit bool - testName string - }{ - { - pod: bestEffortPod, - defaultClusterTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - namespaceTolerations: nil, - podTolerations: []api.Toleration{}, - mergedTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - admit: true, - testName: "default cluster tolerations with empty pod tolerations and nil namespace tolerations", - }, - { - pod: bestEffortPod, - defaultClusterTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - namespaceTolerations: []api.Toleration{}, - podTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - mergedTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - admit: true, - testName: "default cluster tolerations with pod tolerations specified", - }, - { - pod: bestEffortPod, - defaultClusterTolerations: []api.Toleration{}, - namespaceTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - podTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - mergedTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - admit: true, - testName: "namespace tolerations", - }, - { - pod: bestEffortPod, - defaultClusterTolerations: []api.Toleration{}, - namespaceTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - podTolerations: []api.Toleration{}, - mergedTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - admit: true, - testName: "no pod tolerations", - }, - { - pod: bestEffortPod, - defaultClusterTolerations: []api.Toleration{}, - namespaceTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - podTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule", TolerationSeconds: nil}}, - admit: false, - testName: "conflicting pod and namespace tolerations", - }, - { - pod: bestEffortPod, - defaultClusterTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue2", Effect: "NoSchedule", TolerationSeconds: nil}}, - namespaceTolerations: []api.Toleration{}, - podTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule", TolerationSeconds: nil}}, - mergedTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule", TolerationSeconds: nil}}, - admit: true, - testName: "conflicting pod and default cluster tolerations but overridden by empty namespace tolerations", - }, - { - pod: bestEffortPod, - defaultClusterTolerations: []api.Toleration{}, - namespaceTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - whitelist: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - podTolerations: []api.Toleration{}, - mergedTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - admit: true, - testName: "merged pod tolerations satisfy whitelist", - }, - { - pod: bestEffortPod, - defaultClusterTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - namespaceTolerations: []api.Toleration{}, - podTolerations: []api.Toleration{}, - mergedTolerations: []api.Toleration{}, - admit: true, - testName: "Override default cluster toleration by empty namespace level toleration", - }, - { - pod: bestEffortPod, - whitelist: []api.Toleration{}, - clusterWhitelist: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule", TolerationSeconds: nil}}, - podTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - mergedTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - admit: true, - testName: "pod toleration conflicts with default cluster white list which is overridden by empty namespace whitelist", - }, - { - pod: bestEffortPod, - defaultClusterTolerations: []api.Toleration{}, - namespaceTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - whitelist: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule", TolerationSeconds: nil}}, - podTolerations: []api.Toleration{}, - admit: false, - testName: "merged pod tolerations conflict with the whitelist", - }, - { - pod: burstablePod, - defaultClusterTolerations: []api.Toleration{}, - namespaceTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - whitelist: []api.Toleration{}, - podTolerations: []api.Toleration{}, - mergedTolerations: []api.Toleration{ - {Key: schedulerapi.TaintNodeMemoryPressure, Operator: api.TolerationOpExists, Effect: api.TaintEffectNoSchedule, TolerationSeconds: nil}, - {Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}, - }, - admit: true, - testName: "added memoryPressure/DiskPressure for Burstable pod", - }, - { - pod: guaranteedPod, - defaultClusterTolerations: []api.Toleration{}, - namespaceTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}}, - whitelist: []api.Toleration{}, - podTolerations: []api.Toleration{}, - mergedTolerations: []api.Toleration{ - {Key: schedulerapi.TaintNodeMemoryPressure, Operator: api.TolerationOpExists, Effect: api.TaintEffectNoSchedule, TolerationSeconds: nil}, - {Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}, - }, - admit: true, - testName: "added memoryPressure/DiskPressure for Guaranteed pod", - }, - } - for _, test := range tests { - t.Run(test.testName, func(t *testing.T) { - namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testNamespace", - Namespace: "", - Annotations: map[string]string{}, - }, - } - - if test.namespaceTolerations != nil { - tolerationStr, err := json.Marshal(test.namespaceTolerations) - if err != nil { - t.Errorf("error in marshalling namespace tolerations %v", test.namespaceTolerations) - } - namespace.Annotations = map[string]string{NSDefaultTolerations: string(tolerationStr)} - } - - if test.whitelist != nil { - tolerationStr, err := json.Marshal(test.whitelist) - if err != nil { - t.Errorf("error in marshalling namespace whitelist %v", test.whitelist) - } - namespace.Annotations[NSWLTolerations] = string(tolerationStr) - } - - mockClient := fake.NewSimpleClientset(namespace) - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Fatalf("unexpected error initializing handler: %v", err) - } - stopCh := make(chan struct{}) - defer close(stopCh) - informerFactory.Start(stopCh) - - handler.pluginConfig = &pluginapi.Configuration{Default: test.defaultClusterTolerations, Whitelist: test.clusterWhitelist} - pod := test.pod - pod.Spec.Tolerations = test.podTolerations - - // copy the original pod for tests of uninitialized pod updates. - oldPod := *pod - oldPod.Initializers = &metav1.Initializers{Pending: []metav1.Initializer{{Name: "init"}}} - oldPod.Spec.Tolerations = []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue1", Effect: "NoSchedule", TolerationSeconds: nil}} - - err = handler.Admit(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, false, nil)) - if test.admit && err != nil { - t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) - } else if !test.admit && err == nil { - t.Errorf("Test: %s, expected an error", test.testName) - } - - updatedPodTolerations := pod.Spec.Tolerations - if test.admit && !tolerations.EqualTolerations(updatedPodTolerations, test.mergedTolerations) { - t.Errorf("Test: %s, expected: %#v but got: %#v", test.testName, test.mergedTolerations, updatedPodTolerations) - } - - // handles update of uninitialized pod like it's newly created. - err = handler.Admit(admission.NewAttributesRecord(pod, &oldPod, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil)) - if test.admit && err != nil { - t.Errorf("Test: %s, expected no error but got: %s", test.testName, err) - } else if !test.admit && err == nil { - t.Errorf("Test: %s, expected an error", test.testName) - } - - updatedPodTolerations = pod.Spec.Tolerations - if test.admit && !tolerations.EqualTolerations(updatedPodTolerations, test.mergedTolerations) { - t.Errorf("Test: %s, expected: %#v but got: %#v", test.testName, test.mergedTolerations, updatedPodTolerations) - } - }) - } -} - -func TestHandles(t *testing.T) { - for op, shouldHandle := range map[admission.Operation]bool{ - admission.Create: true, - admission.Update: true, - admission.Connect: false, - admission.Delete: false, - } { - - pluginConfig, err := loadConfiguration(nil) - // must not fail - if err != nil { - t.Errorf("%v: error reading default configuration", op) - } - ptPlugin := NewPodTolerationsPlugin(pluginConfig) - if e, a := shouldHandle, ptPlugin.Handles(op); e != a { - t.Errorf("%v: shouldHandle=%t, handles=%t", op, e, a) - } - } -} - -func TestIgnoreUpdatingInitializedPod(t *testing.T) { - mockClient := &fake.Clientset{} - handler, informerFactory, err := newHandlerForTest(mockClient) - if err != nil { - t.Errorf("unexpected error initializing handler: %v", err) - } - handler.SetReadyFunc(func() bool { return true }) - - pod := &api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "testPod", Namespace: "testNamespace"}, - Spec: api.PodSpec{}, - } - podToleration := api.Toleration{ - Key: "testKey", - Operator: "Equal", - Value: "testValue1", - Effect: "NoSchedule", - TolerationSeconds: nil, - } - pod.Spec.Tolerations = []api.Toleration{podToleration} - - // this conflicts with pod's Tolerations - namespaceToleration := podToleration - namespaceToleration.Value = "testValue2" - namespaceTolerations := []api.Toleration{namespaceToleration} - tolerationsStr, err := json.Marshal(namespaceTolerations) - if err != nil { - t.Errorf("error in marshalling namespace tolerations %v", namespaceTolerations) - } - namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testNamespace", - Namespace: "", - }, - } - namespace.Annotations = map[string]string{NSDefaultTolerations: string(tolerationsStr)} - err = informerFactory.Core().V1().Namespaces().Informer().GetStore().Update(namespace) - if err != nil { - t.Fatal(err) - } - - // if the update of initialized pod is not ignored, an error will be returned because the pod's Tolerations conflicts with namespace's Tolerations. - err = handler.Admit(admission.NewAttributesRecord(pod, pod, api.Kind("Pod").WithVersion("version"), "testNamespace", pod.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, false, nil)) - if err != nil { - t.Errorf("expected no error, got: %v", err) - } -} - -// newHandlerForTest returns the admission controller configured for testing. -func newHandlerForTest(c kubernetes.Interface) (*podTolerationsPlugin, informers.SharedInformerFactory, error) { - f := informers.NewSharedInformerFactory(c, 5*time.Minute) - pluginConfig, err := loadConfiguration(nil) - // must not fail - if err != nil { - return nil, nil, err - } - handler := NewPodTolerationsPlugin(pluginConfig) - pluginInitializer := genericadmissioninitializer.New(c, f, nil, nil) - pluginInitializer.Initialize(handler) - err = admission.ValidateInitialization(handler) - return handler, f, err -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/BUILD b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/BUILD deleted file mode 100644 index 49dce5a9d6..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/BUILD +++ /dev/null @@ -1,41 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = [ - "doc.go", - "register.go", - "types.go", - "zz_generated.deepcopy.go", - ], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction", - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [ - ":package-srcs", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install:all-srcs", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1:all-srcs", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation:all-srcs", - ], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/OWNERS b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/OWNERS deleted file mode 100644 index 2563b9f766..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -reviewers: -approvers: diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/doc.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/doc.go deleted file mode 100644 index 1ea05752d4..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -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 - -package podtolerationrestriction // import "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install/BUILD b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install/BUILD deleted file mode 100644 index 530f04f339..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install/BUILD +++ /dev/null @@ -1,31 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = ["install.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install", - deps = [ - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install/install.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install/install.go deleted file mode 100644 index 3bd5deb158..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -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 installs the experimental API group, making it available as -// an option to all of the API encoding/decoding machinery. -package install - -import ( - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - internalapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" - versionedapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1" -) - -// Install registers the API group and adds types to a scheme -func Install(scheme *runtime.Scheme) { - utilruntime.Must(internalapi.AddToScheme(scheme)) - utilruntime.Must(versionedapi.AddToScheme(scheme)) - utilruntime.Must(scheme.SetVersionPriority(versionedapi.SchemeGroupVersion)) -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/register.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/register.go deleted file mode 100644 index 2dc917b0ff..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/register.go +++ /dev/null @@ -1,50 +0,0 @@ -/* -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 podtolerationrestriction - -import ( - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// GroupName is the group name use in this package -const GroupName = "podtolerationrestriction.admission.k8s.io" - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme -) - -// Kind takes an unqualified kind and returns a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Configuration{}, - ) - return nil -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/types.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/types.go deleted file mode 100644 index d38c6e0613..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/types.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -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 podtolerationrestriction - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Configuration provides configuration for the PodTolerationRestriction admission controller. -type Configuration struct { - metav1.TypeMeta - - // cluster level default tolerations - Default []api.Toleration - - // cluster level whitelist of tolerations - Whitelist []api.Toleration -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/BUILD b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/BUILD deleted file mode 100644 index c65d05221b..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/BUILD +++ /dev/null @@ -1,42 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = [ - "defaults.go", - "doc.go", - "register.go", - "types.go", - "zz_generated.conversion.go", - "zz_generated.deepcopy.go", - "zz_generated.defaults.go", - ], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1", - deps = [ - "//pkg/apis/core:go_default_library", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/defaults.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/defaults.go deleted file mode 100644 index a4733c3db6..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/defaults.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -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) -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/doc.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/doc.go deleted file mode 100644 index 03571ab2bc..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -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 -// +k8s:conversion-gen=k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction -// +k8s:defaulter-gen=TypeMeta -// +groupName=podtolerationrestriction.admission.k8s.io - -// Package v1alpha1 is the v1alpha1 version of the API. -package v1alpha1 // import "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1" diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/register.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/register.go deleted file mode 100644 index 3270ec8003..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/register.go +++ /dev/null @@ -1,50 +0,0 @@ -/* -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" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// GroupName is the group name use in this package -const GroupName = "podtolerationrestriction.admission.k8s.io" - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} - -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) -} - -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Configuration{}, - ) - return nil -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/types.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/types.go deleted file mode 100644 index 38277b59f4..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/types.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -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/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Configuration provides configuration for the PodTolerationRestriction admission controller. -type Configuration struct { - metav1.TypeMeta `json:",inline"` - - // cluster level default tolerations - Default []v1.Toleration `json:"default,omitempty"` - - // cluster level whitelist of tolerations - Whitelist []v1.Toleration `json:"whitelist,omitempty"` -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/zz_generated.conversion.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/zz_generated.conversion.go deleted file mode 100644 index ceb0ed0ba8..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/zz_generated.conversion.go +++ /dev/null @@ -1,73 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 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. -*/ - -// Code generated by conversion-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - unsafe "unsafe" - - v1 "k8s.io/api/core/v1" - conversion "k8s.io/apimachinery/pkg/conversion" - runtime "k8s.io/apimachinery/pkg/runtime" - core "k8s.io/kubernetes/pkg/apis/core" - podtolerationrestriction "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" -) - -func init() { - localSchemeBuilder.Register(RegisterConversions) -} - -// RegisterConversions adds conversion functions to the given scheme. -// Public to allow building arbitrary schemes. -func RegisterConversions(s *runtime.Scheme) error { - if err := s.AddGeneratedConversionFunc((*Configuration)(nil), (*podtolerationrestriction.Configuration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_Configuration_To_podtolerationrestriction_Configuration(a.(*Configuration), b.(*podtolerationrestriction.Configuration), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*podtolerationrestriction.Configuration)(nil), (*Configuration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_podtolerationrestriction_Configuration_To_v1alpha1_Configuration(a.(*podtolerationrestriction.Configuration), b.(*Configuration), scope) - }); err != nil { - return err - } - return nil -} - -func autoConvert_v1alpha1_Configuration_To_podtolerationrestriction_Configuration(in *Configuration, out *podtolerationrestriction.Configuration, s conversion.Scope) error { - out.Default = *(*[]core.Toleration)(unsafe.Pointer(&in.Default)) - out.Whitelist = *(*[]core.Toleration)(unsafe.Pointer(&in.Whitelist)) - return nil -} - -// Convert_v1alpha1_Configuration_To_podtolerationrestriction_Configuration is an autogenerated conversion function. -func Convert_v1alpha1_Configuration_To_podtolerationrestriction_Configuration(in *Configuration, out *podtolerationrestriction.Configuration, s conversion.Scope) error { - return autoConvert_v1alpha1_Configuration_To_podtolerationrestriction_Configuration(in, out, s) -} - -func autoConvert_podtolerationrestriction_Configuration_To_v1alpha1_Configuration(in *podtolerationrestriction.Configuration, out *Configuration, s conversion.Scope) error { - out.Default = *(*[]v1.Toleration)(unsafe.Pointer(&in.Default)) - out.Whitelist = *(*[]v1.Toleration)(unsafe.Pointer(&in.Whitelist)) - return nil -} - -// Convert_podtolerationrestriction_Configuration_To_v1alpha1_Configuration is an autogenerated conversion function. -func Convert_podtolerationrestriction_Configuration_To_v1alpha1_Configuration(in *podtolerationrestriction.Configuration, out *Configuration, s conversion.Scope) error { - return autoConvert_podtolerationrestriction_Configuration_To_v1alpha1_Configuration(in, out, s) -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/zz_generated.deepcopy.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 008c428fc7..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,65 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 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. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1 "k8s.io/api/core/v1" - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Configuration) DeepCopyInto(out *Configuration) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Default != nil { - in, out := &in.Default, &out.Default - *out = make([]v1.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Whitelist != nil { - in, out := &in.Whitelist, &out.Whitelist - *out = make([]v1.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Configuration. -func (in *Configuration) DeepCopy() *Configuration { - if in == nil { - return nil - } - out := new(Configuration) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Configuration) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/zz_generated.defaults.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/zz_generated.defaults.go deleted file mode 100644 index dd621a3acd..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/zz_generated.defaults.go +++ /dev/null @@ -1,32 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 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. -*/ - -// Code generated by defaulter-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// RegisterDefaults adds defaulters functions to the given scheme. -// Public to allow building arbitrary schemes. -// All generated defaulters are covering - they call all nested defaulters. -func RegisterDefaults(scheme *runtime.Scheme) error { - return nil -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/BUILD b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/BUILD deleted file mode 100644 index 188300c152..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/BUILD +++ /dev/null @@ -1,41 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["validation.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation", - deps = [ - "//pkg/apis/core/validation:go_default_library", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) - -go_test( - name = "go_default_test", - srcs = ["validation_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library", - ], -) diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation.go deleted file mode 100644 index 99a36ed3aa..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -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 ( - "fmt" - - "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/kubernetes/pkg/apis/core/validation" - internalapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" -) - -// ValidateConfiguration validates the configuration. -func ValidateConfiguration(config *internalapi.Configuration) error { - allErrs := field.ErrorList{} - fldpath := field.NewPath("podtolerationrestriction") - allErrs = append(allErrs, validation.ValidateTolerations(config.Default, fldpath.Child("default"))...) - allErrs = append(allErrs, validation.ValidateTolerations(config.Whitelist, fldpath.Child("whitelist"))...) - if len(allErrs) > 0 { - return fmt.Errorf("invalid config: %v", allErrs) - } - return nil -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation_test.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation_test.go deleted file mode 100644 index d444921a51..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation_test.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -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 ( - api "k8s.io/kubernetes/pkg/apis/core" - internalapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" - "testing" -) - -func TestValidateConfiguration(t *testing.T) { - - tests := []struct { - config internalapi.Configuration - testName string - testStatus bool - }{ - { - config: internalapi.Configuration{ - Default: []api.Toleration{ - {Key: "foo", Operator: "Exists", Value: "", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]}, - {Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]}, - {Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}, - {Operator: "Exists", Effect: "NoSchedule"}, - }, - Whitelist: []api.Toleration{ - {Key: "foo", Value: "bar", Effect: "NoSchedule"}, - {Key: "foo", Operator: "Equal", Value: "bar"}, - }, - }, - testName: "Valid cases", - testStatus: true, - }, - { - config: internalapi.Configuration{ - Whitelist: []api.Toleration{{Key: "foo", Operator: "Exists", Value: "bar", Effect: "NoSchedule"}}, - }, - testName: "Invalid case", - testStatus: false, - }, - { - config: internalapi.Configuration{ - Default: []api.Toleration{{Operator: "Equal", Value: "bar", Effect: "NoSchedule"}}, - }, - testName: "Invalid case", - testStatus: false, - }, - } - - for i := range tests { - errs := ValidateConfiguration(&tests[i].config) - if tests[i].testStatus && errs != nil { - t.Errorf("Test: %s, expected success: %v", tests[i].testName, errs) - } - if !tests[i].testStatus && errs == nil { - t.Errorf("Test: %s, expected errors: %v", tests[i].testName, errs) - } - } -} diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/zz_generated.deepcopy.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/zz_generated.deepcopy.go deleted file mode 100644 index ea467c145b..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/zz_generated.deepcopy.go +++ /dev/null @@ -1,65 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 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. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package podtolerationrestriction - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" - core "k8s.io/kubernetes/pkg/apis/core" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Configuration) DeepCopyInto(out *Configuration) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Default != nil { - in, out := &in.Default, &out.Default - *out = make([]core.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Whitelist != nil { - in, out := &in.Whitelist, &out.Whitelist - *out = make([]core.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Configuration. -func (in *Configuration) DeepCopy() *Configuration { - if in == nil { - return nil - } - out := new(Configuration) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Configuration) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} diff --git a/plugin/pkg/admission/podtolerationrestriction/config.go b/plugin/pkg/admission/podtolerationrestriction/config.go deleted file mode 100644 index d5d954a50b..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/config.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -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 podtolerationrestriction - -import ( - "fmt" - "io" - "io/ioutil" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - internalapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" - "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install" - versionedapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1" - "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation" -) - -var ( - scheme = runtime.NewScheme() - codecs = serializer.NewCodecFactory(scheme) -) - -func init() { - install.Install(scheme) -} - -// LoadConfiguration loads the provided configuration. -func loadConfiguration(config io.Reader) (*internalapi.Configuration, error) { - // if no config is provided, return a default configuration - if config == nil { - externalConfig := &versionedapi.Configuration{} - scheme.Default(externalConfig) - internalConfig := &internalapi.Configuration{} - if err := scheme.Convert(externalConfig, internalConfig, nil); err != nil { - return nil, err - } - return internalConfig, nil - } - // we have a config so parse it. - data, err := ioutil.ReadAll(config) - if err != nil { - return nil, err - } - decoder := codecs.UniversalDecoder() - decodedObj, err := runtime.Decode(decoder, data) - if err != nil { - return nil, err - } - externalConfig, ok := decodedObj.(*internalapi.Configuration) - if !ok { - return nil, fmt.Errorf("unexpected type: %T", decodedObj) - } - - if err := validation.ValidateConfiguration(externalConfig); err != nil { - return nil, err - } - - return externalConfig, nil -} diff --git a/plugin/pkg/admission/podtolerationrestriction/main_test.go b/plugin/pkg/admission/podtolerationrestriction/main_test.go deleted file mode 100644 index d500cb63af..0000000000 --- a/plugin/pkg/admission/podtolerationrestriction/main_test.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2018 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 podtolerationrestriction - -import ( - "testing" - - utilfeature "k8s.io/apiserver/pkg/util/feature" - utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" - _ "k8s.io/kubernetes/pkg/features" -) - -func TestMain(m *testing.M) { - utilfeaturetesting.VerifyFeatureGatesUnchanged(utilfeature.DefaultFeatureGate, m.Run) -} diff --git a/plugin/pkg/admission/security/BUILD b/plugin/pkg/admission/security/BUILD deleted file mode 100644 index 0ae32eb54c..0000000000 --- a/plugin/pkg/admission/security/BUILD +++ /dev/null @@ -1,28 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = ["doc.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/security", -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [ - ":package-srcs", - "//plugin/pkg/admission/security/podsecuritypolicy:all-srcs", - ], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/security/doc.go b/plugin/pkg/admission/security/doc.go deleted file mode 100644 index 4177e557c4..0000000000 --- a/plugin/pkg/admission/security/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -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. -*/ - -// security contains admission plugins specific to cluster security. -package security // import "k8s.io/kubernetes/plugin/pkg/admission/security" diff --git a/plugin/pkg/admission/security/podsecuritypolicy/BUILD b/plugin/pkg/admission/security/podsecuritypolicy/BUILD deleted file mode 100644 index 6480ddde4d..0000000000 --- a/plugin/pkg/admission/security/podsecuritypolicy/BUILD +++ /dev/null @@ -1,75 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy", - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/apis/extensions:go_default_library", - "//pkg/apis/policy:go_default_library", - "//pkg/registry/rbac:go_default_library", - "//pkg/security/podsecuritypolicy:go_default_library", - "//pkg/security/podsecuritypolicy/util:go_default_library", - "//pkg/serviceaccount:go_default_library", - "//staging/src/k8s.io/api/policy/v1beta1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//staging/src/k8s.io/client-go/listers/policy/v1beta1:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/api/legacyscheme:go_default_library", - "//pkg/apis/core:go_default_library", - "//pkg/apis/core/v1:go_default_library", - "//pkg/controller:go_default_library", - "//pkg/security/apparmor:go_default_library", - "//pkg/security/podsecuritypolicy:go_default_library", - "//pkg/security/podsecuritypolicy/seccomp:go_default_library", - "//pkg/security/podsecuritypolicy/util:go_default_library", - "//staging/src/k8s.io/api/core/v1:go_default_library", - "//staging/src/k8s.io/api/policy/v1beta1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library", - "//staging/src/k8s.io/client-go/informers:go_default_library", - "//vendor/github.com/stretchr/testify/assert:go_default_library", - "//vendor/k8s.io/utils/pointer:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/security/podsecuritypolicy/OWNERS b/plugin/pkg/admission/security/podsecuritypolicy/OWNERS deleted file mode 100644 index 787630abd2..0000000000 --- a/plugin/pkg/admission/security/podsecuritypolicy/OWNERS +++ /dev/null @@ -1,7 +0,0 @@ -approvers: -- sig-auth-policy-approvers -reviewers: -- sig-auth-policy-reviewers -labels: -- sig/auth - diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission.go b/plugin/pkg/admission/security/podsecuritypolicy/admission.go deleted file mode 100644 index f980b93221..0000000000 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission.go +++ /dev/null @@ -1,400 +0,0 @@ -/* -Copyright 2016 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 podsecuritypolicy - -import ( - "fmt" - "io" - "sort" - "strings" - - "k8s.io/klog" - - policyv1beta1 "k8s.io/api/policy/v1beta1" - apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/apiserver/pkg/admission" - genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer" - "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/apiserver/pkg/authorization/authorizer" - "k8s.io/client-go/informers" - policylisters "k8s.io/client-go/listers/policy/v1beta1" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/apis/policy" - rbacregistry "k8s.io/kubernetes/pkg/registry/rbac" - psp "k8s.io/kubernetes/pkg/security/podsecuritypolicy" - psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util" - "k8s.io/kubernetes/pkg/serviceaccount" -) - -const ( - PluginName = "PodSecurityPolicy" -) - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - plugin := newPlugin(psp.NewSimpleStrategyFactory(), true) - return plugin, nil - }) -} - -// PodSecurityPolicyPlugin holds state for and implements the admission plugin. -type PodSecurityPolicyPlugin struct { - *admission.Handler - strategyFactory psp.StrategyFactory - failOnNoPolicies bool - authz authorizer.Authorizer - lister policylisters.PodSecurityPolicyLister -} - -// SetAuthorizer sets the authorizer. -func (plugin *PodSecurityPolicyPlugin) SetAuthorizer(authz authorizer.Authorizer) { - plugin.authz = authz -} - -// ValidateInitialization ensures an authorizer is set. -func (plugin *PodSecurityPolicyPlugin) ValidateInitialization() error { - if plugin.authz == nil { - return fmt.Errorf("%s requires an authorizer", PluginName) - } - if plugin.lister == nil { - return fmt.Errorf("%s requires a lister", PluginName) - } - return nil -} - -var _ admission.MutationInterface = &PodSecurityPolicyPlugin{} -var _ admission.ValidationInterface = &PodSecurityPolicyPlugin{} -var _ genericadmissioninit.WantsAuthorizer = &PodSecurityPolicyPlugin{} -var _ genericadmissioninit.WantsExternalKubeInformerFactory = &PodSecurityPolicyPlugin{} -var auditKeyPrefix = strings.ToLower(PluginName) + "." + policy.GroupName + ".k8s.io" - -// newPlugin creates a new PSP admission plugin. -func newPlugin(strategyFactory psp.StrategyFactory, failOnNoPolicies bool) *PodSecurityPolicyPlugin { - return &PodSecurityPolicyPlugin{ - Handler: admission.NewHandler(admission.Create, admission.Update), - strategyFactory: strategyFactory, - failOnNoPolicies: failOnNoPolicies, - } -} - -func (a *PodSecurityPolicyPlugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { - podSecurityPolicyInformer := f.Policy().V1beta1().PodSecurityPolicies() - a.lister = podSecurityPolicyInformer.Lister() - a.SetReadyFunc(podSecurityPolicyInformer.Informer().HasSynced) -} - -// Admit determines if the pod should be admitted based on the requested security context -// and the available PSPs. -// -// 1. Find available PSPs. -// 2. Create the providers, includes setting pre-allocated values if necessary. -// 3. Try to generate and validate a PSP with providers. If we find one then admit the pod -// with the validated PSP. If we don't find any reject the pod and give all errors from the -// failed attempts. -func (c *PodSecurityPolicyPlugin) Admit(a admission.Attributes) error { - if ignore, err := shouldIgnore(a); err != nil { - return err - } else if ignore { - return nil - } - - // only mutate if this is a CREATE request. On updates we only validate. - // TODO(liggitt): allow spec mutation during initializing updates? - if a.GetOperation() != admission.Create { - return nil - } - - pod := a.GetObject().(*api.Pod) - - // compute the context. Mutation is allowed. ValidatedPSPAnnotation is not taken into account. - allowedPod, pspName, validationErrs, err := c.computeSecurityContext(a, pod, true, "") - if err != nil { - return admission.NewForbidden(a, err) - } - if allowedPod != nil { - *pod = *allowedPod - // annotate and accept the pod - klog.V(4).Infof("pod %s (generate: %s) in namespace %s validated against provider %s", pod.Name, pod.GenerateName, a.GetNamespace(), pspName) - if pod.ObjectMeta.Annotations == nil { - pod.ObjectMeta.Annotations = map[string]string{} - } - pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation] = pspName - key := auditKeyPrefix + "/" + "admit-policy" - if err := a.AddAnnotation(key, pspName); err != nil { - klog.Warningf("failed to set admission audit annotation %s to %s: %v", key, pspName, err) - } - return nil - } - - // we didn't validate against any provider, reject the pod and give the errors for each attempt - klog.V(4).Infof("unable to validate pod %s (generate: %s) in namespace %s against any pod security policy: %v", pod.Name, pod.GenerateName, a.GetNamespace(), validationErrs) - return admission.NewForbidden(a, fmt.Errorf("unable to validate against any pod security policy: %v", validationErrs)) -} - -func (c *PodSecurityPolicyPlugin) Validate(a admission.Attributes) error { - if ignore, err := shouldIgnore(a); err != nil { - return err - } else if ignore { - return nil - } - - pod := a.GetObject().(*api.Pod) - - // compute the context. Mutation is not allowed. ValidatedPSPAnnotation is used as a hint to gain same speed-up. - allowedPod, pspName, validationErrs, err := c.computeSecurityContext(a, pod, false, pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation]) - if err != nil { - return admission.NewForbidden(a, err) - } - if apiequality.Semantic.DeepEqual(pod, allowedPod) { - key := auditKeyPrefix + "/" + "validate-policy" - if err := a.AddAnnotation(key, pspName); err != nil { - klog.Warningf("failed to set admission audit annotation %s to %s: %v", key, pspName, err) - } - return nil - } - - // we didn't validate against any provider, reject the pod and give the errors for each attempt - klog.V(4).Infof("unable to validate pod %s (generate: %s) in namespace %s against any pod security policy: %v", pod.Name, pod.GenerateName, a.GetNamespace(), validationErrs) - return admission.NewForbidden(a, fmt.Errorf("unable to validate against any pod security policy: %v", validationErrs)) -} - -func shouldIgnore(a admission.Attributes) (bool, error) { - if a.GetResource().GroupResource() != api.Resource("pods") { - return true, nil - } - if len(a.GetSubresource()) != 0 { - return true, nil - } - - // if we can't convert then fail closed since we've already checked that this is supposed to be a pod object. - // this shouldn't normally happen during admission but could happen if an integrator passes a versioned - // pod object rather than an internal object. - if _, ok := a.GetObject().(*api.Pod); !ok { - return false, admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject())) - } - - // if this is an update, see if we are only updating the ownerRef/finalizers. Garbage collection does this - // and we should allow it in general, since you had the power to update and the power to delete. - // The worst that happens is that you delete something, but you aren't controlling the privileged object itself - if a.GetOperation() == admission.Update && rbacregistry.IsOnlyMutatingGCFields(a.GetObject(), a.GetOldObject(), apiequality.Semantic) { - return true, nil - } - - return false, nil -} - -// computeSecurityContext derives a valid security context while trying to avoid any changes to the given pod. I.e. -// if there is a matching policy with the same security context as given, it will be reused. If there is no -// matching policy the returned pod will be nil and the pspName empty. validatedPSPHint is the validated psp name -// saved in kubernetes.io/psp annotation. This psp is usually the one we are looking for. -func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes, pod *api.Pod, specMutationAllowed bool, validatedPSPHint string) (*api.Pod, string, field.ErrorList, error) { - // get all constraints that are usable by the user - klog.V(4).Infof("getting pod security policies for pod %s (generate: %s)", pod.Name, pod.GenerateName) - var saInfo user.Info - if len(pod.Spec.ServiceAccountName) > 0 { - saInfo = serviceaccount.UserInfo(a.GetNamespace(), pod.Spec.ServiceAccountName, "") - } - - policies, err := c.lister.List(labels.Everything()) - if err != nil { - return nil, "", nil, err - } - - // if we have no policies and want to succeed then return. Otherwise we'll end up with no - // providers and fail with "unable to validate against any pod security policy" below. - if len(policies) == 0 && !c.failOnNoPolicies { - return pod, "", nil, nil - } - - // sort policies by name to make order deterministic - // If mutation is not allowed and validatedPSPHint is provided, check the validated policy first. - // TODO(liggitt): add priority field to allow admins to bucket differently - sort.SliceStable(policies, func(i, j int) bool { - if !specMutationAllowed { - if policies[i].Name == validatedPSPHint { - return true - } - if policies[j].Name == validatedPSPHint { - return false - } - } - return strings.Compare(policies[i].Name, policies[j].Name) < 0 - }) - - providers, errs := c.createProvidersFromPolicies(policies, pod.Namespace) - for _, err := range errs { - klog.V(4).Infof("provider creation error: %v", err) - } - - if len(providers) == 0 { - return nil, "", nil, fmt.Errorf("no providers available to validate pod request") - } - - var ( - allowedMutatedPod *api.Pod - allowingMutatingPSP string - // Map of PSP name to associated validation errors. - validationErrs = map[string]field.ErrorList{} - ) - - for _, provider := range providers { - podCopy := pod.DeepCopy() - - if errs := assignSecurityContext(provider, podCopy); len(errs) > 0 { - validationErrs[provider.GetPSPName()] = errs - continue - } - - // the entire pod validated - mutated := !apiequality.Semantic.DeepEqual(pod, podCopy) - if mutated && !specMutationAllowed { - continue - } - - if !isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), provider.GetPSPName(), c.authz) { - continue - } - - switch { - case !mutated: - // if it validated without mutating anything, use this result - return podCopy, provider.GetPSPName(), nil, nil - - case specMutationAllowed && allowedMutatedPod == nil: - // if mutation is allowed and this is the first PSP to allow the pod, remember it, - // but continue to see if another PSP allows without mutating - allowedMutatedPod = podCopy - allowingMutatingPSP = provider.GetPSPName() - } - } - - if allowedMutatedPod != nil { - return allowedMutatedPod, allowingMutatingPSP, nil, nil - } - - // Pod is rejected. Filter the validation errors to only include errors from authorized PSPs. - aggregate := field.ErrorList{} - for psp, errs := range validationErrs { - if isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), psp, c.authz) { - aggregate = append(aggregate, errs...) - } - } - return nil, "", aggregate, nil -} - -// assignSecurityContext creates a security context for each container in the pod -// and validates that the sc falls within the psp constraints. All containers must validate against -// the same psp or is not considered valid. -func assignSecurityContext(provider psp.Provider, pod *api.Pod) field.ErrorList { - errs := field.ErrorList{} - - err := provider.DefaultPodSecurityContext(pod) - if err != nil { - errs = append(errs, field.Invalid(field.NewPath("spec", "securityContext"), pod.Spec.SecurityContext, err.Error())) - } - - errs = append(errs, provider.ValidatePod(pod)...) - - for i := range pod.Spec.InitContainers { - err := provider.DefaultContainerSecurityContext(pod, &pod.Spec.InitContainers[i]) - if err != nil { - errs = append(errs, field.Invalid(field.NewPath("spec", "initContainers").Index(i).Child("securityContext"), "", err.Error())) - continue - } - errs = append(errs, provider.ValidateContainer(pod, &pod.Spec.InitContainers[i], field.NewPath("spec", "initContainers").Index(i))...) - } - - for i := range pod.Spec.Containers { - err := provider.DefaultContainerSecurityContext(pod, &pod.Spec.Containers[i]) - if err != nil { - errs = append(errs, field.Invalid(field.NewPath("spec", "containers").Index(i).Child("securityContext"), "", err.Error())) - continue - } - errs = append(errs, provider.ValidateContainer(pod, &pod.Spec.Containers[i], field.NewPath("spec", "containers").Index(i))...) - } - - if len(errs) > 0 { - return errs - } - return nil -} - -// createProvidersFromPolicies creates providers from the constraints supplied. -func (c *PodSecurityPolicyPlugin) createProvidersFromPolicies(psps []*policyv1beta1.PodSecurityPolicy, namespace string) ([]psp.Provider, []error) { - var ( - // collected providers - providers []psp.Provider - // collected errors to return - errs []error - ) - - for _, constraint := range psps { - provider, err := psp.NewSimpleProvider(constraint, namespace, c.strategyFactory) - if err != nil { - errs = append(errs, fmt.Errorf("error creating provider for PSP %s: %v", constraint.Name, err)) - continue - } - providers = append(providers, provider) - } - return providers, errs -} - -func isAuthorizedForPolicy(user, sa user.Info, namespace, policyName string, authz authorizer.Authorizer) bool { - // Check the service account first, as that is the more common use case. - return authorizedForPolicy(sa, namespace, policyName, authz) || - authorizedForPolicy(user, namespace, policyName, authz) -} - -// authorizedForPolicy returns true if info is authorized to perform the "use" verb on the policy resource. -// TODO: check against only the policy group when PSP will be completely moved out of the extensions -func authorizedForPolicy(info user.Info, namespace string, policyName string, authz authorizer.Authorizer) bool { - // Check against extensions API group for backward compatibility - return authorizedForPolicyInAPIGroup(info, namespace, policyName, policy.GroupName, authz) || - authorizedForPolicyInAPIGroup(info, namespace, policyName, extensions.GroupName, authz) -} - -// authorizedForPolicyInAPIGroup returns true if info is authorized to perform the "use" verb on the policy resource in the specified API group. -func authorizedForPolicyInAPIGroup(info user.Info, namespace, policyName, apiGroupName string, authz authorizer.Authorizer) bool { - if info == nil { - return false - } - attr := buildAttributes(info, namespace, policyName, apiGroupName) - decision, reason, err := authz.Authorize(attr) - if err != nil { - klog.V(5).Infof("cannot authorize for policy: %v,%v", reason, err) - } - return (decision == authorizer.DecisionAllow) -} - -// buildAttributes builds an attributes record for a SAR based on the user info and policy. -func buildAttributes(info user.Info, namespace, policyName, apiGroupName string) authorizer.Attributes { - // check against the namespace that the pod is being created in to allow per-namespace PSP grants. - attr := authorizer.AttributesRecord{ - User: info, - Verb: "use", - Namespace: namespace, - Name: policyName, - APIGroup: apiGroupName, - Resource: "podsecuritypolicies", - ResourceRequest: true, - } - return attr -} diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go deleted file mode 100644 index a0ce808020..0000000000 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go +++ /dev/null @@ -1,2444 +0,0 @@ -/* -Copyright 2016 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 podsecuritypolicy - -import ( - "fmt" - "reflect" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "k8s.io/api/core/v1" - policy "k8s.io/api/policy/v1beta1" - apiequality "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/diff" - kadmission "k8s.io/apiserver/pkg/admission" - "k8s.io/apiserver/pkg/authentication/serviceaccount" - "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/apiserver/pkg/authorization/authorizer" - "k8s.io/apiserver/pkg/authorization/authorizerfactory" - "k8s.io/client-go/informers" - "k8s.io/kubernetes/pkg/api/legacyscheme" - kapi "k8s.io/kubernetes/pkg/apis/core" - k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1" - "k8s.io/kubernetes/pkg/controller" - "k8s.io/kubernetes/pkg/security/apparmor" - kpsp "k8s.io/kubernetes/pkg/security/podsecuritypolicy" - "k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp" - psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util" - utilpointer "k8s.io/utils/pointer" -) - -const defaultContainerName = "test-c" - -// NewTestAdmission provides an admission plugin with test implementations of internal structs. -func NewTestAdmission(psps []*policy.PodSecurityPolicy, authz authorizer.Authorizer) *PodSecurityPolicyPlugin { - informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) - store := informerFactory.Policy().V1beta1().PodSecurityPolicies().Informer().GetStore() - for _, psp := range psps { - store.Add(psp) - } - lister := informerFactory.Policy().V1beta1().PodSecurityPolicies().Lister() - if authz == nil { - authz = &TestAuthorizer{} - } - return &PodSecurityPolicyPlugin{ - Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update), - strategyFactory: kpsp.NewSimpleStrategyFactory(), - authz: authz, - lister: lister, - } -} - -// TestAuthorizer is a testing struct for testing that fulfills the authorizer interface. -type TestAuthorizer struct { - // usernameToNamespaceToAllowedPSPs contains the map of allowed PSPs. - // if nil, all PSPs are allowed. - usernameToNamespaceToAllowedPSPs map[string]map[string]map[string]bool - // allowedAPIGroupName specifies an API Group name that contains PSP resources. - // In order to be authorized, AttributesRecord must have this group name. - // When empty, API Group name isn't taken into account. - // TODO: remove this when PSP will be completely moved out of the extensions and we'll lookup only in "policy" group. - allowedAPIGroupName string -} - -func (t *TestAuthorizer) Authorize(a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { - if t.usernameToNamespaceToAllowedPSPs == nil { - return authorizer.DecisionAllow, "", nil - } - allowedInNamespace := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][a.GetNamespace()][a.GetName()] - allowedClusterWide := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][""][a.GetName()] - allowedAPIGroup := len(t.allowedAPIGroupName) == 0 || a.GetAPIGroup() == t.allowedAPIGroupName - if allowedAPIGroup && (allowedInNamespace || allowedClusterWide) { - return authorizer.DecisionAllow, "", nil - } - return authorizer.DecisionNoOpinion, "", nil -} - -var _ authorizer.Authorizer = &TestAuthorizer{} - -func useInitContainers(pod *kapi.Pod) *kapi.Pod { - pod.Spec.InitContainers = pod.Spec.Containers - pod.Spec.Containers = []kapi.Container{} - return pod -} - -func TestAdmitSeccomp(t *testing.T) { - containerName := "container" - tests := map[string]struct { - pspAnnotations map[string]string - podAnnotations map[string]string - shouldPassAdmit bool - shouldPassValidate bool - }{ - "no seccomp, no pod annotations": { - pspAnnotations: nil, - podAnnotations: nil, - shouldPassAdmit: true, - shouldPassValidate: true, - }, - "no seccomp, pod annotations": { - pspAnnotations: nil, - podAnnotations: map[string]string{ - kapi.SeccompPodAnnotationKey: "foo", - }, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "no seccomp, container annotations": { - pspAnnotations: nil, - podAnnotations: map[string]string{ - kapi.SeccompContainerAnnotationKeyPrefix + containerName: "foo", - }, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "seccomp, allow any no pod annotation": { - pspAnnotations: map[string]string{ - seccomp.AllowedProfilesAnnotationKey: seccomp.AllowAny, - }, - podAnnotations: nil, - shouldPassAdmit: true, - shouldPassValidate: true, - }, - "seccomp, allow any pod annotation": { - pspAnnotations: map[string]string{ - seccomp.AllowedProfilesAnnotationKey: seccomp.AllowAny, - }, - podAnnotations: map[string]string{ - kapi.SeccompPodAnnotationKey: "foo", - }, - shouldPassAdmit: true, - shouldPassValidate: true, - }, - "seccomp, allow any container annotation": { - pspAnnotations: map[string]string{ - seccomp.AllowedProfilesAnnotationKey: seccomp.AllowAny, - }, - podAnnotations: map[string]string{ - kapi.SeccompContainerAnnotationKeyPrefix + containerName: "foo", - }, - shouldPassAdmit: true, - shouldPassValidate: true, - }, - "seccomp, allow specific pod annotation failure": { - pspAnnotations: map[string]string{ - seccomp.AllowedProfilesAnnotationKey: "foo", - }, - podAnnotations: map[string]string{ - kapi.SeccompPodAnnotationKey: "bar", - }, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "seccomp, allow specific container annotation failure": { - pspAnnotations: map[string]string{ - // provide a default so we don't have to give the pod annotation - seccomp.DefaultProfileAnnotationKey: "foo", - seccomp.AllowedProfilesAnnotationKey: "foo", - }, - podAnnotations: map[string]string{ - kapi.SeccompContainerAnnotationKeyPrefix + containerName: "bar", - }, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "seccomp, allow specific pod annotation pass": { - pspAnnotations: map[string]string{ - seccomp.AllowedProfilesAnnotationKey: "foo", - }, - podAnnotations: map[string]string{ - kapi.SeccompPodAnnotationKey: "foo", - }, - shouldPassAdmit: true, - shouldPassValidate: true, - }, - "seccomp, allow specific container annotation pass": { - pspAnnotations: map[string]string{ - // provide a default so we don't have to give the pod annotation - seccomp.DefaultProfileAnnotationKey: "foo", - seccomp.AllowedProfilesAnnotationKey: "foo,bar", - }, - podAnnotations: map[string]string{ - kapi.SeccompContainerAnnotationKeyPrefix + containerName: "bar", - }, - shouldPassAdmit: true, - shouldPassValidate: true, - }, - } - for k, v := range tests { - psp := restrictivePSP() - psp.Annotations = v.pspAnnotations - pod := &kapi.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: v.podAnnotations, - }, - Spec: kapi.PodSpec{ - Containers: []kapi.Container{ - {Name: containerName}, - }, - }, - } - testPSPAdmit(k, []*policy.PodSecurityPolicy{psp}, pod, v.shouldPassAdmit, v.shouldPassValidate, psp.Name, t) - } -} - -func TestAdmitPrivileged(t *testing.T) { - createPodWithPriv := func(priv bool) *kapi.Pod { - pod := goodPod() - pod.Spec.Containers[0].SecurityContext.Privileged = &priv - return pod - } - - nonPrivilegedPSP := restrictivePSP() - nonPrivilegedPSP.Name = "non-priv" - nonPrivilegedPSP.Spec.Privileged = false - - privilegedPSP := restrictivePSP() - privilegedPSP.Name = "priv" - privilegedPSP.Spec.Privileged = true - - trueValue := true - falseValue := false - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedPriv *bool - expectedPSP string - }{ - "pod with priv=nil allowed under non priv PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{nonPrivilegedPSP}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPriv: nil, - expectedPSP: nonPrivilegedPSP.Name, - }, - "pod with priv=nil allowed under priv PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{privilegedPSP}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPriv: nil, - expectedPSP: privilegedPSP.Name, - }, - "pod with priv=false allowed under non priv PSP": { - pod: createPodWithPriv(false), - psps: []*policy.PodSecurityPolicy{nonPrivilegedPSP}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPriv: &falseValue, - expectedPSP: nonPrivilegedPSP.Name, - }, - "pod with priv=false allowed under priv PSP": { - pod: createPodWithPriv(false), - psps: []*policy.PodSecurityPolicy{privilegedPSP}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPriv: &falseValue, - expectedPSP: privilegedPSP.Name, - }, - "pod with priv=true denied by non priv PSP": { - pod: createPodWithPriv(true), - psps: []*policy.PodSecurityPolicy{nonPrivilegedPSP}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "pod with priv=true allowed by priv PSP": { - pod: createPodWithPriv(true), - psps: []*policy.PodSecurityPolicy{nonPrivilegedPSP, privilegedPSP}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPriv: &trueValue, - expectedPSP: privilegedPSP.Name, - }, - } - - for k, v := range tests { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - priv := v.pod.Spec.Containers[0].SecurityContext.Privileged - if (priv == nil) != (v.expectedPriv == nil) { - t.Errorf("%s expected privileged to be %v, got %v", k, v.expectedPriv, priv) - } else if priv != nil && *priv != *v.expectedPriv { - t.Errorf("%s expected privileged to be %v, got %v", k, *v.expectedPriv, *priv) - } - } - } -} - -func defaultPod(t *testing.T, pod *kapi.Pod) *kapi.Pod { - v1Pod := &v1.Pod{} - if err := legacyscheme.Scheme.Convert(pod, v1Pod, nil); err != nil { - t.Fatal(err) - } - legacyscheme.Scheme.Default(v1Pod) - apiPod := &kapi.Pod{} - if err := legacyscheme.Scheme.Convert(v1Pod, apiPod, nil); err != nil { - t.Fatal(err) - } - return apiPod -} - -func TestAdmitPreferNonmutating(t *testing.T) { - mutating1 := restrictivePSP() - mutating1.Name = "mutating1" - mutating1.Spec.RunAsUser.Ranges = []policy.IDRange{{Min: int64(1), Max: int64(1)}} - - mutating2 := restrictivePSP() - mutating2.Name = "mutating2" - mutating2.Spec.RunAsUser.Ranges = []policy.IDRange{{Min: int64(2), Max: int64(2)}} - - privilegedPSP := permissivePSP() - privilegedPSP.Name = "privileged" - - unprivilegedRunAsAnyPod := defaultPod(t, &kapi.Pod{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: kapi.PodSpec{ - ServiceAccountName: "default", - Containers: []kapi.Container{{Name: "mycontainer", Image: "myimage"}}, - }, - }) - changedPod := unprivilegedRunAsAnyPod.DeepCopy() - changedPod.Spec.Containers[0].Image = "myimage2" - - podWithSC := unprivilegedRunAsAnyPod.DeepCopy() - podWithSC.Annotations = map[string]string{psputil.ValidatedPSPAnnotation: privilegedPSP.Name} - changedPodWithSC := changedPod.DeepCopy() - changedPodWithSC.Annotations = map[string]string{psputil.ValidatedPSPAnnotation: privilegedPSP.Name} - - gcChangedPod := unprivilegedRunAsAnyPod.DeepCopy() - gcChangedPod.OwnerReferences = []metav1.OwnerReference{{Kind: "Foo", Name: "bar"}} - gcChangedPod.Finalizers = []string{"foo"} - - podWithAnnotation := unprivilegedRunAsAnyPod.DeepCopy() - podWithAnnotation.ObjectMeta.Annotations = map[string]string{ - // "mutating2" is lexicographically behind "mutating1", so "mutating1" should be - // chosen because it's the canonical PSP order. - psputil.ValidatedPSPAnnotation: mutating2.Name, - } - - tests := map[string]struct { - operation kadmission.Operation - pod *kapi.Pod - podBeforeUpdate *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassValidate bool - expectMutation bool - expectedContainerUser *int64 - expectedPSP string - }{ - "pod should not be mutated by allow-all strategies": { - operation: kadmission.Create, - pod: unprivilegedRunAsAnyPod.DeepCopy(), - psps: []*policy.PodSecurityPolicy{privilegedPSP}, - shouldPassValidate: true, - expectMutation: false, - expectedContainerUser: nil, - expectedPSP: privilegedPSP.Name, - }, - "pod should prefer non-mutating PSP on create": { - operation: kadmission.Create, - pod: unprivilegedRunAsAnyPod.DeepCopy(), - psps: []*policy.PodSecurityPolicy{mutating2, mutating1, privilegedPSP}, - shouldPassValidate: true, - expectMutation: false, - expectedContainerUser: nil, - expectedPSP: privilegedPSP.Name, - }, - "pod should use deterministic mutating PSP on create": { - operation: kadmission.Create, - pod: unprivilegedRunAsAnyPod.DeepCopy(), - psps: []*policy.PodSecurityPolicy{mutating2, mutating1}, - shouldPassValidate: true, - expectMutation: true, - expectedContainerUser: &mutating1.Spec.RunAsUser.Ranges[0].Min, - expectedPSP: mutating1.Name, - }, - "pod should use deterministic mutating PSP on create even if ValidatedPSPAnnotation is set": { - operation: kadmission.Create, - pod: podWithAnnotation, - psps: []*policy.PodSecurityPolicy{mutating2, mutating1}, - shouldPassValidate: true, - expectMutation: true, - expectedContainerUser: &mutating1.Spec.RunAsUser.Ranges[0].Min, - expectedPSP: mutating1.Name, - }, - "pod should prefer non-mutating PSP on update": { - operation: kadmission.Update, - pod: changedPodWithSC.DeepCopy(), - podBeforeUpdate: podWithSC.DeepCopy(), - psps: []*policy.PodSecurityPolicy{mutating2, mutating1, privilegedPSP}, - shouldPassValidate: true, - expectMutation: false, - expectedContainerUser: nil, - expectedPSP: privilegedPSP.Name, - }, - "pod should not mutate on update, but fail validation": { - operation: kadmission.Update, - pod: changedPod.DeepCopy(), - podBeforeUpdate: unprivilegedRunAsAnyPod.DeepCopy(), - psps: []*policy.PodSecurityPolicy{mutating2, mutating1}, - shouldPassValidate: false, - expectMutation: false, - expectedContainerUser: nil, - expectedPSP: "", - }, - "pod should be allowed if completely unchanged on update": { - operation: kadmission.Update, - pod: unprivilegedRunAsAnyPod.DeepCopy(), - podBeforeUpdate: unprivilegedRunAsAnyPod.DeepCopy(), - psps: []*policy.PodSecurityPolicy{mutating2, mutating1}, - shouldPassValidate: true, - expectMutation: false, - expectedContainerUser: nil, - expectedPSP: "", - }, - "pod should be allowed if unchanged on update except finalizers,ownerrefs": { - operation: kadmission.Update, - pod: gcChangedPod.DeepCopy(), - podBeforeUpdate: unprivilegedRunAsAnyPod.DeepCopy(), - psps: []*policy.PodSecurityPolicy{mutating2, mutating1}, - shouldPassValidate: true, - expectMutation: false, - expectedContainerUser: nil, - expectedPSP: "", - }, - } - - for k, v := range tests { - testPSPAdmitAdvanced(k, v.operation, v.psps, nil, &user.DefaultInfo{}, v.pod, v.podBeforeUpdate, true, v.shouldPassValidate, v.expectMutation, v.expectedPSP, t) - - actualPodUser := (*int64)(nil) - if v.pod.Spec.SecurityContext != nil { - actualPodUser = v.pod.Spec.SecurityContext.RunAsUser - } - if actualPodUser != nil { - t.Errorf("%s expected pod user nil, got %v", k, *actualPodUser) - } - - actualContainerUser := (*int64)(nil) - if v.pod.Spec.Containers[0].SecurityContext != nil { - actualContainerUser = v.pod.Spec.Containers[0].SecurityContext.RunAsUser - } - if (actualContainerUser == nil) != (v.expectedContainerUser == nil) { - t.Errorf("%s expected container user %v, got %v", k, v.expectedContainerUser, actualContainerUser) - } else if actualContainerUser != nil && *actualContainerUser != *v.expectedContainerUser { - t.Errorf("%s expected container user %v, got %v", k, *v.expectedContainerUser, *actualContainerUser) - } - } -} - -func TestFailClosedOnInvalidPod(t *testing.T) { - plugin := NewTestAdmission(nil, nil) - pod := &v1.Pod{} - attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, false, &user.DefaultInfo{}) - - err := plugin.Admit(attrs) - if err == nil { - t.Fatalf("expected versioned pod object to fail mutating admission") - } - if !strings.Contains(err.Error(), "unexpected type") { - t.Errorf("expected type error on Admit but got: %v", err) - } - - err = plugin.Validate(attrs) - if err == nil { - t.Fatalf("expected versioned pod object to fail validating admission") - } - if !strings.Contains(err.Error(), "unexpected type") { - t.Errorf("expected type error on Validate but got: %v", err) - } -} - -func TestAdmitCaps(t *testing.T) { - createPodWithCaps := func(caps *kapi.Capabilities) *kapi.Pod { - pod := goodPod() - pod.Spec.Containers[0].SecurityContext.Capabilities = caps - return pod - } - - restricted := restrictivePSP() - - allowsFooInAllowed := restrictivePSP() - allowsFooInAllowed.Name = "allowCapInAllowed" - allowsFooInAllowed.Spec.AllowedCapabilities = []v1.Capability{"foo"} - - allowsFooInRequired := restrictivePSP() - allowsFooInRequired.Name = "allowCapInRequired" - allowsFooInRequired.Spec.DefaultAddCapabilities = []v1.Capability{"foo"} - - requiresFooToBeDropped := restrictivePSP() - requiresFooToBeDropped.Name = "requireDrop" - requiresFooToBeDropped.Spec.RequiredDropCapabilities = []v1.Capability{"foo"} - - allowAllInAllowed := restrictivePSP() - allowAllInAllowed.Name = "allowAllCapsInAllowed" - allowAllInAllowed.Spec.AllowedCapabilities = []v1.Capability{policy.AllowAllCapabilities} - - tc := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedCapabilities *kapi.Capabilities - expectedPSP string - }{ - // UC 1: if a PSP does not define allowed or required caps then a pod requesting a cap - // should be rejected. - "should reject cap add when not allowed or required": { - pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}), - psps: []*policy.PodSecurityPolicy{restricted}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - // UC 2: if a PSP allows a cap in the allowed field it should accept the pod request - // to add the cap. - "should accept cap add when in allowed": { - pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}), - psps: []*policy.PodSecurityPolicy{restricted, allowsFooInAllowed}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: allowsFooInAllowed.Name, - }, - // UC 3: if a PSP requires a cap then it should accept the pod request - // to add the cap. - "should accept cap add when in required": { - pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}), - psps: []*policy.PodSecurityPolicy{restricted, allowsFooInRequired}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: allowsFooInRequired.Name, - }, - // UC 4: if a PSP requires a cap to be dropped then it should fail both - // in the verification of adds and verification of drops - "should reject cap add when requested cap is required to be dropped": { - pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}), - psps: []*policy.PodSecurityPolicy{restricted, requiresFooToBeDropped}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - // UC 5: if a PSP requires a cap to be dropped it should accept - // a manual request to drop the cap. - "should accept cap drop when cap is required to be dropped": { - pod: createPodWithCaps(&kapi.Capabilities{Drop: []kapi.Capability{"foo"}}), - psps: []*policy.PodSecurityPolicy{requiresFooToBeDropped}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: requiresFooToBeDropped.Name, - }, - // UC 6: required add is defaulted - "required add is defaulted": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{allowsFooInRequired}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedCapabilities: &kapi.Capabilities{ - Add: []kapi.Capability{"foo"}, - }, - expectedPSP: allowsFooInRequired.Name, - }, - // UC 7: required drop is defaulted - "required drop is defaulted": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{requiresFooToBeDropped}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedCapabilities: &kapi.Capabilities{ - Drop: []kapi.Capability{"foo"}, - }, - expectedPSP: requiresFooToBeDropped.Name, - }, - // UC 8: using '*' in allowed caps - "should accept cap add when all caps are allowed": { - pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}), - psps: []*policy.PodSecurityPolicy{restricted, allowAllInAllowed}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: allowAllInAllowed.Name, - }, - } - - for k, v := range tc { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.expectedCapabilities != nil { - if !reflect.DeepEqual(v.expectedCapabilities, v.pod.Spec.Containers[0].SecurityContext.Capabilities) { - t.Errorf("%s resulted in caps that were not expected - expected: %v, received: %v", k, v.expectedCapabilities, v.pod.Spec.Containers[0].SecurityContext.Capabilities) - } - } - } - - for k, v := range tc { - useInitContainers(v.pod) - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.expectedCapabilities != nil { - if !reflect.DeepEqual(v.expectedCapabilities, v.pod.Spec.InitContainers[0].SecurityContext.Capabilities) { - t.Errorf("%s resulted in caps that were not expected - expected: %v, received: %v", k, v.expectedCapabilities, v.pod.Spec.InitContainers[0].SecurityContext.Capabilities) - } - } - } - -} - -func TestAdmitVolumes(t *testing.T) { - val := reflect.ValueOf(kapi.VolumeSource{}) - - for i := 0; i < val.NumField(); i++ { - // reflectively create the volume source - fieldVal := val.Type().Field(i) - - volumeSource := kapi.VolumeSource{} - volumeSourceVolume := reflect.New(fieldVal.Type.Elem()) - - reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume) - volume := kapi.Volume{VolumeSource: volumeSource} - - // sanity check before moving on - fsType, err := psputil.GetVolumeFSType(volume) - if err != nil { - t.Errorf("error getting FSType for %s: %s", fieldVal.Name, err.Error()) - continue - } - - // add the volume to the pod - pod := goodPod() - pod.Spec.Volumes = []kapi.Volume{volume} - - // create a PSP that allows no volumes - psp := restrictivePSP() - - // expect a denial for this PSP - testPSPAdmit(fmt.Sprintf("%s denial", string(fsType)), []*policy.PodSecurityPolicy{psp}, pod, false, false, "", t) - - // also expect a denial for this PSP if it's an init container - useInitContainers(pod) - testPSPAdmit(fmt.Sprintf("%s denial", string(fsType)), []*policy.PodSecurityPolicy{psp}, pod, false, false, "", t) - - // now add the fstype directly to the psp and it should validate - psp.Spec.Volumes = []policy.FSType{fsType} - testPSPAdmit(fmt.Sprintf("%s direct accept", string(fsType)), []*policy.PodSecurityPolicy{psp}, pod, true, true, psp.Name, t) - - // now change the psp to allow any volumes and the pod should still validate - psp.Spec.Volumes = []policy.FSType{policy.All} - testPSPAdmit(fmt.Sprintf("%s wildcard accept", string(fsType)), []*policy.PodSecurityPolicy{psp}, pod, true, true, psp.Name, t) - } -} - -func TestAdmitHostNetwork(t *testing.T) { - createPodWithHostNetwork := func(hostNetwork bool) *kapi.Pod { - pod := goodPod() - pod.Spec.SecurityContext.HostNetwork = hostNetwork - return pod - } - - noHostNetwork := restrictivePSP() - noHostNetwork.Name = "no-hostnetwork" - noHostNetwork.Spec.HostNetwork = false - - hostNetwork := restrictivePSP() - hostNetwork.Name = "hostnetwork" - hostNetwork.Spec.HostNetwork = true - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedHostNetwork bool - expectedPSP string - }{ - "pod without hostnetwork request allowed under noHostNetwork PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{noHostNetwork}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedHostNetwork: false, - expectedPSP: noHostNetwork.Name, - }, - "pod without hostnetwork request allowed under hostNetwork PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{hostNetwork}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedHostNetwork: false, - expectedPSP: hostNetwork.Name, - }, - "pod with hostnetwork request denied by noHostNetwork PSP": { - pod: createPodWithHostNetwork(true), - psps: []*policy.PodSecurityPolicy{noHostNetwork}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "pod with hostnetwork request allowed by hostNetwork PSP": { - pod: createPodWithHostNetwork(true), - psps: []*policy.PodSecurityPolicy{noHostNetwork, hostNetwork}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedHostNetwork: true, - expectedPSP: hostNetwork.Name, - }, - } - - for k, v := range tests { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if v.pod.Spec.SecurityContext.HostNetwork != v.expectedHostNetwork { - t.Errorf("%s expected hostNetwork to be %t", k, v.expectedHostNetwork) - } - } - } - - // test again with init containers - for k, v := range tests { - useInitContainers(v.pod) - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if v.pod.Spec.SecurityContext.HostNetwork != v.expectedHostNetwork { - t.Errorf("%s expected hostNetwork to be %t", k, v.expectedHostNetwork) - } - } - } -} - -func TestAdmitHostPorts(t *testing.T) { - createPodWithHostPorts := func(port int32) *kapi.Pod { - pod := goodPod() - pod.Spec.Containers[0].Ports = []kapi.ContainerPort{ - {HostPort: port}, - } - return pod - } - - noHostPorts := restrictivePSP() - noHostPorts.Name = "noHostPorts" - - hostPorts := restrictivePSP() - hostPorts.Name = "hostPorts" - hostPorts.Spec.HostPorts = []policy.HostPortRange{ - {Min: 1, Max: 10}, - } - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedPSP string - }{ - "host port out of range": { - pod: createPodWithHostPorts(11), - psps: []*policy.PodSecurityPolicy{hostPorts}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "host port in range": { - pod: createPodWithHostPorts(5), - psps: []*policy.PodSecurityPolicy{hostPorts}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: hostPorts.Name, - }, - "no host ports with range": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{hostPorts}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: hostPorts.Name, - }, - "no host ports without range": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{noHostPorts}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: noHostPorts.Name, - }, - "host ports without range": { - pod: createPodWithHostPorts(5), - psps: []*policy.PodSecurityPolicy{noHostPorts}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - } - - for i := 0; i < 2; i++ { - for k, v := range tests { - v.pod.Spec.Containers, v.pod.Spec.InitContainers = v.pod.Spec.InitContainers, v.pod.Spec.Containers - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - } - } -} - -func TestAdmitHostPID(t *testing.T) { - createPodWithHostPID := func(hostPID bool) *kapi.Pod { - pod := goodPod() - pod.Spec.SecurityContext.HostPID = hostPID - return pod - } - - noHostPID := restrictivePSP() - noHostPID.Name = "no-hostpid" - noHostPID.Spec.HostPID = false - - hostPID := restrictivePSP() - hostPID.Name = "hostpid" - hostPID.Spec.HostPID = true - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedHostPID bool - expectedPSP string - }{ - "pod without hostpid request allowed under noHostPID PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{noHostPID}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedHostPID: false, - expectedPSP: noHostPID.Name, - }, - "pod without hostpid request allowed under hostPID PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{hostPID}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedHostPID: false, - expectedPSP: hostPID.Name, - }, - "pod with hostpid request denied by noHostPID PSP": { - pod: createPodWithHostPID(true), - psps: []*policy.PodSecurityPolicy{noHostPID}, - shouldPassAdmit: false, - }, - "pod with hostpid request allowed by hostPID PSP": { - pod: createPodWithHostPID(true), - psps: []*policy.PodSecurityPolicy{noHostPID, hostPID}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedHostPID: true, - expectedPSP: hostPID.Name, - }, - } - - for k, v := range tests { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if v.pod.Spec.SecurityContext.HostPID != v.expectedHostPID { - t.Errorf("%s expected hostPID to be %t", k, v.expectedHostPID) - } - } - } -} - -func TestAdmitHostIPC(t *testing.T) { - createPodWithHostIPC := func(hostIPC bool) *kapi.Pod { - pod := goodPod() - pod.Spec.SecurityContext.HostIPC = hostIPC - return pod - } - - noHostIPC := restrictivePSP() - noHostIPC.Name = "no-hostIPC" - noHostIPC.Spec.HostIPC = false - - hostIPC := restrictivePSP() - hostIPC.Name = "hostIPC" - hostIPC.Spec.HostIPC = true - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedHostIPC bool - expectedPSP string - }{ - "pod without hostIPC request allowed under noHostIPC PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{noHostIPC}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedHostIPC: false, - expectedPSP: noHostIPC.Name, - }, - "pod without hostIPC request allowed under hostIPC PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{hostIPC}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedHostIPC: false, - expectedPSP: hostIPC.Name, - }, - "pod with hostIPC request denied by noHostIPC PSP": { - pod: createPodWithHostIPC(true), - psps: []*policy.PodSecurityPolicy{noHostIPC}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "pod with hostIPC request allowed by hostIPC PSP": { - pod: createPodWithHostIPC(true), - psps: []*policy.PodSecurityPolicy{noHostIPC, hostIPC}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedHostIPC: true, - expectedPSP: hostIPC.Name, - }, - } - - for k, v := range tests { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if v.pod.Spec.SecurityContext.HostIPC != v.expectedHostIPC { - t.Errorf("%s expected hostIPC to be %t", k, v.expectedHostIPC) - } - } - } -} - -func createPodWithSecurityContexts(podSC *kapi.PodSecurityContext, containerSC *kapi.SecurityContext) *kapi.Pod { - pod := goodPod() - pod.Spec.SecurityContext = podSC - pod.Spec.Containers[0].SecurityContext = containerSC - return pod -} - -func TestAdmitSELinux(t *testing.T) { - runAsAny := permissivePSP() - runAsAny.Name = "runAsAny" - runAsAny.Spec.SELinux.Rule = policy.SELinuxStrategyRunAsAny - runAsAny.Spec.SELinux.SELinuxOptions = nil - - mustRunAs := permissivePSP() - mustRunAs.Name = "mustRunAs" - mustRunAs.Spec.SELinux.Rule = policy.SELinuxStrategyMustRunAs - mustRunAs.Spec.SELinux.SELinuxOptions = &v1.SELinuxOptions{} - mustRunAs.Spec.SELinux.SELinuxOptions.Level = "level" - mustRunAs.Spec.SELinux.SELinuxOptions.Role = "role" - mustRunAs.Spec.SELinux.SELinuxOptions.Type = "type" - mustRunAs.Spec.SELinux.SELinuxOptions.User = "user" - - getInternalSEOptions := func(policy *policy.PodSecurityPolicy) *kapi.SELinuxOptions { - opt := kapi.SELinuxOptions{} - k8s_api_v1.Convert_v1_SELinuxOptions_To_core_SELinuxOptions(policy.Spec.SELinux.SELinuxOptions, &opt, nil) - return &opt - } - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedPodSC *kapi.PodSecurityContext - expectedContainerSC *kapi.SecurityContext - expectedPSP string - }{ - "runAsAny with no request": { - pod: createPodWithSecurityContexts(nil, nil), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: nil, - expectedContainerSC: nil, - expectedPSP: runAsAny.Name, - }, - "runAsAny with empty pod request": { - pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{}, nil), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: &kapi.PodSecurityContext{}, - expectedContainerSC: nil, - expectedPSP: runAsAny.Name, - }, - "runAsAny with empty container request": { - pod: createPodWithSecurityContexts(nil, &kapi.SecurityContext{}), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: nil, - expectedContainerSC: &kapi.SecurityContext{}, - expectedPSP: runAsAny.Name, - }, - - "runAsAny with pod request": { - pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}, nil), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}, - expectedContainerSC: nil, - expectedPSP: runAsAny.Name, - }, - "runAsAny with container request": { - pod: createPodWithSecurityContexts(nil, &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: nil, - expectedContainerSC: &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}, - expectedPSP: runAsAny.Name, - }, - "runAsAny with pod and container request": { - pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "bar"}}, &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "bar"}}, - expectedContainerSC: &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}, - expectedPSP: runAsAny.Name, - }, - - "mustRunAs with bad pod request": { - pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}, nil), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "mustRunAs with bad container request": { - pod: createPodWithSecurityContexts(nil, &kapi.SecurityContext{SELinuxOptions: &kapi.SELinuxOptions{User: "foo"}}), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "mustRunAs with no request": { - pod: createPodWithSecurityContexts(nil, nil), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: getInternalSEOptions(mustRunAs)}, - expectedContainerSC: nil, - expectedPSP: mustRunAs.Name, - }, - "mustRunAs with good pod request": { - pod: createPodWithSecurityContexts( - &kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{Level: "level", Role: "role", Type: "type", User: "user"}}, - nil, - ), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: getInternalSEOptions(mustRunAs)}, - expectedContainerSC: nil, - expectedPSP: mustRunAs.Name, - }, - "mustRunAs with good container request": { - pod: createPodWithSecurityContexts( - &kapi.PodSecurityContext{SELinuxOptions: &kapi.SELinuxOptions{Level: "level", Role: "role", Type: "type", User: "user"}}, - nil, - ), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: &kapi.PodSecurityContext{SELinuxOptions: getInternalSEOptions(mustRunAs)}, - expectedContainerSC: nil, - expectedPSP: mustRunAs.Name, - }, - } - - for k, v := range tests { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if !reflect.DeepEqual(v.expectedPodSC, v.pod.Spec.SecurityContext) { - t.Errorf("%s unexpected diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedPodSC, v.pod.Spec.SecurityContext)) - } - if !reflect.DeepEqual(v.expectedContainerSC, v.pod.Spec.Containers[0].SecurityContext) { - t.Errorf("%s unexpected diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedContainerSC, v.pod.Spec.Containers[0].SecurityContext)) - } - } - } -} - -func TestAdmitAppArmor(t *testing.T) { - createPodWithAppArmor := func(profile string) *kapi.Pod { - pod := goodPod() - apparmor.SetProfileNameFromPodAnnotations(pod.Annotations, defaultContainerName, profile) - return pod - } - - unconstrainedPSP := restrictivePSP() - defaultedPSP := restrictivePSP() - defaultedPSP.Annotations = map[string]string{ - apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault, - } - appArmorPSP := restrictivePSP() - appArmorPSP.Annotations = map[string]string{ - apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault, - } - appArmorDefaultPSP := restrictivePSP() - appArmorDefaultPSP.Annotations = map[string]string{ - apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault, - apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault + "," + apparmor.ProfileNamePrefix + "foo", - } - - tests := map[string]struct { - pod *kapi.Pod - psp *policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedProfile string - }{ - "unconstrained with no profile": { - pod: goodPod(), - psp: unconstrainedPSP, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedProfile: "", - }, - "unconstrained with profile": { - pod: createPodWithAppArmor(apparmor.ProfileRuntimeDefault), - psp: unconstrainedPSP, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedProfile: apparmor.ProfileRuntimeDefault, - }, - "unconstrained with default profile": { - pod: goodPod(), - psp: defaultedPSP, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedProfile: apparmor.ProfileRuntimeDefault, - }, - "AppArmor enforced with no profile": { - pod: goodPod(), - psp: appArmorPSP, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "AppArmor enforced with default profile": { - pod: goodPod(), - psp: appArmorDefaultPSP, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedProfile: apparmor.ProfileRuntimeDefault, - }, - "AppArmor enforced with good profile": { - pod: createPodWithAppArmor(apparmor.ProfileNamePrefix + "foo"), - psp: appArmorDefaultPSP, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedProfile: apparmor.ProfileNamePrefix + "foo", - }, - "AppArmor enforced with local profile": { - pod: createPodWithAppArmor(apparmor.ProfileNamePrefix + "bar"), - psp: appArmorPSP, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - } - - for k, v := range tests { - testPSPAdmit(k, []*policy.PodSecurityPolicy{v.psp}, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.psp.Name, t) - - if v.shouldPassAdmit { - assert.Equal(t, v.expectedProfile, apparmor.GetProfileNameFromPodAnnotations(v.pod.Annotations, defaultContainerName), k) - } - } -} - -func TestAdmitRunAsUser(t *testing.T) { - podSC := func(user *int64) *kapi.PodSecurityContext { - return &kapi.PodSecurityContext{RunAsUser: user} - } - containerSC := func(user *int64) *kapi.SecurityContext { - return &kapi.SecurityContext{RunAsUser: user} - } - - runAsAny := permissivePSP() - runAsAny.Name = "runAsAny" - runAsAny.Spec.RunAsUser.Rule = policy.RunAsUserStrategyRunAsAny - - mustRunAs := permissivePSP() - mustRunAs.Name = "mustRunAs" - mustRunAs.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAs - mustRunAs.Spec.RunAsUser.Ranges = []policy.IDRange{ - {Min: int64(999), Max: int64(1000)}, - } - - runAsNonRoot := permissivePSP() - runAsNonRoot.Name = "runAsNonRoot" - runAsNonRoot.Spec.RunAsUser.Rule = policy.RunAsUserStrategyMustRunAsNonRoot - - trueValue := true - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedPodSC *kapi.PodSecurityContext - expectedContainerSC *kapi.SecurityContext - expectedPSP string - }{ - "runAsAny no pod request": { - pod: createPodWithSecurityContexts(nil, nil), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: nil, - expectedContainerSC: nil, - expectedPSP: runAsAny.Name, - }, - "runAsAny pod request": { - pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), nil), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: podSC(utilpointer.Int64Ptr(1)), - expectedContainerSC: nil, - expectedPSP: runAsAny.Name, - }, - "runAsAny container request": { - pod: createPodWithSecurityContexts(nil, containerSC(utilpointer.Int64Ptr(1))), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: nil, - expectedContainerSC: containerSC(utilpointer.Int64Ptr(1)), - expectedPSP: runAsAny.Name, - }, - - "mustRunAs pod request out of range": { - pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), nil), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "mustRunAs container request out of range": { - pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(999)), containerSC(utilpointer.Int64Ptr(1))), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - - "mustRunAs pod request in range": { - pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(999)), nil), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: podSC(&mustRunAs.Spec.RunAsUser.Ranges[0].Min), - expectedContainerSC: nil, - expectedPSP: mustRunAs.Name, - }, - "mustRunAs container request in range": { - pod: createPodWithSecurityContexts(nil, containerSC(utilpointer.Int64Ptr(999))), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: nil, - expectedContainerSC: containerSC(&mustRunAs.Spec.RunAsUser.Ranges[0].Min), - expectedPSP: mustRunAs.Name, - }, - "mustRunAs pod and container request in range": { - pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(999)), containerSC(utilpointer.Int64Ptr(1000))), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: podSC(utilpointer.Int64Ptr(999)), - expectedContainerSC: containerSC(utilpointer.Int64Ptr(1000)), - expectedPSP: mustRunAs.Name, - }, - "mustRunAs no request": { - pod: createPodWithSecurityContexts(nil, nil), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: nil, - expectedContainerSC: containerSC(&mustRunAs.Spec.RunAsUser.Ranges[0].Min), - expectedPSP: mustRunAs.Name, - }, - - "runAsNonRoot no request": { - pod: createPodWithSecurityContexts(nil, nil), - psps: []*policy.PodSecurityPolicy{runAsNonRoot}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: nil, - expectedContainerSC: &kapi.SecurityContext{RunAsNonRoot: &trueValue}, - expectedPSP: runAsNonRoot.Name, - }, - "runAsNonRoot pod request root": { - pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(0)), nil), - psps: []*policy.PodSecurityPolicy{runAsNonRoot}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "runAsNonRoot pod request non-root": { - pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), nil), - psps: []*policy.PodSecurityPolicy{runAsNonRoot}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: podSC(utilpointer.Int64Ptr(1)), - expectedPSP: runAsNonRoot.Name, - }, - "runAsNonRoot container request root": { - pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), containerSC(utilpointer.Int64Ptr(0))), - psps: []*policy.PodSecurityPolicy{runAsNonRoot}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "runAsNonRoot container request non-root": { - pod: createPodWithSecurityContexts(podSC(utilpointer.Int64Ptr(1)), containerSC(utilpointer.Int64Ptr(2))), - psps: []*policy.PodSecurityPolicy{runAsNonRoot}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: podSC(utilpointer.Int64Ptr(1)), - expectedContainerSC: containerSC(utilpointer.Int64Ptr(2)), - expectedPSP: runAsNonRoot.Name, - }, - } - - for k, v := range tests { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if !reflect.DeepEqual(v.expectedPodSC, v.pod.Spec.SecurityContext) { - t.Errorf("%s unexpected pod sc diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedPodSC, v.pod.Spec.SecurityContext)) - } - if !reflect.DeepEqual(v.expectedContainerSC, v.pod.Spec.Containers[0].SecurityContext) { - t.Errorf("%s unexpected container sc diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedContainerSC, v.pod.Spec.Containers[0].SecurityContext)) - } - } - } -} - -func TestAdmitSupplementalGroups(t *testing.T) { - podSC := func(group int64) *kapi.PodSecurityContext { - return &kapi.PodSecurityContext{SupplementalGroups: []int64{group}} - } - - runAsAny := permissivePSP() - runAsAny.Name = "runAsAny" - runAsAny.Spec.SupplementalGroups.Rule = policy.SupplementalGroupsStrategyRunAsAny - - mustRunAs := permissivePSP() - mustRunAs.Name = "mustRunAs" - mustRunAs.Spec.SupplementalGroups.Rule = policy.SupplementalGroupsStrategyMustRunAs - mustRunAs.Spec.SupplementalGroups.Ranges = []policy.IDRange{{Min: int64(999), Max: int64(1000)}} - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedPodSC *kapi.PodSecurityContext - expectedPSP string - }{ - "runAsAny no pod request": { - pod: createPodWithSecurityContexts(nil, nil), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: nil, - expectedPSP: runAsAny.Name, - }, - "runAsAny empty pod request": { - pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{}, nil), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: &kapi.PodSecurityContext{}, - expectedPSP: runAsAny.Name, - }, - "runAsAny empty pod request empty supplemental groups": { - pod: createPodWithSecurityContexts(&kapi.PodSecurityContext{SupplementalGroups: []int64{}}, nil), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: &kapi.PodSecurityContext{SupplementalGroups: []int64{}}, - expectedPSP: runAsAny.Name, - }, - "runAsAny pod request": { - pod: createPodWithSecurityContexts(podSC(1), nil), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: &kapi.PodSecurityContext{SupplementalGroups: []int64{1}}, - expectedPSP: runAsAny.Name, - }, - "mustRunAs no pod request": { - pod: createPodWithSecurityContexts(nil, nil), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: podSC(mustRunAs.Spec.SupplementalGroups.Ranges[0].Min), - expectedPSP: mustRunAs.Name, - }, - "mustRunAs bad pod request": { - pod: createPodWithSecurityContexts(podSC(1), nil), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "mustRunAs good pod request": { - pod: createPodWithSecurityContexts(podSC(999), nil), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPodSC: podSC(999), - expectedPSP: mustRunAs.Name, - }, - } - - for k, v := range tests { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if !reflect.DeepEqual(v.expectedPodSC, v.pod.Spec.SecurityContext) { - t.Errorf("%s unexpected pod sc diff:\n%s", k, diff.ObjectGoPrintSideBySide(v.expectedPodSC, v.pod.Spec.SecurityContext)) - } - } - } -} - -func TestAdmitFSGroup(t *testing.T) { - createPodWithFSGroup := func(group int64) *kapi.Pod { - pod := goodPod() - // doesn't matter if we set it here or on the container, the - // admission controller uses DetermineEffectiveSC to get the defaulting - // behavior so it can validate what will be applied at runtime - pod.Spec.SecurityContext.FSGroup = utilpointer.Int64Ptr(group) - return pod - } - - runAsAny := restrictivePSP() - runAsAny.Name = "runAsAny" - runAsAny.Spec.FSGroup.Rule = policy.FSGroupStrategyRunAsAny - - mustRunAs := restrictivePSP() - mustRunAs.Name = "mustRunAs" - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedFSGroup *int64 - expectedPSP string - }{ - "runAsAny no pod request": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedFSGroup: nil, - expectedPSP: runAsAny.Name, - }, - "runAsAny pod request": { - pod: createPodWithFSGroup(1), - psps: []*policy.PodSecurityPolicy{runAsAny}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedFSGroup: utilpointer.Int64Ptr(1), - expectedPSP: runAsAny.Name, - }, - "mustRunAs no pod request": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedFSGroup: &mustRunAs.Spec.SupplementalGroups.Ranges[0].Min, - expectedPSP: mustRunAs.Name, - }, - "mustRunAs bad pod request": { - pod: createPodWithFSGroup(1), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "mustRunAs good pod request": { - pod: createPodWithFSGroup(999), - psps: []*policy.PodSecurityPolicy{mustRunAs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedFSGroup: utilpointer.Int64Ptr(999), - expectedPSP: mustRunAs.Name, - }, - } - - for k, v := range tests { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if v.pod.Spec.SecurityContext.FSGroup == nil && v.expectedFSGroup == nil { - // ok, don't need to worry about identifying specific diffs - continue - } - if v.pod.Spec.SecurityContext.FSGroup == nil && v.expectedFSGroup != nil { - t.Errorf("%s expected FSGroup to be: %v but found nil", k, *v.expectedFSGroup) - continue - } - if v.pod.Spec.SecurityContext.FSGroup != nil && v.expectedFSGroup == nil { - t.Errorf("%s expected FSGroup to be nil but found: %v", k, *v.pod.Spec.SecurityContext.FSGroup) - continue - } - if *v.expectedFSGroup != *v.pod.Spec.SecurityContext.FSGroup { - t.Errorf("%s expected FSGroup to be: %v but found %v", k, *v.expectedFSGroup, *v.pod.Spec.SecurityContext.FSGroup) - } - } - } -} - -func TestAdmitReadOnlyRootFilesystem(t *testing.T) { - createPodWithRORFS := func(rorfs bool) *kapi.Pod { - pod := goodPod() - pod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &rorfs - return pod - } - - noRORFS := restrictivePSP() - noRORFS.Name = "no-rorfs" - noRORFS.Spec.ReadOnlyRootFilesystem = false - - rorfs := restrictivePSP() - rorfs.Name = "rorfs" - rorfs.Spec.ReadOnlyRootFilesystem = true - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedRORFS bool - expectedPSP string - }{ - "no-rorfs allows pod request with rorfs": { - pod: createPodWithRORFS(true), - psps: []*policy.PodSecurityPolicy{noRORFS}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedRORFS: true, - expectedPSP: noRORFS.Name, - }, - "no-rorfs allows pod request without rorfs": { - pod: createPodWithRORFS(false), - psps: []*policy.PodSecurityPolicy{noRORFS}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedRORFS: false, - expectedPSP: noRORFS.Name, - }, - "rorfs rejects pod request without rorfs": { - pod: createPodWithRORFS(false), - psps: []*policy.PodSecurityPolicy{rorfs}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "rorfs defaults nil pod request": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{rorfs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedRORFS: true, - expectedPSP: rorfs.Name, - }, - "rorfs accepts pod request with rorfs": { - pod: createPodWithRORFS(true), - psps: []*policy.PodSecurityPolicy{rorfs}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedRORFS: true, - expectedPSP: rorfs.Name, - }, - } - - for k, v := range tests { - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if v.pod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem == nil || - *v.pod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem != v.expectedRORFS { - t.Errorf("%s expected ReadOnlyRootFilesystem to be %t but found %#v", k, v.expectedRORFS, v.pod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem) - } - } - } -} - -func TestAdmitSysctls(t *testing.T) { - podWithSysctls := func(safeSysctls []string, unsafeSysctls []string) *kapi.Pod { - pod := goodPod() - dummySysctls := func(names []string) []kapi.Sysctl { - sysctls := make([]kapi.Sysctl, len(names)) - for i, n := range names { - sysctls[i].Name = n - sysctls[i].Value = "dummy" - } - return sysctls - } - pod.Spec.SecurityContext = &kapi.PodSecurityContext{ - Sysctls: dummySysctls(append(safeSysctls, unsafeSysctls...)), - } - - return pod - } - - safeSysctls := restrictivePSP() - safeSysctls.Name = "no sysctls" - - noSysctls := restrictivePSP() - noSysctls.Name = "empty sysctls" - noSysctls.Spec.ForbiddenSysctls = []string{"*"} - - mixedSysctls := restrictivePSP() - mixedSysctls.Name = "wildcard sysctls" - mixedSysctls.Spec.ForbiddenSysctls = []string{"net.*"} - mixedSysctls.Spec.AllowedUnsafeSysctls = []string{"a.*", "b.*"} - - aUnsafeSysctl := restrictivePSP() - aUnsafeSysctl.Name = "a sysctl" - aUnsafeSysctl.Spec.AllowedUnsafeSysctls = []string{"a"} - - bUnsafeSysctl := restrictivePSP() - bUnsafeSysctl.Name = "b sysctl" - bUnsafeSysctl.Spec.AllowedUnsafeSysctls = []string{"b"} - - cUnsafeSysctl := restrictivePSP() - cUnsafeSysctl.Name = "c sysctl" - cUnsafeSysctl.Spec.AllowedUnsafeSysctls = []string{"c"} - - catchallSysctls := restrictivePSP() - catchallSysctls.Name = "catchall sysctl" - catchallSysctls.Spec.AllowedUnsafeSysctls = []string{"*"} - - tests := map[string]struct { - pod *kapi.Pod - psps []*policy.PodSecurityPolicy - shouldPassAdmit bool - shouldPassValidate bool - expectedPSP string - }{ - "pod without any sysctls request allowed under safeSysctls PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{safeSysctls}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: safeSysctls.Name, - }, - "pod without any sysctls request allowed under noSysctls PSP": { - pod: goodPod(), - psps: []*policy.PodSecurityPolicy{noSysctls}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: noSysctls.Name, - }, - "pod with safe sysctls request allowed under safeSysctls PSP": { - pod: podWithSysctls([]string{"kernel.shm_rmid_forced", "net.ipv4.tcp_syncookies"}, []string{}), - psps: []*policy.PodSecurityPolicy{safeSysctls}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: safeSysctls.Name, - }, - "pod with unsafe sysctls request disallowed under noSysctls PSP": { - pod: podWithSysctls([]string{}, []string{"a", "b"}), - psps: []*policy.PodSecurityPolicy{noSysctls}, - shouldPassAdmit: false, - shouldPassValidate: false, - expectedPSP: noSysctls.Name, - }, - "pod with unsafe sysctls a, b request disallowed under aUnsafeSysctl SCC": { - pod: podWithSysctls([]string{}, []string{"a", "b"}), - psps: []*policy.PodSecurityPolicy{aUnsafeSysctl}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "pod with unsafe sysctls b request disallowed under aUnsafeSysctl SCC": { - pod: podWithSysctls([]string{}, []string{"b"}), - psps: []*policy.PodSecurityPolicy{aUnsafeSysctl}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "pod with unsafe sysctls a request allowed under aUnsafeSysctl SCC": { - pod: podWithSysctls([]string{}, []string{"a"}), - psps: []*policy.PodSecurityPolicy{aUnsafeSysctl}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: aUnsafeSysctl.Name, - }, - "pod with safe net sysctl request allowed under aUnsafeSysctl SCC": { - pod: podWithSysctls([]string{"net.ipv4.ip_local_port_range"}, []string{}), - psps: []*policy.PodSecurityPolicy{aUnsafeSysctl}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: aUnsafeSysctl.Name, - }, - "pod with safe sysctls request disallowed under noSysctls PSP": { - pod: podWithSysctls([]string{"net.ipv4.ip_local_port_range"}, []string{}), - psps: []*policy.PodSecurityPolicy{noSysctls}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "pod with matching sysctls request allowed under mixedSysctls PSP": { - pod: podWithSysctls([]string{"kernel.shm_rmid_forced"}, []string{"a.b", "b.a"}), - psps: []*policy.PodSecurityPolicy{mixedSysctls}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: mixedSysctls.Name, - }, - "pod with not-matching unsafe sysctls request disallowed under mixedSysctls PSP": { - pod: podWithSysctls([]string{}, []string{"e"}), - psps: []*policy.PodSecurityPolicy{mixedSysctls}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "pod with not-matching safe sysctls request disallowed under mixedSysctls PSP": { - pod: podWithSysctls([]string{"net.ipv4.ip_local_port_range"}, []string{}), - psps: []*policy.PodSecurityPolicy{mixedSysctls}, - shouldPassAdmit: false, - shouldPassValidate: false, - }, - "pod with sysctls request allowed under catchallSysctls PSP": { - pod: podWithSysctls([]string{"net.ipv4.ip_local_port_range"}, []string{"f"}), - psps: []*policy.PodSecurityPolicy{catchallSysctls}, - shouldPassAdmit: true, - shouldPassValidate: true, - expectedPSP: catchallSysctls.Name, - }, - } - - for k, v := range tests { - origSysctl := v.pod.Spec.SecurityContext.Sysctls - - testPSPAdmit(k, v.psps, v.pod, v.shouldPassAdmit, v.shouldPassValidate, v.expectedPSP, t) - - if v.shouldPassAdmit { - if !reflect.DeepEqual(v.pod.Spec.SecurityContext.Sysctls, origSysctl) { - t.Errorf("%s: wrong sysctls: expected=%v, got=%v", k, origSysctl, v.pod.Spec.SecurityContext.Sysctls) - } - } - } -} - -func testPSPAdmit(testCaseName string, psps []*policy.PodSecurityPolicy, pod *kapi.Pod, shouldPassAdmit, shouldPassValidate bool, expectedPSP string, t *testing.T) { - testPSPAdmitAdvanced(testCaseName, kadmission.Create, psps, nil, &user.DefaultInfo{}, pod, nil, shouldPassAdmit, shouldPassValidate, true, expectedPSP, t) -} - -// fakeAttributes decorate kadmission.Attributes. It's used to trace the added annotations. -type fakeAttributes struct { - kadmission.Attributes - annotations map[string]string -} - -func (f fakeAttributes) AddAnnotation(k, v string) error { - f.annotations[k] = v - return f.Attributes.AddAnnotation(k, v) -} - -func testPSPAdmitAdvanced(testCaseName string, op kadmission.Operation, psps []*policy.PodSecurityPolicy, authz authorizer.Authorizer, userInfo user.Info, pod, oldPod *kapi.Pod, shouldPassAdmit, shouldPassValidate bool, canMutate bool, expectedPSP string, t *testing.T) { - originalPod := pod.DeepCopy() - plugin := NewTestAdmission(psps, authz) - - attrs := kadmission.NewAttributesRecord(pod, oldPod, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, "", kapi.Resource("pods").WithVersion("version"), "", op, false, userInfo) - annotations := make(map[string]string) - attrs = &fakeAttributes{attrs, annotations} - err := plugin.Admit(attrs) - - if shouldPassAdmit && err != nil { - t.Errorf("%s: expected no errors on Admit but received %v", testCaseName, err) - } - - if shouldPassAdmit && err == nil { - if pod.Annotations[psputil.ValidatedPSPAnnotation] != expectedPSP { - t.Errorf("%s: expected to be admitted under %q PSP but found %q", testCaseName, expectedPSP, pod.Annotations[psputil.ValidatedPSPAnnotation]) - } - - if !canMutate { - podWithoutPSPAnnotation := pod.DeepCopy() - delete(podWithoutPSPAnnotation.Annotations, psputil.ValidatedPSPAnnotation) - - originalPodWithoutPSPAnnotation := originalPod.DeepCopy() - delete(originalPodWithoutPSPAnnotation.Annotations, psputil.ValidatedPSPAnnotation) - - if !apiequality.Semantic.DeepEqual(originalPodWithoutPSPAnnotation.Spec, podWithoutPSPAnnotation.Spec) { - t.Errorf("%s: expected no mutation on Admit, got %s", testCaseName, diff.ObjectGoPrintSideBySide(originalPodWithoutPSPAnnotation.Spec, podWithoutPSPAnnotation.Spec)) - } - } - } - - if !shouldPassAdmit && err == nil { - t.Errorf("%s: expected errors on Admit but received none", testCaseName) - } - - err = plugin.Validate(attrs) - psp := "" - if shouldPassAdmit && op == kadmission.Create { - psp = expectedPSP - } - validateAuditAnnotation(t, testCaseName, annotations, "podsecuritypolicy.policy.k8s.io/admit-policy", psp) - if shouldPassValidate && err != nil { - t.Errorf("%s: expected no errors on Validate but received %v", testCaseName, err) - } else if !shouldPassValidate && err == nil { - t.Errorf("%s: expected errors on Validate but received none", testCaseName) - } - if shouldPassValidate { - validateAuditAnnotation(t, testCaseName, annotations, "podsecuritypolicy.policy.k8s.io/validate-policy", expectedPSP) - } else { - validateAuditAnnotation(t, testCaseName, annotations, "podsecuritypolicy.policy.k8s.io/validate-policy", "") - } -} - -func validateAuditAnnotation(t *testing.T, testCaseName string, annotations map[string]string, key, value string) { - if annotations[key] != value { - t.Errorf("%s: expected to have annotations[%s] set to %q, got %q", testCaseName, key, value, annotations[key]) - } -} - -func TestAssignSecurityContext(t *testing.T) { - // psp that will deny privileged container requests and has a default value for a field (uid) - psp := restrictivePSP() - provider, err := kpsp.NewSimpleProvider(psp, "namespace", kpsp.NewSimpleStrategyFactory()) - if err != nil { - t.Fatalf("failed to create provider: %v", err) - } - - createContainer := func(priv bool) kapi.Container { - return kapi.Container{ - SecurityContext: &kapi.SecurityContext{ - Privileged: &priv, - }, - } - } - - testCases := map[string]struct { - pod *kapi.Pod - shouldValidate bool - expectedUID *int64 - }{ - "pod and container SC is not changed when invalid": { - pod: &kapi.Pod{ - Spec: kapi.PodSpec{ - SecurityContext: &kapi.PodSecurityContext{}, - Containers: []kapi.Container{createContainer(true)}, - }, - }, - shouldValidate: false, - }, - "must validate all containers": { - pod: &kapi.Pod{ - Spec: kapi.PodSpec{ - // good container and bad container - SecurityContext: &kapi.PodSecurityContext{}, - Containers: []kapi.Container{createContainer(false), createContainer(true)}, - }, - }, - shouldValidate: false, - }, - "pod validates": { - pod: &kapi.Pod{ - Spec: kapi.PodSpec{ - SecurityContext: &kapi.PodSecurityContext{}, - Containers: []kapi.Container{createContainer(false)}, - }, - }, - shouldValidate: true, - }, - } - - for k, v := range testCases { - errs := assignSecurityContext(provider, v.pod) - if v.shouldValidate && len(errs) > 0 { - t.Errorf("%s expected to validate but received errors %v", k, errs) - continue - } - if !v.shouldValidate && len(errs) == 0 { - t.Errorf("%s expected validation errors but received none", k) - continue - } - } -} - -func TestCreateProvidersFromConstraints(t *testing.T) { - testCases := map[string]struct { - // use a generating function so we can test for non-mutation - psp func() *policy.PodSecurityPolicy - expectedErr string - }{ - "valid psp": { - psp: func() *policy.PodSecurityPolicy { - return &policy.PodSecurityPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "valid psp", - }, - Spec: policy.PodSecurityPolicySpec{ - SELinux: policy.SELinuxStrategyOptions{ - Rule: policy.SELinuxStrategyRunAsAny, - }, - RunAsUser: policy.RunAsUserStrategyOptions{ - Rule: policy.RunAsUserStrategyRunAsAny, - }, - RunAsGroup: &policy.RunAsGroupStrategyOptions{ - Rule: policy.RunAsGroupStrategyRunAsAny, - }, - FSGroup: policy.FSGroupStrategyOptions{ - Rule: policy.FSGroupStrategyRunAsAny, - }, - SupplementalGroups: policy.SupplementalGroupsStrategyOptions{ - Rule: policy.SupplementalGroupsStrategyRunAsAny, - }, - }, - } - }, - }, - "bad psp strategy options": { - psp: func() *policy.PodSecurityPolicy { - return &policy.PodSecurityPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "bad psp user options", - }, - Spec: policy.PodSecurityPolicySpec{ - SELinux: policy.SELinuxStrategyOptions{ - Rule: policy.SELinuxStrategyRunAsAny, - }, - RunAsUser: policy.RunAsUserStrategyOptions{ - Rule: policy.RunAsUserStrategyMustRunAs, - }, - RunAsGroup: &policy.RunAsGroupStrategyOptions{ - Rule: policy.RunAsGroupStrategyRunAsAny, - }, - FSGroup: policy.FSGroupStrategyOptions{ - Rule: policy.FSGroupStrategyRunAsAny, - }, - SupplementalGroups: policy.SupplementalGroupsStrategyOptions{ - Rule: policy.SupplementalGroupsStrategyRunAsAny, - }, - }, - } - }, - expectedErr: "MustRunAs requires at least one range", - }, - } - - for k, v := range testCases { - admit := &PodSecurityPolicyPlugin{ - Handler: kadmission.NewHandler(kadmission.Create, kadmission.Update), - strategyFactory: kpsp.NewSimpleStrategyFactory(), - } - - psp := v.psp() - _, errs := admit.createProvidersFromPolicies([]*policy.PodSecurityPolicy{psp}, "namespace") - - if !reflect.DeepEqual(psp, v.psp()) { - diff := diff.ObjectDiff(psp, v.psp()) - t.Errorf("%s createProvidersFromPolicies mutated policy. diff:\n%s", k, diff) - } - if len(v.expectedErr) > 0 && len(errs) != 1 { - t.Errorf("%s expected a single error '%s' but received %v", k, v.expectedErr, errs) - continue - } - if len(v.expectedErr) == 0 && len(errs) != 0 { - t.Errorf("%s did not expect an error but received %v", k, errs) - continue - } - - // check that we got the error we expected - if len(v.expectedErr) > 0 { - if !strings.Contains(errs[0].Error(), v.expectedErr) { - t.Errorf("%s expected error '%s' but received %v", k, v.expectedErr, errs[0]) - } - } - } -} - -func TestPolicyAuthorization(t *testing.T) { - policyWithName := func(name string) *policy.PodSecurityPolicy { - p := permissivePSP() - p.Name = name - return p - } - - tests := map[string]struct { - user user.Info - sa string - ns string - expectedPolicy string - inPolicies []*policy.PodSecurityPolicy - allowed map[string]map[string]map[string]bool - allowedGroup string - }{ - "policy allowed by user (extensions API Group)": { - user: &user.DefaultInfo{Name: "user"}, - sa: "sa", - ns: "test", - allowed: map[string]map[string]map[string]bool{ - "user": { - "test": {"policy": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")}, - expectedPolicy: "policy", - }, - "policy allowed by sa (extensions API Group)": { - user: &user.DefaultInfo{Name: "user"}, - sa: "sa", - ns: "test", - allowed: map[string]map[string]map[string]bool{ - serviceaccount.MakeUsername("test", "sa"): { - "test": {"policy": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")}, - expectedPolicy: "policy", - }, - "policy allowed by user (policy API Group)": { - user: &user.DefaultInfo{Name: "user"}, - sa: "sa", - ns: "test", - allowed: map[string]map[string]map[string]bool{ - "user": { - "test": {"policy": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")}, - expectedPolicy: "policy", - allowedGroup: policy.GroupName, - }, - "policy allowed by sa (policy API Group)": { - user: &user.DefaultInfo{Name: "user"}, - sa: "sa", - ns: "test", - allowed: map[string]map[string]map[string]bool{ - serviceaccount.MakeUsername("test", "sa"): { - "test": {"policy": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")}, - expectedPolicy: "policy", - allowedGroup: policy.GroupName, - }, - "no policies allowed": { - user: &user.DefaultInfo{Name: "user"}, - sa: "sa", - ns: "test", - allowed: map[string]map[string]map[string]bool{}, - inPolicies: []*policy.PodSecurityPolicy{policyWithName("policy")}, - expectedPolicy: "", - }, - "multiple policies allowed": { - user: &user.DefaultInfo{Name: "user"}, - sa: "sa", - ns: "test", - allowed: map[string]map[string]map[string]bool{ - serviceaccount.MakeUsername("test", "sa"): { - "test": {"policy1": true}, - "": {"policy4": true}, - "other": {"policy6": true}, - }, - "user": { - "test": {"policy2": true}, - "": {"policy5": true}, - "other": {"policy7": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{ - // Prefix to force checking these policies first. - policyWithName("a_policy1"), // not allowed in this namespace - policyWithName("a_policy2"), // not allowed in this namespace - policyWithName("policy2"), // allowed by sa - policyWithName("policy3"), // allowed by user - policyWithName("policy4"), // not allowed - policyWithName("policy5"), // allowed by sa at cluster level - policyWithName("policy6"), // allowed by user at cluster level - }, - expectedPolicy: "policy2", - }, - "policies are not allowed for nil user info": { - user: nil, - sa: "sa", - ns: "test", - allowed: map[string]map[string]map[string]bool{ - serviceaccount.MakeUsername("test", "sa"): { - "test": {"policy1": true}, - }, - "user": { - "test": {"policy2": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{ - policyWithName("policy1"), - policyWithName("policy2"), - policyWithName("policy3"), - }, - // only the policies for the sa are allowed when user info is nil - expectedPolicy: "policy1", - }, - "policies are not allowed for nil sa info": { - user: &user.DefaultInfo{Name: "user"}, - sa: "", - ns: "test", - allowed: map[string]map[string]map[string]bool{ - serviceaccount.MakeUsername("test", "sa"): { - "test": {"policy1": true}, - }, - "user": { - "test": {"policy2": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{ - policyWithName("policy1"), - policyWithName("policy2"), - policyWithName("policy3"), - }, - // only the policies for the user are allowed when sa info is nil - expectedPolicy: "policy2", - }, - "policies are not allowed for nil sa and user info": { - user: nil, - sa: "", - ns: "test", - allowed: map[string]map[string]map[string]bool{ - serviceaccount.MakeUsername("test", "sa"): { - "test": {"policy1": true}, - }, - "user": { - "test": {"policy2": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{ - policyWithName("policy1"), - policyWithName("policy2"), - policyWithName("policy3"), - }, - // no policies are allowed if sa and user are both nil - expectedPolicy: "", - }, - } - for k, v := range tests { - var ( - oldPod *kapi.Pod - shouldPass = v.expectedPolicy != "" - authz = &TestAuthorizer{usernameToNamespaceToAllowedPSPs: v.allowed, allowedAPIGroupName: v.allowedGroup} - canMutate = true - ) - pod := goodPod() - pod.Namespace = v.ns - pod.Spec.ServiceAccountName = v.sa - testPSPAdmitAdvanced(k, kadmission.Create, v.inPolicies, authz, v.user, - pod, oldPod, shouldPass, shouldPass, canMutate, v.expectedPolicy, t) - } -} - -func TestPolicyAuthorizationErrors(t *testing.T) { - policyWithName := func(name string) *policy.PodSecurityPolicy { - p := restrictivePSP() - p.Name = name - return p - } - - const ( - sa = "sa" - ns = "test" - userName = "user" - ) - - tests := map[string]struct { - inPolicies []*policy.PodSecurityPolicy - allowed map[string]map[string]map[string]bool - expectValidationErrs int - }{ - "policies not allowed": { - allowed: map[string]map[string]map[string]bool{}, - inPolicies: []*policy.PodSecurityPolicy{ - policyWithName("policy1"), - policyWithName("policy2"), - }, - expectValidationErrs: 0, - }, - "policy allowed by user": { - allowed: map[string]map[string]map[string]bool{ - "user": { - "test": {"policy1": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{ - policyWithName("policy1"), - policyWithName("policy2"), - }, - expectValidationErrs: 1, - }, - "policy allowed by service account": { - allowed: map[string]map[string]map[string]bool{ - serviceaccount.MakeUsername("test", "sa"): { - "test": {"policy2": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{ - policyWithName("policy1"), - policyWithName("policy2"), - }, - expectValidationErrs: 1, - }, - "multiple policies allowed": { - allowed: map[string]map[string]map[string]bool{ - "user": { - "test": {"policy1": true}, - }, - serviceaccount.MakeUsername("test", "sa"): { - "test": {"policy2": true}, - }, - }, - inPolicies: []*policy.PodSecurityPolicy{ - policyWithName("policy1"), - policyWithName("policy2"), - }, - expectValidationErrs: 2, - }, - } - for desc, tc := range tests { - t.Run(desc, func(t *testing.T) { - authz := &TestAuthorizer{usernameToNamespaceToAllowedPSPs: tc.allowed} - pod := goodPod() - pod.Namespace = ns - pod.Spec.ServiceAccountName = sa - pod.Spec.SecurityContext.HostPID = true - - plugin := NewTestAdmission(tc.inPolicies, authz) - attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), ns, "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, false, &user.DefaultInfo{Name: userName}) - - allowedPod, _, validationErrs, err := plugin.computeSecurityContext(attrs, pod, true, "") - assert.Nil(t, allowedPod) - assert.NoError(t, err) - assert.Len(t, validationErrs, tc.expectValidationErrs) - }) - } -} - -func TestPreferValidatedPSP(t *testing.T) { - restrictivePSPWithName := func(name string) *policy.PodSecurityPolicy { - p := restrictivePSP() - p.Name = name - return p - } - - permissivePSPWithName := func(name string) *policy.PodSecurityPolicy { - p := permissivePSP() - p.Name = name - return p - } - - tests := map[string]struct { - inPolicies []*policy.PodSecurityPolicy - expectValidationErrs int - validatedPSPHint string - expectedPSP string - }{ - "no policy saved in annotations, PSPs are ordered lexicographically": { - inPolicies: []*policy.PodSecurityPolicy{ - restrictivePSPWithName("001restrictive"), - restrictivePSPWithName("002restrictive"), - permissivePSPWithName("002permissive"), - permissivePSPWithName("001permissive"), - permissivePSPWithName("003permissive"), - }, - expectValidationErrs: 0, - validatedPSPHint: "", - expectedPSP: "001permissive", - }, - "policy saved in annotations is preferred": { - inPolicies: []*policy.PodSecurityPolicy{ - restrictivePSPWithName("001restrictive"), - restrictivePSPWithName("002restrictive"), - permissivePSPWithName("001permissive"), - permissivePSPWithName("002permissive"), - permissivePSPWithName("003permissive"), - }, - expectValidationErrs: 0, - validatedPSPHint: "002permissive", - expectedPSP: "002permissive", - }, - "policy saved in annotations is invalid": { - inPolicies: []*policy.PodSecurityPolicy{ - restrictivePSPWithName("001restrictive"), - restrictivePSPWithName("002restrictive"), - }, - expectValidationErrs: 2, - validatedPSPHint: "foo", - expectedPSP: "", - }, - "policy saved in annotations is disallowed anymore": { - inPolicies: []*policy.PodSecurityPolicy{ - restrictivePSPWithName("001restrictive"), - restrictivePSPWithName("002restrictive"), - }, - expectValidationErrs: 2, - validatedPSPHint: "001restrictive", - expectedPSP: "", - }, - "policy saved in annotations is disallowed anymore, but find another one": { - inPolicies: []*policy.PodSecurityPolicy{ - restrictivePSPWithName("001restrictive"), - restrictivePSPWithName("002restrictive"), - permissivePSPWithName("002permissive"), - permissivePSPWithName("001permissive"), - }, - expectValidationErrs: 0, - validatedPSPHint: "001restrictive", - expectedPSP: "001permissive", - }, - } - for desc, tc := range tests { - t.Run(desc, func(t *testing.T) { - authz := authorizerfactory.NewAlwaysAllowAuthorizer() - allowPrivilegeEscalation := true - pod := goodPod() - pod.Namespace = "ns" - pod.Spec.ServiceAccountName = "sa" - pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = &allowPrivilegeEscalation - - plugin := NewTestAdmission(tc.inPolicies, authz) - attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), "ns", "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Update, false, &user.DefaultInfo{Name: "test"}) - - _, pspName, validationErrs, err := plugin.computeSecurityContext(attrs, pod, false, tc.validatedPSPHint) - assert.NoError(t, err) - assert.Len(t, validationErrs, tc.expectValidationErrs) - assert.Equal(t, tc.expectedPSP, pspName) - }) - } -} - -func restrictivePSP() *policy.PodSecurityPolicy { - allowPrivilegeEscalation := false - return &policy.PodSecurityPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "restrictive", - Annotations: map[string]string{}, - }, - Spec: policy.PodSecurityPolicySpec{ - AllowPrivilegeEscalation: &allowPrivilegeEscalation, - RunAsUser: policy.RunAsUserStrategyOptions{ - Rule: policy.RunAsUserStrategyMustRunAs, - Ranges: []policy.IDRange{ - {Min: int64(999), Max: int64(999)}, - }, - }, - RunAsGroup: &policy.RunAsGroupStrategyOptions{ - Rule: policy.RunAsGroupStrategyMustRunAs, - Ranges: []policy.IDRange{ - {Min: int64(999), Max: int64(999)}, - }, - }, - SELinux: policy.SELinuxStrategyOptions{ - Rule: policy.SELinuxStrategyMustRunAs, - SELinuxOptions: &v1.SELinuxOptions{ - Level: "s9:z0,z1", - }, - }, - FSGroup: policy.FSGroupStrategyOptions{ - Rule: policy.FSGroupStrategyMustRunAs, - Ranges: []policy.IDRange{ - {Min: int64(999), Max: int64(999)}, - }, - }, - SupplementalGroups: policy.SupplementalGroupsStrategyOptions{ - Rule: policy.SupplementalGroupsStrategyMustRunAs, - Ranges: []policy.IDRange{ - {Min: int64(999), Max: int64(999)}, - }, - }, - }, - } -} - -func permissivePSP() *policy.PodSecurityPolicy { - allowPrivilegeEscalation := true - return &policy.PodSecurityPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "privileged", - Annotations: map[string]string{}, - }, - Spec: policy.PodSecurityPolicySpec{ - AllowPrivilegeEscalation: &allowPrivilegeEscalation, - HostIPC: true, - HostNetwork: true, - HostPID: true, - HostPorts: []policy.HostPortRange{{Min: 0, Max: 65536}}, - Volumes: []policy.FSType{policy.All}, - AllowedCapabilities: []v1.Capability{policy.AllowAllCapabilities}, - RunAsUser: policy.RunAsUserStrategyOptions{ - Rule: policy.RunAsUserStrategyRunAsAny, - }, - RunAsGroup: &policy.RunAsGroupStrategyOptions{ - Rule: policy.RunAsGroupStrategyRunAsAny, - }, - SELinux: policy.SELinuxStrategyOptions{ - Rule: policy.SELinuxStrategyRunAsAny, - }, - FSGroup: policy.FSGroupStrategyOptions{ - Rule: policy.FSGroupStrategyRunAsAny, - }, - SupplementalGroups: policy.SupplementalGroupsStrategyOptions{ - Rule: policy.SupplementalGroupsStrategyRunAsAny, - }, - }, - } -} - -// goodPod is empty and should not be used directly for testing since we're providing -// two different PSPs. Since no values are specified it would be allowed to match any -// psp when defaults are filled in. -func goodPod() *kapi.Pod { - return &kapi.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pod", - Namespace: "namespace", - Annotations: map[string]string{}, - }, - Spec: kapi.PodSpec{ - ServiceAccountName: "default", - SecurityContext: &kapi.PodSecurityContext{}, - Containers: []kapi.Container{ - { - Name: defaultContainerName, - SecurityContext: &kapi.SecurityContext{}, - }, - }, - }, - } -} diff --git a/plugin/pkg/admission/securitycontext/scdeny/BUILD b/plugin/pkg/admission/securitycontext/scdeny/BUILD deleted file mode 100644 index 7e9ec60624..0000000000 --- a/plugin/pkg/admission/securitycontext/scdeny/BUILD +++ /dev/null @@ -1,41 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny", - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["admission_test.go"], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/plugin/pkg/admission/securitycontext/scdeny/admission.go b/plugin/pkg/admission/securitycontext/scdeny/admission.go deleted file mode 100644 index 5ff6c07a0f..0000000000 --- a/plugin/pkg/admission/securitycontext/scdeny/admission.go +++ /dev/null @@ -1,100 +0,0 @@ -/* -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 scdeny - -import ( - "fmt" - "io" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// PluginName indicates name of admission plugin. -const PluginName = "SecurityContextDeny" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - return NewSecurityContextDeny(), nil - }) -} - -// Plugin implements admission.Interface. -type Plugin struct { - *admission.Handler -} - -var _ admission.ValidationInterface = &Plugin{} - -// NewSecurityContextDeny creates a new instance of the SecurityContextDeny admission controller -func NewSecurityContextDeny() *Plugin { - return &Plugin{ - Handler: admission.NewHandler(admission.Create, admission.Update), - } -} - -// Validate will deny any pod that defines SupplementalGroups, SELinuxOptions, RunAsUser or FSGroup -func (p *Plugin) Validate(a admission.Attributes) (err error) { - if a.GetSubresource() != "" || a.GetResource().GroupResource() != api.Resource("pods") { - return nil - } - - pod, ok := a.GetObject().(*api.Pod) - if !ok { - return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted") - } - - if pod.Spec.SecurityContext != nil { - if pod.Spec.SecurityContext.SupplementalGroups != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.SupplementalGroups is forbidden")) - } - if pod.Spec.SecurityContext.SELinuxOptions != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.SELinuxOptions is forbidden")) - } - if pod.Spec.SecurityContext.RunAsUser != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.RunAsUser is forbidden")) - } - if pod.Spec.SecurityContext.FSGroup != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.FSGroup is forbidden")) - } - } - - for _, v := range pod.Spec.InitContainers { - if v.SecurityContext != nil { - if v.SecurityContext.SELinuxOptions != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.SELinuxOptions is forbidden")) - } - if v.SecurityContext.RunAsUser != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.RunAsUser is forbidden")) - } - } - } - - for _, v := range pod.Spec.Containers { - if v.SecurityContext != nil { - if v.SecurityContext.SELinuxOptions != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.SELinuxOptions is forbidden")) - } - if v.SecurityContext.RunAsUser != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.RunAsUser is forbidden")) - } - } - } - return nil -} diff --git a/plugin/pkg/admission/securitycontext/scdeny/admission_test.go b/plugin/pkg/admission/securitycontext/scdeny/admission_test.go deleted file mode 100644 index aca4352bec..0000000000 --- a/plugin/pkg/admission/securitycontext/scdeny/admission_test.go +++ /dev/null @@ -1,179 +0,0 @@ -/* -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 scdeny - -import ( - "testing" - - "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// ensures the SecurityContext is denied if it defines anything more than Caps or Privileged -func TestAdmission(t *testing.T) { - handler := NewSecurityContextDeny() - - runAsUser := int64(1) - priv := true - - cases := []struct { - name string - sc *api.SecurityContext - podSc *api.PodSecurityContext - expectError bool - }{ - { - name: "unset", - }, - { - name: "empty container.SecurityContext", - sc: &api.SecurityContext{}, - }, - { - name: "empty pod.Spec.SecurityContext", - podSc: &api.PodSecurityContext{}, - }, - { - name: "valid container.SecurityContext", - sc: &api.SecurityContext{Privileged: &priv, Capabilities: &api.Capabilities{}}, - }, - { - name: "valid pod.Spec.SecurityContext", - podSc: &api.PodSecurityContext{}, - }, - { - name: "container.SecurityContext.RunAsUser", - sc: &api.SecurityContext{RunAsUser: &runAsUser}, - expectError: true, - }, - { - name: "container.SecurityContext.SELinuxOptions", - sc: &api.SecurityContext{SELinuxOptions: &api.SELinuxOptions{}}, - expectError: true, - }, - { - name: "pod.Spec.SecurityContext.RunAsUser", - podSc: &api.PodSecurityContext{RunAsUser: &runAsUser}, - expectError: true, - }, - { - name: "pod.Spec.SecurityContext.SELinuxOptions", - podSc: &api.PodSecurityContext{SELinuxOptions: &api.SELinuxOptions{}}, - expectError: true, - }, - } - - for _, tc := range cases { - p := pod() - p.Spec.SecurityContext = tc.podSc - p.Spec.Containers[0].SecurityContext = tc.sc - - err := handler.Validate(admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", false, nil)) - if err != nil && !tc.expectError { - t.Errorf("%v: unexpected error: %v", tc.name, err) - } else if err == nil && tc.expectError { - t.Errorf("%v: expected error", tc.name) - } - - // verify init containers are also checked - p = pod() - p.Spec.SecurityContext = tc.podSc - p.Spec.Containers[0].SecurityContext = tc.sc - p.Spec.InitContainers = p.Spec.Containers - p.Spec.Containers = nil - - err = handler.Validate(admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", false, nil)) - if err != nil && !tc.expectError { - t.Errorf("%v: unexpected error: %v", tc.name, err) - } else if err == nil && tc.expectError { - t.Errorf("%v: expected error", tc.name) - } - } -} - -func TestPodSecurityContextAdmission(t *testing.T) { - handler := NewSecurityContextDeny() - pod := api.Pod{ - Spec: api.PodSpec{ - Containers: []api.Container{ - {}, - }, - }, - } - - fsGroup := int64(1001) - - tests := []struct { - securityContext api.PodSecurityContext - errorExpected bool - }{ - { - securityContext: api.PodSecurityContext{}, - errorExpected: false, - }, - { - securityContext: api.PodSecurityContext{ - SupplementalGroups: []int64{int64(1234)}, - }, - errorExpected: true, - }, - { - securityContext: api.PodSecurityContext{ - FSGroup: &fsGroup, - }, - errorExpected: true, - }, - } - for _, test := range tests { - pod.Spec.SecurityContext = &test.securityContext - err := handler.Validate(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", false, nil)) - - if test.errorExpected && err == nil { - t.Errorf("Expected error for security context %+v but did not get an error", test.securityContext) - } - - if !test.errorExpected && err != nil { - t.Errorf("Unexpected error %v for security context %+v", err, test.securityContext) - } - } -} - -func TestHandles(t *testing.T) { - handler := NewSecurityContextDeny() - tests := map[admission.Operation]bool{ - admission.Update: true, - admission.Create: true, - admission.Delete: false, - admission.Connect: false, - } - for op, expected := range tests { - result := handler.Handles(op) - if result != expected { - t.Errorf("Unexpected result for operation %s: %v\n", op, result) - } - } -} - -func pod() *api.Pod { - return &api.Pod{ - Spec: api.PodSpec{ - Containers: []api.Container{ - {}, - }, - }, - } -} diff --git a/plugin/pkg/admission/storage/storageobjectinuseprotection/BUILD b/plugin/pkg/admission/storage/storageobjectinuseprotection/BUILD deleted file mode 100644 index 6073526d6b..0000000000 --- a/plugin/pkg/admission/storage/storageobjectinuseprotection/BUILD +++ /dev/null @@ -1,51 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["admission.go"], - importpath = "k8s.io/kubernetes/plugin/pkg/admission/storage/storageobjectinuseprotection", - visibility = ["//visibility:public"], - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/features:go_default_library", - "//pkg/volume/util:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = [ - "admission_test.go", - "main_test.go", - ], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/features:go_default_library", - "//pkg/volume/util:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library", - "//vendor/github.com/davecgh/go-spew/spew:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], - visibility = ["//visibility:public"], -) diff --git a/plugin/pkg/admission/storage/storageobjectinuseprotection/admission.go b/plugin/pkg/admission/storage/storageobjectinuseprotection/admission.go deleted file mode 100644 index f9d769c326..0000000000 --- a/plugin/pkg/admission/storage/storageobjectinuseprotection/admission.go +++ /dev/null @@ -1,127 +0,0 @@ -/* -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 storageobjectinuseprotection - -import ( - "io" - - "k8s.io/klog" - - "k8s.io/apiserver/pkg/admission" - "k8s.io/apiserver/pkg/util/feature" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/features" - volumeutil "k8s.io/kubernetes/pkg/volume/util" -) - -const ( - // PluginName is the name of this admission controller plugin - PluginName = "StorageObjectInUseProtection" -) - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - plugin := newPlugin() - return plugin, nil - }) -} - -// storageProtectionPlugin holds state for and implements the admission plugin. -type storageProtectionPlugin struct { - *admission.Handler -} - -var _ admission.Interface = &storageProtectionPlugin{} - -// newPlugin creates a new admission plugin. -func newPlugin() *storageProtectionPlugin { - return &storageProtectionPlugin{ - Handler: admission.NewHandler(admission.Create), - } -} - -var ( - pvResource = api.Resource("persistentvolumes") - pvcResource = api.Resource("persistentvolumeclaims") -) - -// Admit sets finalizer on all PVCs(PVs). The finalizer is removed by -// PVCProtectionController(PVProtectionController) when it's not referenced. -// -// This prevents users from deleting a PVC that's used by a running pod. -// This also prevents admin from deleting a PV that's bound by a PVC -func (c *storageProtectionPlugin) Admit(a admission.Attributes) error { - if !feature.DefaultFeatureGate.Enabled(features.StorageObjectInUseProtection) { - return nil - } - - switch a.GetResource().GroupResource() { - case pvResource: - return c.admitPV(a) - case pvcResource: - return c.admitPVC(a) - - default: - return nil - } -} - -func (c *storageProtectionPlugin) admitPV(a admission.Attributes) error { - if len(a.GetSubresource()) != 0 { - return nil - } - - pv, ok := a.GetObject().(*api.PersistentVolume) - // if we can't convert the obj to PV, just return - if !ok { - return nil - } - for _, f := range pv.Finalizers { - if f == volumeutil.PVProtectionFinalizer { - // Finalizer is already present, nothing to do - return nil - } - } - klog.V(4).Infof("adding PV protection finalizer to %s", pv.Name) - pv.Finalizers = append(pv.Finalizers, volumeutil.PVProtectionFinalizer) - - return nil -} - -func (c *storageProtectionPlugin) admitPVC(a admission.Attributes) error { - if len(a.GetSubresource()) != 0 { - return nil - } - - pvc, ok := a.GetObject().(*api.PersistentVolumeClaim) - // if we can't convert the obj to PVC, just return - if !ok { - return nil - } - - for _, f := range pvc.Finalizers { - if f == volumeutil.PVCProtectionFinalizer { - // Finalizer is already present, nothing to do - return nil - } - } - - klog.V(4).Infof("adding PVC protection finalizer to %s/%s", pvc.Namespace, pvc.Name) - pvc.Finalizers = append(pvc.Finalizers, volumeutil.PVCProtectionFinalizer) - return nil -} diff --git a/plugin/pkg/admission/storage/storageobjectinuseprotection/admission_test.go b/plugin/pkg/admission/storage/storageobjectinuseprotection/admission_test.go deleted file mode 100644 index 54ee9da6ae..0000000000 --- a/plugin/pkg/admission/storage/storageobjectinuseprotection/admission_test.go +++ /dev/null @@ -1,147 +0,0 @@ -/* -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 storageobjectinuseprotection - -import ( - "reflect" - "testing" - - "github.com/davecgh/go-spew/spew" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apiserver/pkg/admission" - utilfeature "k8s.io/apiserver/pkg/util/feature" - utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/features" - volumeutil "k8s.io/kubernetes/pkg/volume/util" -) - -func TestAdmit(t *testing.T) { - claim := &api.PersistentVolumeClaim{ - TypeMeta: metav1.TypeMeta{ - Kind: "PersistentVolumeClaim", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "claim", - Namespace: "ns", - }, - } - - pv := &api.PersistentVolume{ - TypeMeta: metav1.TypeMeta{ - Kind: "PersistentVolume", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "pv", - }, - } - claimWithFinalizer := claim.DeepCopy() - claimWithFinalizer.Finalizers = []string{volumeutil.PVCProtectionFinalizer} - - pvWithFinalizer := pv.DeepCopy() - pvWithFinalizer.Finalizers = []string{volumeutil.PVProtectionFinalizer} - - tests := []struct { - name string - resource schema.GroupVersionResource - object runtime.Object - expectedObject runtime.Object - featureEnabled bool - namespace string - }{ - { - "create -> add finalizer", - api.SchemeGroupVersion.WithResource("persistentvolumeclaims"), - claim, - claimWithFinalizer, - true, - claim.Namespace, - }, - { - "finalizer already exists -> no new finalizer", - api.SchemeGroupVersion.WithResource("persistentvolumeclaims"), - claimWithFinalizer, - claimWithFinalizer, - true, - claimWithFinalizer.Namespace, - }, - { - "disabled feature -> no finalizer", - api.SchemeGroupVersion.WithResource("persistentvolumeclaims"), - claim, - claim, - false, - claim.Namespace, - }, - { - "create -> add finalizer", - api.SchemeGroupVersion.WithResource("persistentvolumes"), - pv, - pvWithFinalizer, - true, - pv.Namespace, - }, - { - "finalizer already exists -> no new finalizer", - api.SchemeGroupVersion.WithResource("persistentvolumes"), - pvWithFinalizer, - pvWithFinalizer, - true, - pvWithFinalizer.Namespace, - }, - { - "disabled feature -> no finalizer", - api.SchemeGroupVersion.WithResource("persistentvolumes"), - pv, - pv, - false, - pv.Namespace, - }, - } - - ctrl := newPlugin() - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StorageObjectInUseProtection, test.featureEnabled)() - obj := test.object.DeepCopyObject() - attrs := admission.NewAttributesRecord( - obj, // new object - obj.DeepCopyObject(), // old object, copy to be sure it's not modified - schema.GroupVersionKind{}, - test.namespace, - "foo", - test.resource, - "", // subresource - admission.Create, - false, // dryRun - nil, // userInfo - ) - - err := ctrl.Admit(attrs) - if err != nil { - t.Errorf("Test %q: got unexpected error: %v", test.name, err) - } - if !reflect.DeepEqual(test.expectedObject, obj) { - t.Errorf("Test %q: Expected object:\n%s\ngot:\n%s", test.name, spew.Sdump(test.expectedObject), spew.Sdump(obj)) - } - }) - } -} diff --git a/plugin/pkg/admission/storage/storageobjectinuseprotection/main_test.go b/plugin/pkg/admission/storage/storageobjectinuseprotection/main_test.go deleted file mode 100644 index 1c7d89390a..0000000000 --- a/plugin/pkg/admission/storage/storageobjectinuseprotection/main_test.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2018 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 storageobjectinuseprotection - -import ( - "testing" - - utilfeature "k8s.io/apiserver/pkg/util/feature" - utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" - _ "k8s.io/kubernetes/pkg/features" -) - -func TestMain(m *testing.M) { - utilfeaturetesting.VerifyFeatureGatesUnchanged(utilfeature.DefaultFeatureGate, m.Run) -}