Make admission control plug-ins work from indexes

This commit is contained in:
derekwaynecarr
2015-02-16 10:54:29 -05:00
parent d4755704b1
commit 2ed8eed004
13 changed files with 148 additions and 19 deletions

View File

@@ -23,10 +23,13 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
apierrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
func init() {
@@ -39,6 +42,7 @@ func init() {
type limitRanger struct {
client client.Interface
limitFunc LimitFunc
indexer cache.Indexer
}
// Admit admits resources into cluster that do not violate any defined LimitRange in the namespace
@@ -48,16 +52,30 @@ func (l *limitRanger) Admit(a admission.Attributes) (err error) {
return nil
}
// look for a limit range in current namespace that requires enforcement
// TODO: Move to cache when issue is resolved: https://github.com/GoogleCloudPlatform/kubernetes/issues/2294
items, err := l.client.LimitRanges(a.GetNamespace()).List(labels.Everything())
obj := a.GetObject()
resource := a.GetResource()
name := "Unknown"
if obj != nil {
name, _ = meta.NewAccessor().Name(obj)
}
key := &api.LimitRange{
ObjectMeta: api.ObjectMeta{
Namespace: a.GetNamespace(),
Name: "",
},
}
items, err := l.indexer.Index("namespace", key)
if err != nil {
return err
return apierrors.NewForbidden(a.GetResource(), name, fmt.Errorf("Unable to %s %s at this time because there was an error enforcing limit ranges", a.GetOperation(), resource))
}
if len(items) == 0 {
return nil
}
// ensure it meets each prescribed min/max
for i := range items.Items {
limitRange := &items.Items[i]
for i := range items {
limitRange := items[i].(*api.LimitRange)
err = l.limitFunc(limitRange, a.GetResource(), a.GetObject())
if err != nil {
return err
@@ -68,7 +86,17 @@ 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 client.Interface, limitFunc LimitFunc) admission.Interface {
return &limitRanger{client: client, limitFunc: limitFunc}
lw := &cache.ListWatch{
ListFunc: func() (runtime.Object, error) {
return client.LimitRanges(api.NamespaceAll).List(labels.Everything())
},
WatchFunc: func(resourceVersion string) (watch.Interface, error) {
return client.LimitRanges(api.NamespaceAll).Watch(labels.Everything(), labels.Everything(), resourceVersion)
},
}
indexer, reflector := cache.NewNamespaceKeyedIndexerAndReflector(lw, &api.LimitRange{}, 0)
reflector.Run()
return &limitRanger{client: client, limitFunc: limitFunc, indexer: indexer}
}
func Min(a int64, b int64) int64 {

View File

@@ -82,7 +82,6 @@ func (e *exists) Admit(a admission.Attributes) (err error) {
func NewExists(c client.Interface) admission.Interface {
store := cache.NewStore(cache.MetaNamespaceKeyFunc)
// TODO: look into a list/watch that can work with client.Interface, maybe pass it a ListFunc and a WatchFunc
reflector := cache.NewReflector(
&cache.ListWatch{
ListFunc: func() (runtime.Object, error) {

View File

@@ -26,8 +26,11 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/resourcequota"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
func init() {
@@ -37,11 +40,22 @@ func init() {
}
type quota struct {
client client.Interface
client client.Interface
indexer cache.Indexer
}
func NewResourceQuota(client client.Interface) admission.Interface {
return &quota{client: client}
lw := &cache.ListWatch{
ListFunc: func() (runtime.Object, error) {
return client.ResourceQuotas(api.NamespaceAll).List(labels.Everything())
},
WatchFunc: func(resourceVersion string) (watch.Interface, error) {
return client.ResourceQuotas(api.NamespaceAll).Watch(labels.Everything(), labels.Everything(), resourceVersion)
},
}
indexer, reflector := cache.NewNamespaceKeyedIndexerAndReflector(lw, &api.ResourceQuota{}, 0)
reflector.Run()
return &quota{client: client, indexer: indexer}
}
var resourceToResourceName = map[string]api.ResourceName{
@@ -63,18 +77,36 @@ func (q *quota) Admit(a admission.Attributes) (err error) {
name, _ = meta.NewAccessor().Name(obj)
}
list, err := q.client.ResourceQuotas(a.GetNamespace()).List(labels.Everything())
key := &api.ResourceQuota{
ObjectMeta: api.ObjectMeta{
Namespace: a.GetNamespace(),
Name: "",
},
}
items, err := q.indexer.Index("namespace", key)
if err != nil {
return apierrors.NewForbidden(a.GetResource(), name, fmt.Errorf("Unable to %s %s at this time because there was an error enforcing quota", a.GetOperation(), resource))
}
if len(list.Items) == 0 {
if len(items) == 0 {
return nil
}
for i := range list.Items {
quota := list.Items[i]
dirty, err := IncrementUsage(a, &quota.Status, q.client)
for i := range items {
quota := items[i].(*api.ResourceQuota)
// we cannot modify the value directly in the cache, so we copy
status := &api.ResourceQuotaStatus{
Hard: api.ResourceList{},
Used: api.ResourceList{},
}
for k, v := range quota.Status.Hard {
status.Hard[k] = *v.Copy()
}
for k, v := range quota.Status.Used {
status.Used[k] = *v.Copy()
}
dirty, err := IncrementUsage(a, status, q.client)
if err != nil {
return err
}
@@ -87,7 +119,7 @@ func (q *quota) Admit(a admission.Attributes) (err error) {
Namespace: quota.Namespace,
ResourceVersion: quota.ResourceVersion},
}
usage.Status = quota.Status
usage.Status = *status
err = q.client.ResourceQuotaUsages(usage.Namespace).Create(&usage)
if err != nil {
return apierrors.NewForbidden(a.GetResource(), name, fmt.Errorf("Unable to %s %s at this time because there was an error enforcing quota", a.GetOperation(), a.GetResource()))