diff --git a/pkg/util/priority/priority.go b/pkg/util/priority/priority.go index c82b642c02..155544e4e4 100644 --- a/pkg/util/priority/priority.go +++ b/pkg/util/priority/priority.go @@ -77,9 +77,10 @@ func getDefaultPriorityClass(ctx context.Context, client client.Client) (*schedu // we pick the one with the lowest priority value. var defaultPC *schedulingv1.PriorityClass for _, pci := range pcs.Items { - if pci.GlobalDefault { - if defaultPC == nil || defaultPC.Value > pci.Value { - defaultPC = &pci + item := pci + if item.GlobalDefault { + if defaultPC == nil || defaultPC.Value > item.Value { + defaultPC = &item } } } diff --git a/pkg/util/priority/priority_test.go b/pkg/util/priority/priority_test.go new file mode 100644 index 0000000000..530efd04a4 --- /dev/null +++ b/pkg/util/priority/priority_test.go @@ -0,0 +1,173 @@ +/* +Copyright 2022 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 priority + +import ( + "context" + "strings" + "testing" + + schedulingv1 "k8s.io/api/scheduling/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + kueue "sigs.k8s.io/kueue/apis/kueue/v1alpha1" + "sigs.k8s.io/kueue/pkg/constants" + utiltesting "sigs.k8s.io/kueue/pkg/util/testing" +) + +func TestPriority(t *testing.T) { + tests := []struct { + desc string + workload *kueue.Workload + expected int32 + }{ + { + desc: "priority is specified", + workload: utiltesting.MakeWorkload("name", "ns").Priority(100).Obj(), + expected: 100, + }, + { + desc: "priority is empty", + workload: &kueue.Workload{ + Spec: kueue.WorkloadSpec{}, + }, + expected: constants.DefaultPriority, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + got := Priority(tt.workload) + if got != tt.expected { + t.Errorf("Priority does not match: got: %d, expected: %d", got, tt.expected) + } + }) + } +} + +func TestGetPriorityFromPriorityClass(t *testing.T) { + scheme := runtime.NewScheme() + if err := schedulingv1.AddToScheme(scheme); err != nil { + t.Fatalf("Failed adding scheduling scheme: %v", err) + } + + tests := []struct { + desc string + priorityClassList *schedulingv1.PriorityClassList + priorityClassName string + expectedPriorityClassName string + expectedPriorityClassValue int32 + expectedErr string + }{ + { + desc: "priorityClass is specified and it exists", + priorityClassList: &schedulingv1.PriorityClassList{ + Items: []schedulingv1.PriorityClass{ + { + ObjectMeta: v1.ObjectMeta{Name: "test"}, + Value: 50, + }, + }, + }, + priorityClassName: "test", + expectedPriorityClassName: "test", + expectedPriorityClassValue: 50, + }, + { + desc: "priorityClass is specified and it does not exist", + priorityClassList: &schedulingv1.PriorityClassList{ + Items: []schedulingv1.PriorityClass{}, + }, + priorityClassName: "test", + expectedErr: `priorityclasses.scheduling.k8s.io "test" not found`, + }, + { + desc: "priorityClass is unspecified and one global default exists", + priorityClassList: &schedulingv1.PriorityClassList{ + Items: []schedulingv1.PriorityClass{ + { + ObjectMeta: v1.ObjectMeta{Name: "globalDefault"}, + GlobalDefault: true, + Value: 40, + }, + }, + }, + expectedPriorityClassName: "globalDefault", + expectedPriorityClassValue: 40, + }, + { + desc: "priorityClass is unspecified and multiple global defaults exist", + priorityClassList: &schedulingv1.PriorityClassList{ + Items: []schedulingv1.PriorityClass{ + { + ObjectMeta: v1.ObjectMeta{Name: "globalDefault1"}, + GlobalDefault: true, + Value: 90, + }, + { + ObjectMeta: v1.ObjectMeta{Name: "globalDefault2"}, + GlobalDefault: true, + Value: 20, + }, + { + ObjectMeta: v1.ObjectMeta{Name: "globalDefault3"}, + GlobalDefault: true, + Value: 50, + }, + }, + }, + expectedPriorityClassName: "globalDefault2", + expectedPriorityClassValue: 20, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.desc, func(t *testing.T) { + t.Parallel() + + builder := fake.NewClientBuilder().WithScheme(scheme).WithLists(tt.priorityClassList) + client := builder.Build() + + name, value, err := GetPriorityFromPriorityClass(context.Background(), client, tt.priorityClassName) + if tt.expectedErr != "" { + if err == nil { + t.Fatalf("expected error but error is nil") + } + + if !strings.Contains(err.Error(), tt.expectedErr) { + t.Fatalf("unexpecter error message: got: %s, expected: %s", err.Error(), tt.expectedErr) + } + return + } + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if name != tt.expectedPriorityClassName { + t.Errorf("unexpected name: got: %s, expected: %s", name, tt.expectedPriorityClassName) + } + + if value != tt.expectedPriorityClassValue { + t.Errorf("unexpected value: got: %d, expected: %d", value, tt.expectedPriorityClassValue) + } + }) + } +} diff --git a/pkg/util/testing/wrappers.go b/pkg/util/testing/wrappers.go index ccb291182b..e2bf267319 100644 --- a/pkg/util/testing/wrappers.go +++ b/pkg/util/testing/wrappers.go @@ -191,6 +191,11 @@ func (w *WorkloadWrapper) PriorityClass(priorityClassName string) *WorkloadWrapp return w } +func (w *WorkloadWrapper) Priority(priority int32) *WorkloadWrapper { + w.Spec.Priority = pointer.Int32(priority) + return w +} + // AdmissionWrapper wraps an Admission type AdmissionWrapper struct{ kueue.Admission }