From 14620447c1559f99337884fd0e2a3b46196f42f8 Mon Sep 17 00:00:00 2001 From: Paul Weil Date: Mon, 21 Mar 2016 09:46:03 -0400 Subject: [PATCH] allow supported resource overrides in the limitranger plugin --- plugin/pkg/admission/limitranger/admission.go | 76 ++++++++++++------- .../admission/limitranger/admission_test.go | 14 ++-- .../pkg/admission/limitranger/interfaces.go | 13 +++- 3 files changed, 68 insertions(+), 35 deletions(-) diff --git a/plugin/pkg/admission/limitranger/admission.go b/plugin/pkg/admission/limitranger/admission.go index 6438280833a..70291baa69a 100644 --- a/plugin/pkg/admission/limitranger/admission.go +++ b/plugin/pkg/admission/limitranger/admission.go @@ -43,16 +43,16 @@ const ( func init() { admission.RegisterPlugin("LimitRanger", func(client clientset.Interface, config io.Reader) (admission.Interface, error) { - return NewLimitRanger(client, Limit) + return NewLimitRanger(client, &DefaultLimitRangerActions{}) }) } // limitRanger enforces usage limits on a per resource basis in the namespace type limitRanger struct { *admission.Handler - client clientset.Interface - limitFunc LimitFunc - indexer cache.Indexer + client clientset.Interface + actions LimitRangerActions + indexer cache.Indexer // 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. @@ -68,14 +68,7 @@ type liveLookupEntry struct { // Admit admits resources into cluster that do not violate any defined LimitRange in the namespace func (l *limitRanger) Admit(a admission.Attributes) (err error) { - - // Ignore all calls to subresources - if a.GetSubresource() != "" { - return nil - } - - // ignore all calls that do not deal with pod resources since that is all this supports now. - if a.GetKind() != api.Kind("Pod") { + if !l.actions.SupportsAttributes(a) { return nil } @@ -130,7 +123,12 @@ func (l *limitRanger) Admit(a admission.Attributes) (err error) { // ensure it meets each prescribed min/max for i := range items { limitRange := items[i].(*api.LimitRange) - err = l.limitFunc(limitRange, a.GetResource().Resource, a.GetObject()) + + if !l.actions.SupportsLimit(limitRange) { + continue + } + + err = l.actions.Limit(limitRange, a.GetResource().Resource, a.GetObject()) if err != nil { return admission.NewForbidden(a, err) } @@ -139,7 +137,7 @@ func (l *limitRanger) Admit(a admission.Attributes) (err error) { } // NewLimitRanger returns an object that enforces limits based on the supplied limit function -func NewLimitRanger(client clientset.Interface, limitFunc LimitFunc) (admission.Interface, error) { +func NewLimitRanger(client clientset.Interface, actions LimitRangerActions) (admission.Interface, error) { liveLookupCache, err := lru.New(10000) if err != nil { return nil, err @@ -155,10 +153,15 @@ func NewLimitRanger(client clientset.Interface, limitFunc LimitFunc) (admission. } indexer, reflector := cache.NewNamespaceKeyedIndexerAndReflector(lw, &api.LimitRange{}, 0) reflector.Run() + + if actions == nil { + actions = &DefaultLimitRangerActions{} + } + return &limitRanger{ Handler: admission.NewHandler(admission.Create, admission.Update), client: client, - limitFunc: limitFunc, + actions: actions, indexer: indexer, liveLookupCache: liveLookupCache, liveTTL: time.Duration(30 * time.Second), @@ -181,17 +184,6 @@ func Max(a int64, b int64) int64 { return b } -// 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 Limit(limitRange *api.LimitRange, resourceName string, obj runtime.Object) error { - switch resourceName { - case "pods": - return PodLimitFunc(limitRange, obj.(*api.Pod)) - } - return nil -} - // defaultContainerResourceRequirements returns the default requirements for a container // the requirement.Limits are taken from the LimitRange defaults (if specified) // the requirement.Requests are taken from the LimitRange default request (if specified) @@ -383,6 +375,38 @@ func sum(inputs []api.ResourceList) api.ResourceList { return result } +// DefaultLimitRangerActions is the default implementatation of LimitRangerActions. +type DefaultLimitRangerActions struct{} + +// ensure DefaultLimitRangerActions implements the LimitRangerActions interface. +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 { + switch resourceName { + case "pods": + return PodLimitFunc(limitRange, obj.(*api.Pod)) + } + return nil +} + +// SupportsAttributes ignores all calls that do not deal with pod resources since that is +// all this supports now. Also ignores any call that has a subresource defined. +func (d *DefaultLimitRangerActions) SupportsAttributes(a admission.Attributes) bool { + if a.GetSubresource() != "" { + return false + } + + return a.GetKind() == api.Kind("Pod") +} + +// SupportsLimit always returns true. +func (d *DefaultLimitRangerActions) SupportsLimit(limitRange *api.LimitRange) bool { + return true +} + // PodLimitFunc enforces 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 diff --git a/plugin/pkg/admission/limitranger/admission_test.go b/plugin/pkg/admission/limitranger/admission_test.go index e0279ff0f6a..54747ae8d8f 100644 --- a/plugin/pkg/admission/limitranger/admission_test.go +++ b/plugin/pkg/admission/limitranger/admission_test.go @@ -435,10 +435,10 @@ func TestLimitRangerIgnoresSubresource(t *testing.T) { client := fake.NewSimpleClientset() indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"namespace": cache.MetaNamespaceIndexFunc}) handler := &limitRanger{ - Handler: admission.NewHandler(admission.Create, admission.Update), - client: client, - limitFunc: Limit, - indexer: indexer, + Handler: admission.NewHandler(admission.Create, admission.Update), + client: client, + actions: &DefaultLimitRangerActions{}, + indexer: indexer, } limitRange := validLimitRangeNoDefaults() @@ -468,7 +468,7 @@ func TestLimitRangerCacheMisses(t *testing.T) { handler := &limitRanger{ Handler: admission.NewHandler(admission.Create, admission.Update), client: client, - limitFunc: Limit, + actions: &DefaultLimitRangerActions{}, indexer: indexer, liveLookupCache: liveLookupCache, } @@ -502,7 +502,7 @@ func TestLimitRangerCacheAndLRUMisses(t *testing.T) { handler := &limitRanger{ Handler: admission.NewHandler(admission.Create, admission.Update), client: client, - limitFunc: Limit, + actions: &DefaultLimitRangerActions{}, indexer: indexer, liveLookupCache: liveLookupCache, } @@ -532,7 +532,7 @@ func TestLimitRangerCacheAndLRUExpiredMisses(t *testing.T) { handler := &limitRanger{ Handler: admission.NewHandler(admission.Create, admission.Update), client: client, - limitFunc: Limit, + actions: &DefaultLimitRangerActions{}, indexer: indexer, liveLookupCache: liveLookupCache, } diff --git a/plugin/pkg/admission/limitranger/interfaces.go b/plugin/pkg/admission/limitranger/interfaces.go index 61cde7866d1..b5eb6632f2e 100644 --- a/plugin/pkg/admission/limitranger/interfaces.go +++ b/plugin/pkg/admission/limitranger/interfaces.go @@ -17,9 +17,18 @@ limitations under the License. package limitranger import ( + "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/runtime" ) -// LimitFunc is a pluggable function to enforce limits on the object -type LimitFunc func(limitRange *api.LimitRange, kind string, obj runtime.Object) error +type LimitRangerActions interface { + // Limit is a pluggable function to enforce limits on the object. + Limit(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 + // SupportsLimit is a pluggable function to allow ignoring limits that should not be applied + // for any reason. + SupportsLimit(limitRange *api.LimitRange) bool +}