diff --git a/pkg/quota/BUILD b/pkg/quota/BUILD index 1fb06434993..8659f8169d5 100644 --- a/pkg/quota/BUILD +++ b/pkg/quota/BUILD @@ -1,40 +1,5 @@ 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( name = "package-srcs", srcs = glob(["**"]), @@ -46,9 +11,7 @@ filegroup( name = "all-srcs", srcs = [ ":package-srcs", - "//pkg/quota/evaluator/core:all-srcs", - "//pkg/quota/generic:all-srcs", - "//pkg/quota/install:all-srcs", + "//pkg/quota/v1:all-srcs", ], tags = ["automanaged"], ) diff --git a/pkg/quota/resources_test.go b/pkg/quota/resources_test.go deleted file mode 100644 index 2df5dbd74b0..00000000000 --- a/pkg/quota/resources_test.go +++ /dev/null @@ -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) - } - } -} diff --git a/pkg/quota/v1/BUILD b/pkg/quota/v1/BUILD new file mode 100644 index 00000000000..7b3cb195b78 --- /dev/null +++ b/pkg/quota/v1/BUILD @@ -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"], +) diff --git a/pkg/quota/OWNERS b/pkg/quota/v1/OWNERS similarity index 100% rename from pkg/quota/OWNERS rename to pkg/quota/v1/OWNERS diff --git a/pkg/quota/evaluator/OWNERS b/pkg/quota/v1/evaluator/OWNERS similarity index 100% rename from pkg/quota/evaluator/OWNERS rename to pkg/quota/v1/evaluator/OWNERS diff --git a/pkg/quota/evaluator/core/BUILD b/pkg/quota/v1/evaluator/core/BUILD similarity index 84% rename from pkg/quota/evaluator/core/BUILD rename to pkg/quota/v1/evaluator/core/BUILD index 13f06d3cb9c..800a34d7525 100644 --- a/pkg/quota/evaluator/core/BUILD +++ b/pkg/quota/v1/evaluator/core/BUILD @@ -15,16 +15,16 @@ go_library( "registry.go", "services.go", ], - importpath = "k8s.io/kubernetes/pkg/quota/evaluator/core", + importpath = "k8s.io/kubernetes/pkg/quota/v1/evaluator/core", deps = [ "//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/helper:go_default_library", + "//pkg/apis/core/v1/helper/qos:go_default_library", "//pkg/features:go_default_library", "//pkg/kubeapiserver/admission/util:go_default_library", - "//pkg/quota:go_default_library", - "//pkg/quota/generic:go_default_library", + "//pkg/quota/v1:go_default_library", + "//pkg/quota/v1/generic: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/labels:go_default_library", @@ -50,9 +50,10 @@ go_test( embed = [":go_default_library"], deps = [ "//pkg/apis/core:go_default_library", - "//pkg/quota:go_default_library", - "//pkg/quota/generic:go_default_library", + "//pkg/quota/v1:go_default_library", + "//pkg/quota/v1/generic: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/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", diff --git a/pkg/quota/evaluator/core/doc.go b/pkg/quota/v1/evaluator/core/doc.go similarity index 89% rename from pkg/quota/evaluator/core/doc.go rename to pkg/quota/v1/evaluator/core/doc.go index 3c9d632cbfe..a8649344199 100644 --- a/pkg/quota/evaluator/core/doc.go +++ b/pkg/quota/v1/evaluator/core/doc.go @@ -15,4 +15,4 @@ limitations under the License. */ // 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" diff --git a/pkg/quota/evaluator/core/persistent_volume_claims.go b/pkg/quota/v1/evaluator/core/persistent_volume_claims.go similarity index 70% rename from pkg/quota/evaluator/core/persistent_volume_claims.go rename to pkg/quota/v1/evaluator/core/persistent_volume_claims.go index 62051e45147..ea8ecad52df 100644 --- a/pkg/quota/evaluator/core/persistent_volume_claims.go +++ b/pkg/quota/v1/evaluator/core/persistent_volume_claims.go @@ -20,7 +20,7 @@ import ( "fmt" "strings" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -30,22 +30,22 @@ import ( "k8s.io/apiserver/pkg/features" utilfeature "k8s.io/apiserver/pkg/util/feature" 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.io/kubernetes/pkg/apis/core/v1/helper" k8sfeatures "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/kubeapiserver/admission/util" - "k8s.io/kubernetes/pkg/quota" - "k8s.io/kubernetes/pkg/quota/generic" + quota "k8s.io/kubernetes/pkg/quota/v1" + "k8s.io/kubernetes/pkg/quota/v1/generic" ) // 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. // for each resource in this list, it may be refined dynamically based on storage class. -var pvcResources = []api.ResourceName{ - api.ResourcePersistentVolumeClaims, - api.ResourceRequestsStorage, +var pvcResources = []corev1.ResourceName{ + corev1.ResourcePersistentVolumeClaims, + corev1.ResourceRequestsStorage, } // 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 const storageClassSuffix string = ".storageclass.storage.k8s.io/" +/* TODO: prune? // ResourceByStorageClass returns a quota resource name by storage class. -func ResourceByStorageClass(storageClass string, resourceName api.ResourceName) api.ResourceName { - return api.ResourceName(string(storageClass + storageClassSuffix + string(resourceName))) +func ResourceByStorageClass(storageClass string, resourceName corev1.ResourceName) corev1.ResourceName { + return corev1.ResourceName(string(storageClass + storageClassSuffix + string(resourceName))) } +*/ // V1ResourceByStorageClass returns a quota resource name by storage class. -func V1ResourceByStorageClass(storageClass string, resourceName v1.ResourceName) v1.ResourceName { - return v1.ResourceName(string(storageClass + storageClassSuffix + string(resourceName))) +func V1ResourceByStorageClass(storageClass string, resourceName corev1.ResourceName) corev1.ResourceName { + return corev1.ResourceName(string(storageClass + storageClassSuffix + string(resourceName))) } // NewPersistentVolumeClaimEvaluator returns an evaluator that can evaluate persistent volume claims 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} return pvcEvaluator } @@ -80,14 +82,14 @@ type pvcEvaluator struct { } // 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 return nil } // GroupResource that this evaluator tracks 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. @@ -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 -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) } // 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) { - return []api.ScopedResourceSelectorRequirement{}, nil +func (p *pvcEvaluator) MatchingScopes(item runtime.Object, scopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) { + return []corev1.ScopedResourceSelectorRequirement{}, nil } // 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 -func (p *pvcEvaluator) UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSelectorRequirement, matchedQuotaScopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { - return []api.ScopedResourceSelectorRequirement{}, nil +func (p *pvcEvaluator) UncoveredQuotaScopes(limitedScopes []corev1.ScopedResourceSelectorRequirement, matchedQuotaScopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) { + return []corev1.ScopedResourceSelectorRequirement{}, nil } // 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 { - result := []api.ResourceName{} +func (p *pvcEvaluator) MatchingResources(items []corev1.ResourceName) []corev1.ResourceName { + result := []corev1.ResourceName{} for _, item := range items { // match object count quota fields - if quota.Contains([]api.ResourceName{pvcObjectCountName}, item) { + if quota.Contains([]corev1.ResourceName{pvcObjectCountName}, item) { result = append(result, item) continue } @@ -161,15 +163,15 @@ func (p *pvcEvaluator) MatchingResources(items []api.ResourceName) []api.Resourc } // Usage knows how to measure usage associated with item. -func (p *pvcEvaluator) Usage(item runtime.Object) (api.ResourceList, error) { - result := api.ResourceList{} - pvc, err := toInternalPersistentVolumeClaimOrError(item) +func (p *pvcEvaluator) Usage(item runtime.Object) (corev1.ResourceList, error) { + result := corev1.ResourceList{} + pvc, err := toExternalPersistentVolumeClaimOrError(item) if err != nil { return result, err } // 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)) if utilfeature.DefaultFeatureGate.Enabled(features.Initializers) { if !initialization.IsInitialized(pvc.Initializers) { @@ -179,16 +181,16 @@ func (p *pvcEvaluator) Usage(item runtime.Object) (api.ResourceList, error) { } storageClassRef := helper.GetPersistentVolumeClaimClass(pvc) 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)) } // charge for storage - if request, found := pvc.Spec.Resources.Requests[api.ResourceStorage]; found { - result[api.ResourceRequestsStorage] = request + if request, found := pvc.Spec.Resources.Requests[corev1.ResourceStorage]; found { + result[corev1.ResourceRequestsStorage] = request // charge usage to the storage class (if present) if len(storageClassRef) > 0 { - storageClassStorage := api.ResourceName(storageClassRef + storageClassSuffix + string(api.ResourceRequestsStorage)) + storageClassStorage := corev1.ResourceName(storageClassRef + storageClassSuffix + string(corev1.ResourceRequestsStorage)) result[storageClassStorage] = request } } @@ -203,15 +205,15 @@ func (p *pvcEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.UsageS // ensure we implement required interface var _ quota.Evaluator = &pvcEvaluator{} -func toInternalPersistentVolumeClaimOrError(obj runtime.Object) (*api.PersistentVolumeClaim, error) { - pvc := &api.PersistentVolumeClaim{} +func toExternalPersistentVolumeClaimOrError(obj runtime.Object) (*corev1.PersistentVolumeClaim, error) { + pvc := &corev1.PersistentVolumeClaim{} switch t := obj.(type) { - case *v1.PersistentVolumeClaim: - if err := k8s_api_v1.Convert_v1_PersistentVolumeClaim_To_core_PersistentVolumeClaim(t, pvc, nil); err != nil { + case *corev1.PersistentVolumeClaim: + pvc = t + case *api.PersistentVolumeClaim: + if err := k8s_api_v1.Convert_core_PersistentVolumeClaim_To_v1_PersistentVolumeClaim(t, pvc, nil); err != nil { return nil, err } - case *api.PersistentVolumeClaim: - pvc = t default: return nil, fmt.Errorf("expect *api.PersistentVolumeClaim or *v1.PersistentVolumeClaim, got %v", t) } diff --git a/pkg/quota/evaluator/core/persistent_volume_claims_test.go b/pkg/quota/v1/evaluator/core/persistent_volume_claims_test.go similarity index 81% rename from pkg/quota/evaluator/core/persistent_volume_claims_test.go rename to pkg/quota/v1/evaluator/core/persistent_volume_claims_test.go index e2b1c69d98a..12e0dc89a3b 100644 --- a/pkg/quota/evaluator/core/persistent_volume_claims_test.go +++ b/pkg/quota/v1/evaluator/core/persistent_volume_claims_test.go @@ -19,12 +19,13 @@ package core import ( "testing" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/quota" - "k8s.io/kubernetes/pkg/quota/generic" + quota "k8s.io/kubernetes/pkg/quota/v1" + "k8s.io/kubernetes/pkg/quota/v1/generic" ) func testVolumeClaim(name string, namespace string, spec api.PersistentVolumeClaimSpec) *api.PersistentVolumeClaim { @@ -79,23 +80,23 @@ func TestPersistentVolumeClaimEvaluatorUsage(t *testing.T) { evaluator := NewPersistentVolumeClaimEvaluator(nil) testCases := map[string]struct { pvc *api.PersistentVolumeClaim - usage api.ResourceList + usage corev1.ResourceList }{ "pvc-usage": { pvc: validClaim, - usage: api.ResourceList{ - api.ResourceRequestsStorage: resource.MustParse("10Gi"), - api.ResourcePersistentVolumeClaims: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceRequestsStorage: resource.MustParse("10Gi"), + corev1.ResourcePersistentVolumeClaims: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "persistentvolumeclaims"}): resource.MustParse("1"), }, }, "pvc-usage-by-class": { pvc: validClaimByStorageClass, - usage: api.ResourceList{ - api.ResourceRequestsStorage: resource.MustParse("10Gi"), - api.ResourcePersistentVolumeClaims: resource.MustParse("1"), - ResourceByStorageClass(classGold, api.ResourceRequestsStorage): resource.MustParse("10Gi"), - ResourceByStorageClass(classGold, api.ResourcePersistentVolumeClaims): resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceRequestsStorage: resource.MustParse("10Gi"), + corev1.ResourcePersistentVolumeClaims: resource.MustParse("1"), + V1ResourceByStorageClass(classGold, corev1.ResourceRequestsStorage): resource.MustParse("10Gi"), + V1ResourceByStorageClass(classGold, corev1.ResourcePersistentVolumeClaims): resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "persistentvolumeclaims"}): resource.MustParse("1"), }, }, diff --git a/pkg/quota/evaluator/core/pods.go b/pkg/quota/v1/evaluator/core/pods.go similarity index 64% rename from pkg/quota/evaluator/core/pods.go rename to pkg/quota/v1/evaluator/core/pods.go index 7861c3d0a13..dbf20e5661c 100644 --- a/pkg/quota/evaluator/core/pods.go +++ b/pkg/quota/v1/evaluator/core/pods.go @@ -21,7 +21,7 @@ import ( "strings" "time" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -32,57 +32,57 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apiserver/pkg/admission" 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.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/quota" - "k8s.io/kubernetes/pkg/quota/generic" + quota "k8s.io/kubernetes/pkg/quota/v1" + "k8s.io/kubernetes/pkg/quota/v1/generic" ) // 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. -var podResources = []api.ResourceName{ +var podResources = []corev1.ResourceName{ podObjectCountName, - api.ResourceCPU, - api.ResourceMemory, - api.ResourceEphemeralStorage, - api.ResourceRequestsCPU, - api.ResourceRequestsMemory, - api.ResourceRequestsEphemeralStorage, - api.ResourceLimitsCPU, - api.ResourceLimitsMemory, - api.ResourceLimitsEphemeralStorage, - api.ResourcePods, + corev1.ResourceCPU, + corev1.ResourceMemory, + corev1.ResourceEphemeralStorage, + corev1.ResourceRequestsCPU, + corev1.ResourceRequestsMemory, + corev1.ResourceRequestsEphemeralStorage, + corev1.ResourceLimitsCPU, + corev1.ResourceLimitsMemory, + corev1.ResourceLimitsEphemeralStorage, + corev1.ResourcePods, } // podResourcePrefixes are the set of prefixes for resources (Hugepages, and other // potential extended reources with specific prefix) managed by quota associated with pods. var podResourcePrefixes = []string{ - api.ResourceHugePagesPrefix, - api.ResourceRequestsHugePagesPrefix, + corev1.ResourceHugePagesPrefix, + corev1.ResourceRequestsHugePagesPrefix, } // requestedResourcePrefixes are the set of prefixes for resources // that might be declared in pod's Resources.Requests/Limits var requestedResourcePrefixes = []string{ - api.ResourceHugePagesPrefix, + corev1.ResourceHugePagesPrefix, } // maskResourceWithPrefix mask resource with certain prefix // e.g. hugepages-XXX -> requests.hugepages-XXX -func maskResourceWithPrefix(resource api.ResourceName, prefix string) api.ResourceName { - return api.ResourceName(fmt.Sprintf("%s%s", prefix, string(resource))) +func maskResourceWithPrefix(resource corev1.ResourceName, prefix string) corev1.ResourceName { + return corev1.ResourceName(fmt.Sprintf("%s%s", prefix, string(resource))) } // isExtendedResourceNameForQuota returns true if the extended resource name // 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, // 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, @@ -90,17 +90,17 @@ func isExtendedResourceNameForQuota(name api.ResourceName) bool { // this mistake for other future resources (gpus, ephemeral-storage,etc). // do not add more resources to this list! var validationSet = sets.NewString( - string(api.ResourceCPU), - string(api.ResourceMemory), - string(api.ResourceRequestsCPU), - string(api.ResourceRequestsMemory), - string(api.ResourceLimitsCPU), - string(api.ResourceLimitsMemory), + string(corev1.ResourceCPU), + string(corev1.ResourceMemory), + string(corev1.ResourceRequestsCPU), + string(corev1.ResourceRequestsMemory), + string(corev1.ResourceLimitsCPU), + string(corev1.ResourceLimitsMemory), ) // NewPodEvaluator returns an evaluator that can evaluate pods 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} return podEvaluator } @@ -115,10 +115,10 @@ type podEvaluator struct { // Constraints verifies that all required resources are present on the pod // In addition, it validates that the resources are valid (i.e. requests < limits) -func (p *podEvaluator) Constraints(required []api.ResourceName, item runtime.Object) error { - pod, ok := item.(*api.Pod) - if !ok { - return fmt.Errorf("unexpected input object %v", item) +func (p *podEvaluator) Constraints(required []corev1.ResourceName, item runtime.Object) error { + pod, err := toExternalPodOrError(item) + if err != nil { + return err } // 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 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. @@ -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 -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) } // 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) for _, resource := range input { // 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. -func (p *podEvaluator) MatchingScopes(item runtime.Object, scopeSelectors []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { - matchedScopes := []api.ScopedResourceSelectorRequirement{} +func (p *podEvaluator) MatchingScopes(item runtime.Object, scopeSelectors []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) { + matchedScopes := []corev1.ScopedResourceSelectorRequirement{} for _, selector := range scopeSelectors { match, err := podMatchesScopeFunc(selector, item) 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 { 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. // 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) { - uncoveredScopes := []api.ScopedResourceSelectorRequirement{} +func (p *podEvaluator) UncoveredQuotaScopes(limitedScopes []corev1.ScopedResourceSelectorRequirement, matchedQuotaScopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) { + uncoveredScopes := []corev1.ScopedResourceSelectorRequirement{} for _, selector := range limitedScopes { isCovered := false for _, matchedScopeSelector := range matchedQuotaScopes { @@ -218,7 +218,7 @@ func (p *podEvaluator) UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSe } // 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 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 // 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 limits := container.Resources.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 -func podComputeUsageHelper(requests api.ResourceList, limits api.ResourceList) api.ResourceList { - result := api.ResourceList{} - result[api.ResourcePods] = resource.MustParse("1") - if request, found := requests[api.ResourceCPU]; found { - result[api.ResourceCPU] = request - result[api.ResourceRequestsCPU] = request +func podComputeUsageHelper(requests corev1.ResourceList, limits corev1.ResourceList) corev1.ResourceList { + result := corev1.ResourceList{} + result[corev1.ResourcePods] = resource.MustParse("1") + if request, found := requests[corev1.ResourceCPU]; found { + result[corev1.ResourceCPU] = request + result[corev1.ResourceRequestsCPU] = request } - if limit, found := limits[api.ResourceCPU]; found { - result[api.ResourceLimitsCPU] = limit + if limit, found := limits[corev1.ResourceCPU]; found { + result[corev1.ResourceLimitsCPU] = limit } - if request, found := requests[api.ResourceMemory]; found { - result[api.ResourceMemory] = request - result[api.ResourceRequestsMemory] = request + if request, found := requests[corev1.ResourceMemory]; found { + result[corev1.ResourceMemory] = request + result[corev1.ResourceRequestsMemory] = request } - if limit, found := limits[api.ResourceMemory]; found { - result[api.ResourceLimitsMemory] = limit + if limit, found := limits[corev1.ResourceMemory]; found { + result[corev1.ResourceLimitsMemory] = limit } - if request, found := requests[api.ResourceEphemeralStorage]; found { - result[api.ResourceEphemeralStorage] = request - result[api.ResourceRequestsEphemeralStorage] = request + if request, found := requests[corev1.ResourceEphemeralStorage]; found { + result[corev1.ResourceEphemeralStorage] = request + result[corev1.ResourceRequestsEphemeralStorage] = request } - if limit, found := limits[api.ResourceEphemeralStorage]; found { - result[api.ResourceLimitsEphemeralStorage] = limit + if limit, found := limits[corev1.ResourceEphemeralStorage]; found { + result[corev1.ResourceLimitsEphemeralStorage] = limit } for resource, request := range requests { // for resources with certain prefix, e.g. hugepages if quota.ContainsPrefix(requestedResourcePrefixes, resource) { result[resource] = request - result[maskResourceWithPrefix(resource, api.DefaultResourceRequestsPrefix)] = request + result[maskResourceWithPrefix(resource, corev1.DefaultResourceRequestsPrefix)] = request } // for extended resources if helper.IsExtendedResourceName(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 } -func toInternalPodOrError(obj runtime.Object) (*api.Pod, error) { - pod := &api.Pod{} +func toExternalPodOrError(obj runtime.Object) (*corev1.Pod, error) { + pod := &corev1.Pod{} switch t := obj.(type) { - case *v1.Pod: - if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(t, pod, nil); err != nil { + case *corev1.Pod: + pod = t + case *api.Pod: + if err := k8s_api_v1.Convert_core_Pod_To_v1_Pod(t, pod, nil); err != nil { return nil, err } - case *api.Pod: - pod = t default: 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 -func podMatchesScopeFunc(selector api.ScopedResourceSelectorRequirement, object runtime.Object) (bool, error) { - pod, err := toInternalPodOrError(object) +func podMatchesScopeFunc(selector corev1.ScopedResourceSelectorRequirement, object runtime.Object) (bool, error) { + pod, err := toExternalPodOrError(object) if err != nil { return false, err } switch selector.ScopeName { - case api.ResourceQuotaScopeTerminating: + case corev1.ResourceQuotaScopeTerminating: return isTerminating(pod), nil - case api.ResourceQuotaScopeNotTerminating: + case corev1.ResourceQuotaScopeNotTerminating: return !isTerminating(pod), nil - case api.ResourceQuotaScopeBestEffort: + case corev1.ResourceQuotaScopeBestEffort: return isBestEffort(pod), nil - case api.ResourceQuotaScopeNotBestEffort: + case corev1.ResourceQuotaScopeNotBestEffort: return !isBestEffort(pod), nil - case api.ResourceQuotaScopePriorityClass: + case corev1.ResourceQuotaScopePriorityClass: return podMatchesSelector(pod, selector) } 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. // - pod has a terminal phase (failed or succeeded) // - pod has been marked for deletion and grace period has expired -func PodUsageFunc(obj runtime.Object, clock clock.Clock) (api.ResourceList, error) { - pod, err := toInternalPodOrError(obj) +func PodUsageFunc(obj runtime.Object, clock clock.Clock) (corev1.ResourceList, error) { + pod, err := toExternalPodOrError(obj) if err != nil { - return api.ResourceList{}, err + return corev1.ResourceList{}, err } // always quota the object count (even if the pod is end of life) // object count quotas track all objects that are in storage. // where "pods" tracks all pods that have not reached a terminal state, // count/pods tracks all pods independent of state. - result := api.ResourceList{ + result := corev1.ResourceList{ podObjectCountName: *(resource.NewQuantity(1, resource.DecimalSI)), } // 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. - if !QuotaPod(pod, clock) { + if !QuotaV1Pod(pod, clock) { return result, nil } - requests := api.ResourceList{} - limits := api.ResourceList{} + requests := corev1.ResourceList{} + limits := corev1.ResourceList{} // TODO: ideally, we have pod level requests and limits in the future. for i := range pod.Spec.Containers { 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 } -func isBestEffort(pod *api.Pod) bool { - return qos.GetPodQOS(pod) == api.PodQOSBestEffort +func isBestEffort(pod *corev1.Pod) bool { + 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) { return true } 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) if err != nil { return false, fmt.Errorf("failed to parse and convert selector: %v", err) } var m map[string]string 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)) { return true, nil @@ -390,36 +390,11 @@ func podMatchesSelector(pod *api.Pod, selector api.ScopedResourceSelectorRequire 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 // 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 v1.PodFailed == pod.Status.Phase || v1.PodSucceeded == pod.Status.Phase { + if corev1.PodFailed == pod.Status.Phase || corev1.PodSucceeded == pod.Status.Phase { return false } // if pods are stuck terminating (for example, a node is lost), we do not want diff --git a/pkg/quota/evaluator/core/pods_test.go b/pkg/quota/v1/evaluator/core/pods_test.go similarity index 74% rename from pkg/quota/evaluator/core/pods_test.go rename to pkg/quota/v1/evaluator/core/pods_test.go index 4d0744373fa..8d0b93c9f8f 100644 --- a/pkg/quota/evaluator/core/pods_test.go +++ b/pkg/quota/v1/evaluator/core/pods_test.go @@ -20,20 +20,21 @@ import ( "testing" "time" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/clock" api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/quota" - "k8s.io/kubernetes/pkg/quota/generic" + quota "k8s.io/kubernetes/pkg/quota/v1" + "k8s.io/kubernetes/pkg/quota/v1/generic" "k8s.io/kubernetes/pkg/util/node" ) func TestPodConstraintsFunc(t *testing.T) { testCases := map[string]struct { pod *api.Pod - required []api.ResourceName + required []corev1.ResourceName err string }{ "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`, }, "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`, }, } @@ -90,7 +91,7 @@ func TestPodEvaluatorUsage(t *testing.T) { testCases := map[string]struct { pod *api.Pod - usage api.ResourceList + usage corev1.ResourceList }{ "init container CPU": { pod: &api.Pod{ @@ -103,11 +104,11 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceRequestsCPU: resource.MustParse("1m"), - api.ResourceLimitsCPU: resource.MustParse("2m"), - api.ResourcePods: resource.MustParse("1"), - api.ResourceCPU: resource.MustParse("1m"), + usage: corev1.ResourceList{ + corev1.ResourceRequestsCPU: resource.MustParse("1m"), + corev1.ResourceLimitsCPU: resource.MustParse("2m"), + corev1.ResourcePods: resource.MustParse("1"), + corev1.ResourceCPU: resource.MustParse("1m"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -122,11 +123,11 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceRequestsMemory: resource.MustParse("1m"), - api.ResourceLimitsMemory: resource.MustParse("2m"), - api.ResourcePods: resource.MustParse("1"), - api.ResourceMemory: resource.MustParse("1m"), + usage: corev1.ResourceList{ + corev1.ResourceRequestsMemory: resource.MustParse("1m"), + corev1.ResourceLimitsMemory: resource.MustParse("2m"), + corev1.ResourcePods: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1m"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -141,11 +142,11 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceEphemeralStorage: resource.MustParse("32Mi"), - api.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"), - api.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"), - api.ResourcePods: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceEphemeralStorage: resource.MustParse("32Mi"), + corev1.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"), + corev1.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"), + corev1.ResourcePods: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -159,10 +160,10 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceName(api.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), - api.ResourceName(api.ResourceRequestsHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), - api.ResourcePods: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceName(corev1.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), + corev1.ResourceName(corev1.ResourceRequestsHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), + corev1.ResourcePods: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -177,9 +178,9 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceName("requests.example.com/dongle"): resource.MustParse("3"), - api.ResourcePods: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceName("requests.example.com/dongle"): resource.MustParse("3"), + corev1.ResourcePods: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -194,11 +195,11 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceRequestsCPU: resource.MustParse("1m"), - api.ResourceLimitsCPU: resource.MustParse("2m"), - api.ResourcePods: resource.MustParse("1"), - api.ResourceCPU: resource.MustParse("1m"), + usage: corev1.ResourceList{ + corev1.ResourceRequestsCPU: resource.MustParse("1m"), + corev1.ResourceLimitsCPU: resource.MustParse("2m"), + corev1.ResourcePods: resource.MustParse("1"), + corev1.ResourceCPU: resource.MustParse("1m"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -213,11 +214,11 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceRequestsMemory: resource.MustParse("1m"), - api.ResourceLimitsMemory: resource.MustParse("2m"), - api.ResourcePods: resource.MustParse("1"), - api.ResourceMemory: resource.MustParse("1m"), + usage: corev1.ResourceList{ + corev1.ResourceRequestsMemory: resource.MustParse("1m"), + corev1.ResourceLimitsMemory: resource.MustParse("2m"), + corev1.ResourcePods: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1m"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -232,11 +233,11 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceEphemeralStorage: resource.MustParse("32Mi"), - api.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"), - api.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"), - api.ResourcePods: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceEphemeralStorage: resource.MustParse("32Mi"), + corev1.ResourceRequestsEphemeralStorage: resource.MustParse("32Mi"), + corev1.ResourceLimitsEphemeralStorage: resource.MustParse("64Mi"), + corev1.ResourcePods: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -250,10 +251,10 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceName(api.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), - api.ResourceName(api.ResourceRequestsHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), - api.ResourcePods: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceName(api.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), + corev1.ResourceName(api.ResourceRequestsHugePagesPrefix + "2Mi"): resource.MustParse("100Mi"), + corev1.ResourcePods: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -268,9 +269,9 @@ func TestPodEvaluatorUsage(t *testing.T) { }}, }, }, - usage: api.ResourceList{ - api.ResourceName("requests.example.com/dongle"): resource.MustParse("3"), - api.ResourcePods: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceName("requests.example.com/dongle"): resource.MustParse("3"), + corev1.ResourcePods: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, @@ -339,15 +340,15 @@ func TestPodEvaluatorUsage(t *testing.T) { }, }, }, - usage: api.ResourceList{ - api.ResourceRequestsCPU: resource.MustParse("4"), - api.ResourceRequestsMemory: resource.MustParse("100M"), - api.ResourceLimitsCPU: resource.MustParse("8"), - api.ResourceLimitsMemory: resource.MustParse("200M"), - api.ResourcePods: resource.MustParse("1"), - api.ResourceCPU: resource.MustParse("4"), - api.ResourceMemory: resource.MustParse("100M"), - api.ResourceName("requests.example.com/dongle"): resource.MustParse("4"), + usage: corev1.ResourceList{ + corev1.ResourceRequestsCPU: resource.MustParse("4"), + corev1.ResourceRequestsMemory: resource.MustParse("100M"), + corev1.ResourceLimitsCPU: resource.MustParse("8"), + corev1.ResourceLimitsMemory: resource.MustParse("200M"), + corev1.ResourcePods: resource.MustParse("1"), + corev1.ResourceCPU: resource.MustParse("4"), + corev1.ResourceMemory: resource.MustParse("100M"), + corev1.ResourceName("requests.example.com/dongle"): resource.MustParse("4"), 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"), }, }, @@ -406,11 +407,11 @@ func TestPodEvaluatorUsage(t *testing.T) { }, }, }, - usage: api.ResourceList{ - api.ResourceRequestsCPU: resource.MustParse("1"), - api.ResourceLimitsCPU: resource.MustParse("2"), - api.ResourcePods: resource.MustParse("1"), - api.ResourceCPU: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceRequestsCPU: resource.MustParse("1"), + corev1.ResourceLimitsCPU: resource.MustParse("2"), + corev1.ResourcePods: resource.MustParse("1"), + corev1.ResourceCPU: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "pods"}): resource.MustParse("1"), }, }, diff --git a/pkg/quota/evaluator/core/registry.go b/pkg/quota/v1/evaluator/core/registry.go similarity index 70% rename from pkg/quota/evaluator/core/registry.go rename to pkg/quota/v1/evaluator/core/registry.go index ba54143c32e..43a86d318ce 100644 --- a/pkg/quota/evaluator/core/registry.go +++ b/pkg/quota/v1/evaluator/core/registry.go @@ -17,20 +17,19 @@ limitations under the License. package core import ( - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/clock" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/quota" - "k8s.io/kubernetes/pkg/quota/generic" + quota "k8s.io/kubernetes/pkg/quota/v1" + "k8s.io/kubernetes/pkg/quota/v1/generic" ) // legacyObjectCountAliases are what we used to do simple object counting quota with mapped to alias -var legacyObjectCountAliases = map[schema.GroupVersionResource]api.ResourceName{ - v1.SchemeGroupVersion.WithResource("configmaps"): api.ResourceConfigMaps, - v1.SchemeGroupVersion.WithResource("resourcequotas"): api.ResourceQuotas, - v1.SchemeGroupVersion.WithResource("replicationcontrollers"): api.ResourceReplicationControllers, - v1.SchemeGroupVersion.WithResource("secrets"): api.ResourceSecrets, +var legacyObjectCountAliases = map[schema.GroupVersionResource]corev1.ResourceName{ + corev1.SchemeGroupVersion.WithResource("configmaps"): corev1.ResourceConfigMaps, + corev1.SchemeGroupVersion.WithResource("resourcequotas"): corev1.ResourceQuotas, + corev1.SchemeGroupVersion.WithResource("replicationcontrollers"): corev1.ResourceReplicationControllers, + corev1.SchemeGroupVersion.WithResource("secrets"): corev1.ResourceSecrets, } // NewEvaluators returns the list of static evaluators that manage more than counts diff --git a/pkg/quota/evaluator/core/services.go b/pkg/quota/v1/evaluator/core/services.go similarity index 64% rename from pkg/quota/evaluator/core/services.go rename to pkg/quota/v1/evaluator/core/services.go index 006c0e9b113..eaebcc698a2 100644 --- a/pkg/quota/evaluator/core/services.go +++ b/pkg/quota/v1/evaluator/core/services.go @@ -19,31 +19,31 @@ package core import ( "fmt" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" api "k8s.io/kubernetes/pkg/apis/core" k8s_api_v1 "k8s.io/kubernetes/pkg/apis/core/v1" - "k8s.io/kubernetes/pkg/quota" - "k8s.io/kubernetes/pkg/quota/generic" + "k8s.io/kubernetes/pkg/quota/v1" + "k8s.io/kubernetes/pkg/quota/v1/generic" ) // 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. -var serviceResources = []api.ResourceName{ +var serviceResources = []corev1.ResourceName{ serviceObjectCountName, - api.ResourceServices, - api.ResourceServicesNodePorts, - api.ResourceServicesLoadBalancers, + corev1.ResourceServices, + corev1.ResourceServicesNodePorts, + corev1.ResourceServicesLoadBalancers, } // NewServiceEvaluator returns an evaluator that can evaluate services. 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} return serviceEvaluator } @@ -55,14 +55,14 @@ type serviceEvaluator struct { } // 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 return nil } // GroupResource that this evaluator tracks 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. @@ -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 -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) } // 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) } // 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) { - return []api.ScopedResourceSelectorRequirement{}, nil +func (p *serviceEvaluator) MatchingScopes(item runtime.Object, scopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) { + return []corev1.ScopedResourceSelectorRequirement{}, nil } // 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 -func (p *serviceEvaluator) UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSelectorRequirement, matchedQuotaScopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { - return []api.ScopedResourceSelectorRequirement{}, nil +func (p *serviceEvaluator) UncoveredQuotaScopes(limitedScopes []corev1.ScopedResourceSelectorRequirement, matchedQuotaScopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) { + return []corev1.ScopedResourceSelectorRequirement{}, nil } // convert the input object to an internal service object or error. -func toInternalServiceOrError(obj runtime.Object) (*api.Service, error) { - svc := &api.Service{} +func toExternalServiceOrError(obj runtime.Object) (*corev1.Service, error) { + svc := &corev1.Service{} switch t := obj.(type) { - case *v1.Service: - if err := k8s_api_v1.Convert_v1_Service_To_core_Service(t, svc, nil); err != nil { + case *corev1.Service: + svc = t + case *api.Service: + if err := k8s_api_v1.Convert_core_Service_To_v1_Service(t, svc, nil); err != nil { return nil, err } - case *api.Service: - svc = t default: 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 -func (p *serviceEvaluator) Usage(item runtime.Object) (api.ResourceList, error) { - result := api.ResourceList{} - svc, err := toInternalServiceOrError(item) +func (p *serviceEvaluator) Usage(item runtime.Object) (corev1.ResourceList, error) { + result := corev1.ResourceList{} + svc, err := toExternalServiceOrError(item) if err != nil { return result, err } ports := len(svc.Spec.Ports) // default service usage result[serviceObjectCountName] = *(resource.NewQuantity(1, resource.DecimalSI)) - result[api.ResourceServices] = *(resource.NewQuantity(1, resource.DecimalSI)) - result[api.ResourceServicesLoadBalancers] = resource.Quantity{Format: resource.DecimalSI} - result[api.ResourceServicesNodePorts] = resource.Quantity{Format: resource.DecimalSI} + result[corev1.ResourceServices] = *(resource.NewQuantity(1, resource.DecimalSI)) + result[corev1.ResourceServicesLoadBalancers] = resource.Quantity{Format: resource.DecimalSI} + result[corev1.ResourceServicesNodePorts] = resource.Quantity{Format: resource.DecimalSI} switch svc.Spec.Type { - case api.ServiceTypeNodePort: + case corev1.ServiceTypeNodePort: // node port services need to count node ports value := resource.NewQuantity(int64(ports), resource.DecimalSI) - result[api.ResourceServicesNodePorts] = *value - case api.ServiceTypeLoadBalancer: + result[corev1.ResourceServicesNodePorts] = *value + case corev1.ServiceTypeLoadBalancer: // load balancer services need to count node ports and load balancers value := resource.NewQuantity(int64(ports), resource.DecimalSI) - result[api.ResourceServicesNodePorts] = *value - result[api.ResourceServicesLoadBalancers] = *(resource.NewQuantity(1, resource.DecimalSI)) + result[corev1.ResourceServicesNodePorts] = *value + result[corev1.ResourceServicesLoadBalancers] = *(resource.NewQuantity(1, resource.DecimalSI)) } return result, nil } @@ -144,12 +144,12 @@ func (p *serviceEvaluator) UsageStats(options quota.UsageStatsOptions) (quota.Us var _ quota.Evaluator = &serviceEvaluator{} //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 { - case v1.ServiceTypeNodePort: - return v1.ServiceTypeNodePort - case v1.ServiceTypeLoadBalancer: - return v1.ServiceTypeLoadBalancer + case corev1.ServiceTypeNodePort: + return corev1.ServiceTypeNodePort + case corev1.ServiceTypeLoadBalancer: + return corev1.ServiceTypeLoadBalancer } - return v1.ServiceType("") + return corev1.ServiceType("") } diff --git a/pkg/quota/evaluator/core/services_test.go b/pkg/quota/v1/evaluator/core/services_test.go similarity index 67% rename from pkg/quota/evaluator/core/services_test.go rename to pkg/quota/v1/evaluator/core/services_test.go index 601397ce193..b9f227a0c82 100644 --- a/pkg/quota/evaluator/core/services_test.go +++ b/pkg/quota/v1/evaluator/core/services_test.go @@ -19,28 +19,29 @@ package core import ( "testing" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime/schema" api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/quota" - "k8s.io/kubernetes/pkg/quota/generic" + quota "k8s.io/kubernetes/pkg/quota/v1" + "k8s.io/kubernetes/pkg/quota/v1/generic" ) func TestServiceEvaluatorMatchesResources(t *testing.T) { evaluator := NewServiceEvaluator(nil) // we give a lot of resources - input := []api.ResourceName{ - api.ResourceConfigMaps, - api.ResourceCPU, - api.ResourceServices, - api.ResourceServicesNodePorts, - api.ResourceServicesLoadBalancers, + input := []corev1.ResourceName{ + corev1.ResourceConfigMaps, + corev1.ResourceCPU, + corev1.ResourceServices, + corev1.ResourceServicesNodePorts, + corev1.ResourceServicesLoadBalancers, } // but we only match these... - expected := quota.ToSet([]api.ResourceName{ - api.ResourceServices, - api.ResourceServicesNodePorts, - api.ResourceServicesLoadBalancers, + expected := quota.ToSet([]corev1.ResourceName{ + corev1.ResourceServices, + corev1.ResourceServicesNodePorts, + corev1.ResourceServicesLoadBalancers, }) actual := quota.ToSet(evaluator.MatchingResources(input)) if !expected.Equal(actual) { @@ -52,7 +53,7 @@ func TestServiceEvaluatorUsage(t *testing.T) { evaluator := NewServiceEvaluator(nil) testCases := map[string]struct { service *api.Service - usage api.ResourceList + usage corev1.ResourceList }{ "loadbalancer": { service: &api.Service{ @@ -60,10 +61,10 @@ func TestServiceEvaluatorUsage(t *testing.T) { Type: api.ServiceTypeLoadBalancer, }, }, - usage: api.ResourceList{ - api.ResourceServicesNodePorts: resource.MustParse("0"), - api.ResourceServicesLoadBalancers: resource.MustParse("1"), - api.ResourceServices: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceServicesNodePorts: resource.MustParse("0"), + corev1.ResourceServicesLoadBalancers: resource.MustParse("1"), + corev1.ResourceServices: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), }, }, @@ -78,10 +79,10 @@ func TestServiceEvaluatorUsage(t *testing.T) { }, }, }, - usage: api.ResourceList{ - api.ResourceServicesNodePorts: resource.MustParse("1"), - api.ResourceServicesLoadBalancers: resource.MustParse("1"), - api.ResourceServices: resource.MustParse("1"), + usage: corev1.ResourceList{ + corev1.ResourceServicesNodePorts: resource.MustParse("1"), + corev1.ResourceServicesLoadBalancers: resource.MustParse("1"), + corev1.ResourceServices: resource.MustParse("1"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), }, }, @@ -91,10 +92,10 @@ func TestServiceEvaluatorUsage(t *testing.T) { Type: api.ServiceTypeClusterIP, }, }, - usage: api.ResourceList{ - api.ResourceServices: resource.MustParse("1"), - api.ResourceServicesNodePorts: resource.MustParse("0"), - api.ResourceServicesLoadBalancers: resource.MustParse("0"), + usage: corev1.ResourceList{ + corev1.ResourceServices: resource.MustParse("1"), + corev1.ResourceServicesNodePorts: resource.MustParse("0"), + corev1.ResourceServicesLoadBalancers: resource.MustParse("0"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), }, }, @@ -109,10 +110,10 @@ func TestServiceEvaluatorUsage(t *testing.T) { }, }, }, - usage: api.ResourceList{ - api.ResourceServices: resource.MustParse("1"), - api.ResourceServicesNodePorts: resource.MustParse("1"), - api.ResourceServicesLoadBalancers: resource.MustParse("0"), + usage: corev1.ResourceList{ + corev1.ResourceServices: resource.MustParse("1"), + corev1.ResourceServicesNodePorts: resource.MustParse("1"), + corev1.ResourceServicesLoadBalancers: resource.MustParse("0"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), }, }, @@ -130,10 +131,10 @@ func TestServiceEvaluatorUsage(t *testing.T) { }, }, }, - usage: api.ResourceList{ - api.ResourceServices: resource.MustParse("1"), - api.ResourceServicesNodePorts: resource.MustParse("2"), - api.ResourceServicesLoadBalancers: resource.MustParse("0"), + usage: corev1.ResourceList{ + corev1.ResourceServices: resource.MustParse("1"), + corev1.ResourceServicesNodePorts: resource.MustParse("2"), + corev1.ResourceServicesLoadBalancers: resource.MustParse("0"), generic.ObjectCountQuotaResourceNameFor(schema.GroupResource{Resource: "services"}): resource.MustParse("1"), }, }, @@ -152,7 +153,7 @@ func TestServiceEvaluatorUsage(t *testing.T) { func TestServiceConstraintsFunc(t *testing.T) { testCases := map[string]struct { service *api.Service - required []api.ResourceName + required []corev1.ResourceName err string }{ "loadbalancer": { @@ -161,7 +162,7 @@ func TestServiceConstraintsFunc(t *testing.T) { Type: api.ServiceTypeLoadBalancer, }, }, - required: []api.ResourceName{api.ResourceServicesLoadBalancers}, + required: []corev1.ResourceName{corev1.ResourceServicesLoadBalancers}, }, "clusterip": { service: &api.Service{ @@ -169,7 +170,7 @@ func TestServiceConstraintsFunc(t *testing.T) { Type: api.ServiceTypeClusterIP, }, }, - required: []api.ResourceName{api.ResourceServicesLoadBalancers, api.ResourceServices}, + required: []corev1.ResourceName{corev1.ResourceServicesLoadBalancers, corev1.ResourceServices}, }, "nodeports": { service: &api.Service{ @@ -182,7 +183,7 @@ func TestServiceConstraintsFunc(t *testing.T) { }, }, }, - required: []api.ResourceName{api.ResourceServicesNodePorts}, + required: []corev1.ResourceName{corev1.ResourceServicesNodePorts}, }, "multi-nodeports": { service: &api.Service{ @@ -198,7 +199,7 @@ func TestServiceConstraintsFunc(t *testing.T) { }, }, }, - required: []api.ResourceName{api.ResourceServicesNodePorts}, + required: []corev1.ResourceName{corev1.ResourceServicesNodePorts}, }, } diff --git a/pkg/quota/generic/BUILD b/pkg/quota/v1/generic/BUILD similarity index 86% rename from pkg/quota/generic/BUILD rename to pkg/quota/v1/generic/BUILD index d7c63ceb69d..c99ade4e3f5 100644 --- a/pkg/quota/generic/BUILD +++ b/pkg/quota/v1/generic/BUILD @@ -12,10 +12,10 @@ go_library( "evaluator.go", "registry.go", ], - importpath = "k8s.io/kubernetes/pkg/quota/generic", + importpath = "k8s.io/kubernetes/pkg/quota/v1/generic", deps = [ - "//pkg/apis/core:go_default_library", - "//pkg/quota:go_default_library", + "//pkg/quota/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/labels:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", diff --git a/pkg/quota/generic/OWNERS b/pkg/quota/v1/generic/OWNERS similarity index 100% rename from pkg/quota/generic/OWNERS rename to pkg/quota/v1/generic/OWNERS diff --git a/pkg/quota/generic/configuration.go b/pkg/quota/v1/generic/configuration.go similarity index 96% rename from pkg/quota/generic/configuration.go rename to pkg/quota/v1/generic/configuration.go index 59c009e13d3..1a1acc44185 100644 --- a/pkg/quota/generic/configuration.go +++ b/pkg/quota/v1/generic/configuration.go @@ -18,7 +18,7 @@ package generic import ( "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/kubernetes/pkg/quota" + quota "k8s.io/kubernetes/pkg/quota/v1" ) // implements a basic configuration diff --git a/pkg/quota/generic/evaluator.go b/pkg/quota/v1/generic/evaluator.go similarity index 78% rename from pkg/quota/generic/evaluator.go rename to pkg/quota/v1/generic/evaluator.go index 60da7d634bb..4e377175445 100644 --- a/pkg/quota/generic/evaluator.go +++ b/pkg/quota/v1/generic/evaluator.go @@ -19,6 +19,7 @@ package generic import ( "fmt" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -26,8 +27,7 @@ import ( "k8s.io/apiserver/pkg/admission" "k8s.io/client-go/informers" "k8s.io/client-go/tools/cache" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/quota" + quota "k8s.io/kubernetes/pkg/quota/v1" ) // 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 -func ObjectCountQuotaResourceNameFor(groupResource schema.GroupResource) api.ResourceName { +func ObjectCountQuotaResourceNameFor(groupResource schema.GroupResource) corev1.ResourceName { 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 type ListFuncByNamespace func(namespace string) ([]runtime.Object, error) // 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 -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 -type MatchingResourceNamesFunc func(input []api.ResourceName) []api.ResourceName +type MatchingResourceNamesFunc func(input []corev1.ResourceName) []corev1.ResourceName // 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 } // Matches returns true if the quota matches the specified item. func Matches( - resourceQuota *api.ResourceQuota, item runtime.Object, + resourceQuota *corev1.ResourceQuota, item runtime.Object, matchFunc MatchingResourceNamesFunc, scopeFunc MatchesScopeFunc) (bool, error) { if resourceQuota == nil { return false, fmt.Errorf("expected non-nil quota") @@ -101,12 +101,12 @@ func Matches( return matchResource && matchScope, nil } -func getScopeSelectorsFromQuota(quota *api.ResourceQuota) []api.ScopedResourceSelectorRequirement { - selectors := []api.ScopedResourceSelectorRequirement{} +func getScopeSelectorsFromQuota(quota *corev1.ResourceQuota) []corev1.ScopedResourceSelectorRequirement { + selectors := []corev1.ScopedResourceSelectorRequirement{} for _, scope := range quota.Spec.Scopes { - selectors = append(selectors, api.ScopedResourceSelectorRequirement{ + selectors = append(selectors, corev1.ScopedResourceSelectorRequirement{ ScopeName: scope, - Operator: api.ScopeSelectorOpExists}) + Operator: corev1.ScopeSelectorOpExists}) } if quota.Spec.ScopeSelector != nil { for _, scopeSelector := range quota.Spec.ScopeSelector.MatchExpressions { @@ -122,7 +122,7 @@ func CalculateUsageStats(options quota.UsageStatsOptions, scopeFunc MatchesScopeFunc, usageFunc UsageFunc) (quota.UsageStats, error) { // default each tracked resource to zero - result := quota.UsageStats{Used: api.ResourceList{}} + result := quota.UsageStats{Used: corev1.ResourceList{}} for _, resourceName := range options.Resources { 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 matchesScopes := true for _, scope := range options.Scopes { - innerMatch, err := scopeFunc(api.ScopedResourceSelectorRequirement{ScopeName: scope}, item) + innerMatch, err := scopeFunc(corev1.ScopedResourceSelectorRequirement{ScopeName: scope}, item) if err != nil { return result, nil } @@ -174,11 +174,11 @@ type objectCountEvaluator struct { // TODO move to dynamic client in future listFuncByNamespace ListFuncByNamespace // 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. -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 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 -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) } // 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) } // 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) { - return []api.ScopedResourceSelectorRequirement{}, nil +func (o *objectCountEvaluator) MatchingScopes(item runtime.Object, scopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) { + return []corev1.ScopedResourceSelectorRequirement{}, nil } // 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 -func (o *objectCountEvaluator) UncoveredQuotaScopes(limitedScopes []api.ScopedResourceSelectorRequirement, matchedQuotaScopes []api.ScopedResourceSelectorRequirement) ([]api.ScopedResourceSelectorRequirement, error) { - return []api.ScopedResourceSelectorRequirement{}, nil +func (o *objectCountEvaluator) UncoveredQuotaScopes(limitedScopes []corev1.ScopedResourceSelectorRequirement, matchedQuotaScopes []corev1.ScopedResourceSelectorRequirement) ([]corev1.ScopedResourceSelectorRequirement, error) { + return []corev1.ScopedResourceSelectorRequirement{}, nil } // 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) - resourceList := api.ResourceList{} + resourceList := corev1.ResourceList{} for _, resourceName := range o.resourceNames { resourceList[resourceName] = *quantity } @@ -239,9 +239,9 @@ var _ quota.Evaluator = &objectCountEvaluator{} // backward compatibility, alias should not be used. func NewObjectCountEvaluator( 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 { resourceNames = append(resourceNames, alias) } diff --git a/pkg/quota/generic/registry.go b/pkg/quota/v1/generic/registry.go similarity index 98% rename from pkg/quota/generic/registry.go rename to pkg/quota/v1/generic/registry.go index fdc38e02b1c..10404a3f288 100644 --- a/pkg/quota/generic/registry.go +++ b/pkg/quota/v1/generic/registry.go @@ -20,7 +20,7 @@ import ( "sync" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/kubernetes/pkg/quota" + quota "k8s.io/kubernetes/pkg/quota/v1" ) // implements a basic registry diff --git a/pkg/quota/install/BUILD b/pkg/quota/v1/install/BUILD similarity index 70% rename from pkg/quota/install/BUILD rename to pkg/quota/v1/install/BUILD index 31678067ea6..96425fa422c 100644 --- a/pkg/quota/install/BUILD +++ b/pkg/quota/v1/install/BUILD @@ -8,11 +8,11 @@ load( go_library( name = "go_default_library", srcs = ["registry.go"], - importpath = "k8s.io/kubernetes/pkg/quota/install", + importpath = "k8s.io/kubernetes/pkg/quota/v1/install", deps = [ - "//pkg/quota:go_default_library", - "//pkg/quota/evaluator/core:go_default_library", - "//pkg/quota/generic:go_default_library", + "//pkg/quota/v1:go_default_library", + "//pkg/quota/v1/evaluator/core:go_default_library", + "//pkg/quota/v1/generic:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", ], ) diff --git a/pkg/quota/install/OWNERS b/pkg/quota/v1/install/OWNERS similarity index 100% rename from pkg/quota/install/OWNERS rename to pkg/quota/v1/install/OWNERS diff --git a/pkg/quota/install/registry.go b/pkg/quota/v1/install/registry.go similarity index 91% rename from pkg/quota/install/registry.go rename to pkg/quota/v1/install/registry.go index dd4596d310a..b870368530f 100644 --- a/pkg/quota/install/registry.go +++ b/pkg/quota/v1/install/registry.go @@ -18,9 +18,9 @@ package install import ( "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/kubernetes/pkg/quota" - "k8s.io/kubernetes/pkg/quota/evaluator/core" - "k8s.io/kubernetes/pkg/quota/generic" + quota "k8s.io/kubernetes/pkg/quota/v1" + core "k8s.io/kubernetes/pkg/quota/v1/evaluator/core" + generic "k8s.io/kubernetes/pkg/quota/v1/generic" ) // NewQuotaConfigurationForAdmission returns a quota configuration for admission control. diff --git a/pkg/quota/interfaces.go b/pkg/quota/v1/interfaces.go similarity index 80% rename from pkg/quota/interfaces.go rename to pkg/quota/v1/interfaces.go index e6723b8aef7..d71b6641830 100644 --- a/pkg/quota/interfaces.go +++ b/pkg/quota/v1/interfaces.go @@ -17,11 +17,11 @@ limitations under the License. package quota import ( + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/admission" "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 @@ -29,37 +29,37 @@ type UsageStatsOptions struct { // Namespace where stats should be calculate Namespace string // Scopes that must match counted objects - Scopes []api.ResourceQuotaScope + Scopes []corev1.ResourceQuotaScope // Resources are the set of resources to include in the measurement - Resources []api.ResourceName - ScopeSelector *api.ScopeSelector + Resources []corev1.ResourceName + ScopeSelector *corev1.ScopeSelector } // UsageStats is result of measuring observed resource use in the system type UsageStats struct { // Used maps resource to quantity used - Used api.ResourceList + Used corev1.ResourceList } // Evaluator knows how to evaluate quota usage for a particular group resource type Evaluator interface { // 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() schema.GroupResource // 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. Handles(operation admission.Attributes) bool // 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(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(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(input []api.ResourceName) []api.ResourceName + MatchingResources(input []corev1.ResourceName) []corev1.ResourceName // 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(options UsageStatsOptions) (UsageStats, error) } diff --git a/pkg/quota/resources.go b/pkg/quota/v1/resources.go similarity index 77% rename from pkg/quota/resources.go rename to pkg/quota/v1/resources.go index b261aedef5f..b6aa3210d4b 100644 --- a/pkg/quota/resources.go +++ b/pkg/quota/v1/resources.go @@ -19,14 +19,13 @@ package quota import ( "strings" - "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/sets" - api "k8s.io/kubernetes/pkg/apis/core" ) // 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) { return false } @@ -45,7 +44,7 @@ func Equals(a api.ResourceList, b api.ResourceList) bool { } // 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) { 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 // 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 - resourceNames := []api.ResourceName{} + resourceNames := []corev1.ResourceName{} for key, value := range b { if other, found := a[key]; found { 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 -func Max(a api.ResourceList, b api.ResourceList) api.ResourceList { - result := api.ResourceList{} +func Max(a corev1.ResourceList, b corev1.ResourceList) corev1.ResourceList { + result := corev1.ResourceList{} for key, value := range a { if other, found := b[key]; found { 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 -func Add(a api.ResourceList, b api.ResourceList) api.ResourceList { - result := api.ResourceList{} +func Add(a corev1.ResourceList, b corev1.ResourceList) corev1.ResourceList { + result := corev1.ResourceList{} for key, value := range a { quantity := *value.Copy() 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 // 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") - result := api.ResourceList{} + result := corev1.ResourceList{} for key, value := range a { quantity := *value.Copy() 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 -func Subtract(a api.ResourceList, b api.ResourceList) api.ResourceList { - result := api.ResourceList{} +func Subtract(a corev1.ResourceList, b corev1.ResourceList) corev1.ResourceList { + result := corev1.ResourceList{} for key, value := range a { quantity := *value.Copy() 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 -func Mask(resources api.ResourceList, names []api.ResourceName) api.ResourceList { +func Mask(resources corev1.ResourceList, names []corev1.ResourceName) corev1.ResourceList { nameSet := ToSet(names) - result := api.ResourceList{} + result := corev1.ResourceList{} for key, value := range resources { if nameSet.Has(string(key)) { 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 -func ResourceNames(resources api.ResourceList) []api.ResourceName { - result := []api.ResourceName{} +func ResourceNames(resources corev1.ResourceList) []corev1.ResourceName { + result := []corev1.ResourceName{} for resourceName := range resources { 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 -func Contains(items []api.ResourceName, item api.ResourceName) bool { +func Contains(items []corev1.ResourceName, item corev1.ResourceName) bool { return ToSet(items).Has(string(item)) } // 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 { if strings.HasPrefix(string(item), prefix) { return true @@ -201,19 +200,19 @@ func ContainsPrefix(prefixSet []string, item api.ResourceName) bool { } // 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) setB := ToSet(b) setC := setA.Intersection(setB) - result := []api.ResourceName{} + result := []corev1.ResourceName{} for _, resourceName := range setC.List() { - result = append(result, api.ResourceName(resourceName)) + result = append(result, corev1.ResourceName(resourceName)) } return result } // 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") for _, v := range a { 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. -func IsNegative(a api.ResourceList) []api.ResourceName { - results := []api.ResourceName{} +func IsNegative(a corev1.ResourceList) []corev1.ResourceName { + results := []corev1.ResourceName{} zero := resource.MustParse("0") for k, v := range a { 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 -func ToSet(resourceNames []api.ResourceName) sets.String { +func ToSet(resourceNames []corev1.ResourceName) sets.String { result := sets.NewString() for _, resourceName := range resourceNames { result.Insert(string(resourceName)) @@ -245,12 +244,12 @@ func ToSet(resourceNames []api.ResourceName) sets.String { } // 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 // and the resources this controller can track to know what we can // look to measure updated usage stats for hardResources := ResourceNames(hardLimits) - potentialResources := []api.ResourceName{} + potentialResources := []corev1.ResourceName{} evaluators := registry.List() for _, evaluator := range evaluators { potentialResources = append(potentialResources, evaluator.MatchingResources(hardResources)...) @@ -259,7 +258,7 @@ func CalculateUsage(namespaceName string, scopes []api.ResourceQuotaScope, hardL matchedResources := Intersection(hardResources, potentialResources) // sum the observed usage from each evaluator - newUsage := api.ResourceList{} + newUsage := corev1.ResourceList{} for _, evaluator := range evaluators { // only trigger the evaluator if it matches a resource in the quota, otherwise, skip calculating anything intersection := evaluator.MatchingResources(matchedResources) diff --git a/pkg/quota/v1/resources_test.go b/pkg/quota/v1/resources_test.go new file mode 100644 index 00000000000..61175c706eb --- /dev/null +++ b/pkg/quota/v1/resources_test.go @@ -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) + } + } +}