Merge pull request #55487 from deads2k/admission-16-limit

Automatic merge from submit-queue (batch tested with PRs 55283, 55461, 55288, 53970, 55487). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

split limitranger admission

I think this is the last admission plugin that needs to be split into mutating and validating halves.

@k8s-mirror-api-machinery-pr-reviews
This commit is contained in:
Kubernetes Submit Queue 2017-11-13 12:32:31 -08:00 committed by GitHub
commit d2153dff8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 100 additions and 34 deletions

View File

@ -55,6 +55,8 @@ type Plugin struct {
*admission.Handler
}
var _ admission.MutationInterface = &Plugin{}
// NewDefaultTolerationSeconds creates a new instance of the DefaultTolerationSeconds admission controller
func NewDefaultTolerationSeconds() *Plugin {
return &Plugin{

View File

@ -64,6 +64,8 @@ type InitialResources struct {
nsOnly bool
}
var _ admission.MutationInterface = &InitialResources{}
func newInitialResources(source dataSource, percentile int64, nsOnly bool) *InitialResources {
return &InitialResources{
Handler: admission.NewHandler(admission.Create),

View File

@ -64,6 +64,10 @@ type LimitRanger struct {
liveTTL time.Duration
}
var _ admission.MutationInterface = &LimitRanger{}
var _ admission.ValidationInterface = &LimitRanger{}
var _ kubeapiserveradmission.WantsInternalKubeInformerFactory = &LimitRanger{}
type liveLookupEntry struct {
expiry time.Time
items []*api.LimitRange
@ -87,6 +91,15 @@ func (l *LimitRanger) ValidateInitialization() error {
// Admit admits resources into cluster that do not violate any defined LimitRange in the namespace
func (l *LimitRanger) Admit(a admission.Attributes) (err error) {
return l.runLimitFunc(a, l.actions.MutateLimit)
}
// Validate admits resources into cluster that do not violate any defined LimitRange in the namespace
func (l *LimitRanger) Validate(a admission.Attributes) (err error) {
return l.runLimitFunc(a, l.actions.ValidateLimit)
}
func (l *LimitRanger) runLimitFunc(a admission.Attributes, limitFn func(limitRange *api.LimitRange, kind string, obj runtime.Object) error) (err error) {
if !l.actions.SupportsAttributes(a) {
return nil
}
@ -100,9 +113,31 @@ func (l *LimitRanger) Admit(a admission.Attributes) (err error) {
}
}
items, err := l.GetLimitRanges(a)
if err != nil {
return err
}
// ensure it meets each prescribed min/max
for i := range items {
limitRange := items[i]
if !l.actions.SupportsLimit(limitRange) {
continue
}
err = limitFn(limitRange, a.GetResource().Resource, a.GetObject())
if err != nil {
return admission.NewForbidden(a, err)
}
}
return nil
}
func (l *LimitRanger) GetLimitRanges(a admission.Attributes) ([]*api.LimitRange, error) {
items, err := l.lister.LimitRanges(a.GetNamespace()).List(labels.Everything())
if err != nil {
return admission.NewForbidden(a, fmt.Errorf("unable to %s %v at this time because there was an error enforcing limit ranges", a.GetOperation(), a.GetResource()))
return nil, admission.NewForbidden(a, fmt.Errorf("unable to %s %v at this time because there was an error enforcing limit ranges", a.GetOperation(), a.GetResource()))
}
// if there are no items held in our indexer, check our live-lookup LRU, if that misses, do the live lookup to prime it.
@ -116,7 +151,7 @@ func (l *LimitRanger) Admit(a admission.Attributes) (err error) {
// throttling - see #22422 for details.
liveList, err := l.client.Core().LimitRanges(a.GetNamespace()).List(metav1.ListOptions{})
if err != nil {
return admission.NewForbidden(a, err)
return nil, admission.NewForbidden(a, err)
}
newEntry := liveLookupEntry{expiry: time.Now().Add(l.liveTTL)}
for i := range liveList.Items {
@ -133,20 +168,7 @@ func (l *LimitRanger) Admit(a admission.Attributes) (err error) {
}
// ensure it meets each prescribed min/max
for i := range items {
limitRange := items[i]
if !l.actions.SupportsLimit(limitRange) {
continue
}
err = l.actions.Limit(limitRange, a.GetResource().Resource, a.GetObject())
if err != nil {
return admission.NewForbidden(a, err)
}
}
return nil
return items, nil
}
// NewLimitRanger returns an object that enforces limits based on the supplied limit function
@ -399,12 +421,23 @@ var _ LimitRangerActions = &DefaultLimitRangerActions{}
// Limit enforces resource requirements of incoming resources against enumerated constraints
// on the LimitRange. It may modify the incoming object to apply default resource requirements
// if not specified, and enumerated on the LimitRange
func (d *DefaultLimitRangerActions) Limit(limitRange *api.LimitRange, resourceName string, obj runtime.Object) error {
func (d *DefaultLimitRangerActions) MutateLimit(limitRange *api.LimitRange, resourceName string, obj runtime.Object) error {
switch resourceName {
case "pods":
return PodLimitFunc(limitRange, obj.(*api.Pod))
return PodMutateLimitFunc(limitRange, obj.(*api.Pod))
}
return nil
}
// Limit enforces resource requirements of incoming resources against enumerated constraints
// on the LimitRange. It may modify the incoming object to apply default resource requirements
// if not specified, and enumerated on the LimitRange
func (d *DefaultLimitRangerActions) ValidateLimit(limitRange *api.LimitRange, resourceName string, obj runtime.Object) error {
switch resourceName {
case "pods":
return PodValidateLimitFunc(limitRange, obj.(*api.Pod))
case "persistentvolumeclaims":
return PersistentVolumeClaimLimitFunc(limitRange, obj.(*api.PersistentVolumeClaim))
return PersistentVolumeClaimValidateLimitFunc(limitRange, obj.(*api.PersistentVolumeClaim))
}
return nil
}
@ -424,11 +457,11 @@ func (d *DefaultLimitRangerActions) SupportsLimit(limitRange *api.LimitRange) bo
return true
}
// PersistentVolumeClaimLimitFunc enforces storage limits for PVCs.
// PersistentVolumeClaimValidateLimitFunc enforces storage limits for PVCs.
// Users request storage via pvc.Spec.Resources.Requests. Min/Max is enforced by an admin with LimitRange.
// Claims will not be modified with default values because storage is a required part of pvc.Spec.
// All storage enforced values *only* apply to pvc.Spec.Resources.Requests.
func PersistentVolumeClaimLimitFunc(limitRange *api.LimitRange, pvc *api.PersistentVolumeClaim) error {
func PersistentVolumeClaimValidateLimitFunc(limitRange *api.LimitRange, pvc *api.PersistentVolumeClaim) error {
var errs []error
for i := range limitRange.Spec.Limits {
limit := limitRange.Spec.Limits[i]
@ -452,14 +485,19 @@ func PersistentVolumeClaimLimitFunc(limitRange *api.LimitRange, pvc *api.Persist
return utilerrors.NewAggregate(errs)
}
// PodLimitFunc enforces resource requirements enumerated by the pod against
// PodMutateLimitFunc sets resource requirements enumerated by the pod against
// the specified LimitRange. The pod may be modified to apply default resource
// requirements if not specified, and enumerated on the LimitRange
func PodLimitFunc(limitRange *api.LimitRange, pod *api.Pod) error {
var errs []error
func PodMutateLimitFunc(limitRange *api.LimitRange, pod *api.Pod) error {
defaultResources := defaultContainerResourceRequirements(limitRange)
mergePodResourceRequirements(pod, &defaultResources)
return nil
}
// PodValidateLimitFunc enforces resource requirements enumerated by the pod against
// the specified LimitRange.
func PodValidateLimitFunc(limitRange *api.LimitRange, pod *api.Pod) error {
var errs []error
for i := range limitRange.Spec.Limits {
limit := limitRange.Spec.Limits[i]

View File

@ -430,7 +430,11 @@ func TestPodLimitFunc(t *testing.T) {
}
for i := range successCases {
test := successCases[i]
err := PodLimitFunc(&test.limitRange, &test.pod)
err := PodMutateLimitFunc(&test.limitRange, &test.pod)
if err != nil {
t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err)
}
err = PodValidateLimitFunc(&test.limitRange, &test.pod)
if err != nil {
t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err)
}
@ -610,7 +614,11 @@ func TestPodLimitFunc(t *testing.T) {
}
for i := range errorCases {
test := errorCases[i]
err := PodLimitFunc(&test.limitRange, &test.pod)
err := PodMutateLimitFunc(&test.limitRange, &test.pod)
if err != nil {
t.Errorf("Unexpected error for pod: %s, %v", test.pod.Name, err)
}
err = PodValidateLimitFunc(&test.limitRange, &test.pod)
if err == nil {
t.Errorf("Expected error for pod: %s", test.pod.Name)
}
@ -628,7 +636,7 @@ func getLocalStorageResourceList(ephemeralStorage string) api.ResourceList {
func TestPodLimitFuncApplyDefault(t *testing.T) {
limitRange := validLimitRange()
testPod := validPodInit(validPod("foo", 1, getResourceRequirements(api.ResourceList{}, api.ResourceList{})), getResourceRequirements(api.ResourceList{}, api.ResourceList{}))
err := PodLimitFunc(&limitRange, &testPod)
err := PodMutateLimitFunc(&limitRange, &testPod)
if err != nil {
t.Errorf("Unexpected error for valid pod: %s, %v", testPod.Name, err)
}
@ -687,11 +695,15 @@ func TestLimitRangerIgnoresSubresource(t *testing.T) {
testPod := validPod("testPod", 1, api.ResourceRequirements{})
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil))
if err != nil {
t.Fatal(err)
}
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil))
if err == nil {
t.Errorf("Expected an error since the pod did not specify resource limits in its update call")
}
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil))
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil))
if err != nil {
t.Errorf("Should have ignored calls to any subresource of pod %v", err)
}
@ -709,11 +721,15 @@ func TestLimitRangerAdmitPod(t *testing.T) {
testPod := validPod("testPod", 1, api.ResourceRequirements{})
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil))
if err != nil {
t.Fatal(err)
}
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Update, nil))
if err == nil {
t.Errorf("Expected an error since the pod did not specify resource limits in its update call")
}
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil))
err = handler.Validate(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "status", admission.Update, nil))
if err != nil {
t.Errorf("Should have ignored calls to any subresource of pod %v", err)
}
@ -786,7 +802,7 @@ func TestPersistentVolumeClaimLimitFunc(t *testing.T) {
}
for i := range successCases {
test := successCases[i]
err := PersistentVolumeClaimLimitFunc(&test.limitRange, &test.pvc)
err := PersistentVolumeClaimValidateLimitFunc(&test.limitRange, &test.pvc)
if err != nil {
t.Errorf("Unexpected error for pvc: %s, %v", test.pvc.Name, err)
}
@ -804,7 +820,7 @@ func TestPersistentVolumeClaimLimitFunc(t *testing.T) {
}
for i := range errorCases {
test := errorCases[i]
err := PersistentVolumeClaimLimitFunc(&test.limitRange, &test.pvc)
err := PersistentVolumeClaimValidateLimitFunc(&test.limitRange, &test.pvc)
if err == nil {
t.Errorf("Expected error for pvc: %s", test.pvc.Name)
}

View File

@ -23,8 +23,10 @@ import (
)
type LimitRangerActions interface {
// Limit is a pluggable function to enforce limits on the object.
Limit(limitRange *api.LimitRange, kind string, obj runtime.Object) error
// MutateLimit is a pluggable function to set limits on the object.
MutateLimit(limitRange *api.LimitRange, kind string, obj runtime.Object) error
// ValidateLimits is a pluggable function to enforce limits on the object.
ValidateLimit(limitRange *api.LimitRange, kind string, obj runtime.Object) error
// SupportsAttributes is a pluggable function to allow overridding what resources the limitranger
// supports.
SupportsAttributes(attr admission.Attributes) bool

View File

@ -46,6 +46,7 @@ type Provision struct {
namespaceLister corelisters.NamespaceLister
}
var _ admission.MutationInterface = &Provision{}
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&Provision{})
var _ = kubeapiserveradmission.WantsInternalKubeClientSet(&Provision{})

View File

@ -52,6 +52,7 @@ type persistentVolumeLabel struct {
gceCloudProvider *gce.GCECloud
}
var _ admission.MutationInterface = &persistentVolumeLabel{}
var _ kubeapiserveradmission.WantsCloudConfig = &persistentVolumeLabel{}
// NewPersistentVolumeLabel returns an admission.Interface implementation which adds labels to PersistentVolume CREATE requests,

View File

@ -59,6 +59,7 @@ type podPresetPlugin struct {
lister settingslisters.PodPresetLister
}
var _ admission.MutationInterface = &podPresetPlugin{}
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&podPresetPlugin{})
var _ = kubeapiserveradmission.WantsInternalKubeClientSet(&podPresetPlugin{})

View File

@ -57,6 +57,8 @@ const (
NSWLTolerations string = "scheduler.alpha.kubernetes.io/tolerationsWhitelist"
)
var _ admission.MutationInterface = &podTolerationsPlugin{}
var _ admission.ValidationInterface = &podTolerationsPlugin{}
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&podTolerationsPlugin{})
type podTolerationsPlugin struct {

View File

@ -54,6 +54,7 @@ type claimDefaulterPlugin struct {
}
var _ admission.Interface = &claimDefaulterPlugin{}
var _ admission.MutationInterface = &claimDefaulterPlugin{}
var _ = kubeapiserveradmission.WantsInternalKubeInformerFactory(&claimDefaulterPlugin{})
// newPlugin creates a new admission plugin.