mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-08 11:38:15 +00:00
Fix incorrect calculation for ResourceQuota with PriorityClass as its scope
This commit is contained in:
parent
f9340f3cf1
commit
edd032e22b
@ -328,6 +328,10 @@ func podMatchesScopeFunc(selector corev1.ScopedResourceSelectorRequirement, obje
|
|||||||
case corev1.ResourceQuotaScopeNotBestEffort:
|
case corev1.ResourceQuotaScopeNotBestEffort:
|
||||||
return !isBestEffort(pod), nil
|
return !isBestEffort(pod), nil
|
||||||
case corev1.ResourceQuotaScopePriorityClass:
|
case corev1.ResourceQuotaScopePriorityClass:
|
||||||
|
if len(selector.Operator) == 0 && selector.Values == nil {
|
||||||
|
// this is just checking for existence of a priorityClass on the pod
|
||||||
|
return len(pod.Spec.PriorityClassName) != 0, nil
|
||||||
|
}
|
||||||
return podMatchesSelector(pod, selector)
|
return podMatchesSelector(pod, selector)
|
||||||
case corev1.ResourceQuotaScopeCrossNamespacePodAffinity:
|
case corev1.ResourceQuotaScopeCrossNamespacePodAffinity:
|
||||||
return usesCrossNamespacePodAffinity(pod), nil
|
return usesCrossNamespacePodAffinity(pod), nil
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -24,10 +25,12 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
quota "k8s.io/apiserver/pkg/quota/v1"
|
quota "k8s.io/apiserver/pkg/quota/v1"
|
||||||
"k8s.io/apiserver/pkg/quota/v1/generic"
|
"k8s.io/apiserver/pkg/quota/v1/generic"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
@ -540,6 +543,146 @@ func TestPodEvaluatorUsage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPodEvaluatorUsageStats(t *testing.T) {
|
||||||
|
cpu1 := api.ResourceList{api.ResourceCPU: resource.MustParse("1")}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
objs []runtime.Object
|
||||||
|
quotaScopes []corev1.ResourceQuotaScope
|
||||||
|
quotaScopeSelector *corev1.ScopeSelector
|
||||||
|
want corev1.ResourceList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil case",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all pods in running state",
|
||||||
|
objs: []runtime.Object{
|
||||||
|
makePod("p1", "", cpu1, api.PodRunning),
|
||||||
|
makePod("p2", "", cpu1, api.PodRunning),
|
||||||
|
},
|
||||||
|
want: corev1.ResourceList{
|
||||||
|
corev1.ResourcePods: resource.MustParse("2"),
|
||||||
|
corev1.ResourceName("count/pods"): resource.MustParse("2"),
|
||||||
|
corev1.ResourceCPU: resource.MustParse("2"),
|
||||||
|
corev1.ResourceRequestsCPU: resource.MustParse("2"),
|
||||||
|
corev1.ResourceLimitsCPU: resource.MustParse("2"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one pods in terminal state",
|
||||||
|
objs: []runtime.Object{
|
||||||
|
makePod("p1", "", cpu1, api.PodRunning),
|
||||||
|
makePod("p2", "", cpu1, api.PodSucceeded),
|
||||||
|
},
|
||||||
|
want: corev1.ResourceList{
|
||||||
|
corev1.ResourcePods: resource.MustParse("1"),
|
||||||
|
corev1.ResourceName("count/pods"): resource.MustParse("2"),
|
||||||
|
corev1.ResourceCPU: resource.MustParse("1"),
|
||||||
|
corev1.ResourceRequestsCPU: resource.MustParse("1"),
|
||||||
|
corev1.ResourceLimitsCPU: resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "partial pods matching quotaScopeSelector",
|
||||||
|
objs: []runtime.Object{
|
||||||
|
makePod("p1", "high-priority", cpu1, api.PodRunning),
|
||||||
|
makePod("p2", "high-priority", cpu1, api.PodSucceeded),
|
||||||
|
makePod("p3", "low-priority", cpu1, api.PodRunning),
|
||||||
|
},
|
||||||
|
quotaScopeSelector: &corev1.ScopeSelector{
|
||||||
|
MatchExpressions: []corev1.ScopedResourceSelectorRequirement{
|
||||||
|
{
|
||||||
|
ScopeName: corev1.ResourceQuotaScopePriorityClass,
|
||||||
|
Operator: corev1.ScopeSelectorOpIn,
|
||||||
|
Values: []string{"high-priority"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: corev1.ResourceList{
|
||||||
|
corev1.ResourcePods: resource.MustParse("1"),
|
||||||
|
corev1.ResourceName("count/pods"): resource.MustParse("2"),
|
||||||
|
corev1.ResourceCPU: resource.MustParse("1"),
|
||||||
|
corev1.ResourceRequestsCPU: resource.MustParse("1"),
|
||||||
|
corev1.ResourceLimitsCPU: resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "partial pods matching quotaScopeSelector - w/ scopeName specified",
|
||||||
|
objs: []runtime.Object{
|
||||||
|
makePod("p1", "high-priority", cpu1, api.PodRunning),
|
||||||
|
makePod("p2", "high-priority", cpu1, api.PodSucceeded),
|
||||||
|
makePod("p3", "low-priority", cpu1, api.PodRunning),
|
||||||
|
},
|
||||||
|
quotaScopes: []corev1.ResourceQuotaScope{
|
||||||
|
corev1.ResourceQuotaScopePriorityClass,
|
||||||
|
},
|
||||||
|
quotaScopeSelector: &corev1.ScopeSelector{
|
||||||
|
MatchExpressions: []corev1.ScopedResourceSelectorRequirement{
|
||||||
|
{
|
||||||
|
ScopeName: corev1.ResourceQuotaScopePriorityClass,
|
||||||
|
Operator: corev1.ScopeSelectorOpIn,
|
||||||
|
Values: []string{"high-priority"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: corev1.ResourceList{
|
||||||
|
corev1.ResourcePods: resource.MustParse("1"),
|
||||||
|
corev1.ResourceName("count/pods"): resource.MustParse("2"),
|
||||||
|
corev1.ResourceCPU: resource.MustParse("1"),
|
||||||
|
corev1.ResourceRequestsCPU: resource.MustParse("1"),
|
||||||
|
corev1.ResourceLimitsCPU: resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "partial pods matching quotaScopeSelector - w/ multiple scopeNames specified",
|
||||||
|
objs: []runtime.Object{
|
||||||
|
makePod("p1", "high-priority", cpu1, api.PodRunning),
|
||||||
|
makePod("p2", "high-priority", cpu1, api.PodSucceeded),
|
||||||
|
makePod("p3", "low-priority", cpu1, api.PodRunning),
|
||||||
|
makePod("p4", "high-priority", nil, api.PodFailed),
|
||||||
|
},
|
||||||
|
quotaScopes: []corev1.ResourceQuotaScope{
|
||||||
|
corev1.ResourceQuotaScopePriorityClass,
|
||||||
|
corev1.ResourceQuotaScopeBestEffort,
|
||||||
|
},
|
||||||
|
quotaScopeSelector: &corev1.ScopeSelector{
|
||||||
|
MatchExpressions: []corev1.ScopedResourceSelectorRequirement{
|
||||||
|
{
|
||||||
|
ScopeName: corev1.ResourceQuotaScopePriorityClass,
|
||||||
|
Operator: corev1.ScopeSelectorOpIn,
|
||||||
|
Values: []string{"high-priority"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: corev1.ResourceList{
|
||||||
|
corev1.ResourceName("count/pods"): resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
gvr := corev1.SchemeGroupVersion.WithResource("pods")
|
||||||
|
listerForPod := map[schema.GroupVersionResource]cache.GenericLister{
|
||||||
|
gvr: newGenericLister(gvr.GroupResource(), tt.objs),
|
||||||
|
}
|
||||||
|
evaluator := NewPodEvaluator(mockListerForResourceFunc(listerForPod), testingclock.NewFakeClock(time.Now()))
|
||||||
|
usageStatsOption := quota.UsageStatsOptions{
|
||||||
|
Scopes: tt.quotaScopes,
|
||||||
|
ScopeSelector: tt.quotaScopeSelector,
|
||||||
|
}
|
||||||
|
actual, err := evaluator.UsageStats(usageStatsOption)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !quota.Equals(tt.want, actual.Used) {
|
||||||
|
t.Errorf("expected: %v, actual: %v", tt.want, actual.Used)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPodEvaluatorMatchingScopes(t *testing.T) {
|
func TestPodEvaluatorMatchingScopes(t *testing.T) {
|
||||||
fakeClock := testingclock.NewFakeClock(time.Now())
|
fakeClock := testingclock.NewFakeClock(time.Now())
|
||||||
evaluator := NewPodEvaluator(nil, fakeClock)
|
evaluator := NewPodEvaluator(nil, fakeClock)
|
||||||
@ -961,3 +1104,43 @@ func TestPodEvaluatorUsageResourceResize(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mockListerForResourceFunc(listerForResource map[schema.GroupVersionResource]cache.GenericLister) quota.ListerForResourceFunc {
|
||||||
|
return func(gvr schema.GroupVersionResource) (cache.GenericLister, error) {
|
||||||
|
lister, found := listerForResource[gvr]
|
||||||
|
if !found {
|
||||||
|
return nil, fmt.Errorf("no lister found for resource %v", gvr)
|
||||||
|
}
|
||||||
|
return lister, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGenericLister(groupResource schema.GroupResource, items []runtime.Object) cache.GenericLister {
|
||||||
|
store := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"namespace": cache.MetaNamespaceIndexFunc})
|
||||||
|
for _, item := range items {
|
||||||
|
store.Add(item)
|
||||||
|
}
|
||||||
|
return cache.NewGenericLister(store, groupResource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePod(name, pcName string, resList api.ResourceList, phase api.PodPhase) *api.Pod {
|
||||||
|
return &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
PriorityClassName: pcName,
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Resources: api.ResourceRequirements{
|
||||||
|
Requests: resList,
|
||||||
|
Limits: resList,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Status: api.PodStatus{
|
||||||
|
Phase: phase,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user