mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Graduate JobMutableNodeSchedulingDirectives feature to GA
This commit is contained in:
parent
6f68a13696
commit
2ecd24011a
@ -402,6 +402,7 @@ const (
|
|||||||
|
|
||||||
// owner: @ahg
|
// owner: @ahg
|
||||||
// beta: v1.23
|
// beta: v1.23
|
||||||
|
// stable: v1.27
|
||||||
//
|
//
|
||||||
// Allow updating node scheduling directives in the pod template of jobs. Specifically,
|
// Allow updating node scheduling directives in the pod template of jobs. Specifically,
|
||||||
// node affinity, selector and tolerations. This is allowed only for suspended jobs
|
// node affinity, selector and tolerations. This is allowed only for suspended jobs
|
||||||
@ -963,7 +964,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
JobPodFailurePolicy: {Default: true, PreRelease: featuregate.Beta},
|
JobPodFailurePolicy: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
JobMutableNodeSchedulingDirectives: {Default: true, PreRelease: featuregate.Beta},
|
JobMutableNodeSchedulingDirectives: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.29
|
||||||
|
|
||||||
JobReadyPods: {Default: true, PreRelease: featuregate.Beta},
|
JobReadyPods: {Default: true, PreRelease: featuregate.Beta},
|
||||||
|
|
||||||
|
@ -183,8 +183,7 @@ func validationOptionsForJob(newJob, oldJob *batch.Job) batchvalidation.JobValid
|
|||||||
// only for suspended jobs that never started before.
|
// only for suspended jobs that never started before.
|
||||||
suspended := oldJob.Spec.Suspend != nil && *oldJob.Spec.Suspend
|
suspended := oldJob.Spec.Suspend != nil && *oldJob.Spec.Suspend
|
||||||
notStarted := oldJob.Status.StartTime == nil
|
notStarted := oldJob.Status.StartTime == nil
|
||||||
opts.AllowMutableSchedulingDirectives = utilfeature.DefaultFeatureGate.Enabled(features.JobMutableNodeSchedulingDirectives) &&
|
opts.AllowMutableSchedulingDirectives = suspended && notStarted
|
||||||
suspended && notStarted
|
|
||||||
}
|
}
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
@ -475,7 +475,6 @@ func TestJobStrategy_ValidateUpdate(t *testing.T) {
|
|||||||
job *batch.Job
|
job *batch.Job
|
||||||
update func(*batch.Job)
|
update func(*batch.Job)
|
||||||
wantErrs field.ErrorList
|
wantErrs field.ErrorList
|
||||||
mutableSchedulingDirectivesEnabled bool
|
|
||||||
}{
|
}{
|
||||||
"update parallelism": {
|
"update parallelism": {
|
||||||
job: &batch.Job{
|
job: &batch.Job{
|
||||||
@ -603,7 +602,6 @@ func TestJobStrategy_ValidateUpdate(t *testing.T) {
|
|||||||
wantErrs: field.ErrorList{
|
wantErrs: field.ErrorList{
|
||||||
{Type: field.ErrorTypeInvalid, Field: "spec.template"},
|
{Type: field.ErrorTypeInvalid, Field: "spec.template"},
|
||||||
},
|
},
|
||||||
mutableSchedulingDirectivesEnabled: true,
|
|
||||||
},
|
},
|
||||||
"updating node selector for suspended but previously started job disallowed": {
|
"updating node selector for suspended but previously started job disallowed": {
|
||||||
job: &batch.Job{
|
job: &batch.Job{
|
||||||
@ -630,7 +628,6 @@ func TestJobStrategy_ValidateUpdate(t *testing.T) {
|
|||||||
wantErrs: field.ErrorList{
|
wantErrs: field.ErrorList{
|
||||||
{Type: field.ErrorTypeInvalid, Field: "spec.template"},
|
{Type: field.ErrorTypeInvalid, Field: "spec.template"},
|
||||||
},
|
},
|
||||||
mutableSchedulingDirectivesEnabled: true,
|
|
||||||
},
|
},
|
||||||
"updating node selector for suspended and not previously started job allowed": {
|
"updating node selector for suspended and not previously started job allowed": {
|
||||||
job: &batch.Job{
|
job: &batch.Job{
|
||||||
@ -651,31 +648,6 @@ func TestJobStrategy_ValidateUpdate(t *testing.T) {
|
|||||||
update: func(job *batch.Job) {
|
update: func(job *batch.Job) {
|
||||||
job.Spec.Template.Spec.NodeSelector = map[string]string{"foo": "bar"}
|
job.Spec.Template.Spec.NodeSelector = map[string]string{"foo": "bar"}
|
||||||
},
|
},
|
||||||
mutableSchedulingDirectivesEnabled: true,
|
|
||||||
},
|
|
||||||
"updating node selector whilte gate disabled disallowed": {
|
|
||||||
job: &batch.Job{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "myjob",
|
|
||||||
Namespace: metav1.NamespaceDefault,
|
|
||||||
ResourceVersion: "0",
|
|
||||||
Annotations: map[string]string{"foo": "bar"},
|
|
||||||
},
|
|
||||||
Spec: batch.JobSpec{
|
|
||||||
Selector: validSelector,
|
|
||||||
Template: validPodTemplateSpec,
|
|
||||||
ManualSelector: pointer.BoolPtr(true),
|
|
||||||
Parallelism: pointer.Int32Ptr(1),
|
|
||||||
Suspend: pointer.BoolPtr(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
update: func(job *batch.Job) {
|
|
||||||
job.Spec.Template.Spec.NodeSelector = map[string]string{"foo": "bar"}
|
|
||||||
},
|
|
||||||
wantErrs: field.ErrorList{
|
|
||||||
{Type: field.ErrorTypeInvalid, Field: "spec.template"},
|
|
||||||
},
|
|
||||||
mutableSchedulingDirectivesEnabled: false,
|
|
||||||
},
|
},
|
||||||
"invalid label selector": {
|
"invalid label selector": {
|
||||||
job: &batch.Job{
|
job: &batch.Job{
|
||||||
@ -701,7 +673,6 @@ func TestJobStrategy_ValidateUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for name, tc := range cases {
|
for name, tc := range cases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobMutableNodeSchedulingDirectives, tc.mutableSchedulingDirectivesEnabled)()
|
|
||||||
newJob := tc.job.DeepCopy()
|
newJob := tc.job.DeepCopy()
|
||||||
tc.update(newJob)
|
tc.update(newJob)
|
||||||
errs := Strategy.ValidateUpdate(ctx, newJob, tc.job)
|
errs := Strategy.ValidateUpdate(ctx, newJob, tc.job)
|
||||||
|
@ -1574,13 +1574,6 @@ func TestSuspendJobControllerRestart(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeSelectorUpdate(t *testing.T) {
|
func TestNodeSelectorUpdate(t *testing.T) {
|
||||||
for name, featureGate := range map[string]bool{
|
|
||||||
"feature gate disabled": false,
|
|
||||||
"feature gate enabled": true,
|
|
||||||
} {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobMutableNodeSchedulingDirectives, featureGate)()
|
|
||||||
|
|
||||||
closeFn, restConfig, clientSet, ns := setup(t, "suspend")
|
closeFn, restConfig, clientSet, ns := setup(t, "suspend")
|
||||||
defer closeFn()
|
defer closeFn()
|
||||||
ctx, cancel := startJobControllerAndWaitForCaches(restConfig)
|
ctx, cancel := startJobControllerAndWaitForCaches(restConfig)
|
||||||
@ -1599,20 +1592,15 @@ func TestNodeSelectorUpdate(t *testing.T) {
|
|||||||
|
|
||||||
// (1) Unsuspend and set node selector in the same update.
|
// (1) Unsuspend and set node selector in the same update.
|
||||||
nodeSelector := map[string]string{"foo": "bar"}
|
nodeSelector := map[string]string{"foo": "bar"}
|
||||||
_, err = updateJob(ctx, jobClient, jobName, func(j *batchv1.Job) {
|
if _, err := updateJob(ctx, jobClient, jobName, func(j *batchv1.Job) {
|
||||||
j.Spec.Template.Spec.NodeSelector = nodeSelector
|
j.Spec.Template.Spec.NodeSelector = nodeSelector
|
||||||
j.Spec.Suspend = pointer.BoolPtr(false)
|
j.Spec.Suspend = pointer.BoolPtr(false)
|
||||||
})
|
}); err != nil {
|
||||||
if !featureGate {
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "spec.template: Invalid value") {
|
|
||||||
t.Errorf("Expected \"spec.template: Invalid value\" error, got: %v", err)
|
|
||||||
}
|
|
||||||
} else if featureGate && err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// (2) Check that the pod was created using the expected node selector.
|
// (2) Check that the pod was created using the expected node selector.
|
||||||
if featureGate {
|
|
||||||
var pod *v1.Pod
|
var pod *v1.Pod
|
||||||
if err := wait.PollImmediate(waitInterval, wait.ForeverTestTimeout, func() (bool, error) {
|
if err := wait.PollImmediate(waitInterval, wait.ForeverTestTimeout, func() (bool, error) {
|
||||||
pods, err := clientSet.CoreV1().Pods(jobNamespace).List(ctx, metav1.ListOptions{})
|
pods, err := clientSet.CoreV1().Pods(jobNamespace).List(ctx, metav1.ListOptions{})
|
||||||
@ -1633,7 +1621,6 @@ func TestNodeSelectorUpdate(t *testing.T) {
|
|||||||
if diff := cmp.Diff(nodeSelector, pod.Spec.NodeSelector); diff != "" {
|
if diff := cmp.Diff(nodeSelector, pod.Spec.NodeSelector); diff != "" {
|
||||||
t.Errorf("Unexpected nodeSelector (-want,+got):\n%s", diff)
|
t.Errorf("Unexpected nodeSelector (-want,+got):\n%s", diff)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// (3) Update node selector again. It should fail since the job is unsuspended.
|
// (3) Update node selector again. It should fail since the job is unsuspended.
|
||||||
_, err = updateJob(ctx, jobClient, jobName, func(j *batchv1.Job) {
|
_, err = updateJob(ctx, jobClient, jobName, func(j *batchv1.Job) {
|
||||||
@ -1644,8 +1631,6 @@ func TestNodeSelectorUpdate(t *testing.T) {
|
|||||||
t.Errorf("Expected \"spec.template: Invalid value\" error, got: %v", err)
|
t.Errorf("Expected \"spec.template: Invalid value\" error, got: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type podsByStatus struct {
|
type podsByStatus struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user