vendor updates

This commit is contained in:
Serguei Bezverkhi
2018-03-06 17:33:18 -05:00
parent 4b3ebc171b
commit e9033989a0
5854 changed files with 248382 additions and 119809 deletions

View File

@ -10,14 +10,16 @@ go_library(
name = "go_default_library",
srcs = ["admission.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/admit",
deps = ["//vendor/k8s.io/apiserver/pkg/admission:go_default_library"],
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/admit",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",

View File

@ -19,40 +19,46 @@ package admit
import (
"io"
"github.com/golang/glog"
"k8s.io/apiserver/pkg/admission"
)
// PluginName indicates name of admission plugin.
const PluginName = "AlwaysAdmit"
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("AlwaysAdmit", func(config io.Reader) (admission.Interface, error) {
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.
// It is useful in tests and when using kubernetes in an open manner.
type AlwaysAdmit struct{}
// 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{}
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) {
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) {
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 {
func (alwaysAdmit) Handles(operation admission.Operation) bool {
return true
}
// NewAlwaysAdmit creates a new always admit admission handler
func NewAlwaysAdmit() *AlwaysAdmit {
return new(AlwaysAdmit)
func NewAlwaysAdmit() admission.Interface {
// DEPRECATED: AlwaysAdmit admit all admission request, it is no use.
glog.Warningf("%s admission controller is deprecated. "+
"Please remove this controller from your configuration files and scripts.", PluginName)
return new(alwaysAdmit)
}

View File

@ -25,7 +25,7 @@ import (
func TestAdmissionNonNilAttribute(t *testing.T) {
handler := NewAlwaysAdmit()
err := handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil))
err := handler.(*alwaysAdmit).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil))
if err != nil {
t.Errorf("Unexpected error returned from admission handler")
}
@ -33,7 +33,7 @@ func TestAdmissionNonNilAttribute(t *testing.T) {
func TestAdmissionNilAttribute(t *testing.T) {
handler := NewAlwaysAdmit()
err := handler.Admit(nil)
err := handler.(*alwaysAdmit).Admit(nil)
if err != nil {
t.Errorf("Unexpected error returned from admission handler")
}

View File

@ -21,8 +21,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/alwayspullimages",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -33,9 +33,12 @@ import (
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("AlwaysPullImages", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewAlwaysPullImages(), nil
})
}

View File

@ -24,8 +24,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/antiaffinity",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/kubelet/apis:go_default_library",

View File

@ -26,9 +26,11 @@ import (
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
)
const PluginName = "LimitPodHardAntiAffinityTopology"
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("LimitPodHardAntiAffinityTopology", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewInterPodAntiAffinity(), nil
})
}

View File

@ -9,12 +9,11 @@ load(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/defaulttolerationseconds",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
"//plugin/pkg/scheduler/algorithm:go_default_library",
"//pkg/scheduler/algorithm:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
],
)
@ -26,7 +25,7 @@ go_library(
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
"//plugin/pkg/scheduler/algorithm:go_default_library",
"//pkg/scheduler/algorithm:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
],

View File

@ -25,9 +25,12 @@ import (
"k8s.io/apiserver/pkg/admission"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
"k8s.io/kubernetes/pkg/scheduler/algorithm"
)
// PluginName indicates name of admission plugin.
const PluginName = "DefaultTolerationSeconds"
var (
defaultNotReadyTolerationSeconds = flag.Int64("default-not-ready-toleration-seconds", 300,
"Indicates the tolerationSeconds of the toleration for notReady:NoExecute"+
@ -40,7 +43,7 @@ var (
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("DefaultTolerationSeconds", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewDefaultTolerationSeconds(), nil
})
}

View File

@ -22,7 +22,7 @@ import (
"k8s.io/apiserver/pkg/admission"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
"k8s.io/kubernetes/pkg/scheduler/algorithm"
)
func TestForgivenessAdmission(t *testing.T) {

View File

@ -10,14 +10,16 @@ go_library(
name = "go_default_library",
srcs = ["admission.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/deny",
deps = ["//vendor/k8s.io/apiserver/pkg/admission:go_default_library"],
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/deny",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",

View File

@ -20,40 +20,47 @@ import (
"errors"
"io"
"github.com/golang/glog"
"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("AlwaysDeny", func(config io.Reader) (admission.Interface, error) {
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.
// It is useful in unit tests to force an operation to be forbidden.
type AlwaysDeny struct{}
// 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{}
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"))
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"))
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 {
func (alwaysDeny) Handles(operation admission.Operation) bool {
return true
}
// NewAlwaysDeny creates an always deny admission handler
func NewAlwaysDeny() *AlwaysDeny {
return new(AlwaysDeny)
func NewAlwaysDeny() admission.Interface {
// DEPRECATED: AlwaysDeny denys all admission request, it is no use.
glog.Warningf("%s admission controller is deprecated. "+
"Please remove this controller from your configuration files and scripts.", PluginName)
return new(alwaysDeny)
}

View File

@ -25,7 +25,7 @@ import (
func TestAdmission(t *testing.T) {
handler := NewAlwaysDeny()
err := handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil))
err := handler.(*alwaysDeny).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, nil))
if err == nil {
t.Error("Expected error returned from admission handler")
}

View File

@ -12,8 +12,7 @@ go_test(
"admission_test.go",
"cache_test.go",
],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library",

View File

@ -26,9 +26,12 @@ import (
"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("EventRateLimit",
plugins.Register(PluginName,
func(config io.Reader) (admission.Interface, error) {
// load the configuration provided (if any)
configuration, err := LoadConfiguration(config)

View File

@ -16,15 +16,16 @@ 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!
// 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"
unsafe "unsafe"
)
func init() {

View File

@ -16,7 +16,7 @@ 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!
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
@ -50,9 +50,8 @@ func (in *Configuration) DeepCopy() *Configuration {
func (in *Configuration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

View File

@ -16,7 +16,7 @@ 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!
// Code generated by defaulter-gen. DO NOT EDIT.
package v1alpha1

View File

@ -32,7 +32,6 @@ filegroup(
go_test(
name = "go_default_test",
srcs = ["validation_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation",
library = ":go_default_library",
embed = [":go_default_library"],
deps = ["//plugin/pkg/admission/eventratelimit/apis/eventratelimit:go_default_library"],
)

View File

@ -16,7 +16,7 @@ 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!
// Code generated by deepcopy-gen. DO NOT EDIT.
package eventratelimit
@ -50,9 +50,8 @@ func (in *Configuration) DeepCopy() *Configuration {
func (in *Configuration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

View File

@ -24,8 +24,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/exec",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",

View File

@ -29,15 +29,23 @@ import (
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
)
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) {
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) {
plugins.Register(DenyExecOnPrivileged, func(config io.Reader) (admission.Interface, error) {
return NewDenyExecOnPrivileged(), nil
})
}
@ -49,9 +57,10 @@ type DenyExec struct {
client internalclientset.Interface
// these flags control which items will be checked to deny exec/attach
hostIPC bool
hostPID bool
privileged bool
hostNetwork bool
hostIPC bool
hostPID bool
privileged bool
}
var _ admission.ValidationInterface = &DenyExec{}
@ -62,22 +71,24 @@ var _ = kubeapiserveradmission.WantsInternalKubeClientSet(&DenyExec{})
// using host based configurations.
func NewDenyEscalatingExec() *DenyExec {
return &DenyExec{
Handler: admission.NewHandler(admission.Connect),
hostIPC: true,
hostPID: true,
privileged: true,
Handler: admission.NewHandler(admission.Connect),
hostNetwork: true,
hostIPC: true,
hostPID: true,
privileged: true,
}
}
// 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.
// 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),
hostIPC: false,
hostPID: false,
privileged: true,
Handler: admission.NewHandler(admission.Connect),
hostNetwork: false,
hostIPC: false,
hostPID: false,
privileged: true,
}
}
@ -96,12 +107,19 @@ func (d *DenyExec) Validate(a admission.Attributes) (err error) {
return admission.NewForbidden(a, err)
}
if d.hostPID && pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostPID {
return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host pid"))
}
if pod.Spec.SecurityContext != nil {
securityContext := pod.Spec.SecurityContext
if d.hostNetwork && securityContext.HostNetwork {
return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host network"))
}
if d.hostIPC && pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.HostIPC {
return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host ipc"))
if d.hostPID && securityContext.HostPID {
return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host pid"))
}
if d.hostIPC && securityContext.HostIPC {
return admission.NewForbidden(a, fmt.Errorf("cannot exec into or attach to a container using host ipc"))
}
}
if d.privileged && isPrivileged(pod) {

View File

@ -17,8 +17,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/extendedresourcetoleration",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",

View File

@ -27,9 +27,12 @@ import (
"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("ExtendedResourceToleration", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return newExtendedResourceToleration(), nil
})
}

View File

@ -25,8 +25,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["gc_admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/gc",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/core:go_default_library",

View File

@ -30,9 +30,12 @@ import (
"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("OwnerReferencesPermissionEnforcement", func(config io.Reader) (admission.Interface, error) {
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.

View File

@ -37,8 +37,7 @@ go_test(
"certs_test.go",
"config_test.go",
],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/imagepolicy",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/imagepolicy/install:go_default_library",

View File

@ -43,13 +43,16 @@ import (
_ "k8s.io/kubernetes/pkg/apis/imagepolicy/install"
)
// PluginName indicates name of admission plugin.
const PluginName = "ImagePolicyWebhook"
var (
groupVersions = []schema.GroupVersion{v1alpha1.SchemeGroupVersion}
)
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("ImagePolicyWebhook", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
newImagePolicyWebhook, err := NewImagePolicyWebhook(config)
if err != nil {
return nil, err

View File

@ -43,8 +43,7 @@ go_test(
"hawkular_test.go",
"influxdb_test.go",
],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/initialresources",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",

View File

@ -42,12 +42,14 @@ const (
samplesThreshold = 30
week = 7 * 24 * time.Hour
month = 30 * 24 * time.Hour
// PluginName indicates name of admission plugin.
PluginName = "InitialResources"
)
// Register registers a plugin
// WARNING: this feature is experimental and will definitely change.
func Register(plugins *admission.Plugins) {
plugins.Register("InitialResources", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
// TODO: remove the usage of flags in favor of reading versioned configuration
s, err := newDataSource(*source)
if err != nil {

View File

@ -33,8 +33,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/limitranger",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",

View File

@ -41,11 +41,13 @@ import (
const (
limitRangerAnnotation = "kubernetes.io/limit-ranger"
// PluginName indicates name of admission plugin.
PluginName = "LimitRanger"
)
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("LimitRanger", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewLimitRanger(&DefaultLimitRangerActions{})
})
}
@ -113,6 +115,18 @@ func (l *LimitRanger) runLimitFunc(a admission.Attributes, limitFn func(limitRan
}
}
// ignore all objects marked for deletion
oldObj := a.GetOldObject()
if oldObj != nil {
oldAccessor, err := meta.Accessor(oldObj)
if err != nil {
return admission.NewForbidden(a, err)
}
if oldAccessor.GetDeletionTimestamp() != nil {
return nil
}
}
items, err := l.GetLimitRanges(a)
if err != nil {
return err

View File

@ -733,6 +733,15 @@ func TestLimitRangerAdmitPod(t *testing.T) {
if err != nil {
t.Errorf("Should have ignored calls to any subresource of pod %v", err)
}
// a pod that is undergoing termination should never be blocked
terminatingPod := validPod("terminatingPod", 1, api.ResourceRequirements{})
now := metav1.Now()
terminatingPod.DeletionTimestamp = &now
err = handler.Validate(admission.NewAttributesRecord(&terminatingPod, &terminatingPod, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "terminatingPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil))
if err != nil {
t.Errorf("LimitRange should ignore a pod marked for termination")
}
}
// newMockClientForTest creates a mock client that returns a client configured for the specified list of limit ranges

View File

@ -25,8 +25,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",

View File

@ -30,9 +30,12 @@ import (
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
)
// PluginName indicates name of admission plugin.
const PluginName = "NamespaceAutoProvision"
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("NamespaceAutoProvision", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewProvision(), nil
})
}

View File

@ -25,8 +25,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/namespace/exists",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",

View File

@ -30,9 +30,12 @@ import (
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
)
// PluginName indicates name of admission plugin.
const PluginName = "NamespaceExists"
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("NamespaceExists", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewExists(), nil
})
}

View File

@ -12,6 +12,7 @@ go_library(
importpath = "k8s.io/kubernetes/plugin/pkg/admission/noderestriction",
deps = [
"//pkg/api/pod:go_default_library",
"//pkg/apis/authentication:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/auth/nodeidentifier:go_default_library",
@ -31,17 +32,20 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/noderestriction",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/authentication:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/auth/nodeidentifier:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/features:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apiserver/pkg/admission"
utilfeature "k8s.io/apiserver/pkg/util/feature"
podutil "k8s.io/kubernetes/pkg/api/pod"
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
@ -53,6 +54,7 @@ func NewPlugin(nodeIdentifier nodeidentifier.NodeIdentifier) *nodePlugin {
return &nodePlugin{
Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete),
nodeIdentifier: nodeIdentifier,
features: utilfeature.DefaultFeatureGate,
}
}
@ -61,6 +63,8 @@ type nodePlugin struct {
*admission.Handler
nodeIdentifier nodeidentifier.NodeIdentifier
podsGetter coreinternalversion.PodsGetter
// allows overriding for testing
features utilfeature.FeatureGate
}
var (
@ -83,9 +87,10 @@ func (p *nodePlugin) ValidateInitialization() error {
}
var (
podResource = api.Resource("pods")
nodeResource = api.Resource("nodes")
pvcResource = api.Resource("persistentvolumeclaims")
podResource = api.Resource("pods")
nodeResource = api.Resource("nodes")
pvcResource = api.Resource("persistentvolumeclaims")
svcacctResource = api.Resource("serviceaccounts")
)
func (c *nodePlugin) Admit(a admission.Attributes) error {
@ -125,6 +130,12 @@ func (c *nodePlugin) Admit(a admission.Attributes) error {
return admission.NewForbidden(a, fmt.Errorf("may only update PVC status"))
}
case svcacctResource:
if c.features.Enabled(features.TokenRequest) {
return c.admitServiceAccount(nodeName, a)
}
return nil
default:
return nil
}
@ -256,7 +267,7 @@ func (c *nodePlugin) admitPodEviction(nodeName string, a admission.Attributes) e
func (c *nodePlugin) admitPVCStatus(nodeName string, a admission.Attributes) error {
switch a.GetOperation() {
case admission.Update:
if !utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
if !c.features.Enabled(features.ExpandPersistentVolumes) {
return admission.NewForbidden(a, fmt.Errorf("node %q may not update persistentvolumeclaim metadata", nodeName))
}
@ -340,3 +351,44 @@ func (c *nodePlugin) admitNode(nodeName string, a admission.Attributes) error {
return nil
}
func (c *nodePlugin) admitServiceAccount(nodeName string, a admission.Attributes) error {
if a.GetOperation() != admission.Create {
return nil
}
if a.GetSubresource() != "token" {
return nil
}
tr, ok := a.GetObject().(*authenticationapi.TokenRequest)
if !ok {
return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
}
// TokenRequests from a node must have a pod binding. That pod must be
// scheduled on the node.
ref := tr.Spec.BoundObjectRef
if ref == nil ||
ref.APIVersion != "v1" ||
ref.Kind != "Pod" ||
ref.Name == "" {
return admission.NewForbidden(a, fmt.Errorf("node requested token not bound to a pod"))
}
if ref.UID == "" {
return admission.NewForbidden(a, fmt.Errorf("node requested token with a pod binding without a uid"))
}
pod, err := c.podsGetter.Pods(a.GetNamespace()).Get(ref.Name, v1.GetOptions{})
if errors.IsNotFound(err) {
return err
}
if err != nil {
return admission.NewForbidden(a, err)
}
if ref.UID != pod.UID {
return admission.NewForbidden(a, fmt.Errorf("the UID in the bound object reference (%s) does not match the UID in record (%s). The object might have been deleted and then recreated", ref.UID, pod.UID))
}
if pod.Spec.NodeName != nodeName {
return admission.NewForbidden(a, fmt.Errorf("node requested token bound to a pod scheduled on a different node"))
}
return nil
}

View File

@ -21,19 +21,37 @@ import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authentication/user"
utilfeature "k8s.io/apiserver/pkg/util/feature"
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/policy"
policyapi "k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
coreinternalversion "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/features"
)
var (
trEnabledFeature = utilfeature.NewFeatureGate()
trDisabledFeature = utilfeature.NewFeatureGate()
)
func init() {
if err := trEnabledFeature.Add(map[utilfeature.Feature]utilfeature.FeatureSpec{features.TokenRequest: {Default: true}}); err != nil {
panic(err)
}
if err := trDisabledFeature.Add(map[utilfeature.Feature]utilfeature.FeatureSpec{features.TokenRequest: {Default: false}}); err != nil {
panic(err)
}
}
func makeTestPod(namespace, name, node string, mirror bool) *api.Pod {
pod := &api.Pod{}
pod.Namespace = namespace
pod.UID = types.UID("pod-uid")
pod.Name = name
pod.Spec.NodeName = node
if mirror {
@ -48,6 +66,23 @@ func makeTestPodEviction(name string) *policy.Eviction {
return eviction
}
func makeTokenRequest(podname string, poduid types.UID) *authenticationapi.TokenRequest {
tr := &authenticationapi.TokenRequest{
Spec: authenticationapi.TokenRequestSpec{
Audiences: []string{"foo"},
},
}
if podname != "" {
tr.Spec.BoundObjectRef = &authenticationapi.BoundObjectReference{
Kind: "Pod",
APIVersion: "v1",
Name: podname,
UID: poduid,
}
}
return tr
}
func Test_nodePlugin_Admit(t *testing.T) {
var (
mynode = &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}}
@ -82,11 +117,14 @@ func Test_nodePlugin_Admit(t *testing.T) {
podResource = api.Resource("pods").WithVersion("v1")
podKind = api.Kind("Pod").WithVersion("v1")
evictionKind = policyapi.Kind("Eviction").WithVersion("v1beta1")
evictionKind = policy.Kind("Eviction").WithVersion("v1beta1")
nodeResource = api.Resource("nodes").WithVersion("v1")
nodeKind = api.Kind("Node").WithVersion("v1")
svcacctResource = api.Resource("serviceaccounts").WithVersion("v1")
tokenrequestKind = api.Kind("TokenRequest").WithVersion("v1")
noExistingPods = fake.NewSimpleClientset().Core()
existingPods = fake.NewSimpleClientset(mymirrorpod, othermirrorpod, unboundmirrorpod, mypod, otherpod, unboundpod).Core()
)
@ -107,6 +145,7 @@ func Test_nodePlugin_Admit(t *testing.T) {
name string
podsGetter coreinternalversion.PodsGetter
attributes admission.Attributes
features utilfeature.FeatureGate
err string
}{
// Mirror pods bound to us
@ -654,6 +693,42 @@ func Test_nodePlugin_Admit(t *testing.T) {
err: "cannot modify node",
},
// Service accounts
{
name: "forbid create of unbound token",
podsGetter: noExistingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest("", ""), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, mynode),
err: "not bound to a pod",
},
{
name: "forbid create of token bound to nonexistant pod",
podsGetter: noExistingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest("nopod", "someuid"), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, mynode),
err: "not found",
},
{
name: "forbid create of token bound to pod without uid",
podsGetter: existingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest(mypod.Name, ""), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, mynode),
err: "pod binding without a uid",
},
{
name: "forbid create of token bound to pod scheduled on another node",
podsGetter: existingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest(otherpod.Name, otherpod.UID), nil, tokenrequestKind, otherpod.Namespace, "mysa", svcacctResource, "token", admission.Create, mynode),
err: "pod scheduled on a different node",
},
{
name: "allow create of token bound to pod scheduled this node",
podsGetter: existingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest(mypod.Name, mypod.UID), nil, tokenrequestKind, mypod.Namespace, "mysa", svcacctResource, "token", admission.Create, mynode),
},
// Unrelated objects
{
name: "allow create of unrelated object",
@ -715,6 +790,9 @@ func Test_nodePlugin_Admit(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := NewPlugin(nodeidentifier.NewDefaultNodeIdentifier())
if tt.features != nil {
c.features = tt.features
}
c.podsGetter = tt.podsGetter
err := c.Admit(tt.attributes)
if (err == nil) != (len(tt.err) == 0) {

View File

@ -29,8 +29,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/label",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/cloudprovider/providers/aws:go_default_library",

View File

@ -33,9 +33,11 @@ import (
vol "k8s.io/kubernetes/pkg/volume"
)
const PluginName = "PersistentVolumeLabel"
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("PersistentVolumeLabel", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
persistentVolumeLabelAdmission := NewPersistentVolumeLabel()
return persistentVolumeLabelAdmission, nil
})

View File

@ -9,8 +9,7 @@ load(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/resize",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/storage:go_default_library",

View File

@ -161,5 +161,8 @@ func (pvcr *persistentVolumeClaimResize) checkVolumePlugin(pv *api.PersistentVol
return true
}
if pv.Spec.AzureFile != nil {
return true
}
return false
}

View File

@ -1,111 +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 pvcprotection
import (
"fmt"
"io"
"github.com/golang/glog"
admission "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
corelisters "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
"k8s.io/kubernetes/pkg/features"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
volumeutil "k8s.io/kubernetes/pkg/volume/util"
)
const (
// PluginName is the name of this admission controller plugin
PluginName = "PVCProtection"
)
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
plugin := newPlugin()
return plugin, nil
})
}
// pvcProtectionPlugin holds state for and implements the admission plugin.
type pvcProtectionPlugin struct {
*admission.Handler
lister corelisters.PersistentVolumeClaimLister
}
var _ admission.Interface = &pvcProtectionPlugin{}
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&pvcProtectionPlugin{})
// newPlugin creates a new admission plugin.
func newPlugin() *pvcProtectionPlugin {
return &pvcProtectionPlugin{
Handler: admission.NewHandler(admission.Create),
}
}
func (c *pvcProtectionPlugin) SetInternalKubeInformerFactory(f informers.SharedInformerFactory) {
informer := f.Core().InternalVersion().PersistentVolumeClaims()
c.lister = informer.Lister()
c.SetReadyFunc(informer.Informer().HasSynced)
}
// ValidateInitialization ensures lister is set.
func (c *pvcProtectionPlugin) ValidateInitialization() error {
if c.lister == nil {
return fmt.Errorf("missing lister")
}
return nil
}
// Admit sets finalizer on all PVCs. The finalizer is removed by
// PVCProtectionController when it's not referenced by any pod.
//
// This prevents users from deleting a PVC that's used by a running pod.
func (c *pvcProtectionPlugin) Admit(a admission.Attributes) error {
if !feature.DefaultFeatureGate.Enabled(features.PVCProtection) {
return nil
}
if a.GetResource().GroupResource() != api.Resource("persistentvolumeclaims") {
return nil
}
if len(a.GetSubresource()) != 0 {
return nil
}
pvc, ok := a.GetObject().(*api.PersistentVolumeClaim)
// if we can't convert then we don't handle this object so just return
if !ok {
return nil
}
for _, f := range pvc.Finalizers {
if f == volumeutil.PVCProtectionFinalizer {
// Finalizer is already present, nothing to do
return nil
}
}
glog.V(4).Infof("adding PVC protection finalizer to %s/%s", pvc.Namespace, pvc.Name)
pvc.Finalizers = append(pvc.Finalizers, volumeutil.PVCProtectionFinalizer)
return nil
}

View File

@ -29,8 +29,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/podnodeselector",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",

View File

@ -40,9 +40,11 @@ import (
// 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("PodNodeSelector", func(config io.Reader) (admission.Interface, error) {
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)

View File

@ -9,8 +9,7 @@ load(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/podpreset",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/settings:go_default_library",

View File

@ -41,12 +41,12 @@ import (
const (
annotationPrefix = "podpreset.admission.kubernetes.io"
pluginName = "PodPreset"
PluginName = "PodPreset"
)
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register(pluginName, func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewPlugin(), nil
})
}
@ -72,10 +72,10 @@ func NewPlugin() *podPresetPlugin {
func (plugin *podPresetPlugin) ValidateInitialization() error {
if plugin.client == nil {
return fmt.Errorf("%s requires a client", pluginName)
return fmt.Errorf("%s requires a client", PluginName)
}
if plugin.lister == nil {
return fmt.Errorf("%s requires a lister", pluginName)
return fmt.Errorf("%s requires a lister", PluginName)
}
return nil
}

View File

@ -20,7 +20,6 @@ import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kadmission "k8s.io/apiserver/pkg/admission"
@ -426,16 +425,16 @@ func TestAdmitConflictWithDifferentNamespaceShouldDoNothing(t *testing.T) {
}
pip := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "hello",
Namespace: "othernamespace",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
Selector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
@ -472,16 +471,16 @@ func TestAdmitConflictWithNonMatchingLabelsShouldNotError(t *testing.T) {
}
pip := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "hello",
Namespace: "namespace",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
Selector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S3"},
},
},
@ -519,16 +518,16 @@ func TestAdmitConflictShouldNotModifyPod(t *testing.T) {
origPod := *pod
pip := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "hello",
Namespace: "namespace",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
Selector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
@ -568,16 +567,16 @@ func TestAdmit(t *testing.T) {
}
pip := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "hello",
Namespace: "namespace",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
Selector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
@ -628,16 +627,16 @@ func TestAdmitMirrorPod(t *testing.T) {
}
pip := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "hello",
Namespace: "namespace",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
Selector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
@ -698,16 +697,16 @@ func TestExclusionNoAdmit(t *testing.T) {
}
pip := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "hello",
Namespace: "namespace",
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
Selector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
@ -762,16 +761,16 @@ func TestAdmitEmptyPodNamespace(t *testing.T) {
}
pip := &settings.PodPreset{
ObjectMeta: v1.ObjectMeta{
ObjectMeta: metav1.ObjectMeta{
Name: "hello",
Namespace: "different", // (pod will be submitted to namespace 'namespace')
},
Spec: settings.PodPresetSpec{
Selector: v1.LabelSelector{
MatchExpressions: []v1.LabelSelectorRequirement{
Selector: metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: v1.LabelSelectorOpIn,
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},

View File

@ -9,17 +9,16 @@ load(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core: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/scheduler/algorithm:go_default_library",
"//pkg/util/tolerations:go_default_library",
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",
"//plugin/pkg/scheduler/algorithm:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
@ -43,12 +42,12 @@ go_library(
"//pkg/client/listers/core/internalversion:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/kubeapiserver/admission/util:go_default_library",
"//pkg/scheduler/algorithm: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",
"//plugin/pkg/scheduler/algorithm:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",

View File

@ -35,14 +35,16 @@ import (
corelisters "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/kubeapiserver/admission/util"
"k8s.io/kubernetes/pkg/scheduler/algorithm"
"k8s.io/kubernetes/pkg/util/tolerations"
pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
)
const PluginName = "PodTolerationRestriction"
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("PodTolerationRestriction", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
pluginConfig, err := loadConfiguration(config)
if err != nil {
return nil, err

View File

@ -30,9 +30,9 @@ import (
"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/scheduler/algorithm"
"k8s.io/kubernetes/pkg/util/tolerations"
pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
)
// TestPodAdmission verifies various scenarios involving pod/namespace tolerations

View File

@ -16,17 +16,18 @@ 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!
// 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"
unsafe "unsafe"
)
func init() {

View File

@ -16,7 +16,7 @@ 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!
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
@ -60,7 +60,6 @@ func (in *Configuration) DeepCopy() *Configuration {
func (in *Configuration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
return nil
}

View File

@ -16,7 +16,7 @@ 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!
// Code generated by defaulter-gen. DO NOT EDIT.
package v1alpha1

View File

@ -33,8 +33,7 @@ filegroup(
go_test(
name = "go_default_test",
srcs = ["validation_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",

View File

@ -16,7 +16,7 @@ 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!
// Code generated by deepcopy-gen. DO NOT EDIT.
package podtolerationrestriction
@ -60,7 +60,6 @@ func (in *Configuration) DeepCopy() *Configuration {
func (in *Configuration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
return nil
}

View File

@ -9,14 +9,14 @@ load(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/priority",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/scheduling:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/features:go_default_library",
"//pkg/scheduler/api:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
@ -36,6 +36,8 @@ go_library(
"//pkg/client/listers/scheduling/internalversion:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/scheduler/api:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package admission
package priority
import (
"fmt"
@ -31,69 +31,59 @@ import (
schedulinglisters "k8s.io/kubernetes/pkg/client/listers/scheduling/internalversion"
"k8s.io/kubernetes/pkg/features"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
kubelettypes "k8s.io/kubernetes/pkg/kubelet/types"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
)
const (
pluginName = "Priority"
// HighestUserDefinablePriority is the highest priority for user defined priority classes. Priority values larger than 1 billion are reserved for Kubernetes system use.
HighestUserDefinablePriority = 1000000000
// SystemCriticalPriority is the beginning of the range of priority values for critical system components.
SystemCriticalPriority = 2 * HighestUserDefinablePriority
// PluginName indicates name of admission plugin.
PluginName = "Priority"
)
// SystemPriorityClasses defines special priority classes which are used by system critical pods that should not be preempted by workload pods.
var SystemPriorityClasses = map[string]int32{
"system-cluster-critical": SystemCriticalPriority,
"system-node-critical": SystemCriticalPriority + 1000,
}
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register(pluginName, func(config io.Reader) (admission.Interface, error) {
return NewPlugin(), nil
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return newPlugin(), nil
})
}
// PriorityPlugin is an implementation of admission.Interface.
type PriorityPlugin struct {
// priorityPlugin is an implementation of admission.Interface.
type priorityPlugin struct {
*admission.Handler
client internalclientset.Interface
lister schedulinglisters.PriorityClassLister
// globalDefaultPriority caches the value of global default priority class.
globalDefaultPriority *int32
}
var _ admission.MutationInterface = &PriorityPlugin{}
var _ admission.ValidationInterface = &PriorityPlugin{}
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&PriorityPlugin{})
var _ = kubeapiserveradmission.WantsInternalKubeClientSet(&PriorityPlugin{})
var _ admission.MutationInterface = &priorityPlugin{}
var _ admission.ValidationInterface = &priorityPlugin{}
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&priorityPlugin{})
var _ = kubeapiserveradmission.WantsInternalKubeClientSet(&priorityPlugin{})
// NewPlugin creates a new priority admission plugin.
func NewPlugin() *PriorityPlugin {
return &PriorityPlugin{
func newPlugin() *priorityPlugin {
return &priorityPlugin{
Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete),
}
}
// ValidateInitialization implements the InitializationValidator interface.
func (p *PriorityPlugin) ValidateInitialization() error {
func (p *priorityPlugin) ValidateInitialization() error {
if p.client == nil {
return fmt.Errorf("%s requires a client", pluginName)
return fmt.Errorf("%s requires a client", PluginName)
}
if p.lister == nil {
return fmt.Errorf("%s requires a lister", pluginName)
return fmt.Errorf("%s requires a lister", PluginName)
}
return nil
}
// SetInternalKubeClientSet implements the WantsInternalKubeClientSet interface.
func (p *PriorityPlugin) SetInternalKubeClientSet(client internalclientset.Interface) {
func (p *priorityPlugin) SetInternalKubeClientSet(client internalclientset.Interface) {
p.client = client
}
// SetInternalKubeInformerFactory implements the WantsInternalKubeInformerFactory interface.
func (p *PriorityPlugin) SetInternalKubeInformerFactory(f informers.SharedInformerFactory) {
func (p *priorityPlugin) SetInternalKubeInformerFactory(f informers.SharedInformerFactory) {
priorityInformer := f.Scheduling().InternalVersion().PriorityClasses()
p.lister = priorityInformer.Lister()
p.SetReadyFunc(priorityInformer.Informer().HasSynced)
@ -106,7 +96,7 @@ var (
// Admit checks Pods and admits or rejects them. It also resolves the priority of pods based on their PriorityClass.
// Note that pod validation mechanism prevents update of a pod priority.
func (p *PriorityPlugin) Admit(a admission.Attributes) error {
func (p *priorityPlugin) Admit(a admission.Attributes) error {
operation := a.GetOperation()
// Ignore all calls to subresources
if len(a.GetSubresource()) != 0 {
@ -126,7 +116,7 @@ func (p *PriorityPlugin) Admit(a admission.Attributes) error {
}
// Validate checks PriorityClasses and admits or rejects them.
func (p *PriorityPlugin) Validate(a admission.Attributes) error {
func (p *priorityPlugin) Validate(a admission.Attributes) error {
operation := a.GetOperation()
// Ignore all calls to subresources
if len(a.GetSubresource()) != 0 {
@ -138,10 +128,6 @@ func (p *PriorityPlugin) Validate(a admission.Attributes) error {
if operation == admission.Create || operation == admission.Update {
return p.validatePriorityClass(a)
}
if operation == admission.Delete {
p.invalidateCachedDefaultPriority()
return nil
}
return nil
default:
@ -150,21 +136,26 @@ func (p *PriorityPlugin) Validate(a admission.Attributes) error {
}
// admitPod makes sure a new pod does not set spec.Priority field. It also makes sure that the PriorityClassName exists if it is provided and resolves the pod priority from the PriorityClassName.
func (p *PriorityPlugin) admitPod(a admission.Attributes) error {
func (p *priorityPlugin) admitPod(a admission.Attributes) error {
operation := a.GetOperation()
pod, ok := a.GetObject().(*api.Pod)
if !ok {
return errors.NewBadRequest("resource was marked with kind Pod but was unable to be converted")
}
if _, isMirrorPod := pod.Annotations[api.MirrorPodAnnotationKey]; isMirrorPod {
return nil
}
// Make sure that the client has not set `priority` at the time of pod creation.
if operation == admission.Create && pod.Spec.Priority != nil {
return admission.NewForbidden(a, fmt.Errorf("the integer value of priority must not be provided in pod spec. Priority admission controller populates the value from the given PriorityClass name"))
}
if utilfeature.DefaultFeatureGate.Enabled(features.PodPriority) {
var priority int32
// TODO: @ravig - This is for backwards compatibility to ensure that critical pods with annotations just work fine.
// Remove when no longer needed.
if len(pod.Spec.PriorityClassName) == 0 &&
utilfeature.DefaultFeatureGate.Enabled(features.ExperimentalCriticalPodAnnotation) &&
kubelettypes.IsCritical(a.GetNamespace(), pod.Annotations) {
pod.Spec.PriorityClassName = schedulerapi.SystemClusterCritical
}
if len(pod.Spec.PriorityClassName) == 0 {
var err error
priority, err = p.getDefaultPriority()
@ -173,16 +164,19 @@ func (p *PriorityPlugin) admitPod(a admission.Attributes) error {
}
} else {
// First try to resolve by system priority classes.
priority, ok = SystemPriorityClasses[pod.Spec.PriorityClassName]
priority, ok = schedulerapi.SystemPriorityClasses[pod.Spec.PriorityClassName]
if !ok {
// Now that we didn't find any system priority, try resolving by user defined priority classes.
pc, err := p.lister.Get(pod.Spec.PriorityClassName)
if err != nil {
return fmt.Errorf("failed to get default priority class %s: %v", pod.Spec.PriorityClassName, err)
}
if pc == nil {
return admission.NewForbidden(a, fmt.Errorf("no PriorityClass with name %v was found", pod.Spec.PriorityClassName))
if errors.IsNotFound(err) {
return admission.NewForbidden(a, fmt.Errorf("no PriorityClass with name %v was found", pod.Spec.PriorityClassName))
}
return fmt.Errorf("failed to get PriorityClass with name %s: %v", pod.Spec.PriorityClassName, err)
}
priority = pc.Value
}
}
@ -192,16 +186,16 @@ func (p *PriorityPlugin) admitPod(a admission.Attributes) error {
}
// validatePriorityClass ensures that the value field is not larger than the highest user definable priority. If the GlobalDefault is set, it ensures that there is no other PriorityClass whose GlobalDefault is set.
func (p *PriorityPlugin) validatePriorityClass(a admission.Attributes) error {
func (p *priorityPlugin) validatePriorityClass(a admission.Attributes) error {
operation := a.GetOperation()
pc, ok := a.GetObject().(*scheduling.PriorityClass)
if !ok {
return errors.NewBadRequest("resource was marked with kind PriorityClass but was unable to be converted")
}
if pc.Value > HighestUserDefinablePriority {
return admission.NewForbidden(a, fmt.Errorf("maximum allowed value of a user defined priority is %v", HighestUserDefinablePriority))
if pc.Value > schedulerapi.HighestUserDefinablePriority {
return admission.NewForbidden(a, fmt.Errorf("maximum allowed value of a user defined priority is %v", schedulerapi.HighestUserDefinablePriority))
}
if _, ok := SystemPriorityClasses[pc.Name]; ok {
if _, ok := schedulerapi.SystemPriorityClasses[pc.Name]; ok {
return admission.NewForbidden(a, fmt.Errorf("the name of the priority class is a reserved name for system use only: %v", pc.Name))
}
// If the new PriorityClass tries to be the default priority, make sure that no other priority class is marked as default.
@ -217,43 +211,34 @@ func (p *PriorityPlugin) validatePriorityClass(a admission.Attributes) error {
}
}
}
// We conservatively invalidate our cache of global default priority upon any changes to any of the existing classes or creation of a new class.
p.invalidateCachedDefaultPriority()
return nil
}
func (p *PriorityPlugin) getDefaultPriorityClass() (*scheduling.PriorityClass, error) {
func (p *priorityPlugin) getDefaultPriorityClass() (*scheduling.PriorityClass, error) {
list, err := p.lister.List(labels.Everything())
if err != nil {
return nil, err
}
// In case more than one global default priority class is added as a result of a race condition,
// we pick the one with the lowest priority value.
var defaultPC *scheduling.PriorityClass
for _, pci := range list {
if pci.GlobalDefault {
return pci, nil
if defaultPC == nil || defaultPC.Value > pci.Value {
defaultPC = pci
}
}
}
return nil, nil
return defaultPC, nil
}
func (p *PriorityPlugin) getDefaultPriority() (int32, error) {
// If global default priority is cached, return it.
if p.globalDefaultPriority != nil {
return *p.globalDefaultPriority, nil
}
func (p *priorityPlugin) getDefaultPriority() (int32, error) {
dpc, err := p.getDefaultPriorityClass()
if err != nil {
return 0, err
}
priority := int32(scheduling.DefaultPriorityWhenNoDefaultClassExists)
if dpc != nil {
priority = dpc.Value
return dpc.Value, nil
}
// Cache the value.
p.globalDefaultPriority = &priority
return priority, nil
}
// invalidateCachedDefaultPriority sets global default priority to nil to indicate that it should be looked up again.
func (p *PriorityPlugin) invalidateCachedDefaultPriority() {
p.globalDefaultPriority = nil
return int32(scheduling.DefaultPriorityWhenNoDefaultClassExists), nil
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package admission
package priority
import (
"fmt"
@ -30,9 +30,10 @@ import (
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/features"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
)
func addPriorityClasses(ctrl *PriorityPlugin, priorityClasses []*scheduling.PriorityClass) {
func addPriorityClasses(ctrl *priorityPlugin, priorityClasses []*scheduling.PriorityClass) {
informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
ctrl.SetInternalKubeInformerFactory(informerFactory)
// First add the existing classes to the cache.
@ -82,7 +83,7 @@ func TestPriorityClassAdmission(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "toohighclass",
},
Value: HighestUserDefinablePriority + 1,
Value: schedulerapi.HighestUserDefinablePriority + 1,
Description: "Just a test priority class",
}
@ -91,9 +92,9 @@ func TestPriorityClassAdmission(t *testing.T) {
Kind: "PriorityClass",
},
ObjectMeta: metav1.ObjectMeta{
Name: "system-cluster-critical",
Name: schedulerapi.SystemClusterCritical,
},
Value: HighestUserDefinablePriority + 1,
Value: schedulerapi.HighestUserDefinablePriority + 1,
Description: "Name conflicts with system priority class names",
}
@ -132,7 +133,7 @@ func TestPriorityClassAdmission(t *testing.T) {
for _, test := range tests {
glog.V(4).Infof("starting test %q", test.name)
ctrl := NewPlugin()
ctrl := newPlugin()
// Add existing priority classes.
addPriorityClasses(ctrl, test.existingClasses)
// Now add the new class.
@ -189,6 +190,14 @@ func TestDefaultPriority(t *testing.T) {
expectedDefaultBefore: scheduling.DefaultPriorityWhenNoDefaultClassExists,
expectedDefaultAfter: defaultClass1.Value,
},
{
name: "multiple default classes resolves to the minimum value among them",
classesBefore: []*scheduling.PriorityClass{defaultClass1, defaultClass2},
classesAfter: []*scheduling.PriorityClass{defaultClass2},
attributes: admission.NewAttributesRecord(nil, nil, pcKind, "", defaultClass1.Name, pcResource, "", admission.Delete, nil),
expectedDefaultBefore: defaultClass1.Value,
expectedDefaultAfter: defaultClass2.Value,
},
{
name: "delete default priority class",
classesBefore: []*scheduling.PriorityClass{defaultClass1},
@ -209,7 +218,7 @@ func TestDefaultPriority(t *testing.T) {
for _, test := range tests {
glog.V(4).Infof("starting test %q", test.name)
ctrl := NewPlugin()
ctrl := newPlugin()
addPriorityClasses(ctrl, test.classesBefore)
defaultPriority, err := ctrl.getDefaultPriority()
if err != nil {
@ -307,6 +316,22 @@ func TestPodAdmission(t *testing.T) {
Name: "pod-w-system-priority",
Namespace: "namespace",
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: containerName,
},
},
PriorityClassName: schedulerapi.SystemClusterCritical,
},
},
// pod[5]: mirror Pod with a system priority class name
{
ObjectMeta: metav1.ObjectMeta{
Name: "mirror-pod-w-system-priority",
Namespace: "namespace",
Annotations: map[string]string{api.MirrorPodAnnotationKey: ""},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
@ -316,9 +341,44 @@ func TestPodAdmission(t *testing.T) {
PriorityClassName: "system-cluster-critical",
},
},
// pod[6]: mirror Pod with integer value of priority
{
ObjectMeta: metav1.ObjectMeta{
Name: "mirror-pod-w-integer-priority",
Namespace: "namespace",
Annotations: map[string]string{api.MirrorPodAnnotationKey: ""},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: containerName,
},
},
PriorityClassName: "default1",
Priority: &intPriority,
},
},
// pod[7]: Pod with a critical priority annotation. This needs to be automatically assigned
// system-cluster-critical
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-w-system-priority",
Namespace: "kube-system",
Annotations: map[string]string{"scheduler.alpha.kubernetes.io/critical-pod": ""},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: containerName,
},
},
},
},
}
// Enable PodPriority feature gate.
utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("%s=true", features.PodPriority))
// Enable ExperimentalCriticalPodAnnotation feature gate.
utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("%s=true", features.ExperimentalCriticalPodAnnotation))
tests := []struct {
name string
existingClasses []*scheduling.PriorityClass
@ -361,7 +421,7 @@ func TestPodAdmission(t *testing.T) {
"pod with a system priority class",
[]*scheduling.PriorityClass{},
*pods[4],
SystemCriticalPriority,
schedulerapi.SystemCriticalPriority,
false,
},
{
@ -378,12 +438,33 @@ func TestPodAdmission(t *testing.T) {
0,
true,
},
{
"mirror pod with system priority class",
[]*scheduling.PriorityClass{},
*pods[5],
schedulerapi.SystemCriticalPriority,
false,
},
{
"mirror pod with integer priority",
[]*scheduling.PriorityClass{},
*pods[6],
0,
true,
},
{
"pod with critical pod annotation",
[]*scheduling.PriorityClass{},
*pods[7],
schedulerapi.SystemCriticalPriority,
false,
},
}
for _, test := range tests {
glog.V(4).Infof("starting test %q", test.name)
ctrl := NewPlugin()
ctrl := newPlugin()
// Add existing priority classes.
addPriorityClasses(ctrl, test.existingClasses)

View File

@ -52,13 +52,13 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/quota/generic:go_default_library",
"//pkg/quota/install:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
"//vendor/github.com/hashicorp/golang-lru:go_default_library",

View File

@ -27,14 +27,16 @@ import (
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/generic"
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
resourcequotaapiv1alpha1 "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1"
"k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation"
)
const PluginName = "ResourceQuota"
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register("ResourceQuota",
plugins.Register(PluginName,
func(config io.Reader) (admission.Interface, error) {
// load the configuration provided (if any)
configuration, err := LoadConfiguration(config)
@ -49,10 +51,6 @@ func Register(plugins *admission.Plugins) {
}
return NewResourceQuota(configuration, 5, make(chan struct{}))
})
// add our config types
resourcequotaapi.AddToScheme(plugins.ConfigScheme)
resourcequotaapiv1alpha1.AddToScheme(plugins.ConfigScheme)
}
// QuotaAdmission implements an admission controller that can enforce quota constraints
@ -103,7 +101,7 @@ func (a *QuotaAdmission) SetInternalKubeInformerFactory(f informers.SharedInform
func (a *QuotaAdmission) SetQuotaConfiguration(c quota.Configuration) {
a.quotaConfiguration = c
a.evaluator = NewQuotaEvaluator(a.quotaAccessor, a.quotaConfiguration, nil, a.config, a.numEvaluators, a.stopCh)
a.evaluator = NewQuotaEvaluator(a.quotaAccessor, a.quotaConfiguration.IgnoredResources(), generic.NewRegistry(a.quotaConfiguration.Evaluators()), nil, a.config, a.numEvaluators, a.stopCh)
}
// ValidateInitialization ensures an authorizer is set.

View File

@ -35,6 +35,7 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/quota/generic"
"k8s.io/kubernetes/pkg/quota/install"
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
)
@ -133,7 +134,7 @@ func TestAdmissionIgnoresDelete(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -169,7 +170,7 @@ func TestAdmissionIgnoresSubresources(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -214,7 +215,7 @@ func TestAdmitBelowQuotaLimit(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -298,7 +299,7 @@ func TestAdmitHandlesOldObjects(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -405,7 +406,7 @@ func TestAdmitHandlesNegativePVCUpdates(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -472,7 +473,7 @@ func TestAdmitHandlesPVCUpdates(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -571,7 +572,7 @@ func TestAdmitHandlesCreatingUpdates(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -666,7 +667,7 @@ func TestAdmitExceedQuotaLimit(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -711,7 +712,7 @@ func TestAdmitEnforceQuotaConstraints(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -724,12 +725,6 @@ func TestAdmitEnforceQuotaConstraints(t *testing.T) {
if err == nil {
t.Errorf("Expected an error because the pod does not specify a memory limit")
}
// verify the requests and limits are actually valid (in this case, we fail because the limits < requests)
newPod = validPod("not-allowed-pod", 1, getResourceRequirements(getResourceList("200m", "2Gi"), getResourceList("100m", "1Gi")))
err = handler.Validate(admission.NewAttributesRecord(newPod, nil, api.Kind("Pod").WithVersion("version"), newPod.Namespace, newPod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, nil))
if err == nil {
t.Errorf("Expected an error because the pod does not specify a memory limit")
}
}
// TestAdmitPodInNamespaceWithoutQuota ensures that if a namespace has no quota, that a pod can get in
@ -766,7 +761,7 @@ func TestAdmitPodInNamespaceWithoutQuota(t *testing.T) {
quotaAccessor.liveLookupCache = liveLookupCache
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -833,7 +828,7 @@ func TestAdmitBelowTerminatingQuotaLimit(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -939,7 +934,7 @@ func TestAdmitBelowBestEffortQuotaLimit(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -1032,7 +1027,7 @@ func TestAdmitBestEffortQuotaLimitIgnoresBurstable(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -1118,7 +1113,7 @@ func TestAdmissionSetsMissingNamespace(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -1164,7 +1159,7 @@ func TestAdmitRejectsNegativeUsage(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -1211,7 +1206,7 @@ func TestAdmitWhenUnrelatedResourceExceedsQuota(t *testing.T) {
quotaAccessor.lister = informerFactory.Core().InternalVersion().ResourceQuotas().Lister()
config := &resourcequotaapi.Configuration{}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -1248,7 +1243,7 @@ func TestAdmitLimitedResourceNoQuota(t *testing.T) {
},
}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -1282,7 +1277,7 @@ func TestAdmitLimitedResourceNoQuotaIgnoresNonMatchingResources(t *testing.T) {
},
}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -1329,7 +1324,7 @@ func TestAdmitLimitedResourceWithQuota(t *testing.T) {
},
}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -1388,7 +1383,7 @@ func TestAdmitLimitedResourceWithMultipleQuota(t *testing.T) {
},
}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
@ -1437,7 +1432,7 @@ func TestAdmitLimitedResourceWithQuotaThatDoesNotCover(t *testing.T) {
},
}
quotaConfiguration := install.NewQuotaConfigurationForAdmission()
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration, nil, config, 5, stopCh)
evaluator := NewQuotaEvaluator(quotaAccessor, quotaConfiguration.IgnoredResources(), generic.NewRegistry(quotaConfiguration.Evaluators()), nil, config, 5, stopCh)
handler := &QuotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),

View File

@ -16,15 +16,16 @@ 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!
// 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"
resourcequota "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
unsafe "unsafe"
)
func init() {

View File

@ -16,7 +16,7 @@ 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!
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1alpha1
@ -52,9 +52,8 @@ func (in *Configuration) DeepCopy() *Configuration {
func (in *Configuration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

View File

@ -16,7 +16,7 @@ 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!
// Code generated by defaulter-gen. DO NOT EDIT.
package v1alpha1

View File

@ -32,7 +32,6 @@ filegroup(
go_test(
name = "go_default_test",
srcs = ["validation_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation",
library = ":go_default_library",
embed = [":go_default_library"],
deps = ["//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library"],
)

View File

@ -16,7 +16,7 @@ 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!
// Code generated by deepcopy-gen. DO NOT EDIT.
package resourcequota
@ -52,9 +52,8 @@ func (in *Configuration) DeepCopy() *Configuration {
func (in *Configuration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
} else {
return nil
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.

View File

@ -52,8 +52,7 @@ type quotaEvaluator struct {
// lockAcquisitionFunc acquires any required locks and returns a cleanup method to defer
lockAcquisitionFunc func([]api.ResourceQuota) func()
// how quota was configured
quotaConfiguration quota.Configuration
ignoredResources map[schema.GroupResource]struct{}
// registry that knows how to measure usage for objects
registry quota.Registry
@ -110,7 +109,7 @@ func newAdmissionWaiter(a admission.Attributes) *admissionWaiter {
// NewQuotaEvaluator configures an admission controller that can enforce quota constraints
// using the provided registry. The registry must have the capability to handle group/kinds that
// are persisted by the server this admission controller is intercepting
func NewQuotaEvaluator(quotaAccessor QuotaAccessor, quotaConfiguration quota.Configuration, lockAcquisitionFunc func([]api.ResourceQuota) func(), config *resourcequotaapi.Configuration, workers int, stopCh <-chan struct{}) Evaluator {
func NewQuotaEvaluator(quotaAccessor QuotaAccessor, ignoredResources map[schema.GroupResource]struct{}, quotaRegistry quota.Registry, lockAcquisitionFunc func([]api.ResourceQuota) func(), config *resourcequotaapi.Configuration, workers int, stopCh <-chan struct{}) Evaluator {
// if we get a nil config, just create an empty default.
if config == nil {
config = &resourcequotaapi.Configuration{}
@ -120,8 +119,8 @@ func NewQuotaEvaluator(quotaAccessor QuotaAccessor, quotaConfiguration quota.Con
quotaAccessor: quotaAccessor,
lockAcquisitionFunc: lockAcquisitionFunc,
quotaConfiguration: quotaConfiguration,
registry: generic.NewRegistry(quotaConfiguration.Evaluators()),
ignoredResources: ignoredResources,
registry: quotaRegistry,
queue: workqueue.NewNamed("admission_quota_controller"),
work: map[string][]*admissionWaiter{},
@ -524,7 +523,7 @@ func (e *quotaEvaluator) Evaluate(a admission.Attributes) error {
// is this resource ignored?
gvr := a.GetResource()
gr := gvr.GroupResource()
if _, ok := e.quotaConfiguration.IgnoredResources()[gr]; ok {
if _, ok := e.ignoredResources[gr]; ok {
return nil
}

View File

@ -48,7 +48,7 @@ type quotaAccessor struct {
lister corelisters.ResourceQuotaLister
// liveLookups holds the last few live lookups we've done to help ammortize cost on repeated lookup failures.
// This let's us handle the case of latent caches, by looking up actual results for a namespace on cache miss/no results.
// This lets us handle the case of latent caches, by looking up actual results for a namespace on cache miss/no results.
// We track the lookup result here so that for repeated requests, we don't look it up very often.
liveLookupCache *lru.Cache
liveTTL time.Duration

View File

@ -13,6 +13,7 @@ go_library(
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/client/listers/extensions/internalversion:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library",
@ -34,13 +35,13 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/controller:go_default_library",
"//pkg/security/apparmor:go_default_library",
@ -56,6 +57,7 @@ go_test(
"//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
],
)

View File

@ -0,0 +1,6 @@
approvers:
- tallclair
- liggitt
reviewers:
- pweil-
- php-coder

View File

@ -33,6 +33,7 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/policy"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
extensionslisters "k8s.io/kubernetes/pkg/client/listers/extensions/internalversion"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
@ -122,8 +123,8 @@ func (c *PodSecurityPolicyPlugin) Admit(a admission.Attributes) error {
pod := a.GetObject().(*api.Pod)
// compute the context
allowedPod, pspName, validationErrs, err := c.computeSecurityContext(a, pod, true)
// 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)
}
@ -152,8 +153,8 @@ func (c *PodSecurityPolicyPlugin) Validate(a admission.Attributes) error {
pod := a.GetObject().(*api.Pod)
// compute the context. Mutation is not allowed.
allowedPod, _, validationErrs, err := c.computeSecurityContext(a, pod, false)
// compute the context. Mutation is not allowed. ValidatedPSPAnnotation is used as a hint to gain same speed-up.
allowedPod, _, validationErrs, err := c.computeSecurityContext(a, pod, false, pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation])
if err != nil {
return admission.NewForbidden(a, err)
}
@ -193,8 +194,9 @@ func shouldIgnore(a admission.Attributes) (bool, error) {
// 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.
func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes, pod *api.Pod, specMutationAllowed bool) (*api.Pod, string, field.ErrorList, error) {
// 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
glog.V(4).Infof("getting pod security policies for pod %s (generate: %s)", pod.Name, pod.GenerateName)
var saInfo user.Info
@ -213,9 +215,18 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
return pod, "", nil, nil
}
// sort by name to make order deterministic
// 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
})
@ -244,7 +255,6 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
}
// the entire pod validated
mutated := !apiequality.Semantic.DeepEqual(pod, podCopy)
if mutated && !specMutationAllowed {
continue
@ -287,35 +297,28 @@ func (c *PodSecurityPolicyPlugin) computeSecurityContext(a admission.Attributes,
func assignSecurityContext(provider psp.Provider, pod *api.Pod, fldPath *field.Path) field.ErrorList {
errs := field.ErrorList{}
psc, pscAnnotations, err := provider.CreatePodSecurityContext(pod)
err := provider.DefaultPodSecurityContext(pod)
if err != nil {
errs = append(errs, field.Invalid(field.NewPath("spec", "securityContext"), pod.Spec.SecurityContext, err.Error()))
}
pod.Spec.SecurityContext = psc
pod.Annotations = pscAnnotations
errs = append(errs, provider.ValidatePodSecurityContext(pod, field.NewPath("spec", "securityContext"))...)
errs = append(errs, provider.ValidatePod(pod, field.NewPath("spec", "securityContext"))...)
for i := range pod.Spec.InitContainers {
sc, scAnnotations, err := provider.CreateContainerSecurityContext(pod, &pod.Spec.InitContainers[i])
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
}
pod.Spec.InitContainers[i].SecurityContext = sc
pod.Annotations = scAnnotations
errs = append(errs, provider.ValidateContainerSecurityContext(pod, &pod.Spec.InitContainers[i], field.NewPath("spec", "initContainers").Index(i).Child("securityContext"))...)
}
for i := range pod.Spec.Containers {
sc, scAnnotations, err := provider.CreateContainerSecurityContext(pod, &pod.Spec.Containers[i])
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
}
pod.Spec.Containers[i].SecurityContext = sc
pod.Annotations = scAnnotations
errs = append(errs, provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[i], field.NewPath("spec", "containers").Index(i).Child("securityContext"))...)
}
@ -352,11 +355,19 @@ func isAuthorizedForPolicy(user, sa user.Info, namespace, policyName string, aut
}
// 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)
attr := buildAttributes(info, namespace, policyName, apiGroupName)
decision, reason, err := authz.Authorize(attr)
if err != nil {
glog.V(5).Infof("cannot authorize for policy: %v,%v", reason, err)
@ -365,14 +376,14 @@ func authorizedForPolicy(info user.Info, namespace string, policyName string, au
}
// buildAttributes builds an attributes record for a SAR based on the user info and policy.
func buildAttributes(info user.Info, namespace string, policyName string) authorizer.Attributes {
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: extensions.GroupName,
APIGroup: apiGroupName,
Resource: "podsecuritypolicies",
ResourceRequest: true,
}

View File

@ -32,10 +32,12 @@ import (
"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/kubernetes/pkg/api/legacyscheme"
kapi "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/policy"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/security/apparmor"
@ -65,11 +67,16 @@ func NewTestAdmission(psps []*extensions.PodSecurityPolicy, authz authorizer.Aut
}
}
// TestAlwaysAllowedAuthorizer is a testing struct for testing that fulfills the authorizer interface.
// 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) {
@ -78,7 +85,8 @@ func (t *TestAuthorizer) Authorize(a authorizer.Attributes) (authorized authoriz
}
allowedInNamespace := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][a.GetNamespace()][a.GetName()]
allowedClusterWide := t.usernameToNamespaceToAllowedPSPs[a.GetUser().GetName()][""][a.GetName()]
if allowedInNamespace || allowedClusterWide {
allowedAPIGroup := len(t.allowedAPIGroupName) == 0 || a.GetAPIGroup() == t.allowedAPIGroupName
if allowedAPIGroup && (allowedInNamespace || allowedClusterWide) {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionNoOpinion, "", nil
@ -344,15 +352,20 @@ func TestAdmitPreferNonmutating(t *testing.T) {
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 []*extensions.PodSecurityPolicy
shouldPassAdmit bool
shouldPassValidate bool
expectMutation bool
expectedPodUser *int64
expectedContainerUser *int64
expectedPSP string
}{
@ -360,10 +373,8 @@ func TestAdmitPreferNonmutating(t *testing.T) {
operation: kadmission.Create,
pod: unprivilegedRunAsAnyPod.DeepCopy(),
psps: []*extensions.PodSecurityPolicy{privilegedPSP},
shouldPassAdmit: true,
shouldPassValidate: true,
expectMutation: false,
expectedPodUser: nil,
expectedContainerUser: nil,
expectedPSP: privilegedPSP.Name,
},
@ -371,10 +382,8 @@ func TestAdmitPreferNonmutating(t *testing.T) {
operation: kadmission.Create,
pod: unprivilegedRunAsAnyPod.DeepCopy(),
psps: []*extensions.PodSecurityPolicy{mutating2, mutating1, privilegedPSP},
shouldPassAdmit: true,
shouldPassValidate: true,
expectMutation: false,
expectedPodUser: nil,
expectedContainerUser: nil,
expectedPSP: privilegedPSP.Name,
},
@ -382,10 +391,17 @@ func TestAdmitPreferNonmutating(t *testing.T) {
operation: kadmission.Create,
pod: unprivilegedRunAsAnyPod.DeepCopy(),
psps: []*extensions.PodSecurityPolicy{mutating2, mutating1},
shouldPassAdmit: true,
shouldPassValidate: true,
expectMutation: true,
expectedPodUser: nil,
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: []*extensions.PodSecurityPolicy{mutating2, mutating1},
shouldPassValidate: true,
expectMutation: true,
expectedContainerUser: &mutating1.Spec.RunAsUser.Ranges[0].Min,
expectedPSP: mutating1.Name,
},
@ -394,10 +410,8 @@ func TestAdmitPreferNonmutating(t *testing.T) {
pod: changedPodWithSC.DeepCopy(),
podBeforeUpdate: podWithSC.DeepCopy(),
psps: []*extensions.PodSecurityPolicy{mutating2, mutating1, privilegedPSP},
shouldPassAdmit: true,
shouldPassValidate: true,
expectMutation: false,
expectedPodUser: nil,
expectedContainerUser: nil,
expectedPSP: privilegedPSP.Name,
},
@ -406,10 +420,8 @@ func TestAdmitPreferNonmutating(t *testing.T) {
pod: changedPod.DeepCopy(),
podBeforeUpdate: unprivilegedRunAsAnyPod.DeepCopy(),
psps: []*extensions.PodSecurityPolicy{mutating2, mutating1},
shouldPassAdmit: true,
shouldPassValidate: false,
expectMutation: false,
expectedPodUser: nil,
expectedContainerUser: nil,
expectedPSP: "",
},
@ -418,10 +430,8 @@ func TestAdmitPreferNonmutating(t *testing.T) {
pod: unprivilegedRunAsAnyPod.DeepCopy(),
podBeforeUpdate: unprivilegedRunAsAnyPod.DeepCopy(),
psps: []*extensions.PodSecurityPolicy{mutating2, mutating1},
shouldPassAdmit: true,
shouldPassValidate: true,
expectMutation: false,
expectedPodUser: nil,
expectedContainerUser: nil,
expectedPSP: "",
},
@ -430,38 +440,32 @@ func TestAdmitPreferNonmutating(t *testing.T) {
pod: gcChangedPod.DeepCopy(),
podBeforeUpdate: unprivilegedRunAsAnyPod.DeepCopy(),
psps: []*extensions.PodSecurityPolicy{mutating2, mutating1},
shouldPassAdmit: true,
shouldPassValidate: true,
expectMutation: false,
expectedPodUser: nil,
expectedContainerUser: nil,
expectedPSP: "",
},
}
for k, v := range tests {
testPSPAdmitAdvanced(k, v.operation, v.psps, nil, &user.DefaultInfo{}, v.pod, v.podBeforeUpdate, v.shouldPassAdmit, v.shouldPassValidate, v.expectMutation, v.expectedPSP, t)
testPSPAdmitAdvanced(k, v.operation, v.psps, nil, &user.DefaultInfo{}, v.pod, v.podBeforeUpdate, true, v.shouldPassValidate, v.expectMutation, v.expectedPSP, t)
if v.shouldPassAdmit {
actualPodUser := (*int64)(nil)
if v.pod.Spec.SecurityContext != nil {
actualPodUser = v.pod.Spec.SecurityContext.RunAsUser
}
if (actualPodUser == nil) != (v.expectedPodUser == nil) {
t.Errorf("%s expected pod user %v, got %v", k, v.expectedPodUser, actualPodUser)
} else if actualPodUser != nil && *actualPodUser != *v.expectedPodUser {
t.Errorf("%s expected pod user %v, got %v", k, *v.expectedPodUser, *actualPodUser)
}
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)
}
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)
}
}
}
@ -1999,8 +2003,9 @@ func TestPolicyAuthorization(t *testing.T) {
expectedPolicy string
inPolicies []*extensions.PodSecurityPolicy
allowed map[string]map[string]map[string]bool
allowedGroup string
}{
"policy allowed by user": {
"policy allowed by user (extensions API Group)": {
user: &user.DefaultInfo{Name: "user"},
sa: "sa",
ns: "test",
@ -2012,7 +2017,7 @@ func TestPolicyAuthorization(t *testing.T) {
inPolicies: []*extensions.PodSecurityPolicy{policyWithName("policy")},
expectedPolicy: "policy",
},
"policy allowed by sa": {
"policy allowed by sa (extensions API Group)": {
user: &user.DefaultInfo{Name: "user"},
sa: "sa",
ns: "test",
@ -2024,6 +2029,32 @@ func TestPolicyAuthorization(t *testing.T) {
inPolicies: []*extensions.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: []*extensions.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: []*extensions.PodSecurityPolicy{policyWithName("policy")},
expectedPolicy: "policy",
allowedGroup: policy.GroupName,
},
"no policies allowed": {
user: &user.DefaultInfo{Name: "user"},
sa: "sa",
@ -2125,7 +2156,7 @@ func TestPolicyAuthorization(t *testing.T) {
var (
oldPod *kapi.Pod
shouldPass = v.expectedPolicy != ""
authz = &TestAuthorizer{usernameToNamespaceToAllowedPSPs: v.allowed}
authz = &TestAuthorizer{usernameToNamespaceToAllowedPSPs: v.allowed, allowedAPIGroupName: v.allowedGroup}
canMutate = true
)
pod := goodPod()
@ -2150,7 +2181,6 @@ func TestPolicyAuthorizationErrors(t *testing.T) {
)
tests := map[string]struct {
priviliged bool
inPolicies []*extensions.PodSecurityPolicy
allowed map[string]map[string]map[string]bool
expectValidationErrs int
@ -2217,7 +2247,7 @@ func TestPolicyAuthorizationErrors(t *testing.T) {
plugin := NewTestAdmission(tc.inPolicies, authz)
attrs := kadmission.NewAttributesRecord(pod, nil, kapi.Kind("Pod").WithVersion("version"), ns, "", kapi.Resource("pods").WithVersion("version"), "", kadmission.Create, &user.DefaultInfo{Name: userName})
allowedPod, _, validationErrs, err := plugin.computeSecurityContext(attrs, pod, true)
allowedPod, _, validationErrs, err := plugin.computeSecurityContext(attrs, pod, true, "")
assert.Nil(t, allowedPod)
assert.NoError(t, err)
assert.Len(t, validationErrs, tc.expectValidationErrs)
@ -2225,6 +2255,99 @@ func TestPolicyAuthorizationErrors(t *testing.T) {
}
}
func TestPreferValidatedPSP(t *testing.T) {
restrictivePSPWithName := func(name string) *extensions.PodSecurityPolicy {
p := restrictivePSP()
p.Name = name
return p
}
permissivePSPWithName := func(name string) *extensions.PodSecurityPolicy {
p := permissivePSP()
p.Name = name
return p
}
tests := map[string]struct {
inPolicies []*extensions.PodSecurityPolicy
expectValidationErrs int
validatedPSPHint string
expectedPSP string
}{
"no policy saved in annotations, PSPs are ordered lexicographically": {
inPolicies: []*extensions.PodSecurityPolicy{
restrictivePSPWithName("001restrictive"),
restrictivePSPWithName("002restrictive"),
permissivePSPWithName("002permissive"),
permissivePSPWithName("001permissive"),
permissivePSPWithName("003permissive"),
},
expectValidationErrs: 0,
validatedPSPHint: "",
expectedPSP: "001permissive",
},
"policy saved in annotations is preferred": {
inPolicies: []*extensions.PodSecurityPolicy{
restrictivePSPWithName("001restrictive"),
restrictivePSPWithName("002restrictive"),
permissivePSPWithName("001permissive"),
permissivePSPWithName("002permissive"),
permissivePSPWithName("003permissive"),
},
expectValidationErrs: 0,
validatedPSPHint: "002permissive",
expectedPSP: "002permissive",
},
"policy saved in annotations is invalid": {
inPolicies: []*extensions.PodSecurityPolicy{
restrictivePSPWithName("001restrictive"),
restrictivePSPWithName("002restrictive"),
},
expectValidationErrs: 2,
validatedPSPHint: "foo",
expectedPSP: "",
},
"policy saved in annotations is disallowed anymore": {
inPolicies: []*extensions.PodSecurityPolicy{
restrictivePSPWithName("001restrictive"),
restrictivePSPWithName("002restrictive"),
},
expectValidationErrs: 2,
validatedPSPHint: "001restrictive",
expectedPSP: "",
},
"policy saved in annotations is disallowed anymore, but find another one": {
inPolicies: []*extensions.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, &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, pspName, tc.expectedPSP)
})
}
}
func restrictivePSP() *extensions.PodSecurityPolicy {
return &extensions.PodSecurityPolicy{
ObjectMeta: metav1.ObjectMeta{

View File

@ -20,8 +20,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",

View File

@ -25,9 +25,12 @@ import (
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("SecurityContextDeny", func(config io.Reader) (admission.Interface, error) {
plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
return NewSecurityContextDeny(), nil
})
}
@ -46,7 +49,7 @@ func NewSecurityContextDeny() *Plugin {
}
}
// Validate will deny any pod that defines SELinuxOptions or RunAsUser.
// 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
@ -57,20 +60,19 @@ func (p *Plugin) Validate(a admission.Attributes) (err error) {
return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
}
if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SupplementalGroups != nil {
return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.SupplementalGroups is forbidden"))
}
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 != nil && pod.Spec.SecurityContext.FSGroup != nil {
return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.FSGroup 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 {

View File

@ -35,8 +35,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",

View File

@ -9,7 +9,7 @@ load(
go_library(
name = "go_default_library",
srcs = ["admission.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/storageclass/setdefault",
importpath = "k8s.io/kubernetes/plugin/pkg/admission/storage/storageclass/setdefault",
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
@ -28,8 +28,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/storageclass/setdefault",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/storage:go_default_library",

View File

@ -35,6 +35,7 @@ import (
)
const (
// PluginName is the name of this admission controller plugin
PluginName = "DefaultStorageClass"
)
@ -84,16 +85,16 @@ func (a *claimDefaulterPlugin) ValidateInitialization() error {
// 1. Find available StorageClasses.
// 2. Figure which is the default
// 3. Write to the PVClaim
func (c *claimDefaulterPlugin) Admit(a admission.Attributes) error {
if a.GetResource().GroupResource() != api.Resource("persistentvolumeclaims") {
func (a *claimDefaulterPlugin) Admit(attr admission.Attributes) error {
if attr.GetResource().GroupResource() != api.Resource("persistentvolumeclaims") {
return nil
}
if len(a.GetSubresource()) != 0 {
if len(attr.GetSubresource()) != 0 {
return nil
}
pvc, ok := a.GetObject().(*api.PersistentVolumeClaim)
pvc, ok := attr.GetObject().(*api.PersistentVolumeClaim)
// if we can't convert then we don't handle this object so just return
if !ok {
return nil
@ -106,9 +107,9 @@ func (c *claimDefaulterPlugin) Admit(a admission.Attributes) error {
glog.V(4).Infof("no storage class for claim %s (generate: %s)", pvc.Name, pvc.GenerateName)
def, err := getDefaultClass(c.lister)
def, err := getDefaultClass(a.lister)
if err != nil {
return admission.NewForbidden(a, err)
return admission.NewForbidden(attr, err)
}
if def == nil {
// No default class selected, do nothing about the PVC.

View File

@ -3,7 +3,7 @@ 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/persistentvolumeclaim/pvcprotection",
importpath = "k8s.io/kubernetes/plugin/pkg/admission/storage/storageobjectinuseprotection",
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/core:go_default_library",
@ -21,8 +21,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/persistentvolumeclaim/pvcprotection",
library = ":go_default_library",
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
@ -31,6 +30,7 @@ go_test(
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],

View File

@ -0,0 +1,156 @@
/*
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 (
"fmt"
"io"
"github.com/golang/glog"
admission "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
corelisters "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
"k8s.io/kubernetes/pkg/features"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
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
pvcLister corelisters.PersistentVolumeClaimLister
pvLister corelisters.PersistentVolumeLister
}
var _ admission.Interface = &storageProtectionPlugin{}
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&storageProtectionPlugin{})
// newPlugin creates a new admission plugin.
func newPlugin() *storageProtectionPlugin {
return &storageProtectionPlugin{
Handler: admission.NewHandler(admission.Create),
}
}
func (c *storageProtectionPlugin) SetInternalKubeInformerFactory(f informers.SharedInformerFactory) {
pvcInformer := f.Core().InternalVersion().PersistentVolumeClaims()
c.pvcLister = pvcInformer.Lister()
pvInformer := f.Core().InternalVersion().PersistentVolumes()
c.pvLister = pvInformer.Lister()
c.SetReadyFunc(func() bool {
return pvcInformer.Informer().HasSynced() && pvInformer.Informer().HasSynced()
})
}
// ValidateInitialization ensures lister is set.
func (c *storageProtectionPlugin) ValidateInitialization() error {
if c.pvcLister == nil {
return fmt.Errorf("missing PVC lister")
}
if c.pvLister == nil {
return fmt.Errorf("missing PV lister")
}
return nil
}
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
}
}
glog.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
}
}
glog.V(4).Infof("adding PVC protection finalizer to %s/%s", pvc.Namespace, pvc.Name)
pvc.Finalizers = append(pvc.Finalizers, volumeutil.PVCProtectionFinalizer)
return nil
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package pvcprotection
package storageobjectinuseprotection
import (
"fmt"
@ -25,6 +25,7 @@ import (
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/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
@ -43,32 +44,76 @@ func TestAdmit(t *testing.T) {
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,
},
}
@ -77,15 +122,15 @@ func TestAdmit(t *testing.T) {
ctrl.SetInternalKubeInformerFactory(informerFactory)
for _, test := range tests {
feature.DefaultFeatureGate.Set(fmt.Sprintf("PVCProtection=%v", test.featureEnabled))
feature.DefaultFeatureGate.Set(fmt.Sprintf("StorageObjectInUseProtection=%v", test.featureEnabled))
obj := test.object.DeepCopyObject()
attrs := admission.NewAttributesRecord(
obj, // new object
obj.DeepCopyObject(), // old object, copy to be sure it's not modified
api.Kind("PersistentVolumeClaim").WithVersion("version"),
claim.Namespace,
claim.Name,
api.Resource("persistentvolumeclaims").WithVersion("version"),
schema.GroupVersionKind{},
test.namespace,
"foo",
test.resource,
"", // subresource
admission.Create,
nil, // userInfo
@ -102,5 +147,5 @@ func TestAdmit(t *testing.T) {
// Disable the feature for rest of the tests.
// TODO: remove after alpha
feature.DefaultFeatureGate.Set("PVCProtection=false")
feature.DefaultFeatureGate.Set("StorageObjectInUseProtection=false")
}