-
Notifications
You must be signed in to change notification settings - Fork 251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add PriorityClass in Workload api #104
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,7 @@ import ( | |
|
||
kueue "sigs.k8s.io/kueue/api/v1alpha1" | ||
"sigs.k8s.io/kueue/pkg/constants" | ||
utilpriority "sigs.k8s.io/kueue/pkg/util/priority" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: remove the alias. Use just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
"sigs.k8s.io/kueue/pkg/workload" | ||
) | ||
|
||
|
@@ -294,7 +295,7 @@ func (r *JobReconciler) handleJobWithNoWorkload(ctx context.Context, job *batchv | |
} | ||
|
||
// Create the corresponding workload. | ||
wl, err := ConstructWorkloadFor(job, r.scheme) | ||
wl, err := ConstructWorkloadFor(ctx, r.client, job, r.scheme) | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -370,7 +371,8 @@ func (r *JobReconciler) ensureAtMostOneWorkload(ctx context.Context, job *batchv | |
return match, nil | ||
} | ||
|
||
func ConstructWorkloadFor(job *batchv1.Job, scheme *runtime.Scheme) (*kueue.QueuedWorkload, error) { | ||
func ConstructWorkloadFor(ctx context.Context, client client.Client, | ||
job *batchv1.Job, scheme *runtime.Scheme) (*kueue.QueuedWorkload, error) { | ||
w := &kueue.QueuedWorkload{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: job.Name, | ||
|
@@ -386,6 +388,16 @@ func ConstructWorkloadFor(job *batchv1.Job, scheme *runtime.Scheme) (*kueue.Queu | |
QueueName: queueName(job), | ||
}, | ||
} | ||
|
||
// Populate priority from priority class. | ||
priorityClassName, p, err := utilpriority.GetPriorityFromPriorityClass( | ||
ctx, client, job.Spec.Template.Spec.PriorityClassName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
w.Spec.Priority = &p | ||
w.Spec.PriorityClassName = priorityClassName | ||
|
||
if err := ctrl.SetControllerReference(job, w, scheme); err != nil { | ||
return nil, err | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,9 +21,16 @@ import ( | |
"time" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/utils/pointer" | ||
|
||
kueue "sigs.k8s.io/kueue/api/v1alpha1" | ||
) | ||
|
||
const ( | ||
lowPriority = 0 | ||
highPriority = 1000 | ||
) | ||
|
||
func TestFIFOClusterQueue(t *testing.T) { | ||
q := newClusterQueue(&kueue.ClusterQueue{ | ||
Spec: kueue.ClusterQueueSpec{ | ||
|
@@ -83,3 +90,98 @@ func TestFIFOClusterQueue(t *testing.T) { | |
t.Errorf("Queue is not empty, poped workload %q", got.Obj.Name) | ||
} | ||
} | ||
|
||
func TestStrictFIFO(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For a follow up, we should find a way to merge the 2 tests #72 |
||
t1 := time.Now() | ||
t2 := t1.Add(time.Second) | ||
for _, tt := range []struct { | ||
name string | ||
w1 *kueue.QueuedWorkload | ||
w2 *kueue.QueuedWorkload | ||
expected string | ||
}{ | ||
{ | ||
name: "w1.priority is higher than w2.priority", | ||
w1: &kueue.QueuedWorkload{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "w1", | ||
CreationTimestamp: metav1.NewTime(t1), | ||
}, | ||
Spec: kueue.QueuedWorkloadSpec{ | ||
PriorityClassName: "highPriority", | ||
Priority: pointer.Int32(highPriority), | ||
}, | ||
}, | ||
w2: &kueue.QueuedWorkload{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "w2", | ||
CreationTimestamp: metav1.NewTime(t2), | ||
}, | ||
Spec: kueue.QueuedWorkloadSpec{ | ||
PriorityClassName: "lowPriority", | ||
Priority: pointer.Int32(lowPriority), | ||
}, | ||
}, | ||
expected: "w1", | ||
}, | ||
{ | ||
name: "w1.priority equals w2.priority and w1.create time is earlier than w2.create time", | ||
w1: &kueue.QueuedWorkload{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "w1", | ||
CreationTimestamp: metav1.NewTime(t1), | ||
}, | ||
}, | ||
w2: &kueue.QueuedWorkload{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "w2", | ||
CreationTimestamp: metav1.NewTime(t2), | ||
}, | ||
}, | ||
expected: "w1", | ||
}, | ||
{ | ||
name: "p1.priority is lower than p2.priority and w1.create time is earlier than w2.create time", | ||
w1: &kueue.QueuedWorkload{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "w1", | ||
CreationTimestamp: metav1.NewTime(t1), | ||
}, | ||
Spec: kueue.QueuedWorkloadSpec{ | ||
PriorityClassName: "lowPriority", | ||
Priority: pointer.Int32(lowPriority), | ||
}, | ||
}, | ||
w2: &kueue.QueuedWorkload{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "w2", | ||
CreationTimestamp: metav1.NewTime(t2), | ||
}, | ||
Spec: kueue.QueuedWorkloadSpec{ | ||
PriorityClassName: "highPriority", | ||
Priority: pointer.Int32(highPriority), | ||
}, | ||
}, | ||
expected: "w2", | ||
}, | ||
} { | ||
t.Run(tt.name, func(t *testing.T) { | ||
q := newClusterQueue(&kueue.ClusterQueue{ | ||
Spec: kueue.ClusterQueueSpec{ | ||
QueueingStrategy: kueue.StrictFIFO, | ||
}, | ||
}) | ||
|
||
q.PushOrUpdate(tt.w1) | ||
q.PushOrUpdate(tt.w2) | ||
|
||
got := q.Pop() | ||
if got == nil { | ||
t.Fatal("Queue is empty") | ||
} | ||
if got.Obj.Name != tt.expected { | ||
t.Errorf("Poped workload %q want %q", got.Obj.Name, tt.expected) | ||
} | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
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" | ||
|
||
schedulingv1 "k8s.io/api/scheduling/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
kueue "sigs.k8s.io/kueue/api/v1alpha1" | ||
"sigs.k8s.io/kueue/pkg/constants" | ||
) | ||
|
||
// Priority returns priority of the given workload. | ||
func Priority(w *kueue.QueuedWorkload) int32 { | ||
if w.Spec.Priority != nil { | ||
return *w.Spec.Priority | ||
} | ||
// When priority of a running workload is nil, it means it was created at a time | ||
// that there was no global default priority class and the priority class | ||
// name of the pod was empty. So, we resolve to the static default priority. | ||
return constants.DefaultPriority | ||
} | ||
|
||
// GetPriorityFromPriorityClass returns the priority populated from | ||
// priority class. If not specified, priority will be default or | ||
// zero if there is no default. | ||
func GetPriorityFromPriorityClass(ctx context.Context, client client.Client, | ||
priorityClass string) (string, int32, error) { | ||
if len(priorityClass) == 0 { | ||
return getDefaultPriority(ctx, client) | ||
} | ||
|
||
pc := &schedulingv1.PriorityClass{} | ||
if err := client.Get(ctx, types.NamespacedName{Name: priorityClass}, pc); err != nil { | ||
return "", 0, err | ||
} | ||
|
||
return pc.Name, pc.Value, nil | ||
} | ||
|
||
func getDefaultPriority(ctx context.Context, client client.Client) (string, int32, error) { | ||
dpc, err := getDefaultPriorityClass(ctx, client) | ||
if err != nil { | ||
return "", 0, err | ||
} | ||
if dpc != nil { | ||
return dpc.Name, dpc.Value, nil | ||
} | ||
return "", int32(constants.DefaultPriority), nil | ||
} | ||
|
||
func getDefaultPriorityClass(ctx context.Context, client client.Client) (*schedulingv1.PriorityClass, error) { | ||
pcs := schedulingv1.PriorityClassList{} | ||
err := client.List(ctx, &pcs) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// In case more than one global default priority class is added as a result of a race condition, | ||
// 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 | ||
} | ||
} | ||
} | ||
return defaultPC, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,19 @@ | ||
/* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, I wonder if we can make a verify step for this. |
||
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 routine | ||
|
||
// Wrapper is used to wrap a function that will run in a goroutine. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where is this default defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the constant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it wouldn't change by external factors, you should just say that the default priority is zero.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw, I'm referring to
If not specified, the queuedWorkload priority will be default
Is this referring to the default priority class defined for the cluster?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It's the default priority class defined for the cluster. But if there is no default. It will be 0.
Keep the comments as
https://github.com/kubernetes/kubernetes/blob/ca2cd3b18ef145c34311ba7fd9d389fe8233fae8/pkg/apis/core/types.go#L2902