Skip to content

Commit

Permalink
Set a missing namespace on object to admit
Browse files Browse the repository at this point in the history
Custom quota evaluators may need to query a target namespace of an input
object during quota admission check. For this, namespace needs to be
known.

Signed-off-by: Michal Minar <miminar@redhat.com>
  • Loading branch information
Michal Minar committed Mar 15, 2016
1 parent 73017b6 commit 2601ee5
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 0 deletions.
10 changes: 10 additions & 0 deletions plugin/pkg/admission/resourcequota/admission.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ func (q *quotaAdmission) Admit(a admission.Attributes) (err error) {
return nil
}

// Usage of some resources cannot be counted in isolation. For example when
// the resource represents a number of unique references to external
// resource. In such a case an evaluator needs to process other objects in
// the same namespace which needs to be known.
if om, err := api.ObjectMetaFor(inputObject); namespace != "" && err == nil {
if om.Namespace == "" {
om.Namespace = namespace
}
}

// there is at least one quota that definitely matches our object
// as a result, we need to measure the usage of this object for quota
// on updates, we need to subtract the previous measured usage
Expand Down
77 changes: 77 additions & 0 deletions plugin/pkg/admission/resourcequota/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@ import (
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/quota/evaluator/core"
"k8s.io/kubernetes/pkg/quota/generic"
"k8s.io/kubernetes/pkg/quota/install"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/sets"
)

Expand Down Expand Up @@ -566,3 +571,75 @@ func TestHasUsageStats(t *testing.T) {
}
}
}

// TestAdmissionSetsMissingNamespace verifies that if an object lacks a
// namespace, it will be set.
func TestAdmissionSetsMissingNamespace(t *testing.T) {
namespace := "test"
resourceQuota := &api.ResourceQuota{
ObjectMeta: api.ObjectMeta{Name: "quota", Namespace: namespace, ResourceVersion: "124"},
Status: api.ResourceQuotaStatus{
Hard: api.ResourceList{
api.ResourceCPU: resource.MustParse("3"),
},
Used: api.ResourceList{
api.ResourceCPU: resource.MustParse("1"),
},
},
}
kubeClient := fake.NewSimpleClientset(resourceQuota)
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"namespace": cache.MetaNamespaceIndexFunc})

computeResources := []api.ResourceName{
api.ResourcePods,
api.ResourceCPU,
}

usageFunc := func(object runtime.Object) api.ResourceList {
pod, ok := object.(*api.Pod)
if !ok {
t.Fatalf("Expected pod, got %T", object)
}
if pod.Namespace != namespace {
t.Errorf("Expected pod with different namespace: %q != %q", pod.Namespace, namespace)
}
return core.PodUsageFunc(pod)
}

podEvaluator := &generic.GenericEvaluator{
Name: "Test-Evaluator.Pod",
InternalGroupKind: api.Kind("Pod"),
InternalOperationResources: map[admission.Operation][]api.ResourceName{
admission.Create: computeResources,
},
ConstraintsFunc: core.PodConstraintsFunc,
MatchedResourceNames: computeResources,
MatchesScopeFunc: core.PodMatchesScopeFunc,
UsageFunc: usageFunc,
}

registry := &generic.GenericRegistry{
InternalEvaluators: map[unversioned.GroupKind]quota.Evaluator{
podEvaluator.GroupKind(): podEvaluator,
},
}
handler := &quotaAdmission{
Handler: admission.NewHandler(admission.Create, admission.Update),
client: kubeClient,
indexer: indexer,
registry: registry,
}
handler.indexer.Add(resourceQuota)
newPod := validPod("pod-without-namespace", 1, getResourceRequirements(getResourceList("1", "2Gi"), getResourceList("", "")))

// unset the namespace
newPod.ObjectMeta.Namespace = ""

err := handler.Admit(admission.NewAttributesRecord(newPod, api.Kind("Pod"), namespace, newPod.Name, api.Resource("pods"), "", admission.Create, nil))
if err != nil {
t.Errorf("Got unexpected error: %v", err)
}
if newPod.Namespace != namespace {
t.Errorf("Got unexpected pod namespace: %q != %q", newPod.Namespace, namespace)
}
}

0 comments on commit 2601ee5

Please sign in to comment.