Merge pull request #67870 from yue9944882/refactor/externalize-resource-quota-admission-controller

Externalize resource quota admission controller & controller reconciliation
This commit is contained in:
k8s-ci-robot 2018-09-25 02:41:40 -07:00 committed by GitHub
commit 0805860dba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 1432 additions and 1460 deletions

View File

@ -33,7 +33,7 @@ go_library(
"//pkg/master/controller/crdregistration:go_default_library", "//pkg/master/controller/crdregistration:go_default_library",
"//pkg/master/reconcilers:go_default_library", "//pkg/master/reconcilers:go_default_library",
"//pkg/master/tunneler:go_default_library", "//pkg/master/tunneler:go_default_library",
"//pkg/quota/install:go_default_library", "//pkg/quota/v1/install:go_default_library",
"//pkg/registry/cachesize:go_default_library", "//pkg/registry/cachesize:go_default_library",
"//pkg/registry/rbac/rest:go_default_library", "//pkg/registry/rbac/rest:go_default_library",
"//pkg/serviceaccount:go_default_library", "//pkg/serviceaccount:go_default_library",

View File

@ -81,7 +81,7 @@ import (
"k8s.io/kubernetes/pkg/master" "k8s.io/kubernetes/pkg/master"
"k8s.io/kubernetes/pkg/master/reconcilers" "k8s.io/kubernetes/pkg/master/reconcilers"
"k8s.io/kubernetes/pkg/master/tunneler" "k8s.io/kubernetes/pkg/master/tunneler"
quotainstall "k8s.io/kubernetes/pkg/quota/install" quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
"k8s.io/kubernetes/pkg/registry/cachesize" "k8s.io/kubernetes/pkg/registry/cachesize"
rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest" rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest"
"k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/serviceaccount"

View File

@ -75,8 +75,8 @@ go_library(
"//pkg/controller/volume/pvcprotection:go_default_library", "//pkg/controller/volume/pvcprotection:go_default_library",
"//pkg/controller/volume/pvprotection:go_default_library", "//pkg/controller/volume/pvprotection:go_default_library",
"//pkg/features:go_default_library", "//pkg/features:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/v1/generic:go_default_library",
"//pkg/quota/install:go_default_library", "//pkg/quota/v1/install:go_default_library",
"//pkg/serviceaccount:go_default_library", "//pkg/serviceaccount:go_default_library",
"//pkg/util/configz:go_default_library", "//pkg/util/configz:go_default_library",
"//pkg/util/flag:go_default_library", "//pkg/util/flag:go_default_library",

View File

@ -58,8 +58,8 @@ import (
"k8s.io/kubernetes/pkg/controller/volume/pvcprotection" "k8s.io/kubernetes/pkg/controller/volume/pvcprotection"
"k8s.io/kubernetes/pkg/controller/volume/pvprotection" "k8s.io/kubernetes/pkg/controller/volume/pvprotection"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
quotainstall "k8s.io/kubernetes/pkg/quota/install" quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
"k8s.io/kubernetes/pkg/util/metrics" "k8s.io/kubernetes/pkg/util/metrics"
) )

View File

@ -262,7 +262,7 @@ pkg/proxy/userspace
pkg/proxy/util pkg/proxy/util
pkg/proxy/winkernel pkg/proxy/winkernel
pkg/proxy/winuserspace pkg/proxy/winuserspace
pkg/quota/evaluator/core pkg/quota/v1/evaluator/core
pkg/registry/admissionregistration/initializerconfiguration/storage pkg/registry/admissionregistration/initializerconfiguration/storage
pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage pkg/registry/admissionregistration/mutatingwebhookconfiguration/storage
pkg/registry/admissionregistration/rest pkg/registry/admissionregistration/rest

View File

@ -537,28 +537,3 @@ func PersistentVolumeClaimHasClass(claim *core.PersistentVolumeClaim) bool {
return false return false
} }
// ScopedResourceSelectorRequirementsAsSelector converts the ScopedResourceSelectorRequirement api type into a struct that implements
// labels.Selector.
func ScopedResourceSelectorRequirementsAsSelector(ssr core.ScopedResourceSelectorRequirement) (labels.Selector, error) {
selector := labels.NewSelector()
var op selection.Operator
switch ssr.Operator {
case core.ScopeSelectorOpIn:
op = selection.In
case core.ScopeSelectorOpNotIn:
op = selection.NotIn
case core.ScopeSelectorOpExists:
op = selection.Exists
case core.ScopeSelectorOpDoesNotExist:
op = selection.DoesNotExist
default:
return nil, fmt.Errorf("%q is not a valid scope selector operator", ssr.Operator)
}
r, err := labels.NewRequirement(string(ssr.ScopeName), op, ssr.Values)
if err != nil {
return nil, err
}
selector = selector.Add(*r)
return selector, nil
}

View File

@ -500,3 +500,28 @@ func GetPersistentVolumeClaimClass(claim *v1.PersistentVolumeClaim) string {
return "" return ""
} }
// ScopedResourceSelectorRequirementsAsSelector converts the ScopedResourceSelectorRequirement api type into a struct that implements
// labels.Selector.
func ScopedResourceSelectorRequirementsAsSelector(ssr v1.ScopedResourceSelectorRequirement) (labels.Selector, error) {
selector := labels.NewSelector()
var op selection.Operator
switch ssr.Operator {
case v1.ScopeSelectorOpIn:
op = selection.In
case v1.ScopeSelectorOpNotIn:
op = selection.NotIn
case v1.ScopeSelectorOpExists:
op = selection.Exists
case v1.ScopeSelectorOpDoesNotExist:
op = selection.DoesNotExist
default:
return nil, fmt.Errorf("%q is not a valid scope selector operator", ssr.Operator)
}
r, err := labels.NewRequirement(string(ssr.ScopeName), op, ssr.Values)
if err != nil {
return nil, err
}
selector = selector.Add(*r)
return selector, nil
}

View File

