mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 15:25:57 +00:00
Merge pull request #30302 from aveshagarwal/master-project-node-selector-taints-tolerations
Automatic merge from submit-queue (batch tested with PRs 43870, 30302, 42722, 43736) Admission plugin to merge pod and namespace tolerations for restricting pod placement on nodes ```release-note This admission plugin checks for tolerations on the pod being admitted and its namespace, and verifies if there is any conflict. If there is no conflict, then it merges the pod's namespace tolerations with the the pod's tolerations and it verifies them against its namespace' whitelist of tolerations and returns. If a namespace does not have its default or whitelist tolerations specified, then cluster level default and whitelist is used. An example of its versioned config: apiVersion: apiserver.k8s.io/v1alpha1 kind: AdmissionConfiguration plugins: - name: "PodTolerationRestriction" configuration: apiVersion: podtolerationrestriction.admission.k8s.io/v1alpha1 kind: Configuration default: - Key: key1 Value: value1 - Key: key2 Value: value2 whitelist: - Key: key1 Value: value1 - Key: key2 Value: value2 ```
This commit is contained in:
commit
d2e4f54791
@ -57,6 +57,7 @@ go_library(
|
|||||||
"//plugin/pkg/admission/persistentvolume/label:go_default_library",
|
"//plugin/pkg/admission/persistentvolume/label:go_default_library",
|
||||||
"//plugin/pkg/admission/podnodeselector:go_default_library",
|
"//plugin/pkg/admission/podnodeselector:go_default_library",
|
||||||
"//plugin/pkg/admission/podpreset:go_default_library",
|
"//plugin/pkg/admission/podpreset:go_default_library",
|
||||||
|
"//plugin/pkg/admission/podtolerationrestriction:go_default_library",
|
||||||
"//plugin/pkg/admission/resourcequota:go_default_library",
|
"//plugin/pkg/admission/resourcequota:go_default_library",
|
||||||
"//plugin/pkg/admission/security/podsecuritypolicy:go_default_library",
|
"//plugin/pkg/admission/security/podsecuritypolicy:go_default_library",
|
||||||
"//plugin/pkg/admission/securitycontext/scdeny:go_default_library",
|
"//plugin/pkg/admission/securitycontext/scdeny:go_default_library",
|
||||||
|
@ -40,6 +40,7 @@ import (
|
|||||||
_ "k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/label"
|
_ "k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/label"
|
||||||
_ "k8s.io/kubernetes/plugin/pkg/admission/podnodeselector"
|
_ "k8s.io/kubernetes/plugin/pkg/admission/podnodeselector"
|
||||||
_ "k8s.io/kubernetes/plugin/pkg/admission/podpreset"
|
_ "k8s.io/kubernetes/plugin/pkg/admission/podpreset"
|
||||||
|
_ "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction"
|
||||||
_ "k8s.io/kubernetes/plugin/pkg/admission/resourcequota"
|
_ "k8s.io/kubernetes/plugin/pkg/admission/resourcequota"
|
||||||
_ "k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy"
|
_ "k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy"
|
||||||
_ "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny"
|
_ "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny"
|
||||||
|
@ -281,6 +281,8 @@ plugin/pkg/admission/gc
|
|||||||
plugin/pkg/admission/imagepolicy
|
plugin/pkg/admission/imagepolicy
|
||||||
plugin/pkg/admission/namespace/autoprovision
|
plugin/pkg/admission/namespace/autoprovision
|
||||||
plugin/pkg/admission/namespace/exists
|
plugin/pkg/admission/namespace/exists
|
||||||
|
plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install
|
||||||
|
plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation
|
||||||
plugin/pkg/admission/resourcequota/apis/resourcequota/install
|
plugin/pkg/admission/resourcequota/apis/resourcequota/install
|
||||||
plugin/pkg/admission/resourcequota/apis/resourcequota/validation
|
plugin/pkg/admission/resourcequota/apis/resourcequota/validation
|
||||||
plugin/pkg/admission/securitycontext/scdeny
|
plugin/pkg/admission/securitycontext/scdeny
|
||||||
|
@ -146,7 +146,7 @@ func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(tolerations) > 0 {
|
if len(tolerations) > 0 {
|
||||||
allErrs = append(allErrs, validateTolerations(tolerations, fldPath.Child(api.TolerationsAnnotationKey))...)
|
allErrs = append(allErrs, ValidateTolerations(tolerations, fldPath.Child(api.TolerationsAnnotationKey))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
@ -1994,12 +1994,12 @@ func validateOnlyAddedTolerations(newTolerations []api.Toleration, oldToleration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allErrs = append(allErrs, validateTolerations(newTolerations, fldPath)...)
|
allErrs = append(allErrs, ValidateTolerations(newTolerations, fldPath)...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateTolerations tests if given tolerations have valid data.
|
// ValidateTolerations tests if given tolerations have valid data.
|
||||||
func validateTolerations(tolerations []api.Toleration, fldPath *field.Path) field.ErrorList {
|
func ValidateTolerations(tolerations []api.Toleration, fldPath *field.Path) field.ErrorList {
|
||||||
allErrors := field.ErrorList{}
|
allErrors := field.ErrorList{}
|
||||||
for i, toleration := range tolerations {
|
for i, toleration := range tolerations {
|
||||||
idxPath := fldPath.Index(i)
|
idxPath := fldPath.Index(i)
|
||||||
@ -2096,7 +2096,7 @@ func ValidatePodSpec(spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(spec.Tolerations) > 0 {
|
if len(spec.Tolerations) > 0 {
|
||||||
allErrs = append(allErrs, validateTolerations(spec.Tolerations, fldPath.Child("tolerations"))...)
|
allErrs = append(allErrs, ValidateTolerations(spec.Tolerations, fldPath.Child("tolerations"))...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
|
@ -96,6 +96,7 @@ filegroup(
|
|||||||
"//pkg/util/taints:all-srcs",
|
"//pkg/util/taints:all-srcs",
|
||||||
"//pkg/util/term:all-srcs",
|
"//pkg/util/term:all-srcs",
|
||||||
"//pkg/util/threading:all-srcs",
|
"//pkg/util/threading:all-srcs",
|
||||||
|
"//pkg/util/tolerations:all-srcs",
|
||||||
"//pkg/util/uuid:all-srcs",
|
"//pkg/util/uuid:all-srcs",
|
||||||
"//pkg/util/validation:all-srcs",
|
"//pkg/util/validation:all-srcs",
|
||||||
"//pkg/util/version:all-srcs",
|
"//pkg/util/version:all-srcs",
|
||||||
|
40
pkg/util/tolerations/BUILD
Normal file
40
pkg/util/tolerations/BUILD
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
"go_test",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"doc.go",
|
||||||
|
"tolerations.go",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = ["//pkg/api: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 = ["tolerations_test.go"],
|
||||||
|
library = ":go_default_library",
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = ["//pkg/api:go_default_library"],
|
||||||
|
)
|
18
pkg/util/tolerations/doc.go
Normal file
18
pkg/util/tolerations/doc.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
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 tolerations provides utilities to work with pod spec tolerations.
|
||||||
|
package tolerations // import "k8s.io/kubernetes/pkg/util/tolerations"
|
125
pkg/util/tolerations/tolerations.go
Normal file
125
pkg/util/tolerations/tolerations.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
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 tolerations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type key struct {
|
||||||
|
tolerationKey string
|
||||||
|
effect api.TaintEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyAgainstWhitelist checks if the provided tolerations
|
||||||
|
// satisfy the provided whitelist and returns true, otherwise returns false
|
||||||
|
func VerifyAgainstWhitelist(tolerations []api.Toleration, whitelist []api.Toleration) bool {
|
||||||
|
if len(whitelist) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
t := ConvertTolerationToAMap(tolerations)
|
||||||
|
w := ConvertTolerationToAMap(whitelist)
|
||||||
|
|
||||||
|
for k1, v1 := range t {
|
||||||
|
if v2, ok := w[k1]; !ok || !AreEqual(v1, v2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsConflict returns true if the key of two tolerations match
|
||||||
|
// but one or more other fields differ, otherwise returns false
|
||||||
|
func IsConflict(first []api.Toleration, second []api.Toleration) bool {
|
||||||
|
firstMap := ConvertTolerationToAMap(first)
|
||||||
|
secondMap := ConvertTolerationToAMap(second)
|
||||||
|
|
||||||
|
for k1, v1 := range firstMap {
|
||||||
|
if v2, ok := secondMap[k1]; ok && !AreEqual(v1, v2) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeTolerations merges two sets of tolerations into one
|
||||||
|
// it does not check for conflicts
|
||||||
|
func MergeTolerations(first []api.Toleration, second []api.Toleration) []api.Toleration {
|
||||||
|
var mergedTolerations []api.Toleration
|
||||||
|
mergedTolerations = append(mergedTolerations, second...)
|
||||||
|
|
||||||
|
firstMap := ConvertTolerationToAMap(first)
|
||||||
|
secondMap := ConvertTolerationToAMap(second)
|
||||||
|
|
||||||
|
for k1, v1 := range firstMap {
|
||||||
|
if _, ok := secondMap[k1]; !ok {
|
||||||
|
mergedTolerations = append(mergedTolerations, v1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mergedTolerations
|
||||||
|
}
|
||||||
|
|
||||||
|
// EqualTolerations returns true if two sets of tolerations are equal, otherwise false
|
||||||
|
// it assumes no duplicates in individual set of tolerations
|
||||||
|
func EqualTolerations(first []api.Toleration, second []api.Toleration) bool {
|
||||||
|
if len(first) != len(second) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
firstMap := ConvertTolerationToAMap(first)
|
||||||
|
secondMap := ConvertTolerationToAMap(second)
|
||||||
|
|
||||||
|
for k1, v1 := range firstMap {
|
||||||
|
if v2, ok := secondMap[k1]; !ok || !AreEqual(v1, v2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertTolerationToAMap converts toleration list into a map[string]api.Toleration
|
||||||
|
func ConvertTolerationToAMap(in []api.Toleration) map[key]api.Toleration {
|
||||||
|
out := map[key]api.Toleration{}
|
||||||
|
for i := range in {
|
||||||
|
out[key{in[i].Key, in[i].Effect}] = in[i]
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreEqual checks if two provided tolerations are equal or not.
|
||||||
|
func AreEqual(first, second api.Toleration) bool {
|
||||||
|
if first.Key == second.Key &&
|
||||||
|
first.Operator == second.Operator &&
|
||||||
|
first.Value == second.Value &&
|
||||||
|
first.Effect == second.Effect &&
|
||||||
|
AreTolerationSecondsEqual(first.TolerationSeconds, second.TolerationSeconds) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreTolerationSecondsEqual checks if two provided TolerationSeconds are equal or not.
|
||||||
|
func AreTolerationSecondsEqual(ts1, ts2 *int64) bool {
|
||||||
|
if ts1 == ts2 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if ts1 != nil && ts2 != nil && *ts1 == *ts2 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
82
pkg/util/tolerations/tolerations_test.go
Normal file
82
pkg/util/tolerations/tolerations_test.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
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 tolerations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVerifyAgainstWhitelist(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input []api.Toleration
|
||||||
|
whitelist []api.Toleration
|
||||||
|
testName string
|
||||||
|
testStatus bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
|
||||||
|
whitelist: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
|
||||||
|
testName: "equal input and whitelist",
|
||||||
|
testStatus: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
|
||||||
|
whitelist: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute"}},
|
||||||
|
testName: "input does not exist in whitelist",
|
||||||
|
testStatus: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range tests {
|
||||||
|
status := VerifyAgainstWhitelist(c.input, c.whitelist)
|
||||||
|
if status != c.testStatus {
|
||||||
|
t.Errorf("Test: %s, expected %v", c.testName, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsConflict(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input1 []api.Toleration
|
||||||
|
input2 []api.Toleration
|
||||||
|
testName string
|
||||||
|
testStatus bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input1: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
|
||||||
|
input2: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
|
||||||
|
testName: "equal inputs",
|
||||||
|
testStatus: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input1: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "foo", Effect: "NoExecute"}},
|
||||||
|
input2: []api.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute"}},
|
||||||
|
testName: "mismatch values in inputs",
|
||||||
|
testStatus: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range tests {
|
||||||
|
status := IsConflict(c.input1, c.input2)
|
||||||
|
if status == c.testStatus {
|
||||||
|
t.Errorf("Test: %s, expected %v", c.testName, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,7 @@ filegroup(
|
|||||||
"//plugin/pkg/admission/persistentvolume/label:all-srcs",
|
"//plugin/pkg/admission/persistentvolume/label:all-srcs",
|
||||||
"//plugin/pkg/admission/podnodeselector:all-srcs",
|
"//plugin/pkg/admission/podnodeselector:all-srcs",
|
||||||
"//plugin/pkg/admission/podpreset:all-srcs",
|
"//plugin/pkg/admission/podpreset:all-srcs",
|
||||||
|
"//plugin/pkg/admission/podtolerationrestriction:all-srcs",
|
||||||
"//plugin/pkg/admission/resourcequota:all-srcs",
|
"//plugin/pkg/admission/resourcequota:all-srcs",
|
||||||
"//plugin/pkg/admission/security:all-srcs",
|
"//plugin/pkg/admission/security:all-srcs",
|
||||||
"//plugin/pkg/admission/securitycontext/scdeny:all-srcs",
|
"//plugin/pkg/admission/securitycontext/scdeny:all-srcs",
|
||||||
|
73
plugin/pkg/admission/podtolerationrestriction/BUILD
Normal file
73
plugin/pkg/admission/podtolerationrestriction/BUILD
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
"go_test",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["admission_test.go"],
|
||||||
|
library = ":go_default_library",
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/api:go_default_library",
|
||||||
|
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||||
|
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
|
||||||
|
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||||
|
"//pkg/kubeapiserver/admission:go_default_library",
|
||||||
|
"//pkg/util/tolerations:go_default_library",
|
||||||
|
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
|
"//vendor:k8s.io/apiserver/pkg/admission",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"admission.go",
|
||||||
|
"config.go",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/api:go_default_library",
|
||||||
|
"//pkg/api/v1:go_default_library",
|
||||||
|
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||||
|
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||||
|
"//pkg/client/listers/core/internalversion:go_default_library",
|
||||||
|
"//pkg/kubeapiserver/admission: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",
|
||||||
|
"//vendor:github.com/golang/glog",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/api/errors",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/apimachinery/announced",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/apimachinery/registered",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/runtime",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/runtime/serializer",
|
||||||
|
"//vendor:k8s.io/apiserver/pkg/admission",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
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"],
|
||||||
|
)
|
221
plugin/pkg/admission/podtolerationrestriction/admission.go
Normal file
221
plugin/pkg/admission/podtolerationrestriction/admission.go
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
|
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
||||||
|
corelisters "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
|
||||||
|
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
||||||
|
"k8s.io/kubernetes/pkg/util/tolerations"
|
||||||
|
pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
admission.RegisterPlugin("PodTolerationRestriction", 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 _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&podTolerationsPlugin{})
|
||||||
|
|
||||||
|
type podTolerationsPlugin struct {
|
||||||
|
*admission.Handler
|
||||||
|
client clientset.Interface
|
||||||
|
namespaceLister corelisters.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 {
|
||||||
|
resource := a.GetResource().GroupResource()
|
||||||
|
if resource != api.Resource("pods") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if a.GetSubresource() != "" {
|
||||||
|
// only run the checks below on pods proper and not subresources
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := a.GetObject()
|
||||||
|
pod, ok := obj.(*api.Pod)
|
||||||
|
if !ok {
|
||||||
|
glog.Errorf("expected pod but got %s", a.GetKind().Kind)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.WaitForReady() {
|
||||||
|
return admission.NewForbidden(a, fmt.Errorf("not yet ready to handle request"))
|
||||||
|
}
|
||||||
|
|
||||||
|
nsName := a.GetNamespace()
|
||||||
|
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.Core().Namespaces().Get(nsName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return errors.NewInternalError(err)
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
return errors.NewInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var finalTolerations []api.Toleration
|
||||||
|
if a.GetOperation() == admission.Create {
|
||||||
|
ts, err := p.getNamespaceDefaultTolerations(namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the namespace has not specified its default tolerations,
|
||||||
|
// fall back to cluster's default tolerations.
|
||||||
|
if len(ts) == 0 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// whitelist verification.
|
||||||
|
if len(finalTolerations) > 0 {
|
||||||
|
whitelist, err := p.getNamespaceTolerationsWhitelist(namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the namespace has not specified its tolerations whitelist,
|
||||||
|
// fall back to cluster's whitelist of tolerations.
|
||||||
|
if len(whitelist) == 0 {
|
||||||
|
whitelist = p.pluginConfig.Whitelist
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(whitelist) > 0 {
|
||||||
|
// check if the merged pod tolerations satisfy its namespace whitelist
|
||||||
|
if !tolerations.VerifyAgainstWhitelist(finalTolerations, whitelist) {
|
||||||
|
return fmt.Errorf("pod tolerations (possibly merged with namespace default tolerations) conflict with its namespace whitelist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pod.Spec.Tolerations = finalTolerations
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPodTolerationsPlugin(pluginConfig *pluginapi.Configuration) *podTolerationsPlugin {
|
||||||
|
return &podTolerationsPlugin{
|
||||||
|
Handler: admission.NewHandler(admission.Create, admission.Update),
|
||||||
|
pluginConfig: pluginConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *podTolerationsPlugin) SetInternalKubeClientSet(client clientset.Interface) {
|
||||||
|
a.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *podTolerationsPlugin) SetInternalKubeInformerFactory(f informers.SharedInformerFactory) {
|
||||||
|
namespaceInformer := f.Core().InternalVersion().Namespaces()
|
||||||
|
p.namespaceLister = namespaceInformer.Lister()
|
||||||
|
p.SetReadyFunc(namespaceInformer.Informer().HasSynced)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *podTolerationsPlugin) Validate() error {
|
||||||
|
if p.namespaceLister == nil {
|
||||||
|
return fmt.Errorf("missing namespaceLister")
|
||||||
|
}
|
||||||
|
if p.client == nil {
|
||||||
|
return fmt.Errorf("missing client")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *podTolerationsPlugin) getNamespaceDefaultTolerations(ns *api.Namespace) ([]api.Toleration, error) {
|
||||||
|
return extractNSTolerations(ns, NSDefaultTolerations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *podTolerationsPlugin) getNamespaceTolerationsWhitelist(ns *api.Namespace) ([]api.Toleration, error) {
|
||||||
|
return extractNSTolerations(ns, NSWLTolerations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractNSTolerations(ns *api.Namespace, key string) ([]api.Toleration, error) {
|
||||||
|
var v1Tolerations []v1.Toleration
|
||||||
|
if len(ns.Annotations) > 0 && ns.Annotations[key] != "" {
|
||||||
|
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 := v1.Convert_v1_Toleration_To_api_Toleration(&v1Tolerations[i], &ts[i], nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts, nil
|
||||||
|
}
|
200
plugin/pkg/admission/podtolerationrestriction/admission_test.go
Normal file
200
plugin/pkg/admission/podtolerationrestriction/admission_test.go
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||||
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||||
|
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
||||||
|
kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
||||||
|
"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) {
|
||||||
|
namespace := &api.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "testNamespace",
|
||||||
|
Namespace: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockClient := &fake.Clientset{}
|
||||||
|
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"},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
defaultClusterTolerations []api.Toleration
|
||||||
|
namespaceTolerations []api.Toleration
|
||||||
|
whitelist []api.Toleration
|
||||||
|
clusterWhitelist []api.Toleration
|
||||||
|
podTolerations []api.Toleration
|
||||||
|
mergedTolerations []api.Toleration
|
||||||
|
admit bool
|
||||||
|
testName string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
defaultClusterTolerations: []api.Toleration{{Key: "testKey", Operator: "Equal", Value: "testValue", Effect: "NoSchedule", TolerationSeconds: nil}},
|
||||||
|
namespaceTolerations: []api.Toleration{},
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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}},
|
||||||
|
admit: false,
|
||||||
|
testName: "conflicting pod and default cluster tolerations",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
if len(test.namespaceTolerations) > 0 {
|
||||||
|
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 len(test.whitelist) > 0 {
|
||||||
|
tolerationStr, err := json.Marshal(test.whitelist)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error in marshalling namespace whitelist %v", test.whitelist)
|
||||||
|
}
|
||||||
|
namespace.Annotations[NSWLTolerations] = string(tolerationStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
informerFactory.Core().InternalVersion().Namespaces().Informer().GetStore().Update(namespace)
|
||||||
|
|
||||||
|
handler.pluginConfig = &pluginapi.Configuration{Default: test.defaultClusterTolerations, Whitelist: test.clusterWhitelist}
|
||||||
|
pod.Spec.Tolerations = test.podTolerations
|
||||||
|
|
||||||
|
err := handler.Admit(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHandlerForTest returns the admission controller configured for testing.
|
||||||
|
func newHandlerForTest(c clientset.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 := kubeadmission.NewPluginInitializer(c, f, nil, nil)
|
||||||
|
pluginInitializer.Initialize(handler)
|
||||||
|
err = admission.Validate(handler)
|
||||||
|
return handler, f, err
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
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",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/api:go_default_library",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/conversion",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/runtime",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
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"],
|
||||||
|
)
|
@ -0,0 +1,2 @@
|
|||||||
|
reviewers:
|
||||||
|
approvers:
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=package,register
|
||||||
|
|
||||||
|
package podtolerationrestriction // import "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
|
@ -0,0 +1,34 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["install.go"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",
|
||||||
|
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1:go_default_library",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/apimachinery/announced",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/apimachinery/registered",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/runtime",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
)
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package install 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/apimachinery/announced"
|
||||||
|
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||||
|
"k8s.io/apimachinery/pkg/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(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) {
|
||||||
|
if err := announced.NewGroupMetaFactory(
|
||||||
|
&announced.GroupMetaFactoryArgs{
|
||||||
|
GroupName: internalapi.GroupName,
|
||||||
|
VersionPreferenceOrder: []string{versionedapi.SchemeGroupVersion.Version},
|
||||||
|
ImportPrefix: "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction",
|
||||||
|
AddInternalObjectsToScheme: internalapi.AddToScheme,
|
||||||
|
},
|
||||||
|
announced.VersionToSchemeFunc{
|
||||||
|
versionedapi.SchemeGroupVersion.Version: versionedapi.AddToScheme,
|
||||||
|
},
|
||||||
|
).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Configuration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
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",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/api:go_default_library",
|
||||||
|
"//pkg/api/v1:go_default_library",
|
||||||
|
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/conversion",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/runtime",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
)
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
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 {
|
||||||
|
RegisterDefaults(scheme)
|
||||||
|
return scheme.AddDefaultingFuncs(
|
||||||
|
SetDefaults_Configuration,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDefaults_Configuration(obj *Configuration) {}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=package,register
|
||||||
|
// +k8s:conversion-gen=k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction
|
||||||
|
// +k8s:defaulter-gen=TypeMeta
|
||||||
|
|
||||||
|
// Package v1alpha1 is the v1alpha1 version of the API.
|
||||||
|
// +groupName=podtolerationrestriction.admission.k8s.io
|
||||||
|
package v1alpha1 // import "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1"
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs)
|
||||||
|
AddToScheme = SchemeBuilder.AddToScheme
|
||||||
|
)
|
||||||
|
|
||||||
|
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||||
|
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||||
|
&Configuration{},
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *Configuration) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This file was autogenerated by conversion-gen. Do not edit it manually!
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
api "k8s.io/kubernetes/pkg/api"
|
||||||
|
v1 "k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
podtolerationrestriction "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(RegisterConversions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterConversions adds conversion functions to the given scheme.
|
||||||
|
// Public to allow building arbitrary schemes.
|
||||||
|
func RegisterConversions(scheme *runtime.Scheme) error {
|
||||||
|
return scheme.AddGeneratedConversionFuncs(
|
||||||
|
Convert_v1alpha1_Configuration_To_podtolerationrestriction_Configuration,
|
||||||
|
Convert_podtolerationrestriction_Configuration_To_v1alpha1_Configuration,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoConvert_v1alpha1_Configuration_To_podtolerationrestriction_Configuration(in *Configuration, out *podtolerationrestriction.Configuration, s conversion.Scope) error {
|
||||||
|
out.Default = *(*[]api.Toleration)(unsafe.Pointer(&in.Default))
|
||||||
|
out.Whitelist = *(*[]api.Toleration)(unsafe.Pointer(&in.Whitelist))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This file was autogenerated by deepcopy-gen. Do not edit it manually!
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
v1 "k8s.io/kubernetes/pkg/api/v1"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(RegisterDeepCopies)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public
|
||||||
|
// to allow building arbitrary schemes.
|
||||||
|
func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||||
|
return scheme.AddGeneratedDeepCopyFuncs(
|
||||||
|
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1alpha1_Configuration, InType: reflect.TypeOf(&Configuration{})},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeepCopy_v1alpha1_Configuration(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||||
|
{
|
||||||
|
in := in.(*Configuration)
|
||||||
|
out := out.(*Configuration)
|
||||||
|
*out = *in
|
||||||
|
if in.Default != nil {
|
||||||
|
in, out := &in.Default, &out.Default
|
||||||
|
*out = make([]v1.Toleration, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
if err := v1.DeepCopy_v1_Toleration(&(*in)[i], &(*out)[i], c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Whitelist != nil {
|
||||||
|
in, out := &in.Whitelist, &out.Whitelist
|
||||||
|
*out = make([]v1.Toleration, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
if err := v1.DeepCopy_v1_Toleration(&(*in)[i], &(*out)[i], c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This file was autogenerated by defaulter-gen. Do not edit it manually!
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
"go_test",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["validation.go"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/api/validation:go_default_library",
|
||||||
|
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
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"],
|
||||||
|
library = ":go_default_library",
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/api:go_default_library",
|
||||||
|
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
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/api/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
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
internalapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This file was autogenerated by deepcopy-gen. Do not edit it manually!
|
||||||
|
|
||||||
|
package podtolerationrestriction
|
||||||
|
|
||||||
|
import (
|
||||||
|
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
api "k8s.io/kubernetes/pkg/api"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(RegisterDeepCopies)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterDeepCopies adds deep-copy functions to the given scheme. Public
|
||||||
|
// to allow building arbitrary schemes.
|
||||||
|
func RegisterDeepCopies(scheme *runtime.Scheme) error {
|
||||||
|
return scheme.AddGeneratedDeepCopyFuncs(
|
||||||
|
conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_podtolerationrestriction_Configuration, InType: reflect.TypeOf(&Configuration{})},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeepCopy_podtolerationrestriction_Configuration(in interface{}, out interface{}, c *conversion.Cloner) error {
|
||||||
|
{
|
||||||
|
in := in.(*Configuration)
|
||||||
|
out := out.(*Configuration)
|
||||||
|
*out = *in
|
||||||
|
if in.Default != nil {
|
||||||
|
in, out := &in.Default, &out.Default
|
||||||
|
*out = make([]api.Toleration, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
if err := api.DeepCopy_api_Toleration(&(*in)[i], &(*out)[i], c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Whitelist != nil {
|
||||||
|
in, out := &in.Whitelist, &out.Whitelist
|
||||||
|
*out = make([]api.Toleration, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
if err := api.DeepCopy_api_Toleration(&(*in)[i], &(*out)[i], c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
78
plugin/pkg/admission/podtolerationrestriction/config.go
Normal file
78
plugin/pkg/admission/podtolerationrestriction/config.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/apimachinery/announced"
|
||||||
|
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||||
|
"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 (
|
||||||
|
groupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
|
||||||
|
registry = registered.NewOrDie(os.Getenv("KUBE_API_VERSIONS"))
|
||||||
|
scheme = runtime.NewScheme()
|
||||||
|
codecs = serializer.NewCodecFactory(scheme)
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
install.Install(groupFactoryRegistry, registry, 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user