@ -15,12 +15,10 @@ go_library(
], ],
importpath = "k8s.io/kubernetes/pkg/controller/resourcequota", importpath = "k8s.io/kubernetes/pkg/controller/resourcequota",
deps = [ deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/v1:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/quota:go_default_library", "//pkg/quota/v1:go_default_library",
"//pkg/quota/evaluator/core:go_default_library", "//pkg/quota/v1/evaluator/core:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/v1/generic:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
@ -49,9 +47,9 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/quota:go_default_library", "//pkg/quota/v1:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/v1/generic:go_default_library",
"//pkg/quota/install:go_default_library", "//pkg/quota/v1/install:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -39,10 +39,8 @@ import (
corelisters "k8s.io/client-go/listers/core/v1" corelisters "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
api "k8s.io/kubernetes/pkg/apis/core"
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
) )
// NamespacedResourcesFunc knows how to discover namespaced resources. // NamespacedResourcesFunc knows how to discover namespaced resources.
@ -226,7 +224,7 @@ func (rq *ResourceQuotaController) addQuota(obj interface{}) {
// if we declared a constraint that has no usage (which this controller can calculate, prioritize it) // if we declared a constraint that has no usage (which this controller can calculate, prioritize it)
for constraint := range resourceQuota.Status.Hard { for constraint := range resourceQuota.Status.Hard {
if _, usageFound := resourceQuota.Status.Used[constraint]; !usageFound { if _, usageFound := resourceQuota.Status.Used[constraint]; !usageFound {
matchedResources := []api.ResourceName{api.ResourceName(constraint)} matchedResources := []v1.ResourceName{v1.ResourceName(constraint)}
for _, evaluator := range rq.registry.List() { for _, evaluator := range rq.registry.List() {
if intersection := evaluator.MatchingResources(matchedResources); len(intersection) > 0 { if intersection := evaluator.MatchingResources(matchedResources); len(intersection) > 0 {
rq.missingUsageQueue.Add(key) rq.missingUsageQueue.Add(key)
@ -320,25 +318,20 @@ func (rq *ResourceQuotaController) syncResourceQuotaFromKey(key string) (err err
} }
// syncResourceQuota runs a complete sync of resource quota status across all known kinds // syncResourceQuota runs a complete sync of resource quota status across all known kinds
func (rq *ResourceQuotaController) syncResourceQuota(v1ResourceQuota *v1.ResourceQuota) (err error) { func (rq *ResourceQuotaController) syncResourceQuota(resourceQuota *v1.ResourceQuota) (err error) {
// quota is dirty if any part of spec hard limits differs from the status hard limits // quota is dirty if any part of spec hard limits differs from the status hard limits
dirty := !apiequality.Semantic.DeepEqual(v1ResourceQuota.Spec.Hard, v1ResourceQuota.Status.Hard) dirty := !apiequality.Semantic.DeepEqual(resourceQuota.Spec.Hard, resourceQuota.Status.Hard)
resourceQuota := api.ResourceQuota{}
if err := k8s_api_v1.Convert_v1_ResourceQuota_To_core_ResourceQuota(v1ResourceQuota, &resourceQuota, nil); err != nil {
return err
}
// dirty tracks if the usage status differs from the previous sync, // dirty tracks if the usage status differs from the previous sync,
// if so, we send a new usage with latest status // if so, we send a new usage with latest status
// if this is our first sync, it will be dirty by default, since we need track usage // if this is our first sync, it will be dirty by default, since we need track usage
dirty = dirty || (resourceQuota.Status.Hard == nil || resourceQuota.Status.Used == nil) dirty = dirty || resourceQuota.Status.Hard == nil || resourceQuota.Status.Used == nil
used := api.ResourceList{} used := v1.ResourceList{}
if resourceQuota.Status.Used != nil { if resourceQuota.Status.Used != nil {
used = quota.Add(api.ResourceList{}, resourceQuota.Status.Used) used = quota.Add(v1.ResourceList{}, resourceQuota.Status.Used)
} }
hardLimits := quota.Add(api.ResourceList{}, resourceQuota.Spec.Hard) hardLimits := quota.Add(v1.ResourceList{}, resourceQuota.Spec.Hard)
newUsage, err := quota.CalculateUsage(resourceQuota.Namespace, resourceQuota.Spec.Scopes, hardLimits, rq.registry, resourceQuota.Spec.ScopeSelector) newUsage, err := quota.CalculateUsage(resourceQuota.Namespace, resourceQuota.Spec.Scopes, hardLimits, rq.registry, resourceQuota.Spec.ScopeSelector)
if err != nil { if err != nil {
@ -354,14 +347,14 @@ func (rq *ResourceQuotaController) syncResourceQuota(v1ResourceQuota *v1.Resourc
// Create a usage object that is based on the quota resource version that will handle updates // Create a usage object that is based on the quota resource version that will handle updates
// by default, we preserve the past usage observation, and set hard to the current spec // by default, we preserve the past usage observation, and set hard to the current spec
usage := api.ResourceQuota{ usage := v1.ResourceQuota{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: resourceQuota.Name, Name: resourceQuota.Name,
Namespace: resourceQuota.Namespace, Namespace: resourceQuota.Namespace,
ResourceVersion: resourceQuota.ResourceVersion, ResourceVersion: resourceQuota.ResourceVersion,
Labels: resourceQuota.Labels, Labels: resourceQuota.Labels,
Annotations: resourceQuota.Annotations}, Annotations: resourceQuota.Annotations},
Status: api.ResourceQuotaStatus{ Status: v1.ResourceQuotaStatus{
Hard: hardLimits, Hard: hardLimits,
Used: used, Used: used,
}, },
@ -371,11 +364,7 @@ func (rq *ResourceQuotaController) syncResourceQuota(v1ResourceQuota *v1.Resourc
// there was a change observed by this controller that requires we update quota // there was a change observed by this controller that requires we update quota
if dirty { if dirty {
v1Usage := &v1.ResourceQuota{} _, err = rq.rqClient.ResourceQuotas(usage.Namespace).UpdateStatus(&usage)
if err := k8s_api_v1.Convert_core_ResourceQuota_To_v1_ResourceQuota(&usage, v1Usage, nil); err != nil {
return err
}
_, err = rq.rqClient.ResourceQuotas(usage.Namespace).UpdateStatus(v1Usage)
return err return err
} }
return nil return nil
@ -406,12 +395,7 @@ func (rq *ResourceQuotaController) replenishQuota(groupResource schema.GroupReso
// only queue those quotas that are tracking a resource associated with this kind. // only queue those quotas that are tracking a resource associated with this kind.
for i := range resourceQuotas { for i := range resourceQuotas {
resourceQuota := resourceQuotas[i] resourceQuota := resourceQuotas[i]
internalResourceQuota := &api.ResourceQuota{} resourceQuotaResources := quota.ResourceNames(resourceQuota.Status.Hard)
if err := k8s_api_v1.Convert_v1_ResourceQuota_To_core_ResourceQuota(resourceQuota, internalResourceQuota, nil); err != nil {
glog.Error(err)
continue
}
resourceQuotaResources := quota.ResourceNames(internalResourceQuota.Status.Hard)
if intersection := evaluator.MatchingResources(resourceQuotaResources); len(intersection) > 0 { if intersection := evaluator.MatchingResources(resourceQuotaResources); len(intersection) > 0 {
// TODO: make this support targeted replenishment to a specific kind, right now it does a full recalc on that quota. // TODO: make this support targeted replenishment to a specific kind, right now it does a full recalc on that quota.
rq.enqueueResourceQuota(resourceQuota) rq.enqueueResourceQuota(resourceQuota)

View File

@ -33,9 +33,9 @@ import (
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
"k8s.io/kubernetes/pkg/quota/install" "k8s.io/kubernetes/pkg/quota/v1/install"
) )
func getResourceList(cpu, memory string) v1.ResourceList { func getResourceList(cpu, memory string) v1.ResourceList {

View File

@ -33,9 +33,9 @@ import (
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/evaluator/core" "k8s.io/kubernetes/pkg/quota/v1/evaluator/core"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
) )
type eventType int type eventType int

View File

@ -20,7 +20,7 @@ go_library(
deps = [ deps = [
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library", "//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/quota:go_default_library", "//pkg/quota/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",

View File

@ -23,7 +23,7 @@ import (
"k8s.io/apiserver/pkg/util/webhook" "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
) )
// TODO add a `WantsToRun` which takes a stopCh. Might make it generic. // TODO add a `WantsToRun` which takes a stopCh. Might make it generic.

View File

@ -1,40 +1,5 @@
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"interfaces.go",
"resources.go",
],
importpath = "k8s.io/kubernetes/pkg/quota",
deps = [
"//pkg/apis/core:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["resources_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
],
)
filegroup( filegroup(
name = "package-srcs", name = "package-srcs",
srcs = glob(["**"]), srcs = glob(["**"]),
@ -46,9 +11,7 @@ filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [ srcs = [
":package-srcs", ":package-srcs",
"//pkg/quota/evaluator/core:all-srcs", "//pkg/quota/v1:all-srcs",
"//pkg/quota/generic:all-srcs",
"//pkg/quota/install:all-srcs",
], ],
tags = ["automanaged"], tags = ["automanaged"],
) )

View File

@ -1,321 +0,0 @@
/*
Copyright 2016 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 quota
import (
"testing"
"k8s.io/apimachinery/pkg/api/resource"
api "k8s.io/kubernetes/pkg/apis/core"
)
func TestEquals(t *testing.T) {
testCases := map[string]struct {
a api.ResourceList
b api.ResourceList
expected bool
}{
"isEqual": {
a: api.ResourceList{},
b: api.ResourceList{},
expected: true,
},
"isEqualWithKeys": {
a: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("1Gi"),
},
b: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("1Gi"),
},
expected: true,
},
"isNotEqualSameKeys": {
a: api.ResourceList{
api.ResourceCPU: resource.MustParse("200m"),
api.ResourceMemory: resource.MustParse("1Gi"),
},
b: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("1Gi"),
},
expected: false,
},
"isNotEqualDiffKeys": {
a: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("1Gi"),
},
b: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("1Gi"),
api.ResourcePods: resource.MustParse("1"),
},
expected: false,
},
}
for testName, testCase := range testCases {
if result := Equals(testCase.a, testCase.b); result != testCase.expected {
t.Errorf("%s expected: %v, actual: %v, a=%v, b=%v", testName, testCase.expected, result, testCase.a, testCase.b)
}
}
}
func TestMax(t *testing.T) {
testCases := map[string]struct {
a api.ResourceList
b api.ResourceList
expected api.ResourceList
}{
"noKeys": {
a: api.ResourceList{},
b: api.ResourceList{},
expected: api.ResourceList{},
},
"toEmpty": {
a: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
b: api.ResourceList{},
expected: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
},
"matching": {
a: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
b: api.ResourceList{api.ResourceCPU: resource.MustParse("150m")},
expected: api.ResourceList{api.ResourceCPU: resource.MustParse("150m")},
},
"matching(reverse)": {
a: api.ResourceList{api.ResourceCPU: resource.MustParse("150m")},
b: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
expected: api.ResourceList{api.ResourceCPU: resource.MustParse("150m")},
},
"matching-equal": {
a: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
b: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
expected: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
},
}
for testName, testCase := range testCases {
sum := Max(testCase.a, testCase.b)
if result := Equals(testCase.expected, sum); !result {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, sum)
}
}
}
func TestAdd(t *testing.T) {
testCases := map[string]struct {
a api.ResourceList
b api.ResourceList
expected api.ResourceList
}{
"noKeys": {
a: api.ResourceList{},
b: api.ResourceList{},
expected: api.ResourceList{},
},
"toEmpty": {
a: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
b: api.ResourceList{},
expected: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
},
"matching": {
a: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
b: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
expected: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
},
}
for testName, testCase := range testCases {
sum := Add(testCase.a, testCase.b)
if result := Equals(testCase.expected, sum); !result {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, sum)
}
}
}
func TestSubtract(t *testing.T) {
testCases := map[string]struct {
a api.ResourceList
b api.ResourceList
expected api.ResourceList
}{
"noKeys": {
a: api.ResourceList{},
b: api.ResourceList{},
expected: api.ResourceList{},
},
"value-empty": {
a: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
b: api.ResourceList{},
expected: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
},
"empty-value": {
a: api.ResourceList{},
b: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
expected: api.ResourceList{api.ResourceCPU: resource.MustParse("-100m")},
},
"value-value": {
a: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
b: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
expected: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
},
}
for testName, testCase := range testCases {
sub := Subtract(testCase.a, testCase.b)
if result := Equals(testCase.expected, sub); !result {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, sub)
}
}
}
func TestResourceNames(t *testing.T) {
testCases := map[string]struct {
a api.ResourceList
expected []api.ResourceName
}{
"empty": {
a: api.ResourceList{},
expected: []api.ResourceName{},
},
"values": {
a: api.ResourceList{
api.ResourceCPU: resource.MustParse("100m"),
api.ResourceMemory: resource.MustParse("1Gi"),
},
expected: []api.ResourceName{api.ResourceMemory, api.ResourceCPU},
},
}
for testName, testCase := range testCases {
actualSet := ToSet(ResourceNames(testCase.a))
expectedSet := ToSet(testCase.expected)
if !actualSet.Equal(expectedSet) {
t.Errorf("%s expected: %v, actual: %v", testName, expectedSet, actualSet)
}
}
}
func TestContains(t *testing.T) {
testCases := map[string]struct {
a []api.ResourceName
b api.ResourceName
expected bool
}{
"does-not-contain": {
a: []api.ResourceName{api.ResourceMemory},
b: api.ResourceCPU,
expected: false,
},
"does-contain": {
a: []api.ResourceName{api.ResourceMemory, api.ResourceCPU},
b: api.ResourceCPU,
expected: true,
},
}
for testName, testCase := range testCases {
if actual := Contains(testCase.a, testCase.b); actual != testCase.expected {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, actual)
}
}
}
func TestContainsPrefix(t *testing.T) {
testCases := map[string]struct {
a []string
b api.ResourceName
expected bool
}{
"does-not-contain": {
a: []string{api.ResourceHugePagesPrefix},
b: api.ResourceCPU,
expected: false,
},
"does-contain": {
a: []string{api.ResourceHugePagesPrefix},
b: api.ResourceName(api.ResourceHugePagesPrefix + "2Mi"),
expected: true,
},
}
for testName, testCase := range testCases {
if actual := ContainsPrefix(testCase.a, testCase.b); actual != testCase.expected {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, actual)
}
}
}
func TestIsZero(t *testing.T) {
testCases := map[string]struct {
a api.ResourceList
expected bool
}{
"empty": {
a: api.ResourceList{},
expected: true,
},
"zero": {
a: api.ResourceList{
api.ResourceCPU: resource.MustParse("0"),
api.ResourceMemory: resource.MustParse("0"),
},
expected: true,
},
"non-zero": {
a: api.ResourceList{
api.ResourceCPU: resource.MustParse("200m"),
api.ResourceMemory: resource.MustParse("1Gi"),
},
expected: false,
},
}
for testName, testCase := range testCases {
if result := IsZero(testCase.a); result != testCase.expected {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, result)
}
}
}
func TestIsNegative(t *testing.T) {
testCases := map[string]struct {
a api.ResourceList
expected []api.ResourceName
}{
"empty": {
a: api.ResourceList{},
expected: []api.ResourceName{},
},
"some-negative": {
a: api.ResourceList{
api.ResourceCPU: resource.MustParse("-10"),
api.ResourceMemory: resource.MustParse("0"),
},
expected: []api.ResourceName{api.ResourceCPU},
},
"all-negative": {
a: api.ResourceList{
api.ResourceCPU: resource.MustParse("-200m"),
api.ResourceMemory: resource.MustParse("-1Gi"),
},
expected: []api.ResourceName{api.ResourceCPU, api.ResourceMemory},
},
}
for testName, testCase := range testCases {
actual := IsNegative(testCase.a)
actualSet := ToSet(actual)
expectedSet := ToSet(testCase.expected)
if !actualSet.Equal(expectedSet) {
t.Errorf("%s expected: %v, actual: %v", testName, expectedSet, actualSet)
}
}
}

53
pkg/quota/v1/BUILD Normal file
View File

@ -0,0 +1,53 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"interfaces.go",
"resources.go",
],
importpath = "k8s.io/kubernetes/pkg/quota/v1",
deps = [
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["resources_test.go"],
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/quota/v1/evaluator/core:all-srcs",
"//pkg/quota/v1/generic:all-srcs",
"//pkg/quota/v1/install:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -15,16 +15,16 @@ go_library(
"registry.go", "registry.go",
"services.go", "services.go",
], ],
importpath = "k8s.io/kubernetes/pkg/quota/evaluator/core", importpath = "k8s.io/kubernetes/pkg/quota/v1/evaluator/core",
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
"//pkg/apis/core/helper/qos:go_default_library",
"//pkg/apis/core/v1:go_default_library", "//pkg/apis/core/v1:go_default_library",
"//pkg/apis/core/v1/helper:go_default_library",
"//pkg/apis/core/v1/helper/qos:go_default_library",
"//pkg/features:go_default_library", "//pkg/features:go_default_library",
"//pkg/kubeapiserver/admission/util:go_default_library", "//pkg/kubeapiserver/admission/util:go_default_library",
"//pkg/quota:go_default_library", "//pkg/quota/v1:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/v1/generic:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
@ -50,9 +50,10 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/quota:go_default_library", "//pkg/quota/v1:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/v1/generic:go_default_library",
"//pkg/util/node:go_default_library", "//pkg/util/node:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",

View File

@ -15,4 +15,4 @@ limitations under the License.
*/ */
// core contains modules that interface with the core api group // core contains modules that interface with the core api group
package core // import "k8s.io/kubernetes/pkg/quota/evaluator/core" package core // import "k8s.io/kubernetes/pkg/quota/v1/evaluator/core"

View File

@ -20,7 +20,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
@ -30,22 +30,22 @@ import (
"k8s.io/apiserver/pkg/features" "k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1" k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/apis/core/v1/helper"
k8sfeatures "k8s.io/kubernetes/pkg/features" k8sfeatures "k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubeapiserver/admission/util" "k8s.io/kubernetes/pkg/kubeapiserver/admission/util"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
) )
// the name used for object count quota // the name used for object count quota
var pvcObjectCountName = generic.ObjectCountQuotaResourceNameFor(v1.SchemeGroupVersion.WithResource("persistentvolumeclaims").GroupResource()) var pvcObjectCountName = generic.ObjectCountQuotaResourceNameFor(corev1.SchemeGroupVersion.WithResource("persistentvolumeclaims").GroupResource())
// pvcResources are the set of static resources managed by quota associated with pvcs. // pvcResources are the set of static resources managed by quota associated with pvcs.
// for each resource in this list, it may be refined dynamically based on storage class. // for each resource in this list, it may be refined dynamically based on storage class.
var pvcResources = []api.ResourceName{ var pvcResources = []corev1.ResourceName{
api.ResourcePersistentVolumeClaims, corev1.ResourcePersistentVolumeClaims,
api.ResourceRequestsStorage, corev1.ResourceRequestsStorage,
} }
// storageClassSuffix is the suffix to the qualified portion of storage class resource name. // storageClassSuffix is the suffix to the qualified portion of storage class resource name.
@ -56,19 +56,21 @@ var pvcResources = []api.ResourceName{
// * bronze.storageclass.storage.k8s.io/requests.storage: 500Gi // * bronze.storageclass.storage.k8s.io/requests.storage: 500Gi
const storageClassSuffix string = ".storageclass.storage.k8s.io/" const storageClassSuffix string = ".storageclass.storage.k8s.io/"
/* TODO: prune?
// ResourceByStorageClass returns a quota resource name by storage class. // ResourceByStorageClass returns a quota resource name by storage class.
func ResourceByStorageClass(storageClass string, resourceName api.ResourceName) api.ResourceName { func ResourceByStorageClass(storageClass string, resourceName corev1.ResourceName) corev1.ResourceName {
return api.ResourceName(string(storageClass + storageClassSuffix + string(resourceName))) return corev1.ResourceName(string(storageClass + storageClassSuffix + string(resourceName)))
} }
*/
// V1ResourceByStorageClass returns a quota resource name by storage class. // V1ResourceByStorageClass returns a quota resource name by storage class.
func V1ResourceByStorageClass(storageClass string, resourceName v1.ResourceName) v1.ResourceName { func V1ResourceByStorageClass(storageClass string, resourceName corev1.ResourceName) corev1.ResourceName {
return v1.ResourceName(string(storageClass + storageClassSuffix + string(resourceName))) return corev1.ResourceName(string(storageClass + storageClassSuffix + string(resourceName)))
} }
// NewPersistentVolumeClaimEvaluator returns an evaluator that can evaluate persistent volume claims // NewPersistentVolumeClaimEvaluator returns an evaluator that can evaluate persistent volume claims
func NewPersistentVolumeClaimEvaluator(f quota.ListerForResourceFunc) quota.Evaluator { func NewPersistentVolumeClaimEvaluator(f quota.ListerForResourceFunc) quota.Evaluator {
listFuncByNamespace := generic.ListResourceUsingListerFunc(f, v1.SchemeGroupVersion.WithResource("persistentvolumeclaims")) listFuncByNamespace := generic.ListResourceUsingListerFunc(f, corev1.SchemeGroupVersion.WithResource("persistentvolumeclaims"))
pvcEvaluator := &pvcEvaluator{listFuncByNamespace: listFuncByNamespace} pvcEvaluator := &pvcEvaluator{listFuncByNamespace: listFuncByNamespace}
return pvcEvaluator return pvcEvaluator
} }
@ -80,14 +82,14 @@ type pvcEvaluator struct {
} }
// Constraints verifies that all required resources are present on the item. // Constraints verifies that all required resources are present on the item.
func (p *pvcEvaluator) Constraints(required []api.ResourceName, item runtime.Object) error { func (p *pvcEvaluator) Constraints(required []corev1.ResourceName, item runtime.Object) error {
// no-op for persistent volume claims // no-op for persistent volume claims
return nil return nil
} }
// GroupResource that this evaluator tracks // GroupResource that this evaluator tracks
func (p *pvcEvaluator) GroupResource() schema.GroupResource { func (p *pvcEvaluator) GroupResource() schema.GroupResource {
return v1.SchemeGroupVersion.WithResource("persistentvolumeclaims").GroupResource() return corev1.SchemeGroupVersion.WithResource("persistentvolumeclaims").GroupResource()
} }
// Handles returns true if the evaluator should handle the specified operation. // Handles returns true if the evaluator should handle the specified operation.
@ -119,27 +121,27 @@ func (p *pvcEvaluator) Handles(a admission.Attributes) bool {
} }
// Matches returns true if the evaluator matches the specified quota with the provided input item // Matches returns true if the evaluator matches the specified quota with the provided input item
func (p *pvcEvaluator) Matches(resourceQuota *api.ResourceQuota, item runtime.Object) (bool, error) { func (p *pvcEvaluator) Matches(resourceQuota *corev1.ResourceQuota, item runtime.Object) (bool, error) {
return generic.Matches(resourceQuota, item, p.MatchingResources, generic.MatchesNoScopeFunc) return generic.Matches(resourceQuota, item, p.MatchingResources, generic.MatchesNoScopeFunc)
} }
// MatchingScopes takes the input specified list of scopes and input object. Returns the set of scopes resource matches. // MatchingScopes takes the input specified list of scopes and input object. Returns the set of scopes resource matches.
func (p *pvcEvaluator) MatchingScopes(item runtime.Object, scopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { func (p *pvcEvaluator) MatchingScopes(item runtime.Object, scopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) {
return []api.ScopedResourceSelectorRequirement{}, nil return []corev1.ScopedResourceSelectorRequirement{}, nil
} }
// UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes. // UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes.
// It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope // It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope
func (p *pvcEvaluator) UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSelectorRequirement, matchedQuotaScopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { func (p *pvcEvaluator) UncoveredQuotaScopes(limitedScopes []corev1.ScopedResourceSelectorRequirement, matchedQuotaScopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) {
return []api.ScopedResourceSelectorRequirement{}, nil return []corev1.ScopedResourceSelectorRequirement{}, nil
} }
// MatchingResources takes the input specified list of resources and returns the set of resources it matches. // MatchingResources takes the input specified list of resources and returns the set of resources it matches.
func (p *pvcEvaluator) MatchingResources(items []api.ResourceName) []api.ResourceName { func (p *pvcEvaluator) MatchingResources(items []corev1.ResourceName) []corev1.ResourceName {
result := []api.ResourceName{} result := []corev1.ResourceName{}
for _, item := range items { for _, item := range items {
// match object count quota fields // match object count quota fields
if quota.Contains([]api.ResourceName{pvcObjectCountName}, item) { if quota.Contains([]corev1.ResourceName{pvcObjectCountName}, item) {
result = append(result, item) result = append(result, item)
continue continue
} }
@ -161,15 +163,15 @@ func (p *pvcEvaluator) MatchingResources(items []api.ResourceName) []api.Resourc
} }
// Usage knows how to measure usage associated with item. // Usage knows how to measure usage associated with item.
func (p *pvcEvaluator) Usage(item runtime.Object) (api.ResourceList, error) { func (p *pvcEvaluator) Usage(item runtime.Object) (corev1.ResourceList, error) {
result := api.ResourceList{} result := corev1.ResourceList{}
pvc, err := toInternalPersistentVolumeClaimOrError(item) pvc, err := toExternalPersistentVolumeClaimOrError(item)
if err != nil { if err != nil {
return result, err return result, err
} }
// charge for claim // charge for claim
result[api.ResourcePersistentVolumeClaims] = *(resource.NewQuantity(1, resource.DecimalSI)) result[corev1.ResourcePersistentVolumeClaims] = *(resource.NewQuantity(1, resource.DecimalSI))
result[pvcObjectCountName] = *(resource.NewQuantity(1, resource.DecimalSI)) result[pvcObjectCountName] = *(resource.NewQuantity(1, resource.DecimalSI))
if utilfeature.DefaultFeatureGate.Enabled(features.Initializers) { if utilfeature.DefaultFeatureGate.Enabled(features.Initializers) {
if !initialization.IsInitialized(pvc.Initializers) { if !initialization.IsInitialized(pvc.Initializers) {
@ -179,16 +181,16 @@ func (p *pvcEvaluator) Usage(item runtime.Object) (api.ResourceList, error) {
} }
storageClassRef := helper.GetPersistentVolumeClaimClass(pvc) storageClassRef := helper.GetPersistentVolumeClaimClass(pvc)
if len(storageClassRef) > 0 { if len(storageClassRef) > 0 {
storageClassClaim := api.ResourceName(storageClassRef + storageClassSuffix + string(api.ResourcePersistentVolumeClaims)) storageClassClaim := corev1.ResourceName(storageClassRef + storageClassSuffix + string(corev1.ResourcePersistentVolumeClaims))
result[storageClassClaim] = *(resource.NewQuantity(1, resource.DecimalSI)) result[storageClassClaim] = *(resource.NewQuantity(1, resource.DecimalSI))
} }
// charge for storage // charge for storage
if request, found := pvc.Spec.Resources.Requests[api.ResourceStorage]; found { if request, found := pvc.Spec.Resources.Requests[corev1.ResourceStorage]; found {
result[api.ResourceRequestsStorage] = request result[corev1.ResourceRequestsStorage] = request
// charge usage to the storage class (if present) // charge usage to the storage class (if present)
if len(storageClassRef) > 0 { if len(storageClassRef) > 0 {
storageClassStorage := api.ResourceName(storageClassRef + storageClassSuffix + string(api.ResourceRequestsStorage)) storageClassStorage := corev1.ResourceName(storageClassRef + storageClassSuffix + string(corev1.ResourceRequestsStorage))
result[storageClassStorage] = request result[storageClassStorage] = request
} }
} }
@ -203,15 +205,15 @@ func (p *pvcEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.UsageS
// ensure we implement required interface // ensure we implement required interface
var _ quota.Evaluator = &pvcEvaluator{} var _ quota.Evaluator = &pvcEvaluator{}
func toInternalPersistentVolumeClaimOrError(obj runtime.Object) (*api.PersistentVolumeClaim, error) { func toExternalPersistentVolumeClaimOrError(obj runtime.Object) (*corev1.PersistentVolumeClaim, error) {
pvc := &api.PersistentVolumeClaim{} pvc := &corev1.PersistentVolumeClaim{}
switch t := obj.(type) { switch t := obj.(type) {
case *v1.PersistentVolumeClaim: case *corev1.PersistentVolumeClaim:
if err := k8s_api_v1.Convert_v1_PersistentVolumeClaim_To_core_PersistentVolumeClaim(t, pvc, nil); err != nil { pvc = t
case *api.PersistentVolumeClaim:
if err := k8s_api_v1.Convert_core_PersistentVolumeClaim_To_v1_PersistentVolumeClaim(t, pvc, nil); err != nil {
return nil, err return nil, err
} }
case *api.PersistentVolumeClaim:
pvc = t
default: default:
return nil, fmt.Errorf("expect *api.PersistentVolumeClaim or *v1.PersistentVolumeClaim, got %v", t) return nil, fmt.Errorf("expect *api.PersistentVolumeClaim or *v1.PersistentVolumeClaim, got %v", t)
} }

View File

@ -19,12 +19,13 @@ package core
import ( import (
"testing" "testing"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
) )
func testVolumeClaim(name string, namespace string, spec api.PersistentVolumeClaimSpec) *api.PersistentVolumeClaim { func testVolumeClaim(name string, namespace string, spec api.PersistentVolumeClaimSpec) *api.PersistentVolumeClaim {
@ -79,23 +80,23 @@ func TestPersistentVolumeClaimEvaluatorUsage(t *testing.T) {
evaluator := NewPersistentVolumeClaimEvaluator(nil) evaluator := NewPersistentVolumeClaimEvaluator(nil)
testCases := map[string]struct { testCases := map[string]struct {
pvc *api.PersistentVolumeClaim pvc *api.PersistentVolumeClaim
usage api.ResourceList usage corev1.ResourceList
}{ }{
"pvc-usage": { "pvc-usage": {
pvc: validClaim, pvc: validClaim,
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceRequestsStorage: resource.MustParse("10Gi"), corev1.ResourceRequestsStorage: resource.MustParse("10Gi"),
api.ResourcePersistentVolumeClaims: resource.MustParse("1"), corev1.ResourcePersistentVolumeClaims: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "persistentvolumeclaims"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "persistentvolumeclaims"}): resource.MustParse("1"),
}, },
}, },
"pvc-usage-by-class": { "pvc-usage-by-class": {
pvc: validClaimByStorageClass, pvc: validClaimByStorageClass,
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceRequestsStorage: resource.MustParse("10Gi"), corev1.ResourceRequestsStorage: resource.MustParse("10Gi"),
api.ResourcePersistentVolumeClaims: resource.MustParse("1"), corev1.ResourcePersistentVolumeClaims: resource.MustParse("1"),
ResourceByStorageClass(classGold, api.ResourceRequestsStorage): resource.MustParse("10Gi"), V1ResourceByStorageClass(classGold, corev1.ResourceRequestsStorage): resource.MustParse("10Gi"),
ResourceByStorageClass(classGold, api.ResourcePersistentVolumeClaims): resource.MustParse("1"), V1ResourceByStorageClass(classGold, corev1.ResourcePersistentVolumeClaims): resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "persistentvolumeclaims"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "persistentvolumeclaims"}): resource.MustParse("1"),
}, },
}, },

View File

@ -21,7 +21,7 @@ import (
"strings" "strings"
"time" "time"
"k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -32,57 +32,57 @@ import (
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/core/helper/qos"
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1" k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
"k8s.io/kubernetes/pkg/kubeapiserver/admission/util" "k8s.io/kubernetes/pkg/kubeapiserver/admission/util"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
) )
// the name used for object count quota // the name used for object count quota
var podObjectCountName = generic.ObjectCountQuotaResourceNameFor(v1.SchemeGroupVersion.WithResource("pods").GroupResource()) var podObjectCountName = generic.ObjectCountQuotaResourceNameFor(corev1.SchemeGroupVersion.WithResource("pods").GroupResource())
// podResources are the set of resources managed by quota associated with pods. // podResources are the set of resources managed by quota associated with pods.
var podResources = []api.ResourceName{ var podResources = []corev1.ResourceName{
podObjectCountName, podObjectCountName,
api.ResourceCPU, corev1.ResourceCPU,
api.ResourceMemory, corev1.ResourceMemory,
api.ResourceEphemeralStorage, corev1.ResourceEphemeralStorage,
api.ResourceRequestsCPU, corev1.ResourceRequestsCPU,
api.ResourceRequestsMemory, corev1.ResourceRequestsMemory,
api.ResourceRequestsEphemeralStorage, corev1.ResourceRequestsEphemeralStorage,
api.ResourceLimitsCPU, corev1.ResourceLimitsCPU,
api.ResourceLimitsMemory, corev1.ResourceLimitsMemory,
api.ResourceLimitsEphemeralStorage, corev1.ResourceLimitsEphemeralStorage,
api.ResourcePods, corev1.ResourcePods,
} }
// podResourcePrefixes are the set of prefixes for resources (Hugepages, and other // podResourcePrefixes are the set of prefixes for resources (Hugepages, and other
// potential extended reources with specific prefix) managed by quota associated with pods. // potential extended reources with specific prefix) managed by quota associated with pods.
var podResourcePrefixes = []string{ var podResourcePrefixes = []string{
api.ResourceHugePagesPrefix, corev1.ResourceHugePagesPrefix,
api.ResourceRequestsHugePagesPrefix, corev1.ResourceRequestsHugePagesPrefix,
} }
// requestedResourcePrefixes are the set of prefixes for resources // requestedResourcePrefixes are the set of prefixes for resources
// that might be declared in pod's Resources.Requests/Limits // that might be declared in pod's Resources.Requests/Limits
var requestedResourcePrefixes = []string{ var requestedResourcePrefixes = []string{
api.ResourceHugePagesPrefix, corev1.ResourceHugePagesPrefix,
} }
// maskResourceWithPrefix mask resource with certain prefix // maskResourceWithPrefix mask resource with certain prefix
// e.g. hugepages-XXX -> requests.hugepages-XXX // e.g. hugepages-XXX -> requests.hugepages-XXX
func maskResourceWithPrefix(resource api.ResourceName, prefix string) api.ResourceName { func maskResourceWithPrefix(resource corev1.ResourceName, prefix string) corev1.ResourceName {
return api.ResourceName(fmt.Sprintf("%s%s", prefix, string(resource))) return corev1.ResourceName(fmt.Sprintf("%s%s", prefix, string(resource)))
} }
// isExtendedResourceNameForQuota returns true if the extended resource name // isExtendedResourceNameForQuota returns true if the extended resource name
// has the quota related resource prefix. // has the quota related resource prefix.
func isExtendedResourceNameForQuota(name api.ResourceName) bool { func isExtendedResourceNameForQuota(name corev1.ResourceName) bool {
// As overcommit is not supported by extended resources for now, // As overcommit is not supported by extended resources for now,
// only quota objects in format of "requests.resourceName" is allowed. // only quota objects in format of "requests.resourceName" is allowed.
return !helper.IsNativeResource(name) && strings.HasPrefix(string(name), api.DefaultResourceRequestsPrefix) return !helper.IsNativeResource(name) && strings.HasPrefix(string(name), corev1.DefaultResourceRequestsPrefix)
} }
// NOTE: it was a mistake, but if a quota tracks cpu or memory related resources, // NOTE: it was a mistake, but if a quota tracks cpu or memory related resources,
@ -90,17 +90,17 @@ func isExtendedResourceNameForQuota(name api.ResourceName) bool {
// this mistake for other future resources (gpus, ephemeral-storage,etc). // this mistake for other future resources (gpus, ephemeral-storage,etc).
// do not add more resources to this list! // do not add more resources to this list!
var validationSet = sets.NewString( var validationSet = sets.NewString(
string(api.ResourceCPU), string(corev1.ResourceCPU),
string(api.ResourceMemory), string(corev1.ResourceMemory),
string(api.ResourceRequestsCPU), string(corev1.ResourceRequestsCPU),
string(api.ResourceRequestsMemory), string(corev1.ResourceRequestsMemory),
string(api.ResourceLimitsCPU), string(corev1.ResourceLimitsCPU),
string(api.ResourceLimitsMemory), string(corev1.ResourceLimitsMemory),
) )
// NewPodEvaluator returns an evaluator that can evaluate pods // NewPodEvaluator returns an evaluator that can evaluate pods
func NewPodEvaluator(f quota.ListerForResourceFunc, clock clock.Clock) quota.Evaluator { func NewPodEvaluator(f quota.ListerForResourceFunc, clock clock.Clock) quota.Evaluator {
listFuncByNamespace := generic.ListResourceUsingListerFunc(f, v1.SchemeGroupVersion.WithResource("pods")) listFuncByNamespace := generic.ListResourceUsingListerFunc(f, corev1.SchemeGroupVersion.WithResource("pods"))
podEvaluator := &podEvaluator{listFuncByNamespace: listFuncByNamespace, clock: clock} podEvaluator := &podEvaluator{listFuncByNamespace: listFuncByNamespace, clock: clock}
return podEvaluator return podEvaluator
} }
@ -115,10 +115,10 @@ type podEvaluator struct {
// Constraints verifies that all required resources are present on the pod // Constraints verifies that all required resources are present on the pod
// In addition, it validates that the resources are valid (i.e. requests < limits) // In addition, it validates that the resources are valid (i.e. requests < limits)
func (p *podEvaluator) Constraints(required []api.ResourceName, item runtime.Object) error { func (p *podEvaluator) Constraints(required []corev1.ResourceName, item runtime.Object) error {
pod, ok := item.(*api.Pod) pod, err := toExternalPodOrError(item)
if !ok { if err != nil {
return fmt.Errorf("unexpected input object %v", item) return err
} }
// BACKWARD COMPATIBILITY REQUIREMENT: if we quota cpu or memory, then each container // BACKWARD COMPATIBILITY REQUIREMENT: if we quota cpu or memory, then each container
@ -141,7 +141,7 @@ func (p *podEvaluator) Constraints(required []api.ResourceName, item runtime.Obj
// GroupResource that this evaluator tracks // GroupResource that this evaluator tracks
func (p *podEvaluator) GroupResource() schema.GroupResource { func (p *podEvaluator) GroupResource() schema.GroupResource {
return v1.SchemeGroupVersion.WithResource("pods").GroupResource() return corev1.SchemeGroupVersion.WithResource("pods").GroupResource()
} }
// Handles returns true if the evaluator should handle the specified attributes. // Handles returns true if the evaluator should handle the specified attributes.
@ -161,12 +161,12 @@ func (p *podEvaluator) Handles(a admission.Attributes) bool {
} }
// Matches returns true if the evaluator matches the specified quota with the provided input item // Matches returns true if the evaluator matches the specified quota with the provided input item
func (p *podEvaluator) Matches(resourceQuota *api.ResourceQuota, item runtime.Object) (bool, error) { func (p *podEvaluator) Matches(resourceQuota *corev1.ResourceQuota, item runtime.Object) (bool, error) {
return generic.Matches(resourceQuota, item, p.MatchingResources, podMatchesScopeFunc) return generic.Matches(resourceQuota, item, p.MatchingResources, podMatchesScopeFunc)
} }
// MatchingResources takes the input specified list of resources and returns the set of resources it matches. // MatchingResources takes the input specified list of resources and returns the set of resources it matches.
func (p *podEvaluator) MatchingResources(input []api.ResourceName) []api.ResourceName { func (p *podEvaluator) MatchingResources(input []corev1.ResourceName) []corev1.ResourceName {
result := quota.Intersection(input, podResources) result := quota.Intersection(input, podResources)
for _, resource := range input { for _, resource := range input {
// for resources with certain prefix, e.g. hugepages // for resources with certain prefix, e.g. hugepages
@ -183,12 +183,12 @@ func (p *podEvaluator) MatchingResources(input []api.ResourceName) []api.Resourc
} }
// MatchingScopes takes the input specified list of scopes and pod object. Returns the set of scope selectors pod matches. // MatchingScopes takes the input specified list of scopes and pod object. Returns the set of scope selectors pod matches.
func (p *podEvaluator) MatchingScopes(item runtime.Object, scopeSelectors []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { func (p *podEvaluator) MatchingScopes(item runtime.Object, scopeSelectors []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) {
matchedScopes := []api.ScopedResourceSelectorRequirement{} matchedScopes := []corev1.ScopedResourceSelectorRequirement{}
for _, selector := range scopeSelectors { for _, selector := range scopeSelectors {
match, err := podMatchesScopeFunc(selector, item) match, err := podMatchesScopeFunc(selector, item)
if err != nil { if err != nil {
return []api.ScopedResourceSelectorRequirement{}, fmt.Errorf("error on matching scope %v: %v", selector, err) return []corev1.ScopedResourceSelectorRequirement{}, fmt.Errorf("error on matching scope %v: %v", selector, err)
} }
if match { if match {
matchedScopes = append(matchedScopes, selector) matchedScopes = append(matchedScopes, selector)
@ -199,8 +199,8 @@ func (p *podEvaluator) MatchingScopes(item runtime.Object, scopeSelectors []api.
// UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes. // UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes.
// It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope // It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope
func (p *podEvaluator) UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSelectorRequirement, matchedQuotaScopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { func (p *podEvaluator) UncoveredQuotaScopes(limitedScopes []corev1.ScopedResourceSelectorRequirement, matchedQuotaScopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) {
uncoveredScopes := []api.ScopedResourceSelectorRequirement{} uncoveredScopes := []corev1.ScopedResourceSelectorRequirement{}
for _, selector := range limitedScopes { for _, selector := range limitedScopes {
isCovered := false isCovered := false
for _, matchedScopeSelector := range matchedQuotaScopes { for _, matchedScopeSelector := range matchedQuotaScopes {
@ -218,7 +218,7 @@ func (p *podEvaluator) UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSe
} }
// Usage knows how to measure usage associated with pods // Usage knows how to measure usage associated with pods
func (p *podEvaluator) Usage(item runtime.Object) (api.ResourceList, error) { func (p *podEvaluator) Usage(item runtime.Object) (corev1.ResourceList, error) {
// delegate to normal usage // delegate to normal usage
return PodUsageFunc(item, p.clock) return PodUsageFunc(item, p.clock)
} }
@ -233,7 +233,7 @@ var _ quota.Evaluator = &podEvaluator{}
// enforcePodContainerConstraints checks for required resources that are not set on this container and // enforcePodContainerConstraints checks for required resources that are not set on this container and
// adds them to missingSet. // adds them to missingSet.
func enforcePodContainerConstraints(container *api.Container, requiredSet, missingSet sets.String) { func enforcePodContainerConstraints(container *corev1.Container, requiredSet, missingSet sets.String) {
requests := container.Resources.Requests requests := container.Resources.Requests
limits := container.Resources.Limits limits := container.Resources.Limits
containerUsage := podComputeUsageHelper(requests, limits) containerUsage := podComputeUsageHelper(requests, limits)
@ -245,55 +245,55 @@ func enforcePodContainerConstraints(container *api.Container, requiredSet, missi
} }
// podComputeUsageHelper can summarize the pod compute quota usage based on requests and limits // podComputeUsageHelper can summarize the pod compute quota usage based on requests and limits
func podComputeUsageHelper(requests api.ResourceList, limits api.ResourceList) api.ResourceList { func podComputeUsageHelper(requests corev1.ResourceList, limits corev1.ResourceList) corev1.ResourceList {
result := api.ResourceList{} result := corev1.ResourceList{}
result[api.ResourcePods] = resource.MustParse("1") result[corev1.ResourcePods] = resource.MustParse("1")
if request, found := requests[api.ResourceCPU]; found { if request, found := requests[corev1.ResourceCPU]; found {
result[api.ResourceCPU] = request result[corev1.ResourceCPU] = request
result[api.ResourceRequestsCPU] = request result[corev1.ResourceRequestsCPU] = request
} }
if limit, found := limits[api.ResourceCPU]; found { if limit, found := limits[corev1.ResourceCPU]; found {
result[api.ResourceLimitsCPU] = limit result[corev1.ResourceLimitsCPU] = limit
} }
if request, found := requests[api.ResourceMemory]; found { if request, found := requests[corev1.ResourceMemory]; found {
result[api.ResourceMemory] = request result[corev1.ResourceMemory] = request
result[api.ResourceRequestsMemory] = request result[corev1.ResourceRequestsMemory] = request
} }
if limit, found := limits[api.ResourceMemory]; found { if limit, found := limits[corev1.ResourceMemory]; found {
result[api.ResourceLimitsMemory] = limit result[corev1.ResourceLimitsMemory] = limit
} }
if request, found := requests[api.ResourceEphemeralStorage]; found { if request, found := requests[corev1.ResourceEphemeralStorage]; found {
result[api.ResourceEphemeralStorage] = request result[corev1.ResourceEphemeralStorage] = request
result[api.ResourceRequestsEphemeralStorage] = request result[corev1.ResourceRequestsEphemeralStorage] = request
} }
if limit, found := limits[api.ResourceEphemeralStorage]; found { if limit, found := limits[corev1.ResourceEphemeralStorage]; found {
result[api.ResourceLimitsEphemeralStorage] = limit result[corev1.ResourceLimitsEphemeralStorage] = limit
} }
for resource, request := range requests { for resource, request := range requests {
// for resources with certain prefix, e.g. hugepages // for resources with certain prefix, e.g. hugepages
if quota.ContainsPrefix(requestedResourcePrefixes, resource) { if quota.ContainsPrefix(requestedResourcePrefixes, resource) {
result[resource] = request result[resource] = request
result[maskResourceWithPrefix(resource, api.DefaultResourceRequestsPrefix)] = request result[maskResourceWithPrefix(resource, corev1.DefaultResourceRequestsPrefix)] = request
} }
// for extended resources // for extended resources
if helper.IsExtendedResourceName(resource) { if helper.IsExtendedResourceName(resource) {
// only quota objects in format of "requests.resourceName" is allowed for extended resource. // only quota objects in format of "requests.resourceName" is allowed for extended resource.
result[maskResourceWithPrefix(resource, api.DefaultResourceRequestsPrefix)] = request result[maskResourceWithPrefix(resource, corev1.DefaultResourceRequestsPrefix)] = request
} }
} }
return result return result
} }
func toInternalPodOrError(obj runtime.Object) (*api.Pod, error) { func toExternalPodOrError(obj runtime.Object) (*corev1.Pod, error) {
pod := &api.Pod{} pod := &corev1.Pod{}
switch t := obj.(type) { switch t := obj.(type) {
case *v1.Pod: case *corev1.Pod:
if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(t, pod, nil); err != nil { pod = t
case *api.Pod:
if err := k8s_api_v1.Convert_core_Pod_To_v1_Pod(t, pod, nil); err != nil {
return nil, err return nil, err
} }
case *api.Pod:
pod = t
default: default:
return nil, fmt.Errorf("expect *api.Pod or *v1.Pod, got %v", t) return nil, fmt.Errorf("expect *api.Pod or *v1.Pod, got %v", t)
} }
@ -301,21 +301,21 @@ func toInternalPodOrError(obj runtime.Object) (*api.Pod, error) {
} }
// podMatchesScopeFunc is a function that knows how to evaluate if a pod matches a scope // podMatchesScopeFunc is a function that knows how to evaluate if a pod matches a scope
func podMatchesScopeFunc(selector api.ScopedResourceSelectorRequirement, object runtime.Object) (bool, error) { func podMatchesScopeFunc(selector corev1.ScopedResourceSelectorRequirement, object runtime.Object) (bool, error) {
pod, err := toInternalPodOrError(object) pod, err := toExternalPodOrError(object)
if err != nil { if err != nil {
return false, err return false, err
} }
switch selector.ScopeName { switch selector.ScopeName {
case api.ResourceQuotaScopeTerminating: case corev1.ResourceQuotaScopeTerminating:
return isTerminating(pod), nil return isTerminating(pod), nil
case api.ResourceQuotaScopeNotTerminating: case corev1.ResourceQuotaScopeNotTerminating:
return !isTerminating(pod), nil return !isTerminating(pod), nil
case api.ResourceQuotaScopeBestEffort: case corev1.ResourceQuotaScopeBestEffort:
return isBestEffort(pod), nil return isBestEffort(pod), nil
case api.ResourceQuotaScopeNotBestEffort: case corev1.ResourceQuotaScopeNotBestEffort:
return !isBestEffort(pod), nil return !isBestEffort(pod), nil
case api.ResourceQuotaScopePriorityClass: case corev1.ResourceQuotaScopePriorityClass:
return podMatchesSelector(pod, selector) return podMatchesSelector(pod, selector)
} }
return false, nil return false, nil
@ -325,28 +325,28 @@ func podMatchesScopeFunc(selector api.ScopedResourceSelectorRequirement, object
// A pod is charged for quota if the following are not true. // A pod is charged for quota if the following are not true.
// - pod has a terminal phase (failed or succeeded) // - pod has a terminal phase (failed or succeeded)
// - pod has been marked for deletion and grace period has expired // - pod has been marked for deletion and grace period has expired
func PodUsageFunc(obj runtime.Object, clock clock.Clock) (api.ResourceList, error) { func PodUsageFunc(obj runtime.Object, clock clock.Clock) (corev1.ResourceList, error) {
pod, err := toInternalPodOrError(obj) pod, err := toExternalPodOrError(obj)
if err != nil { if err != nil {
return api.ResourceList{}, err return corev1.ResourceList{}, err
} }
// always quota the object count (even if the pod is end of life) // always quota the object count (even if the pod is end of life)
// object count quotas track all objects that are in storage. // object count quotas track all objects that are in storage.
// where "pods" tracks all pods that have not reached a terminal state, // where "pods" tracks all pods that have not reached a terminal state,
// count/pods tracks all pods independent of state. // count/pods tracks all pods independent of state.
result := api.ResourceList{ result := corev1.ResourceList{
podObjectCountName: *(resource.NewQuantity(1, resource.DecimalSI)), podObjectCountName: *(resource.NewQuantity(1, resource.DecimalSI)),
} }
// by convention, we do not quota compute resources that have reached end-of life // by convention, we do not quota compute resources that have reached end-of life
// note: the "pods" resource is considered a compute resource since it is tied to life-cycle. // note: the "pods" resource is considered a compute resource since it is tied to life-cycle.
if !QuotaPod(pod, clock) { if !QuotaV1Pod(pod, clock) {
return result, nil return result, nil
} }
requests := api.ResourceList{} requests := corev1.ResourceList{}
limits := api.ResourceList{} limits := corev1.ResourceList{}
// TODO: ideally, we have pod level requests and limits in the future. // TODO: ideally, we have pod level requests and limits in the future.
for i := range pod.Spec.Containers { for i := range pod.Spec.Containers {
requests = quota.Add(requests, pod.Spec.Containers[i].Resources.Requests) requests = quota.Add(requests, pod.Spec.Containers[i].Resources.Requests)
@ -364,25 +364,25 @@ func PodUsageFunc(obj runtime.Object, clock clock.Clock) (api.ResourceList, erro
return result, nil return result, nil
} }
func isBestEffort(pod *api.Pod) bool { func isBestEffort(pod *corev1.Pod) bool {
return qos.GetPodQOS(pod) == api.PodQOSBestEffort return qos.GetPodQOS(pod) == corev1.PodQOSBestEffort
} }
func isTerminating(pod *api.Pod) bool { func isTerminating(pod *corev1.Pod) bool {
if pod.Spec.ActiveDeadlineSeconds != nil && *pod.Spec.ActiveDeadlineSeconds >= int64(0) { if pod.Spec.ActiveDeadlineSeconds != nil && *pod.Spec.ActiveDeadlineSeconds >= int64(0) {
return true return true
} }
return false return false
} }
func podMatchesSelector(pod *api.Pod, selector api.ScopedResourceSelectorRequirement) (bool, error) { func podMatchesSelector(pod *corev1.Pod, selector corev1.ScopedResourceSelectorRequirement) (bool, error) {
labelSelector, err := helper.ScopedResourceSelectorRequirementsAsSelector(selector) labelSelector, err := helper.ScopedResourceSelectorRequirementsAsSelector(selector)
if err != nil { if err != nil {
return false, fmt.Errorf("failed to parse and convert selector: %v", err) return false, fmt.Errorf("failed to parse and convert selector: %v", err)
} }
var m map[string]string var m map[string]string
if len(pod.Spec.PriorityClassName) != 0 { if len(pod.Spec.PriorityClassName) != 0 {
m = map[string]string{string(api.ResourceQuotaScopePriorityClass): pod.Spec.PriorityClassName} m = map[string]string{string(corev1.ResourceQuotaScopePriorityClass): pod.Spec.PriorityClassName}
} }
if labelSelector.Matches(labels.Set(m)) { if labelSelector.Matches(labels.Set(m)) {
return true, nil return true, nil
@ -390,36 +390,11 @@ func podMatchesSelector(pod *api.Pod, selector api.ScopedResourceSelectorRequire
return false, nil return false, nil
} }
// QuotaPod returns true if the pod is eligible to track against a quota
// A pod is eligible for quota, unless any of the following are true:
// - pod has a terminal phase (failed or succeeded)
// - pod has been marked for deletion and grace period has expired.
func QuotaPod(pod *api.Pod, clock clock.Clock) bool {
// if pod is terminal, ignore it for quota
if api.PodFailed == pod.Status.Phase || api.PodSucceeded == pod.Status.Phase {
return false
}
// deleted pods that should be gone should not be charged to user quota.
// this can happen if a node is lost, and the kubelet is never able to confirm deletion.
// even though the cluster may have drifting clocks, quota makes a reasonable effort
// to balance cluster needs against user needs. user's do not control clocks,
// but at worst a small drive in clocks will only slightly impact quota.
if pod.DeletionTimestamp != nil && pod.DeletionGracePeriodSeconds != nil {
now := clock.Now()
deletionTime := pod.DeletionTimestamp.Time
gracePeriod := time.Duration(*pod.DeletionGracePeriodSeconds) * time.Second
if now.After(deletionTime.Add(gracePeriod)) {
return false
}
}
return true
}
// QuotaV1Pod returns true if the pod is eligible to track against a quota // QuotaV1Pod returns true if the pod is eligible to track against a quota
// if it's not in a terminal state according to its phase. // if it's not in a terminal state according to its phase.
func QuotaV1Pod(pod *v1.Pod, clock clock.Clock) bool { func QuotaV1Pod(pod *corev1.Pod, clock clock.Clock) bool {
// if pod is terminal, ignore it for quota // if pod is terminal, ignore it for quota
if v1.PodFailed == pod.Status.Phase || v1.PodSucceeded == pod.Status.Phase { if corev1.PodFailed == pod.Status.Phase || corev1.PodSucceeded == pod.Status.Phase {
return false return false
} }
// if pods are stuck terminating (for example, a node is lost), we do not want // if pods are stuck terminating (for example, a node is lost), we do not want

View File

@ -20,20 +20,21 @@ import (
"testing" "testing"
"time" "time"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/clock"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
"k8s.io/kubernetes/pkg/util/node" "k8s.io/kubernetes/pkg/util/node"
) )
func TestPodConstraintsFunc(t *testing.T) { func TestPodConstraintsFunc(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
pod *api.Pod pod *api.Pod
required []api.ResourceName required []corev1.ResourceName
err string err string
}{ }{
"init container resource missing": { "init container resource missing": {
@ -47,7 +48,7 @@ func TestPodConstraintsFunc(t *testing.T) {
}}, }},
}, },
}, },
required: []api.ResourceName{api.ResourceMemory}, required: []corev1.ResourceName{corev1.ResourceMemory},
err: `must specify memory`, err: `must specify memory`,
}, },
"container resource missing": { "container resource missing": {
@ -61,7 +62,7 @@ func TestPodConstraintsFunc(t *testing.T) {
}}, }},
}, },
}, },
required: []api.ResourceName{api.ResourceMemory}, required: []corev1.ResourceName{corev1.ResourceMemory},
err: `must specify memory`, err: `must specify memory`,
}, },
} }
@ -90,7 +91,7 @@ func TestPodEvaluatorUsage(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
pod *api.Pod pod *api.Pod
usage api.ResourceList usage corev1.ResourceList
}{ }{
"init container CPU": { "init container CPU": {
pod: &api.Pod{ pod: &api.Pod{
@ -103,11 +104,11 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceRequestsCPU: resource.MustParse("1m"), corev1.ResourceRequestsCPU: resource.MustParse("1m"),
api.ResourceLimitsCPU: resource.MustParse("2m"), corev1.ResourceLimitsCPU: resource.MustParse("2m"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
api.ResourceCPU: resource.MustParse("1m"), corev1.ResourceCPU: resource.MustParse("1m"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -122,11 +123,11 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceRequestsMemory: resource.MustParse("1m"), corev1.ResourceRequestsMemory: resource.MustParse("1m"),
api.ResourceLimitsMemory: resource.MustParse("2m"), corev1.ResourceLimitsMemory: resource.MustParse("2m"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
api.ResourceMemory: resource.MustParse("1m"), corev1.ResourceMemory: resource.MustParse("1m"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -141,11 +142,11 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceEphemeralStorage: resource.MustParse("32Mi"), corev1.ResourceEphemeralStorage: resource.MustParse("32Mi"),
api.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"), corev1.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"),
api.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"), corev1.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -159,10 +160,10 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceName(api.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), corev1.ResourceName(corev1.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"),
api.ResourceName(api.ResourceRequestsHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), corev1.ResourceName(corev1.ResourceRequestsHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -177,9 +178,9 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceName("requests.example.com/dongle"): resource.MustParse("3"), corev1.ResourceName("requests.example.com/dongle"): resource.MustParse("3"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -194,11 +195,11 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceRequestsCPU: resource.MustParse("1m"), corev1.ResourceRequestsCPU: resource.MustParse("1m"),
api.ResourceLimitsCPU: resource.MustParse("2m"), corev1.ResourceLimitsCPU: resource.MustParse("2m"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
api.ResourceCPU: resource.MustParse("1m"), corev1.ResourceCPU: resource.MustParse("1m"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -213,11 +214,11 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceRequestsMemory: resource.MustParse("1m"), corev1.ResourceRequestsMemory: resource.MustParse("1m"),
api.ResourceLimitsMemory: resource.MustParse("2m"), corev1.ResourceLimitsMemory: resource.MustParse("2m"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
api.ResourceMemory: resource.MustParse("1m"), corev1.ResourceMemory: resource.MustParse("1m"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -232,11 +233,11 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceEphemeralStorage: resource.MustParse("32Mi"), corev1.ResourceEphemeralStorage: resource.MustParse("32Mi"),
api.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"), corev1.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"),
api.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"), corev1.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -250,10 +251,10 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceName(api.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), corev1.ResourceName(api.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"),
api.ResourceName(api.ResourceRequestsHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), corev1.ResourceName(api.ResourceRequestsHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -268,9 +269,9 @@ func TestPodEvaluatorUsage(t *testing.T) {
}}, }},
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceName("requests.example.com/dongle"): resource.MustParse("3"), corev1.ResourceName("requests.example.com/dongle"): resource.MustParse("3"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -339,15 +340,15 @@ func TestPodEvaluatorUsage(t *testing.T) {
}, },
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceRequestsCPU: resource.MustParse("4"), corev1.ResourceRequestsCPU: resource.MustParse("4"),
api.ResourceRequestsMemory: resource.MustParse("100M"), corev1.ResourceRequestsMemory: resource.MustParse("100M"),
api.ResourceLimitsCPU: resource.MustParse("8"), corev1.ResourceLimitsCPU: resource.MustParse("8"),
api.ResourceLimitsMemory: resource.MustParse("200M"), corev1.ResourceLimitsMemory: resource.MustParse("200M"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
api.ResourceCPU: resource.MustParse("4"), corev1.ResourceCPU: resource.MustParse("4"),
api.ResourceMemory: resource.MustParse("100M"), corev1.ResourceMemory: resource.MustParse("100M"),
api.ResourceName("requests.example.com/dongle"): resource.MustParse("4"), corev1.ResourceName("requests.example.com/dongle"): resource.MustParse("4"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -378,7 +379,7 @@ func TestPodEvaluatorUsage(t *testing.T) {
}, },
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },
@ -406,11 +407,11 @@ func TestPodEvaluatorUsage(t *testing.T) {
}, },
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceRequestsCPU: resource.MustParse("1"), corev1.ResourceRequestsCPU: resource.MustParse("1"),
api.ResourceLimitsCPU: resource.MustParse("2"), corev1.ResourceLimitsCPU: resource.MustParse("2"),
api.ResourcePods: resource.MustParse("1"), corev1.ResourcePods: resource.MustParse("1"),
api.ResourceCPU: resource.MustParse("1"), corev1.ResourceCPU: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"),
}, },
}, },

View File

@ -17,20 +17,19 @@ limitations under the License.
package core package core
import ( import (
"k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/clock" "k8s.io/apimachinery/pkg/util/clock"
api "k8s.io/kubernetes/pkg/apis/core" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota/v1/generic"
"k8s.io/kubernetes/pkg/quota/generic"
) )
// legacyObjectCountAliases are what we used to do simple object counting quota with mapped to alias // legacyObjectCountAliases are what we used to do simple object counting quota with mapped to alias
var legacyObjectCountAliases = map[schema.GroupVersionResource]api.ResourceName{ var legacyObjectCountAliases = map[schema.GroupVersionResource]corev1.ResourceName{
v1.SchemeGroupVersion.WithResource("configmaps"): api.ResourceConfigMaps, corev1.SchemeGroupVersion.WithResource("configmaps"): corev1.ResourceConfigMaps,
v1.SchemeGroupVersion.WithResource("resourcequotas"): api.ResourceQuotas, corev1.SchemeGroupVersion.WithResource("resourcequotas"): corev1.ResourceQuotas,
v1.SchemeGroupVersion.WithResource("replicationcontrollers"): api.ResourceReplicationControllers, corev1.SchemeGroupVersion.WithResource("replicationcontrollers"): corev1.ResourceReplicationControllers,
v1.SchemeGroupVersion.WithResource("secrets"): api.ResourceSecrets, corev1.SchemeGroupVersion.WithResource("secrets"): corev1.ResourceSecrets,
} }
// NewEvaluators returns the list of static evaluators that manage more than counts // NewEvaluators returns the list of static evaluators that manage more than counts

View File

@ -19,31 +19,31 @@ package core
import ( import (
"fmt" "fmt"
"k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1" k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
) )
// the name used for object count quota // the name used for object count quota
var serviceObjectCountName = generic.ObjectCountQuotaResourceNameFor(v1.SchemeGroupVersion.WithResource("services").GroupResource()) var serviceObjectCountName = generic.ObjectCountQuotaResourceNameFor(corev1.SchemeGroupVersion.WithResource("services").GroupResource())
// serviceResources are the set of resources managed by quota associated with services. // serviceResources are the set of resources managed by quota associated with services.
var serviceResources = []api.ResourceName{ var serviceResources = []corev1.ResourceName{
serviceObjectCountName, serviceObjectCountName,
api.ResourceServices, corev1.ResourceServices,
api.ResourceServicesNodePorts, corev1.ResourceServicesNodePorts,
api.ResourceServicesLoadBalancers, corev1.ResourceServicesLoadBalancers,
} }
// NewServiceEvaluator returns an evaluator that can evaluate services. // NewServiceEvaluator returns an evaluator that can evaluate services.
func NewServiceEvaluator(f quota.ListerForResourceFunc) quota.Evaluator { func NewServiceEvaluator(f quota.ListerForResourceFunc) quota.Evaluator {
listFuncByNamespace := generic.ListResourceUsingListerFunc(f, v1.SchemeGroupVersion.WithResource("services")) listFuncByNamespace := generic.ListResourceUsingListerFunc(f, corev1.SchemeGroupVersion.WithResource("services"))
serviceEvaluator := &serviceEvaluator{listFuncByNamespace: listFuncByNamespace} serviceEvaluator := &serviceEvaluator{listFuncByNamespace: listFuncByNamespace}
return serviceEvaluator return serviceEvaluator
} }
@ -55,14 +55,14 @@ type serviceEvaluator struct {
} }
// Constraints verifies that all required resources are present on the item // Constraints verifies that all required resources are present on the item
func (p *serviceEvaluator) Constraints(required []api.ResourceName, item runtime.Object) error { func (p *serviceEvaluator) Constraints(required []corev1.ResourceName, item runtime.Object) error {
// this is a no-op for services // this is a no-op for services
return nil return nil
} }
// GroupResource that this evaluator tracks // GroupResource that this evaluator tracks
func (p *serviceEvaluator) GroupResource() schema.GroupResource { func (p *serviceEvaluator) GroupResource() schema.GroupResource {
return v1.SchemeGroupVersion.WithResource("services").GroupResource() return corev1.SchemeGroupVersion.WithResource("services").GroupResource()
} }
// Handles returns true of the evaluator should handle the specified operation. // Handles returns true of the evaluator should handle the specified operation.
@ -73,36 +73,36 @@ func (p *serviceEvaluator) Handles(a admission.Attributes) bool {
} }
// Matches returns true if the evaluator matches the specified quota with the provided input item // Matches returns true if the evaluator matches the specified quota with the provided input item
func (p *serviceEvaluator) Matches(resourceQuota *api.ResourceQuota, item runtime.Object) (bool, error) { func (p *serviceEvaluator) Matches(resourceQuota *corev1.ResourceQuota, item runtime.Object) (bool, error) {
return generic.Matches(resourceQuota, item, p.MatchingResources, generic.MatchesNoScopeFunc) return generic.Matches(resourceQuota, item, p.MatchingResources, generic.MatchesNoScopeFunc)
} }
// MatchingResources takes the input specified list of resources and returns the set of resources it matches. // MatchingResources takes the input specified list of resources and returns the set of resources it matches.
func (p *serviceEvaluator) MatchingResources(input []api.ResourceName) []api.ResourceName { func (p *serviceEvaluator) MatchingResources(input []corev1.ResourceName) []corev1.ResourceName {
return quota.Intersection(input, serviceResources) return quota.Intersection(input, serviceResources)
} }
// MatchingScopes takes the input specified list of scopes and input object. Returns the set of scopes resource matches. // MatchingScopes takes the input specified list of scopes and input object. Returns the set of scopes resource matches.
func (p *serviceEvaluator) MatchingScopes(item runtime.Object, scopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { func (p *serviceEvaluator) MatchingScopes(item runtime.Object, scopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) {
return []api.ScopedResourceSelectorRequirement{}, nil return []corev1.ScopedResourceSelectorRequirement{}, nil
} }
// UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes. // UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes.
// It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope // It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope
func (p *serviceEvaluator) UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSelectorRequirement, matchedQuotaScopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { func (p *serviceEvaluator) UncoveredQuotaScopes(limitedScopes []corev1.ScopedResourceSelectorRequirement, matchedQuotaScopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) {
return []api.ScopedResourceSelectorRequirement{}, nil return []corev1.ScopedResourceSelectorRequirement{}, nil
} }
// convert the input object to an internal service object or error. // convert the input object to an internal service object or error.
func toInternalServiceOrError(obj runtime.Object) (*api.Service, error) { func toExternalServiceOrError(obj runtime.Object) (*corev1.Service, error) {
svc := &api.Service{} svc := &corev1.Service{}
switch t := obj.(type) { switch t := obj.(type) {
case *v1.Service: case *corev1.Service:
if err := k8s_api_v1.Convert_v1_Service_To_core_Service(t, svc, nil); err != nil { svc = t
case *api.Service:
if err := k8s_api_v1.Convert_core_Service_To_v1_Service(t, svc, nil); err != nil {
return nil, err return nil, err
} }
case *api.Service:
svc = t
default: default:
return nil, fmt.Errorf("expect *api.Service or *v1.Service, got %v", t) return nil, fmt.Errorf("expect *api.Service or *v1.Service, got %v", t)
} }
@ -110,28 +110,28 @@ func toInternalServiceOrError(obj runtime.Object) (*api.Service, error) {
} }
// Usage knows how to measure usage associated with services // Usage knows how to measure usage associated with services
func (p *serviceEvaluator) Usage(item runtime.Object) (api.ResourceList, error) { func (p *serviceEvaluator) Usage(item runtime.Object) (corev1.ResourceList, error) {
result := api.ResourceList{} result := corev1.ResourceList{}
svc, err := toInternalServiceOrError(item) svc, err := toExternalServiceOrError(item)
if err != nil { if err != nil {
return result, err return result, err
} }
ports := len(svc.Spec.Ports) ports := len(svc.Spec.Ports)
// default service usage // default service usage
result[serviceObjectCountName] = *(resource.NewQuantity(1, resource.DecimalSI)) result[serviceObjectCountName] = *(resource.NewQuantity(1, resource.DecimalSI))
result[api.ResourceServices] = *(resource.NewQuantity(1, resource.DecimalSI)) result[corev1.ResourceServices] = *(resource.NewQuantity(1, resource.DecimalSI))
result[api.ResourceServicesLoadBalancers] = resource.Quantity{Format: resource.DecimalSI} result[corev1.ResourceServicesLoadBalancers] = resource.Quantity{Format: resource.DecimalSI}
result[api.ResourceServicesNodePorts] = resource.Quantity{Format: resource.DecimalSI} result[corev1.ResourceServicesNodePorts] = resource.Quantity{Format: resource.DecimalSI}
switch svc.Spec.Type { switch svc.Spec.Type {
case api.ServiceTypeNodePort: case corev1.ServiceTypeNodePort:
// node port services need to count node ports // node port services need to count node ports
value := resource.NewQuantity(int64(ports), resource.DecimalSI) value := resource.NewQuantity(int64(ports), resource.DecimalSI)
result[api.ResourceServicesNodePorts] = *value result[corev1.ResourceServicesNodePorts] = *value
case api.ServiceTypeLoadBalancer: case corev1.ServiceTypeLoadBalancer:
// load balancer services need to count node ports and load balancers // load balancer services need to count node ports and load balancers
value := resource.NewQuantity(int64(ports), resource.DecimalSI) value := resource.NewQuantity(int64(ports), resource.DecimalSI)
result[api.ResourceServicesNodePorts] = *value result[corev1.ResourceServicesNodePorts] = *value
result[api.ResourceServicesLoadBalancers] = *(resource.NewQuantity(1, resource.DecimalSI)) result[corev1.ResourceServicesLoadBalancers] = *(resource.NewQuantity(1, resource.DecimalSI))
} }
return result, nil return result, nil
} }
@ -144,12 +144,12 @@ func (p *serviceEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.Us
var _ quota.Evaluator = &serviceEvaluator{} var _ quota.Evaluator = &serviceEvaluator{}
//GetQuotaServiceType returns ServiceType if the service type is eligible to track against a quota, nor return "" //GetQuotaServiceType returns ServiceType if the service type is eligible to track against a quota, nor return ""
func GetQuotaServiceType(service *v1.Service) v1.ServiceType { func GetQuotaServiceType(service *corev1.Service) corev1.ServiceType {
switch service.Spec.Type { switch service.Spec.Type {
case v1.ServiceTypeNodePort: case corev1.ServiceTypeNodePort:
return v1.ServiceTypeNodePort return corev1.ServiceTypeNodePort
case v1.ServiceTypeLoadBalancer: case corev1.ServiceTypeLoadBalancer:
return v1.ServiceTypeLoadBalancer return corev1.ServiceTypeLoadBalancer
} }
return v1.ServiceType("") return corev1.ServiceType("")
} }

View File

@ -19,28 +19,29 @@ package core
import ( import (
"testing" "testing"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
) )
func TestServiceEvaluatorMatchesResources(t *testing.T) { func TestServiceEvaluatorMatchesResources(t *testing.T) {
evaluator := NewServiceEvaluator(nil) evaluator := NewServiceEvaluator(nil)
// we give a lot of resources // we give a lot of resources
input := []api.ResourceName{ input := []corev1.ResourceName{
api.ResourceConfigMaps, corev1.ResourceConfigMaps,
api.ResourceCPU, corev1.ResourceCPU,
api.ResourceServices, corev1.ResourceServices,
api.ResourceServicesNodePorts, corev1.ResourceServicesNodePorts,
api.ResourceServicesLoadBalancers, corev1.ResourceServicesLoadBalancers,
} }
// but we only match these... // but we only match these...
expected := quota.ToSet([]api.ResourceName{ expected := quota.ToSet([]corev1.ResourceName{
api.ResourceServices, corev1.ResourceServices,
api.ResourceServicesNodePorts, corev1.ResourceServicesNodePorts,
api.ResourceServicesLoadBalancers, corev1.ResourceServicesLoadBalancers,
}) })
actual := quota.ToSet(evaluator.MatchingResources(input)) actual := quota.ToSet(evaluator.MatchingResources(input))
if !expected.Equal(actual) { if !expected.Equal(actual) {
@ -52,7 +53,7 @@ func TestServiceEvaluatorUsage(t *testing.T) {
evaluator := NewServiceEvaluator(nil) evaluator := NewServiceEvaluator(nil)
testCases := map[string]struct { testCases := map[string]struct {
service *api.Service service *api.Service
usage api.ResourceList usage corev1.ResourceList
}{ }{
"loadbalancer": { "loadbalancer": {
service: &api.Service{ service: &api.Service{
@ -60,10 +61,10 @@ func TestServiceEvaluatorUsage(t *testing.T) {
Type: api.ServiceTypeLoadBalancer, Type: api.ServiceTypeLoadBalancer,
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceServicesNodePorts: resource.MustParse("0"), corev1.ResourceServicesNodePorts: resource.MustParse("0"),
api.ResourceServicesLoadBalancers: resource.MustParse("1"), corev1.ResourceServicesLoadBalancers: resource.MustParse("1"),
api.ResourceServices: resource.MustParse("1"), corev1.ResourceServices: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
}, },
}, },
@ -78,10 +79,10 @@ func TestServiceEvaluatorUsage(t *testing.T) {
}, },
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceServicesNodePorts: resource.MustParse("1"), corev1.ResourceServicesNodePorts: resource.MustParse("1"),
api.ResourceServicesLoadBalancers: resource.MustParse("1"), corev1.ResourceServicesLoadBalancers: resource.MustParse("1"),
api.ResourceServices: resource.MustParse("1"), corev1.ResourceServices: resource.MustParse("1"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
}, },
}, },
@ -91,10 +92,10 @@ func TestServiceEvaluatorUsage(t *testing.T) {
Type: api.ServiceTypeClusterIP, Type: api.ServiceTypeClusterIP,
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceServices: resource.MustParse("1"), corev1.ResourceServices: resource.MustParse("1"),
api.ResourceServicesNodePorts: resource.MustParse("0"), corev1.ResourceServicesNodePorts: resource.MustParse("0"),
api.ResourceServicesLoadBalancers: resource.MustParse("0"), corev1.ResourceServicesLoadBalancers: resource.MustParse("0"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
}, },
}, },
@ -109,10 +110,10 @@ func TestServiceEvaluatorUsage(t *testing.T) {
}, },
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceServices: resource.MustParse("1"), corev1.ResourceServices: resource.MustParse("1"),
api.ResourceServicesNodePorts: resource.MustParse("1"), corev1.ResourceServicesNodePorts: resource.MustParse("1"),
api.ResourceServicesLoadBalancers: resource.MustParse("0"), corev1.ResourceServicesLoadBalancers: resource.MustParse("0"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
}, },
}, },
@ -130,10 +131,10 @@ func TestServiceEvaluatorUsage(t *testing.T) {
}, },
}, },
}, },
usage: api.ResourceList{ usage: corev1.ResourceList{
api.ResourceServices: resource.MustParse("1"), corev1.ResourceServices: resource.MustParse("1"),
api.ResourceServicesNodePorts: resource.MustParse("2"), corev1.ResourceServicesNodePorts: resource.MustParse("2"),
api.ResourceServicesLoadBalancers: resource.MustParse("0"), corev1.ResourceServicesLoadBalancers: resource.MustParse("0"),
generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"),
}, },
}, },
@ -152,7 +153,7 @@ func TestServiceEvaluatorUsage(t *testing.T) {
func TestServiceConstraintsFunc(t *testing.T) { func TestServiceConstraintsFunc(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
service *api.Service service *api.Service
required []api.ResourceName required []corev1.ResourceName
err string err string
}{ }{
"loadbalancer": { "loadbalancer": {
@ -161,7 +162,7 @@ func TestServiceConstraintsFunc(t *testing.T) {
Type: api.ServiceTypeLoadBalancer, Type: api.ServiceTypeLoadBalancer,
}, },
}, },
required: []api.ResourceName{api.ResourceServicesLoadBalancers}, required: []corev1.ResourceName{corev1.ResourceServicesLoadBalancers},
}, },
"clusterip": { "clusterip": {
service: &api.Service{ service: &api.Service{
@ -169,7 +170,7 @@ func TestServiceConstraintsFunc(t *testing.T) {
Type: api.ServiceTypeClusterIP, Type: api.ServiceTypeClusterIP,
}, },
}, },
required: []api.ResourceName{api.ResourceServicesLoadBalancers, api.ResourceServices}, required: []corev1.ResourceName{corev1.ResourceServicesLoadBalancers, corev1.ResourceServices},
}, },
"nodeports": { "nodeports": {
service: &api.Service{ service: &api.Service{
@ -182,7 +183,7 @@ func TestServiceConstraintsFunc(t *testing.T) {
}, },
}, },
}, },
required: []api.ResourceName{api.ResourceServicesNodePorts}, required: []corev1.ResourceName{corev1.ResourceServicesNodePorts},
}, },
"multi-nodeports": { "multi-nodeports": {
service: &api.Service{ service: &api.Service{
@ -198,7 +199,7 @@ func TestServiceConstraintsFunc(t *testing.T) {
}, },
}, },
}, },
required: []api.ResourceName{api.ResourceServicesNodePorts}, required: []corev1.ResourceName{corev1.ResourceServicesNodePorts},
}, },
} }

View File

@ -12,10 +12,10 @@ go_library(
"evaluator.go", "evaluator.go",
"registry.go", "registry.go",
], ],
importpath = "k8s.io/kubernetes/pkg/quota/generic", importpath = "k8s.io/kubernetes/pkg/quota/v1/generic",
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/quota/v1:go_default_library",
"//pkg/quota:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",

View File

@ -18,7 +18,7 @@ package generic
import ( import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
) )
// implements a basic configuration // implements a basic configuration

View File

@ -19,6 +19,7 @@ package generic
import ( import (
"fmt" "fmt"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -26,8 +27,7 @@ import (
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
api "k8s.io/kubernetes/pkg/apis/core" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota"
) )
// InformerForResourceFunc knows how to provision an informer // InformerForResourceFunc knows how to provision an informer
@ -56,33 +56,33 @@ func ListResourceUsingListerFunc(l quota.ListerForResourceFunc, resource schema.
} }
// ObjectCountQuotaResourceNameFor returns the object count quota name for specified groupResource // ObjectCountQuotaResourceNameFor returns the object count quota name for specified groupResource
func ObjectCountQuotaResourceNameFor(groupResource schema.GroupResource) api.ResourceName { func ObjectCountQuotaResourceNameFor(groupResource schema.GroupResource) corev1.ResourceName {
if len(groupResource.Group) == 0 { if len(groupResource.Group) == 0 {
return api.ResourceName("count/" + groupResource.Resource) return corev1.ResourceName("count/" + groupResource.Resource)
} }
return api.ResourceName("count/" + groupResource.Resource + "." + groupResource.Group) return corev1.ResourceName("count/" + groupResource.Resource + "." + groupResource.Group)
} }
// ListFuncByNamespace knows how to list resources in a namespace // ListFuncByNamespace knows how to list resources in a namespace
type ListFuncByNamespace func(namespace string) ([]runtime.Object, error) type ListFuncByNamespace func(namespace string) ([]runtime.Object, error)
// MatchesScopeFunc knows how to evaluate if an object matches a scope // MatchesScopeFunc knows how to evaluate if an object matches a scope
type MatchesScopeFunc func(scope api.ScopedResourceSelectorRequirement, object runtime.Object) (bool, error) type MatchesScopeFunc func(scope corev1.ScopedResourceSelectorRequirement, object runtime.Object) (bool, error)
// UsageFunc knows how to measure usage associated with an object // UsageFunc knows how to measure usage associated with an object
type UsageFunc func(object runtime.Object) (api.ResourceList, error) type UsageFunc func(object runtime.Object) (corev1.ResourceList, error)
// MatchingResourceNamesFunc is a function that returns the list of resources matched // MatchingResourceNamesFunc is a function that returns the list of resources matched
type MatchingResourceNamesFunc func(input []api.ResourceName) []api.ResourceName type MatchingResourceNamesFunc func(input []corev1.ResourceName) []corev1.ResourceName
// MatchesNoScopeFunc returns false on all match checks // MatchesNoScopeFunc returns false on all match checks
func MatchesNoScopeFunc(scope api.ScopedResourceSelectorRequirement, object runtime.Object) (bool, error) { func MatchesNoScopeFunc(scope corev1.ScopedResourceSelectorRequirement, object runtime.Object) (bool, error) {
return false, nil return false, nil
} }
// Matches returns true if the quota matches the specified item. // Matches returns true if the quota matches the specified item.
func Matches( func Matches(
resourceQuota *api.ResourceQuota, item runtime.Object, resourceQuota *corev1.ResourceQuota, item runtime.Object,
matchFunc MatchingResourceNamesFunc, scopeFunc MatchesScopeFunc) (bool, error) { matchFunc MatchingResourceNamesFunc, scopeFunc MatchesScopeFunc) (bool, error) {
if resourceQuota == nil { if resourceQuota == nil {
return false, fmt.Errorf("expected non-nil quota") return false, fmt.Errorf("expected non-nil quota")
@ -101,12 +101,12 @@ func Matches(
return matchResource && matchScope, nil return matchResource && matchScope, nil
} }
func getScopeSelectorsFromQuota(quota *api.ResourceQuota) []api.ScopedResourceSelectorRequirement { func getScopeSelectorsFromQuota(quota *corev1.ResourceQuota) []corev1.ScopedResourceSelectorRequirement {
selectors := []api.ScopedResourceSelectorRequirement{} selectors := []corev1.ScopedResourceSelectorRequirement{}
for _, scope := range quota.Spec.Scopes { for _, scope := range quota.Spec.Scopes {
selectors = append(selectors, api.ScopedResourceSelectorRequirement{ selectors = append(selectors, corev1.ScopedResourceSelectorRequirement{
ScopeName: scope, ScopeName: scope,
Operator: api.ScopeSelectorOpExists}) Operator: corev1.ScopeSelectorOpExists})
} }
if quota.Spec.ScopeSelector != nil { if quota.Spec.ScopeSelector != nil {
for _, scopeSelector := range quota.Spec.ScopeSelector.MatchExpressions { for _, scopeSelector := range quota.Spec.ScopeSelector.MatchExpressions {
@ -122,7 +122,7 @@ func CalculateUsageStats(options quota.UsageStatsOptions,
scopeFunc MatchesScopeFunc, scopeFunc MatchesScopeFunc,
usageFunc UsageFunc) (quota.UsageStats, error) { usageFunc UsageFunc) (quota.UsageStats, error) {
// default each tracked resource to zero // default each tracked resource to zero
result := quota.UsageStats{Used: api.ResourceList{}} result := quota.UsageStats{Used: corev1.ResourceList{}}
for _, resourceName := range options.Resources { for _, resourceName := range options.Resources {
result.Used[resourceName] = resource.Quantity{Format: resource.DecimalSI} result.Used[resourceName] = resource.Quantity{Format: resource.DecimalSI}
} }
@ -134,7 +134,7 @@ func CalculateUsageStats(options quota.UsageStatsOptions,
// need to verify that the item matches the set of scopes // need to verify that the item matches the set of scopes
matchesScopes := true matchesScopes := true
for _, scope := range options.Scopes { for _, scope := range options.Scopes {
innerMatch, err := scopeFunc(api.ScopedResourceSelectorRequirement{ScopeName: scope}, item) innerMatch, err := scopeFunc(corev1.ScopedResourceSelectorRequirement{ScopeName: scope}, item)
if err != nil { if err != nil {
return result, nil return result, nil
} }
@ -174,11 +174,11 @@ type objectCountEvaluator struct {
// TODO move to dynamic client in future // TODO move to dynamic client in future
listFuncByNamespace ListFuncByNamespace listFuncByNamespace ListFuncByNamespace
// Names associated with this resource in the quota for generic counting. // Names associated with this resource in the quota for generic counting.
resourceNames []api.ResourceName resourceNames []corev1.ResourceName
} }
// Constraints returns an error if the configured resource name is not in the required set. // Constraints returns an error if the configured resource name is not in the required set.
func (o *objectCountEvaluator) Constraints(required []api.ResourceName, item runtime.Object) error { func (o *objectCountEvaluator) Constraints(required []corev1.ResourceName, item runtime.Object) error {
// no-op for object counting // no-op for object counting
return nil return nil
} }
@ -190,30 +190,30 @@ func (o *objectCountEvaluator) Handles(a admission.Attributes) bool {
} }
// Matches returns true if the evaluator matches the specified quota with the provided input item // Matches returns true if the evaluator matches the specified quota with the provided input item
func (o *objectCountEvaluator) Matches(resourceQuota *api.ResourceQuota, item runtime.Object) (bool, error) { func (o *objectCountEvaluator) Matches(resourceQuota *corev1.ResourceQuota, item runtime.Object) (bool, error) {
return Matches(resourceQuota, item, o.MatchingResources, MatchesNoScopeFunc) return Matches(resourceQuota, item, o.MatchingResources, MatchesNoScopeFunc)
} }
// MatchingResources takes the input specified list of resources and returns the set of resources it matches. // MatchingResources takes the input specified list of resources and returns the set of resources it matches.
func (o *objectCountEvaluator) MatchingResources(input []api.ResourceName) []api.ResourceName { func (o *objectCountEvaluator) MatchingResources(input []corev1.ResourceName) []corev1.ResourceName {
return quota.Intersection(input, o.resourceNames) return quota.Intersection(input, o.resourceNames)
} }
// MatchingScopes takes the input specified list of scopes and input object. Returns the set of scopes resource matches. // MatchingScopes takes the input specified list of scopes and input object. Returns the set of scopes resource matches.
func (o *objectCountEvaluator) MatchingScopes(item runtime.Object, scopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { func (o *objectCountEvaluator) MatchingScopes(item runtime.Object, scopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) {
return []api.ScopedResourceSelectorRequirement{}, nil return []corev1.ScopedResourceSelectorRequirement{}, nil
} }
// UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes. // UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes.
// It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope // It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope
func (o *objectCountEvaluator) UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSelectorRequirement, matchedQuotaScopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { func (o *objectCountEvaluator) UncoveredQuotaScopes(limitedScopes []corev1.ScopedResourceSelectorRequirement, matchedQuotaScopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) {
return []api.ScopedResourceSelectorRequirement{}, nil return []corev1.ScopedResourceSelectorRequirement{}, nil
} }
// Usage returns the resource usage for the specified object // Usage returns the resource usage for the specified object
func (o *objectCountEvaluator) Usage(object runtime.Object) (api.ResourceList, error) { func (o *objectCountEvaluator) Usage(object runtime.Object) (corev1.ResourceList, error) {
quantity := resource.NewQuantity(1, resource.DecimalSI) quantity := resource.NewQuantity(1, resource.DecimalSI)
resourceList := api.ResourceList{} resourceList := corev1.ResourceList{}
for _, resourceName := range o.resourceNames { for _, resourceName := range o.resourceNames {
resourceList[resourceName] = *quantity resourceList[resourceName] = *quantity
} }
@ -239,9 +239,9 @@ var _ quota.Evaluator = &objectCountEvaluator{}
// backward compatibility, alias should not be used. // backward compatibility, alias should not be used.
func NewObjectCountEvaluator( func NewObjectCountEvaluator(
groupResource schema.GroupResource, listFuncByNamespace ListFuncByNamespace, groupResource schema.GroupResource, listFuncByNamespace ListFuncByNamespace,
alias api.ResourceName) quota.Evaluator { alias corev1.ResourceName) quota.Evaluator {
resourceNames := []api.ResourceName{ObjectCountQuotaResourceNameFor(groupResource)} resourceNames := []corev1.ResourceName{ObjectCountQuotaResourceNameFor(groupResource)}
if len(alias) > 0 { if len(alias) > 0 {
resourceNames = append(resourceNames, alias) resourceNames = append(resourceNames, alias)
} }

View File

@ -20,7 +20,7 @@ import (
"sync" "sync"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
) )
// implements a basic registry // implements a basic registry

View File

@ -8,11 +8,11 @@ load(
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = ["registry.go"], srcs = ["registry.go"],
importpath = "k8s.io/kubernetes/pkg/quota/install", importpath = "k8s.io/kubernetes/pkg/quota/v1/install",
deps = [ deps = [
"//pkg/quota:go_default_library", "//pkg/quota/v1:go_default_library",
"//pkg/quota/evaluator/core:go_default_library", "//pkg/quota/v1/evaluator/core:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/v1/generic:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
], ],
) )

View File

@ -18,9 +18,9 @@ package install
import ( import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/evaluator/core" core "k8s.io/kubernetes/pkg/quota/v1/evaluator/core"
"k8s.io/kubernetes/pkg/quota/generic" generic "k8s.io/kubernetes/pkg/quota/v1/generic"
) )
// NewQuotaConfigurationForAdmission returns a quota configuration for admission control. // NewQuotaConfigurationForAdmission returns a quota configuration for admission control.

View File

@ -17,11 +17,11 @@ limitations under the License.
package quota package quota
import ( import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
api "k8s.io/kubernetes/pkg/apis/core"
) )
// UsageStatsOptions is an options structs that describes how stats should be calculated // UsageStatsOptions is an options structs that describes how stats should be calculated
@ -29,37 +29,37 @@ type UsageStatsOptions struct {
// Namespace where stats should be calculate // Namespace where stats should be calculate
Namespace string Namespace string
// Scopes that must match counted objects // Scopes that must match counted objects
Scopes []api.ResourceQuotaScope Scopes []corev1.ResourceQuotaScope
// Resources are the set of resources to include in the measurement // Resources are the set of resources to include in the measurement
Resources []api.ResourceName Resources []corev1.ResourceName
ScopeSelector *api.ScopeSelector ScopeSelector *corev1.ScopeSelector
} }
// UsageStats is result of measuring observed resource use in the system // UsageStats is result of measuring observed resource use in the system
type UsageStats struct { type UsageStats struct {
// Used maps resource to quantity used // Used maps resource to quantity used
Used api.ResourceList Used corev1.ResourceList
} }
// Evaluator knows how to evaluate quota usage for a particular group resource // Evaluator knows how to evaluate quota usage for a particular group resource
type Evaluator interface { type Evaluator interface {
// Constraints ensures that each required resource is present on item // Constraints ensures that each required resource is present on item
Constraints(required []api.ResourceName, item runtime.Object) error Constraints(required []corev1.ResourceName, item runtime.Object) error
// GroupResource returns the groupResource that this object knows how to evaluate // GroupResource returns the groupResource that this object knows how to evaluate
GroupResource() schema.GroupResource GroupResource() schema.GroupResource
// Handles determines if quota could be impacted by the specified attribute. // Handles determines if quota could be impacted by the specified attribute.
// If true, admission control must perform quota processing for the operation, otherwise it is safe to ignore quota. // If true, admission control must perform quota processing for the operation, otherwise it is safe to ignore quota.
Handles(operation admission.Attributes) bool Handles(operation admission.Attributes) bool
// Matches returns true if the specified quota matches the input item // Matches returns true if the specified quota matches the input item
Matches(resourceQuota *api.ResourceQuota, item runtime.Object) (bool, error) Matches(resourceQuota *corev1.ResourceQuota, item runtime.Object) (bool, error)
// MatchingScopes takes the input specified list of scopes and input object and returns the set of scopes that matches input object. // MatchingScopes takes the input specified list of scopes and input object and returns the set of scopes that matches input object.
MatchingScopes(item runtime.Object, scopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) MatchingScopes(item runtime.Object, scopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error)
// UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes. It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope // UncoveredQuotaScopes takes the input matched scopes which are limited by configuration and the matched quota scopes. It returns the scopes which are in limited scopes but dont have a corresponding covering quota scope
UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSelectorRequirement, matchedQuotaScopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) UncoveredQuotaScopes(limitedScopes []corev1.ScopedResourceSelectorRequirement, matchedQuotaScopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error)
// MatchingResources takes the input specified list of resources and returns the set of resources evaluator matches. // MatchingResources takes the input specified list of resources and returns the set of resources evaluator matches.
MatchingResources(input []api.ResourceName) []api.ResourceName MatchingResources(input []corev1.ResourceName) []corev1.ResourceName
// Usage returns the resource usage for the specified object // Usage returns the resource usage for the specified object
Usage(item runtime.Object) (api.ResourceList, error) Usage(item runtime.Object) (corev1.ResourceList, error)
// UsageStats calculates latest observed usage stats for all objects // UsageStats calculates latest observed usage stats for all objects
UsageStats(options UsageStatsOptions) (UsageStats, error) UsageStats(options UsageStatsOptions) (UsageStats, error)
} }

View File

@ -19,14 +19,13 @@ package quota
import ( import (
"strings" "strings"
"k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
api "k8s.io/kubernetes/pkg/apis/core"
) )
// Equals returns true if the two lists are equivalent // Equals returns true if the two lists are equivalent
func Equals(a api.ResourceList, b api.ResourceList) bool { func Equals(a corev1.ResourceList, b corev1.ResourceList) bool {
if len(a) != len(b) { if len(a) != len(b) {
return false return false
} }
@ -45,7 +44,7 @@ func Equals(a api.ResourceList, b api.ResourceList) bool {
} }
// V1Equals returns true if the two lists are equivalent // V1Equals returns true if the two lists are equivalent
func V1Equals(a v1.ResourceList, b v1.ResourceList) bool { func V1Equals(a corev1.ResourceList, b corev1.ResourceList) bool {
if len(a) != len(b) { if len(a) != len(b) {
return false return false
} }
@ -65,9 +64,9 @@ func V1Equals(a v1.ResourceList, b v1.ResourceList) bool {
// LessThanOrEqual returns true if a < b for each key in b // LessThanOrEqual returns true if a < b for each key in b
// If false, it returns the keys in a that exceeded b // If false, it returns the keys in a that exceeded b
func LessThanOrEqual(a api.ResourceList, b api.ResourceList) (bool, []api.ResourceName) { func LessThanOrEqual(a corev1.ResourceList, b corev1.ResourceList) (bool, []corev1.ResourceName) {
result := true result := true
resourceNames := []api.ResourceName{} resourceNames := []corev1.ResourceName{}
for key, value := range b { for key, value := range b {
if other, found := a[key]; found { if other, found := a[key]; found {
if other.Cmp(value) > 0 { if other.Cmp(value) > 0 {
@ -80,8 +79,8 @@ func LessThanOrEqual(a api.ResourceList, b api.ResourceList) (bool, []api.Resour
} }
// Max returns the result of Max(a, b) for each named resource // Max returns the result of Max(a, b) for each named resource
func Max(a api.ResourceList, b api.ResourceList) api.ResourceList { func Max(a corev1.ResourceList, b corev1.ResourceList) corev1.ResourceList {
result := api.ResourceList{} result := corev1.ResourceList{}
for key, value := range a { for key, value := range a {
if other, found := b[key]; found { if other, found := b[key]; found {
if value.Cmp(other) <= 0 { if value.Cmp(other) <= 0 {
@ -100,8 +99,8 @@ func Max(a api.ResourceList, b api.ResourceList) api.ResourceList {
} }
// Add returns the result of a + b for each named resource // Add returns the result of a + b for each named resource
func Add(a api.ResourceList, b api.ResourceList) api.ResourceList { func Add(a corev1.ResourceList, b corev1.ResourceList) corev1.ResourceList {
result := api.ResourceList{} result := corev1.ResourceList{}
for key, value := range a { for key, value := range a {
quantity := *value.Copy() quantity := *value.Copy()
if other, found := b[key]; found { if other, found := b[key]; found {
@ -120,10 +119,10 @@ func Add(a api.ResourceList, b api.ResourceList) api.ResourceList {
// SubtractWithNonNegativeResult - subtracts and returns result of a - b but // SubtractWithNonNegativeResult - subtracts and returns result of a - b but
// makes sure we don't return negative values to prevent negative resource usage. // makes sure we don't return negative values to prevent negative resource usage.
func SubtractWithNonNegativeResult(a api.ResourceList, b api.ResourceList) api.ResourceList { func SubtractWithNonNegativeResult(a corev1.ResourceList, b corev1.ResourceList) corev1.ResourceList {
zero := resource.MustParse("0") zero := resource.MustParse("0")
result := api.ResourceList{} result := corev1.ResourceList{}
for key, value := range a { for key, value := range a {
quantity := *value.Copy() quantity := *value.Copy()
if other, found := b[key]; found { if other, found := b[key]; found {
@ -145,8 +144,8 @@ func SubtractWithNonNegativeResult(a api.ResourceList, b api.ResourceList) api.R
} }
// Subtract returns the result of a - b for each named resource // Subtract returns the result of a - b for each named resource
func Subtract(a api.ResourceList, b api.ResourceList) api.ResourceList { func Subtract(a corev1.ResourceList, b corev1.ResourceList) corev1.ResourceList {
result := api.ResourceList{} result := corev1.ResourceList{}
for key, value := range a { for key, value := range a {
quantity := *value.Copy() quantity := *value.Copy()
if other, found := b[key]; found { if other, found := b[key]; found {
@ -165,9 +164,9 @@ func Subtract(a api.ResourceList, b api.ResourceList) api.ResourceList {
} }
// Mask returns a new resource list that only has the values with the specified names // Mask returns a new resource list that only has the values with the specified names
func Mask(resources api.ResourceList, names []api.ResourceName) api.ResourceList { func Mask(resources corev1.ResourceList, names []corev1.ResourceName) corev1.ResourceList {
nameSet := ToSet(names) nameSet := ToSet(names)
result := api.ResourceList{} result := corev1.ResourceList{}
for key, value := range resources { for key, value := range resources {
if nameSet.Has(string(key)) { if nameSet.Has(string(key)) {
result[key] = *value.Copy() result[key] = *value.Copy()
@ -177,8 +176,8 @@ func Mask(resources api.ResourceList, names []api.ResourceName) api.ResourceList
} }
// ResourceNames returns a list of all resource names in the ResourceList // ResourceNames returns a list of all resource names in the ResourceList
func ResourceNames(resources api.ResourceList) []api.ResourceName { func ResourceNames(resources corev1.ResourceList) []corev1.ResourceName {
result := []api.ResourceName{} result := []corev1.ResourceName{}
for resourceName := range resources { for resourceName := range resources {
result = append(result, resourceName) result = append(result, resourceName)
} }
@ -186,12 +185,12 @@ func ResourceNames(resources api.ResourceList) []api.ResourceName {
} }
// Contains returns true if the specified item is in the list of items // Contains returns true if the specified item is in the list of items
func Contains(items []api.ResourceName, item api.ResourceName) bool { func Contains(items []corev1.ResourceName, item corev1.ResourceName) bool {
return ToSet(items).Has(string(item)) return ToSet(items).Has(string(item))
} }
// ContainsPrefix returns true if the specified item has a prefix that contained in given prefix Set // ContainsPrefix returns true if the specified item has a prefix that contained in given prefix Set
func ContainsPrefix(prefixSet []string, item api.ResourceName) bool { func ContainsPrefix(prefixSet []string, item corev1.ResourceName) bool {
for _, prefix := range prefixSet { for _, prefix := range prefixSet {
if strings.HasPrefix(string(item), prefix) { if strings.HasPrefix(string(item), prefix) {
return true return true
@ -201,19 +200,19 @@ func ContainsPrefix(prefixSet []string, item api.ResourceName) bool {
} }
// Intersection returns the intersection of both list of resources // Intersection returns the intersection of both list of resources
func Intersection(a []api.ResourceName, b []api.ResourceName) []api.ResourceName { func Intersection(a []corev1.ResourceName, b []corev1.ResourceName) []corev1.ResourceName {
setA := ToSet(a) setA := ToSet(a)
setB := ToSet(b) setB := ToSet(b)
setC := setA.Intersection(setB) setC := setA.Intersection(setB)
result := []api.ResourceName{} result := []corev1.ResourceName{}
for _, resourceName := range setC.List() { for _, resourceName := range setC.List() {
result = append(result, api.ResourceName(resourceName)) result = append(result, corev1.ResourceName(resourceName))
} }
return result return result
} }
// IsZero returns true if each key maps to the quantity value 0 // IsZero returns true if each key maps to the quantity value 0
func IsZero(a api.ResourceList) bool { func IsZero(a corev1.ResourceList) bool {
zero := resource.MustParse("0") zero := resource.MustParse("0")
for _, v := range a { for _, v := range a {
if v.Cmp(zero) != 0 { if v.Cmp(zero) != 0 {
@ -224,8 +223,8 @@ func IsZero(a api.ResourceList) bool {
} }
// IsNegative returns the set of resource names that have a negative value. // IsNegative returns the set of resource names that have a negative value.
func IsNegative(a api.ResourceList) []api.ResourceName { func IsNegative(a corev1.ResourceList) []corev1.ResourceName {
results := []api.ResourceName{} results := []corev1.ResourceName{}
zero := resource.MustParse("0") zero := resource.MustParse("0")
for k, v := range a { for k, v := range a {
if v.Cmp(zero) < 0 { if v.Cmp(zero) < 0 {
@ -236,7 +235,7 @@ func IsNegative(a api.ResourceList) []api.ResourceName {
} }
// ToSet takes a list of resource names and converts to a string set // ToSet takes a list of resource names and converts to a string set
func ToSet(resourceNames []api.ResourceName) sets.String { func ToSet(resourceNames []corev1.ResourceName) sets.String {
result := sets.NewString() result := sets.NewString()
for _, resourceName := range resourceNames { for _, resourceName := range resourceNames {
result.Insert(string(resourceName)) result.Insert(string(resourceName))
@ -245,12 +244,12 @@ func ToSet(resourceNames []api.ResourceName) sets.String {
} }
// CalculateUsage calculates and returns the requested ResourceList usage // CalculateUsage calculates and returns the requested ResourceList usage
func CalculateUsage(namespaceName string, scopes []api.ResourceQuotaScope, hardLimits api.ResourceList, registry Registry, scopeSelector *api.ScopeSelector) (api.ResourceList, error) { func CalculateUsage(namespaceName string, scopes []corev1.ResourceQuotaScope, hardLimits corev1.ResourceList, registry Registry, scopeSelector *corev1.ScopeSelector) (corev1.ResourceList, error) {
// find the intersection between the hard resources on the quota // find the intersection between the hard resources on the quota
// and the resources this controller can track to know what we can // and the resources this controller can track to know what we can
// look to measure updated usage stats for // look to measure updated usage stats for
hardResources := ResourceNames(hardLimits) hardResources := ResourceNames(hardLimits)
potentialResources := []api.ResourceName{} potentialResources := []corev1.ResourceName{}
evaluators := registry.List() evaluators := registry.List()
for _, evaluator := range evaluators { for _, evaluator := range evaluators {
potentialResources = append(potentialResources, evaluator.MatchingResources(hardResources)...) potentialResources = append(potentialResources, evaluator.MatchingResources(hardResources)...)
@ -259,7 +258,7 @@ func CalculateUsage(namespaceName string, scopes []api.ResourceQuotaScope, hardL
matchedResources := Intersection(hardResources, potentialResources) matchedResources := Intersection(hardResources, potentialResources)
// sum the observed usage from each evaluator // sum the observed usage from each evaluator
newUsage := api.ResourceList{} newUsage := corev1.ResourceList{}
for _, evaluator := range evaluators { for _, evaluator := range evaluators {
// only trigger the evaluator if it matches a resource in the quota, otherwise, skip calculating anything // only trigger the evaluator if it matches a resource in the quota, otherwise, skip calculating anything
intersection := evaluator.MatchingResources(matchedResources) intersection := evaluator.MatchingResources(matchedResources)

View File

@ -0,0 +1,321 @@
/*
Copyright 2016 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 quota
import (
"testing"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)
func TestEquals(t *testing.T) {
testCases := map[string]struct {
a corev1.ResourceList
b corev1.ResourceList
expected bool
}{
"isEqual": {
a: corev1.ResourceList{},
b: corev1.ResourceList{},
expected: true,
},
"isEqualWithKeys": {
a: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
b: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
expected: true,
},
"isNotEqualSameKeys": {
a: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
b: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
expected: false,
},
"isNotEqualDiffKeys": {
a: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
b: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
corev1.ResourcePods: resource.MustParse("1"),
},
expected: false,
},
}
for testName, testCase := range testCases {
if result := Equals(testCase.a, testCase.b); result != testCase.expected {
t.Errorf("%s expected: %v, actual: %v, a=%v, b=%v", testName, testCase.expected, result, testCase.a, testCase.b)
}
}
}
func TestMax(t *testing.T) {
testCases := map[string]struct {
a corev1.ResourceList
b corev1.ResourceList
expected corev1.ResourceList
}{
"noKeys": {
a: corev1.ResourceList{},
b: corev1.ResourceList{},
expected: corev1.ResourceList{},
},
"toEmpty": {
a: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
b: corev1.ResourceList{},
expected: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
},
"matching": {
a: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
b: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("150m")},
expected: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("150m")},
},
"matching(reverse)": {
a: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("150m")},
b: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
expected: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("150m")},
},
"matching-equal": {
a: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
b: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
expected: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
},
}
for testName, testCase := range testCases {
sum := Max(testCase.a, testCase.b)
if result := Equals(testCase.expected, sum); !result {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, sum)
}
}
}
func TestAdd(t *testing.T) {
testCases := map[string]struct {
a corev1.ResourceList
b corev1.ResourceList
expected corev1.ResourceList
}{
"noKeys": {
a: corev1.ResourceList{},
b: corev1.ResourceList{},
expected: corev1.ResourceList{},
},
"toEmpty": {
a: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
b: corev1.ResourceList{},
expected: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
},
"matching": {
a: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
b: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
expected: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("200m")},
},
}
for testName, testCase := range testCases {
sum := Add(testCase.a, testCase.b)
if result := Equals(testCase.expected, sum); !result {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, sum)
}
}
}
func TestSubtract(t *testing.T) {
testCases := map[string]struct {
a corev1.ResourceList
b corev1.ResourceList
expected corev1.ResourceList
}{
"noKeys": {
a: corev1.ResourceList{},
b: corev1.ResourceList{},
expected: corev1.ResourceList{},
},
"value-empty": {
a: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
b: corev1.ResourceList{},
expected: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
},
"empty-value": {
a: corev1.ResourceList{},
b: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
expected: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("-100m")},
},
"value-value": {
a: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("200m")},
b: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
expected: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("100m")},
},
}
for testName, testCase := range testCases {
sub := Subtract(testCase.a, testCase.b)
if result := Equals(testCase.expected, sub); !result {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, sub)
}
}
}
func TestResourceNames(t *testing.T) {
testCases := map[string]struct {
a corev1.ResourceList
expected []corev1.ResourceName
}{
"empty": {
a: corev1.ResourceList{},
expected: []corev1.ResourceName{},
},
"values": {
a: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
expected: []corev1.ResourceName{corev1.ResourceMemory, corev1.ResourceCPU},
},
}
for testName, testCase := range testCases {
actualSet := ToSet(ResourceNames(testCase.a))
expectedSet := ToSet(testCase.expected)
if !actualSet.Equal(expectedSet) {
t.Errorf("%s expected: %v, actual: %v", testName, expectedSet, actualSet)
}
}
}
func TestContains(t *testing.T) {
testCases := map[string]struct {
a []corev1.ResourceName
b corev1.ResourceName
expected bool
}{
"does-not-contain": {
a: []corev1.ResourceName{corev1.ResourceMemory},
b: corev1.ResourceCPU,
expected: false,
},
"does-contain": {
a: []corev1.ResourceName{corev1.ResourceMemory, corev1.ResourceCPU},
b: corev1.ResourceCPU,
expected: true,
},
}
for testName, testCase := range testCases {
if actual := Contains(testCase.a, testCase.b); actual != testCase.expected {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, actual)
}
}
}
func TestContainsPrefix(t *testing.T) {
testCases := map[string]struct {
a []string
b corev1.ResourceName
expected bool
}{
"does-not-contain": {
a: []string{corev1.ResourceHugePagesPrefix},
b: corev1.ResourceCPU,
expected: false,
},
"does-contain": {
a: []string{corev1.ResourceHugePagesPrefix},
b: corev1.ResourceName(corev1.ResourceHugePagesPrefix + "2Mi"),
expected: true,
},
}
for testName, testCase := range testCases {
if actual := ContainsPrefix(testCase.a, testCase.b); actual != testCase.expected {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, actual)
}
}
}
func TestIsZero(t *testing.T) {
testCases := map[string]struct {
a corev1.ResourceList
expected bool
}{
"empty": {
a: corev1.ResourceList{},
expected: true,
},
"zero": {
a: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("0"),
corev1.ResourceMemory: resource.MustParse("0"),
},
expected: true,
},
"non-zero": {
a: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
expected: false,
},
}
for testName, testCase := range testCases {
if result := IsZero(testCase.a); result != testCase.expected {
t.Errorf("%s expected: %v, actual: %v", testName, testCase.expected, result)
}
}
}
func TestIsNegative(t *testing.T) {
testCases := map[string]struct {
a corev1.ResourceList
expected []corev1.ResourceName
}{
"empty": {
a: corev1.ResourceList{},
expected: []corev1.ResourceName{},
},
"some-negative": {
a: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("-10"),
corev1.ResourceMemory: resource.MustParse("0"),
},
expected: []corev1.ResourceName{corev1.ResourceCPU},
},
"all-negative": {
a: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("-200m"),
corev1.ResourceMemory: resource.MustParse("-1Gi"),
},
expected: []corev1.ResourceName{corev1.ResourceCPU, corev1.ResourceMemory},
},
}
for testName, testCase := range testCases {
actual := IsNegative(testCase.a)
actualSet := ToSet(actual)
expectedSet := ToSet(testCase.expected)
if !actualSet.Equal(expectedSet) {
t.Errorf("%s expected: %v, actual: %v", testName, expectedSet, actualSet)
}
}
}

View File

@ -17,19 +17,16 @@ go_library(
], ],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota", importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota",
deps = [ deps = [
"//pkg/apis/core:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/client/listers/core/internalversion:go_default_library",
"//pkg/kubeapiserver/admission:go_default_library", "//pkg/kubeapiserver/admission:go_default_library",
"//pkg/quota:go_default_library", "//pkg/quota/v1:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/v1/generic:go_default_library",
"//pkg/util/reflector/prometheus:go_default_library", "//pkg/util/reflector/prometheus:go_default_library",
"//pkg/util/workqueue/prometheus:go_default_library", "//pkg/util/workqueue/prometheus:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library", "//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/install:go_default_library", "//plugin/pkg/admission/resourcequota/apis/resourcequota/install:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1:go_default_library", "//plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/validation:go_default_library", "//plugin/pkg/admission/resourcequota/apis/resourcequota/validation:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
@ -41,7 +38,11 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd:go_default_library", "//staging/src/k8s.io/apiserver/pkg/storage/etcd:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library", "//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/hashicorp/golang-lru:go_default_library", "//vendor/github.com/hashicorp/golang-lru:go_default_library",
@ -54,17 +55,18 @@ go_test(
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//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/controller:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/v1/generic:go_default_library",
"//pkg/quota/install:go_default_library", "//pkg/quota/v1/install:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library", "//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library", "//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/testing:go_default_library", "//staging/src/k8s.io/client-go/testing:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library", "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/github.com/hashicorp/golang-lru:go_default_library", "//vendor/github.com/hashicorp/golang-lru:go_default_library",

View File

@ -21,13 +21,14 @@ import (
"io" "io"
"time" "time"
corev1 "k8s.io/api/core/v1"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
api "k8s.io/kubernetes/pkg/apis/core" genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/client-go/informers"
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" "k8s.io/client-go/kubernetes"
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/quota" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota" resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
"k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation" "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation"
) )
@ -65,12 +66,13 @@ type QuotaAdmission struct {
} }
var _ admission.ValidationInterface = &QuotaAdmission{} var _ admission.ValidationInterface = &QuotaAdmission{}
var _ = kubeapiserveradmission.WantsInternalKubeClientSet(&QuotaAdmission{}) var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&QuotaAdmission{})
var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&QuotaAdmission{})
var _ = kubeapiserveradmission.WantsQuotaConfiguration(&QuotaAdmission{}) var _ = kubeapiserveradmission.WantsQuotaConfiguration(&QuotaAdmission{})
type liveLookupEntry struct { type liveLookupEntry struct {
expiry time.Time expiry time.Time
items []*api.ResourceQuota items []*corev1.ResourceQuota
} }
// NewResourceQuota configures an admission controller that can enforce quota constraints // NewResourceQuota configures an admission controller that can enforce quota constraints
@ -91,12 +93,12 @@ func NewResourceQuota(config *resourcequotaapi.Configuration, numEvaluators int,
}, nil }, nil
} }
func (a *QuotaAdmission) SetInternalKubeClientSet(client internalclientset.Interface) { func (a *QuotaAdmission) SetExternalKubeClientSet(client kubernetes.Interface) {
a.quotaAccessor.client = client a.quotaAccessor.client = client
} }
func (a *QuotaAdmission) SetInternalKubeInformerFactory(f informers.SharedInformerFactory) { func (a *QuotaAdmission) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
a.quotaAccessor.lister = f.Core().InternalVersion().ResourceQuotas().Lister() a.quotaAccessor.lister = f.Core().V1().ResourceQuotas().Lister()
} }
func (a *QuotaAdmission) SetQuotaConfiguration(c quota.Configuration) { func (a *QuotaAdmission) SetQuotaConfiguration(c quota.Configuration) {

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@ go_library(
], ],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota", importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota",
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",

View File

@ -17,8 +17,8 @@ limitations under the License.
package resourcequota package resourcequota
import ( import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/apis/core"
) )
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@ -68,5 +68,5 @@ type LimitedResource struct {
// "PriorityClassNameIn=cluster-services" // "PriorityClassNameIn=cluster-services"
// +optional // +optional
// MatchScopes []string `json:"matchScopes,omitempty"` // MatchScopes []string `json:"matchScopes,omitempty"`
MatchScopes []core.ScopedResourceSelectorRequirement `json:"matchScopes,omitempty"` MatchScopes []corev1.ScopedResourceSelectorRequirement `json:"matchScopes,omitempty"`
} }

View File

@ -18,7 +18,6 @@ go_library(
], ],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1", importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1",
deps = [ deps = [
"//pkg/apis/core:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library", "//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -26,7 +26,6 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
conversion "k8s.io/apimachinery/pkg/conversion" conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
core "k8s.io/kubernetes/pkg/apis/core"
resourcequota "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota" resourcequota "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
) )
@ -84,7 +83,7 @@ func autoConvert_v1alpha1_LimitedResource_To_resourcequota_LimitedResource(in *L
out.APIGroup = in.APIGroup out.APIGroup = in.APIGroup
out.Resource = in.Resource out.Resource = in.Resource
out.MatchContains = *(*[]string)(unsafe.Pointer(&in.MatchContains)) out.MatchContains = *(*[]string)(unsafe.Pointer(&in.MatchContains))
out.MatchScopes = *(*[]core.ScopedResourceSelectorRequirement)(unsafe.Pointer(&in.MatchScopes)) out.MatchScopes = *(*[]v1.ScopedResourceSelectorRequirement)(unsafe.Pointer(&in.MatchScopes))
return nil return nil
} }

View File

@ -18,7 +18,6 @@ go_library(
], ],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1", importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1",
deps = [ deps = [
"//pkg/apis/core:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library", "//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -26,7 +26,6 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
conversion "k8s.io/apimachinery/pkg/conversion" conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
core "k8s.io/kubernetes/pkg/apis/core"
resourcequota "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota" resourcequota "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
) )
@ -84,7 +83,7 @@ func autoConvert_v1beta1_LimitedResource_To_resourcequota_LimitedResource(in *Li
out.APIGroup = in.APIGroup out.APIGroup = in.APIGroup
out.Resource = in.Resource out.Resource = in.Resource
out.MatchContains = *(*[]string)(unsafe.Pointer(&in.MatchContains)) out.MatchContains = *(*[]string)(unsafe.Pointer(&in.MatchContains))
out.MatchScopes = *(*[]core.ScopedResourceSelectorRequirement)(unsafe.Pointer(&in.MatchScopes)) out.MatchScopes = *(*[]v1.ScopedResourceSelectorRequirement)(unsafe.Pointer(&in.MatchScopes))
return nil return nil
} }

View File

@ -21,8 +21,8 @@ limitations under the License.
package resourcequota package resourcequota
import ( import (
v1 "k8s.io/api/core/v1"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
core "k8s.io/kubernetes/pkg/apis/core"
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@ -67,7 +67,7 @@ func (in *LimitedResource) DeepCopyInto(out *LimitedResource) {
} }
if in.MatchScopes != nil { if in.MatchScopes != nil {
in, out := &in.MatchScopes, &out.MatchScopes in, out := &in.MatchScopes, &out.MatchScopes
*out = make([]core.ScopedResourceSelectorRequirement, len(*in)) *out = make([]v1.ScopedResourceSelectorRequirement, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }

View File

@ -25,6 +25,7 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -34,9 +35,8 @@ import (
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
api "k8s.io/kubernetes/pkg/apis/core" quota "k8s.io/kubernetes/pkg/quota/v1"
"k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota/v1/generic"
"k8s.io/kubernetes/pkg/quota/generic"
_ "k8s.io/kubernetes/pkg/util/reflector/prometheus" // for reflector metric registration _ "k8s.io/kubernetes/pkg/util/reflector/prometheus" // for reflector metric registration
_ "k8s.io/kubernetes/pkg/util/workqueue/prometheus" // for workqueue metric registration _ "k8s.io/kubernetes/pkg/util/workqueue/prometheus" // for workqueue metric registration
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota" resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
@ -52,7 +52,7 @@ type Evaluator interface {
type quotaEvaluator struct { type quotaEvaluator struct {
quotaAccessor QuotaAccessor quotaAccessor QuotaAccessor
// lockAcquisitionFunc acquires any required locks and returns a cleanup method to defer // lockAcquisitionFunc acquires any required locks and returns a cleanup method to defer
lockAcquisitionFunc func([]api.ResourceQuota) func() lockAcquisitionFunc func([]corev1.ResourceQuota) func()
ignoredResources map[schema.GroupResource]struct{} ignoredResources map[schema.GroupResource]struct{}
@ -111,7 +111,7 @@ func newAdmissionWaiter(a admission.Attributes) *admissionWaiter {
// NewQuotaEvaluator configures an admission controller that can enforce quota constraints // 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 // 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 // are persisted by the server this admission controller is intercepting
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 { func NewQuotaEvaluator(quotaAccessor QuotaAccessor, ignoredResources map[schema.GroupResource]struct{}, quotaRegistry quota.Registry, lockAcquisitionFunc func([]corev1.ResourceQuota) func(), config *resourcequotaapi.Configuration, workers int, stopCh <-chan struct{}) Evaluator {
// if we get a nil config, just create an empty default. // if we get a nil config, just create an empty default.
if config == nil { if config == nil {
config = &resourcequotaapi.Configuration{} config = &resourcequotaapi.Configuration{}
@ -214,7 +214,7 @@ func (e *quotaEvaluator) checkAttributes(ns string, admissionAttributes []*admis
// updates failed on conflict errors and we have retries left, re-get the failed quota from our cache for the latest version // updates failed on conflict errors and we have retries left, re-get the failed quota from our cache for the latest version
// and recurse into this method with the subset. It's safe for us to evaluate ONLY the subset, because the other quota // and recurse into this method with the subset. It's safe for us to evaluate ONLY the subset, because the other quota
// documents for these waiters have already been evaluated. Step 1, will mark all the ones that should already have succeeded. // documents for these waiters have already been evaluated. Step 1, will mark all the ones that should already have succeeded.
func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttributes []*admissionWaiter, remainingRetries int) { func (e *quotaEvaluator) checkQuotas(quotas []corev1.ResourceQuota, admissionAttributes []*admissionWaiter, remainingRetries int) {
// yet another copy to compare against originals to see if we actually have deltas // yet another copy to compare against originals to see if we actually have deltas
originalQuotas, err := copyQuotas(quotas) originalQuotas, err := copyQuotas(quotas)
if err != nil { if err != nil {
@ -264,7 +264,7 @@ func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttrib
// 1. check to see if the quota changed. If not, skip. // 1. check to see if the quota changed. If not, skip.
// 2. if the quota changed and the update passes, be happy // 2. if the quota changed and the update passes, be happy
// 3. if the quota changed and the update fails, add the original to a retry list // 3. if the quota changed and the update fails, add the original to a retry list
var updatedFailedQuotas []api.ResourceQuota var updatedFailedQuotas []corev1.ResourceQuota
var lastErr error var lastErr error
for i := range quotas { for i := range quotas {
newQuota := quotas[i] newQuota := quotas[i]
@ -318,7 +318,7 @@ func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttrib
// this logic goes through our cache to find the new version of all quotas that failed update. If something has been removed // this logic goes through our cache to find the new version of all quotas that failed update. If something has been removed
// it is skipped on this retry. After all, you removed it. // it is skipped on this retry. After all, you removed it.
quotasToCheck := []api.ResourceQuota{} quotasToCheck := []corev1.ResourceQuota{}
for _, newQuota := range newQuotas { for _, newQuota := range newQuotas {
for _, oldQuota := range updatedFailedQuotas { for _, oldQuota := range updatedFailedQuotas {
if newQuota.Name == oldQuota.Name { if newQuota.Name == oldQuota.Name {
@ -330,8 +330,8 @@ func (e *quotaEvaluator) checkQuotas(quotas []api.ResourceQuota, admissionAttrib
e.checkQuotas(quotasToCheck, admissionAttributes, remainingRetries-1) e.checkQuotas(quotasToCheck, admissionAttributes, remainingRetries-1)
} }
func copyQuotas(in []api.ResourceQuota) ([]api.ResourceQuota, error) { func copyQuotas(in []corev1.ResourceQuota) ([]corev1.ResourceQuota, error) {
out := make([]api.ResourceQuota, 0, len(in)) out := make([]corev1.ResourceQuota, 0, len(in))
for _, quota := range in { for _, quota := range in {
out = append(out, *quota.DeepCopy()) out = append(out, *quota.DeepCopy())
} }
@ -355,8 +355,8 @@ func filterLimitedResourcesByGroupResource(input []resourcequotaapi.LimitedResou
// limitedByDefault determines from the specified usage and limitedResources the set of resources names // limitedByDefault determines from the specified usage and limitedResources the set of resources names
// that must be present in a covering quota. It returns empty set if it was unable to determine if // that must be present in a covering quota. It returns empty set if it was unable to determine if
// a resource was not limited by default. // a resource was not limited by default.
func limitedByDefault(usage api.ResourceList, limitedResources []resourcequotaapi.LimitedResource) []api.ResourceName { func limitedByDefault(usage corev1.ResourceList, limitedResources []resourcequotaapi.LimitedResource) []corev1.ResourceName {
result := []api.ResourceName{} result := []corev1.ResourceName{}
for _, limitedResource := range limitedResources { for _, limitedResource := range limitedResources {
for k, v := range usage { for k, v := range usage {
// if a resource is consumed, we need to check if it matches on the limited resource list. // if a resource is consumed, we need to check if it matches on the limited resource list.
@ -374,13 +374,13 @@ func limitedByDefault(usage api.ResourceList, limitedResources []resourcequotaap
return result return result
} }
func getMatchedLimitedScopes(evaluator quota.Evaluator, inputObject runtime.Object, limitedResources []resourcequotaapi.LimitedResource) ([]api.ScopedResourceSelectorRequirement, error) { func getMatchedLimitedScopes(evaluator quota.Evaluator, inputObject runtime.Object, limitedResources []resourcequotaapi.LimitedResource) ([]corev1.ScopedResourceSelectorRequirement, error) {
scopes := []api.ScopedResourceSelectorRequirement{} scopes := []corev1.ScopedResourceSelectorRequirement{}
for _, limitedResource := range limitedResources { for _, limitedResource := range limitedResources {
matched, err := evaluator.MatchingScopes(inputObject, limitedResource.MatchScopes) matched, err := evaluator.MatchingScopes(inputObject, limitedResource.MatchScopes)
if err != nil { if err != nil {
glog.Errorf("Error while matching limited Scopes: %v", err) glog.Errorf("Error while matching limited Scopes: %v", err)
return []api.ScopedResourceSelectorRequirement{}, err return []corev1.ScopedResourceSelectorRequirement{}, err
} }
for _, scope := range matched { for _, scope := range matched {
scopes = append(scopes, scope) scopes = append(scopes, scope)
@ -391,7 +391,7 @@ func getMatchedLimitedScopes(evaluator quota.Evaluator, inputObject runtime.Obje
// checkRequest verifies that the request does not exceed any quota constraint. it returns a copy of quotas not yet persisted // checkRequest verifies that the request does not exceed any quota constraint. it returns a copy of quotas not yet persisted
// that capture what the usage would be if the request succeeded. It return an error if there is insufficient quota to satisfy the request // that capture what the usage would be if the request succeeded. It return an error if there is insufficient quota to satisfy the request
func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.Attributes) ([]api.ResourceQuota, error) { func (e *quotaEvaluator) checkRequest(quotas []corev1.ResourceQuota, a admission.Attributes) ([]corev1.ResourceQuota, error) {
evaluator := e.registry.Get(a.GetResource().GroupResource()) evaluator := e.registry.Get(a.GetResource().GroupResource())
if evaluator == nil { if evaluator == nil {
return quotas, nil return quotas, nil
@ -400,8 +400,8 @@ func (e *quotaEvaluator) checkRequest(quotas []api.ResourceQuota, a admission.At
} }
// CheckRequest is a static version of quotaEvaluator.checkRequest, possible to be called from outside. // CheckRequest is a static version of quotaEvaluator.checkRequest, possible to be called from outside.
func CheckRequest(quotas []api.ResourceQuota, a admission.Attributes, evaluator quota.Evaluator, func CheckRequest(quotas []corev1.ResourceQuota, a admission.Attributes, evaluator quota.Evaluator,
limited []resourcequotaapi.LimitedResource) ([]api.ResourceQuota, error) { limited []resourcequotaapi.LimitedResource) ([]corev1.ResourceQuota, error) {
if !evaluator.Handles(a) { if !evaluator.Handles(a) {
return quotas, nil return quotas, nil
} }
@ -416,7 +416,7 @@ func CheckRequest(quotas []api.ResourceQuota, a admission.Attributes, evaluator
} }
// determine the set of resource names that must exist in a covering quota // determine the set of resource names that must exist in a covering quota
limitedResourceNames := []api.ResourceName{} limitedResourceNames := []corev1.ResourceName{}
limitedResources := filterLimitedResourcesByGroupResource(limited, a.GetResource().GroupResource()) limitedResources := filterLimitedResourcesByGroupResource(limited, a.GetResource().GroupResource())
if len(limitedResources) > 0 { if len(limitedResources) > 0 {
deltaUsage, err := evaluator.Usage(inputObject) deltaUsage, err := evaluator.Usage(inputObject)
@ -436,7 +436,7 @@ func CheckRequest(quotas []api.ResourceQuota, a admission.Attributes, evaluator
// this is needed to know if we have satisfied any constraints where consumption // this is needed to know if we have satisfied any constraints where consumption
// was limited by default. // was limited by default.
restrictedResourcesSet := sets.String{} restrictedResourcesSet := sets.String{}
restrictedScopes := []api.ScopedResourceSelectorRequirement{} restrictedScopes := []corev1.ScopedResourceSelectorRequirement{}
for i := range quotas { for i := range quotas {
resourceQuota := quotas[i] resourceQuota := quotas[i]
scopeSelectors := getScopeSelectorsFromQuota(resourceQuota) scopeSelectors := getScopeSelectorsFromQuota(resourceQuota)
@ -571,12 +571,12 @@ func CheckRequest(quotas []api.ResourceQuota, a admission.Attributes, evaluator
return outQuotas, nil return outQuotas, nil
} }
func getScopeSelectorsFromQuota(quota api.ResourceQuota) []api.ScopedResourceSelectorRequirement { func getScopeSelectorsFromQuota(quota corev1.ResourceQuota) []corev1.ScopedResourceSelectorRequirement {
selectors := []api.ScopedResourceSelectorRequirement{} selectors := []corev1.ScopedResourceSelectorRequirement{}
for _, scope := range quota.Spec.Scopes { for _, scope := range quota.Spec.Scopes {
selectors = append(selectors, api.ScopedResourceSelectorRequirement{ selectors = append(selectors, corev1.ScopedResourceSelectorRequirement{
ScopeName: scope, ScopeName: scope,
Operator: api.ScopeSelectorOpExists}) Operator: corev1.ScopeSelectorOpExists})
} }
if quota.Spec.ScopeSelector != nil { if quota.Spec.ScopeSelector != nil {
for _, scopeSelector := range quota.Spec.ScopeSelector.MatchExpressions { for _, scopeSelector := range quota.Spec.ScopeSelector.MatchExpressions {
@ -680,7 +680,7 @@ func (e *quotaEvaluator) getWork() (string, []*admissionWaiter, bool) {
// prettyPrint formats a resource list for usage in errors // prettyPrint formats a resource list for usage in errors
// it outputs resources sorted in increasing order // it outputs resources sorted in increasing order
func prettyPrint(item api.ResourceList) string { func prettyPrint(item corev1.ResourceList) string {
parts := []string{} parts := []string{}
keys := []string{} keys := []string{}
for key := range item { for key := range item {
@ -688,14 +688,14 @@ func prettyPrint(item api.ResourceList) string {
} }
sort.Strings(keys) sort.Strings(keys)
for _, key := range keys { for _, key := range keys {
value := item[api.ResourceName(key)] value := item[corev1.ResourceName(key)]
constraint := key + "=" + value.String() constraint := key + "=" + value.String()
parts = append(parts, constraint) parts = append(parts, constraint)
} }
return strings.Join(parts, ",") return strings.Join(parts, ",")
} }
func prettyPrintResourceNames(a []api.ResourceName) string { func prettyPrintResourceNames(a []corev1.ResourceName) string {
values := []string{} values := []string{}
for _, value := range a { for _, value := range a {
values = append(values, string(value)) values = append(values, string(value))
@ -705,7 +705,7 @@ func prettyPrintResourceNames(a []api.ResourceName) string {
} }
// hasUsageStats returns true if for each hard constraint there is a value for its current usage // hasUsageStats returns true if for each hard constraint there is a value for its current usage
func hasUsageStats(resourceQuota *api.ResourceQuota) bool { func hasUsageStats(resourceQuota *corev1.ResourceQuota) bool {
for resourceName := range resourceQuota.Status.Hard { for resourceName := range resourceQuota.Status.Hard {
if _, found := resourceQuota.Status.Used[resourceName]; !found { if _, found := resourceQuota.Status.Used[resourceName]; !found {
return false return false

View File

@ -20,14 +20,14 @@ import (
"fmt" "fmt"
"time" "time"
lru "github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apiserver/pkg/storage/etcd" "k8s.io/apiserver/pkg/storage/etcd"
api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/client-go/kubernetes"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" corev1listers "k8s.io/client-go/listers/core/v1"
corelisters "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
) )
// QuotaAccessor abstracts the get/set logic from the rest of the Evaluator. This could be a test stub, a straight passthrough, // QuotaAccessor abstracts the get/set logic from the rest of the Evaluator. This could be a test stub, a straight passthrough,
@ -35,17 +35,17 @@ import (
type QuotaAccessor interface { type QuotaAccessor interface {
// UpdateQuotaStatus is called to persist final status. This method should write to persistent storage. // UpdateQuotaStatus is called to persist final status. This method should write to persistent storage.
// An error indicates that write didn't complete successfully. // An error indicates that write didn't complete successfully.
UpdateQuotaStatus(newQuota *api.ResourceQuota) error UpdateQuotaStatus(newQuota *corev1.ResourceQuota) error
// GetQuotas gets all possible quotas for a given namespace // GetQuotas gets all possible quotas for a given namespace
GetQuotas(namespace string) ([]api.ResourceQuota, error) GetQuotas(namespace string) ([]corev1.ResourceQuota, error)
} }
type quotaAccessor struct { type quotaAccessor struct {
client clientset.Interface client kubernetes.Interface
// lister can list/get quota objects from a shared informer's cache // lister can list/get quota objects from a shared informer's cache
lister corelisters.ResourceQuotaLister lister corev1listers.ResourceQuotaLister
// liveLookups holds the last few live lookups we've done to help ammortize cost on repeated lookup failures. // liveLookups holds the last few live lookups we've done to help ammortize cost on repeated lookup failures.
// This lets 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.
@ -77,8 +77,8 @@ func newQuotaAccessor() (*quotaAccessor, error) {
}, nil }, nil
} }
func (e *quotaAccessor) UpdateQuotaStatus(newQuota *api.ResourceQuota) error { func (e *quotaAccessor) UpdateQuotaStatus(newQuota *corev1.ResourceQuota) error {
updatedQuota, err := e.client.Core().ResourceQuotas(newQuota.Namespace).UpdateStatus(newQuota) updatedQuota, err := e.client.CoreV1().ResourceQuotas(newQuota.Namespace).UpdateStatus(newQuota)
if err != nil { if err != nil {
return err return err
} }
@ -93,13 +93,13 @@ var etcdVersioner = etcd.APIObjectVersioner{}
// checkCache compares the passed quota against the value in the look-aside cache and returns the newer // checkCache compares the passed quota against the value in the look-aside cache and returns the newer
// if the cache is out of date, it deletes the stale entry. This only works because of etcd resourceVersions // if the cache is out of date, it deletes the stale entry. This only works because of etcd resourceVersions
// being monotonically increasing integers // being monotonically increasing integers
func (e *quotaAccessor) checkCache(quota *api.ResourceQuota) *api.ResourceQuota { func (e *quotaAccessor) checkCache(quota *corev1.ResourceQuota) *corev1.ResourceQuota {
key := quota.Namespace + "/" + quota.Name key := quota.Namespace + "/" + quota.Name
uncastCachedQuota, ok := e.updatedQuotas.Get(key) uncastCachedQuota, ok := e.updatedQuotas.Get(key)
if !ok { if !ok {
return quota return quota
} }
cachedQuota := uncastCachedQuota.(*api.ResourceQuota) cachedQuota := uncastCachedQuota.(*corev1.ResourceQuota)
if etcdVersioner.CompareResourceVersion(quota, cachedQuota) >= 0 { if etcdVersioner.CompareResourceVersion(quota, cachedQuota) >= 0 {
e.updatedQuotas.Remove(key) e.updatedQuotas.Remove(key)
@ -108,7 +108,7 @@ func (e *quotaAccessor) checkCache(quota *api.ResourceQuota) *api.ResourceQuota
return cachedQuota return cachedQuota
} }
func (e *quotaAccessor) GetQuotas(namespace string) ([]api.ResourceQuota, error) { func (e *quotaAccessor) GetQuotas(namespace string) ([]corev1.ResourceQuota, error) {
// determine if there are any quotas in this namespace // determine if there are any quotas in this namespace
// if there are no quotas, we don't need to do anything // if there are no quotas, we don't need to do anything
items, err := e.lister.ResourceQuotas(namespace).List(labels.Everything()) items, err := e.lister.ResourceQuotas(namespace).List(labels.Everything())
@ -142,7 +142,7 @@ func (e *quotaAccessor) GetQuotas(namespace string) ([]api.ResourceQuota, error)
} }
} }
resourceQuotas := []api.ResourceQuota{} resourceQuotas := []corev1.ResourceQuota{}
for i := range items { for i := range items {
quota := items[i] quota := items[i]
quota = e.checkCache(quota) quota = e.checkCache(quota)

View File

@ -22,7 +22,7 @@ go_library(
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/apis/scheduling:go_default_library", "//pkg/apis/scheduling:go_default_library",
"//pkg/kubelet/apis:go_default_library", "//pkg/kubelet/apis:go_default_library",
"//pkg/quota/evaluator/core:go_default_library", "//pkg/quota/v1/evaluator/core:go_default_library",
"//pkg/scheduler/algorithm/priorities/util:go_default_library", "//pkg/scheduler/algorithm/priorities/util:go_default_library",
"//pkg/util/version:go_default_library", "//pkg/util/version:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",

View File

@ -29,7 +29,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/quota/evaluator/core" "k8s.io/kubernetes/pkg/quota/v1/evaluator/core"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
imageutils "k8s.io/kubernetes/test/utils/image" imageutils "k8s.io/kubernetes/test/utils/image"

View File

@ -14,13 +14,11 @@ go_test(
], ],
tags = ["integration"], tags = ["integration"],
deps = [ deps = [
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/controller/replication:go_default_library", "//pkg/controller/replication:go_default_library",
"//pkg/controller/resourcequota:go_default_library", "//pkg/controller/resourcequota:go_default_library",
"//pkg/quota/generic:go_default_library", "//pkg/quota/v1/generic:go_default_library",
"//pkg/quota/install:go_default_library", "//pkg/quota/v1/install:go_default_library",
"//plugin/pkg/admission/resourcequota:go_default_library", "//plugin/pkg/admission/resourcequota:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library", "//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",

View File

@ -37,13 +37,11 @@ import (
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
watchtools "k8s.io/client-go/tools/watch" watchtools "k8s.io/client-go/tools/watch"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
internalinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
replicationcontroller "k8s.io/kubernetes/pkg/controller/replication" replicationcontroller "k8s.io/kubernetes/pkg/controller/replication"
resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota" resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
"k8s.io/kubernetes/pkg/quota/generic" "k8s.io/kubernetes/pkg/quota/v1/generic"
quotainstall "k8s.io/kubernetes/pkg/quota/install" quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
"k8s.io/kubernetes/plugin/pkg/admission/resourcequota" "k8s.io/kubernetes/plugin/pkg/admission/resourcequota"
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota" resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
"k8s.io/kubernetes/test/integration/framework" "k8s.io/kubernetes/test/integration/framework"
@ -65,15 +63,14 @@ func TestQuota(t *testing.T) {
admissionCh := make(chan struct{}) admissionCh := make(chan struct{})
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}) clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
internalClientset := internalclientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
config := &resourcequotaapi.Configuration{} config := &resourcequotaapi.Configuration{}
admission, err := resourcequota.NewResourceQuota(config, 5, admissionCh) admission, err := resourcequota.NewResourceQuota(config, 5, admissionCh)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
admission.SetInternalKubeClientSet(internalClientset) admission.SetExternalKubeClientSet(clientset)
internalInformers := internalinformers.NewSharedInformerFactory(internalClientset, controller.NoResyncPeriodFunc()) internalInformers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
admission.SetInternalKubeInformerFactory(internalInformers) admission.SetExternalKubeInformerFactory(internalInformers)
qca := quotainstall.NewQuotaConfigurationForAdmission() qca := quotainstall.NewQuotaConfigurationForAdmission()
admission.SetQuotaConfiguration(qca) admission.SetQuotaConfiguration(qca)
defer close(admissionCh) defer close(admissionCh)
@ -257,7 +254,6 @@ func TestQuotaLimitedResourceDenial(t *testing.T) {
admissionCh := make(chan struct{}) admissionCh := make(chan struct{})
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}) clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
internalClientset := internalclientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
// stop creation of a pod resource unless there is a quota // stop creation of a pod resource unless there is a quota
config := &resourcequotaapi.Configuration{ config := &resourcequotaapi.Configuration{
@ -273,9 +269,9 @@ func TestQuotaLimitedResourceDenial(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
admission.SetInternalKubeClientSet(internalClientset) admission.SetExternalKubeClientSet(clientset)
internalInformers := internalinformers.NewSharedInformerFactory(internalClientset, controller.NoResyncPeriodFunc()) externalInformers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
admission.SetInternalKubeInformerFactory(internalInformers) admission.SetExternalKubeInformerFactory(externalInformers)
admission.SetQuotaConfiguration(qca) admission.SetQuotaConfiguration(qca)
defer close(admissionCh) defer close(admissionCh)
@ -324,7 +320,7 @@ func TestQuotaLimitedResourceDenial(t *testing.T) {
// Periodically the quota controller to detect new resource types // Periodically the quota controller to detect new resource types
go resourceQuotaController.Sync(discoveryFunc, 30*time.Second, controllerCh) go resourceQuotaController.Sync(discoveryFunc, 30*time.Second, controllerCh)
internalInformers.Start(controllerCh) externalInformers.Start(controllerCh)
informers.Start(controllerCh) informers.Start(controllerCh)
close(informersStarted) close(informersStarted)