mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-22 02:18:51 +00:00
batch: add suspended job
Signed-off-by: Adhityaa Chandrasekar <adtac@google.com>
This commit is contained in:
@@ -20,14 +20,9 @@ import (
|
||||
fuzz "github.com/google/gofuzz"
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func newBool(val bool) *bool {
|
||||
p := new(bool)
|
||||
*p = val
|
||||
return p
|
||||
}
|
||||
|
||||
// Funcs returns the fuzzer functions for the batch api group.
|
||||
var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
return []interface{}{
|
||||
@@ -48,7 +43,7 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
j.Parallelism = ¶llelism
|
||||
j.BackoffLimit = &backoffLimit
|
||||
if c.Rand.Int31()%2 == 0 {
|
||||
j.ManualSelector = newBool(true)
|
||||
j.ManualSelector = pointer.BoolPtr(true)
|
||||
} else {
|
||||
j.ManualSelector = nil
|
||||
}
|
||||
@@ -57,6 +52,9 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
} else {
|
||||
j.CompletionMode = batch.IndexedCompletion
|
||||
}
|
||||
// We're fuzzing the internal JobSpec type, not the v1 type, so we don't
|
||||
// need to fuzz the nil value.
|
||||
j.Suspend = pointer.BoolPtr(c.RandBool())
|
||||
},
|
||||
func(sj *batch.CronJobSpec, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(sj)
|
||||
|
@@ -119,8 +119,11 @@ type JobSpec struct {
|
||||
// +optional
|
||||
Completions *int32
|
||||
|
||||
// Optional duration in seconds relative to the startTime that the job may be active
|
||||
// before the system tries to terminate it; value must be positive integer
|
||||
// Specifies the duration in seconds relative to the startTime that the job
|
||||
// may be continuously active before the system tries to terminate it; value
|
||||
// must be positive integer. If a Job is suspended (at creation or through an
|
||||
// update), this timer will effectively be stopped and reset when the Job is
|
||||
// resumed again.
|
||||
// +optional
|
||||
ActiveDeadlineSeconds *int64
|
||||
|
||||
@@ -187,19 +190,36 @@ type JobSpec struct {
|
||||
// controller skips updates for the Job.
|
||||
// +optional
|
||||
CompletionMode CompletionMode
|
||||
|
||||
// Suspend specifies whether the Job controller should create Pods or not. If
|
||||
// a Job is created with suspend set to true, no Pods are created by the Job
|
||||
// controller. If a Job is suspended after creation (i.e. the flag goes from
|
||||
// false to true), the Job controller will delete all active Pods associated
|
||||
// with this Job. Users must design their workload to gracefully handle this.
|
||||
// Suspending a Job will reset the StartTime field of the Job, effectively
|
||||
// resetting the ActiveDeadlineSeconds timer too. This is an alpha field and
|
||||
// requires the SuspendJob feature gate to be enabled; otherwise this field
|
||||
// may not be set to true. Defaults to false.
|
||||
// +optional
|
||||
Suspend *bool
|
||||
}
|
||||
|
||||
// JobStatus represents the current state of a Job.
|
||||
type JobStatus struct {
|
||||
|
||||
// The latest available observations of an object's current state.
|
||||
// When a job fails, one of the conditions will have type == "Failed".
|
||||
// The latest available observations of an object's current state. When a Job
|
||||
// fails, one of the conditions will have type "Failed" and status true. When
|
||||
// a Job is suspended, one of the conditions will have type "Suspended" and
|
||||
// status true; when the Job is resumed, the status of this condition will
|
||||
// become false. When a Job is completed, one of the conditions will have
|
||||
// type "Complete" and status true.
|
||||
// +optional
|
||||
Conditions []JobCondition
|
||||
|
||||
// Represents time when the job was acknowledged by the job controller.
|
||||
// It is not guaranteed to be set in happens-before order across separate operations.
|
||||
// It is represented in RFC3339 form and is in UTC.
|
||||
// Represents time when the job controller started processing a job. When a
|
||||
// Job is created in the suspended state, this field is not set until the
|
||||
// first time it is resumed. This field is reset every time a Job is resumed
|
||||
// from suspension. It is represented in RFC3339 form and is in UTC.
|
||||
// +optional
|
||||
StartTime *metav1.Time
|
||||
|
||||
@@ -238,6 +258,8 @@ type JobConditionType string
|
||||
|
||||
// These are valid conditions of a job.
|
||||
const (
|
||||
// JobSuspended means the job has been suspended.
|
||||
JobSuspended JobConditionType = "Suspended"
|
||||
// JobComplete means the job has completed its execution.
|
||||
JobComplete JobConditionType = "Complete"
|
||||
// JobFailed means the job has failed its execution.
|
||||
@@ -246,7 +268,7 @@ const (
|
||||
|
||||
// JobCondition describes current state of a job.
|
||||
type JobCondition struct {
|
||||
// Type of job condition, Complete or Failed.
|
||||
// Type of job condition.
|
||||
Type JobConditionType
|
||||
// Status of the condition, one of True, False, Unknown.
|
||||
Status api.ConditionStatus
|
||||
@@ -319,7 +341,7 @@ type CronJobSpec struct {
|
||||
ConcurrencyPolicy ConcurrencyPolicy
|
||||
|
||||
// This flag tells the controller to suspend subsequent executions, it does
|
||||
// not apply to already started executions. Defaults to false.
|
||||
// not apply to already started executions. Defaults to false.
|
||||
// +optional
|
||||
Suspend *bool
|
||||
|
||||
|
@@ -46,6 +46,9 @@ func SetDefaults_Job(obj *batchv1.Job) {
|
||||
if len(obj.Spec.CompletionMode) == 0 {
|
||||
obj.Spec.CompletionMode = batchv1.NonIndexedCompletion
|
||||
}
|
||||
if obj.Spec.Suspend == nil {
|
||||
obj.Spec.Suspend = utilpointer.BoolPtr(false)
|
||||
}
|
||||
}
|
||||
|
||||
func SetDefaults_CronJob(obj *batchv1.CronJob) {
|
||||
|
@@ -20,6 +20,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -27,7 +28,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
_ "k8s.io/kubernetes/pkg/apis/batch/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
. "k8s.io/kubernetes/pkg/apis/batch/v1"
|
||||
)
|
||||
@@ -49,15 +50,36 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
},
|
||||
expected: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: utilpointer.Int32Ptr(1),
|
||||
Parallelism: utilpointer.Int32Ptr(1),
|
||||
BackoffLimit: utilpointer.Int32Ptr(6),
|
||||
Completions: pointer.Int32Ptr(1),
|
||||
Parallelism: pointer.Int32Ptr(1),
|
||||
BackoffLimit: pointer.Int32Ptr(6),
|
||||
CompletionMode: batchv1.NonIndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
expectLabels: true,
|
||||
},
|
||||
"All unspecified -> all integers are defaulted and no default labels": {
|
||||
"suspend set, everything else is defaulted": {
|
||||
original: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Suspend: pointer.BoolPtr(true),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: pointer.Int32Ptr(1),
|
||||
Parallelism: pointer.Int32Ptr(1),
|
||||
BackoffLimit: pointer.Int32Ptr(6),
|
||||
CompletionMode: batchv1.NonIndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(true),
|
||||
},
|
||||
},
|
||||
expectLabels: true,
|
||||
},
|
||||
"All unspecified -> all pointers, CompletionMode are defaulted and no default labels": {
|
||||
original: &batchv1.Job{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{"mylabel": "myvalue"},
|
||||
@@ -70,17 +92,18 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
},
|
||||
expected: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: utilpointer.Int32Ptr(1),
|
||||
Parallelism: utilpointer.Int32Ptr(1),
|
||||
BackoffLimit: utilpointer.Int32Ptr(6),
|
||||
Completions: pointer.Int32Ptr(1),
|
||||
Parallelism: pointer.Int32Ptr(1),
|
||||
BackoffLimit: pointer.Int32Ptr(6),
|
||||
CompletionMode: batchv1.NonIndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
"WQ: Parallelism explicitly 0 and completions unset -> BackoffLimit is defaulted": {
|
||||
original: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Parallelism: utilpointer.Int32Ptr(0),
|
||||
Parallelism: pointer.Int32Ptr(0),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels},
|
||||
},
|
||||
@@ -88,9 +111,10 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
},
|
||||
expected: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Parallelism: utilpointer.Int32Ptr(0),
|
||||
BackoffLimit: utilpointer.Int32Ptr(6),
|
||||
Parallelism: pointer.Int32Ptr(0),
|
||||
BackoffLimit: pointer.Int32Ptr(6),
|
||||
CompletionMode: batchv1.NonIndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
expectLabels: true,
|
||||
@@ -98,7 +122,7 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
"WQ: Parallelism explicitly 2 and completions unset -> BackoffLimit is defaulted": {
|
||||
original: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Parallelism: utilpointer.Int32Ptr(2),
|
||||
Parallelism: pointer.Int32Ptr(2),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels},
|
||||
},
|
||||
@@ -106,9 +130,10 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
},
|
||||
expected: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Parallelism: utilpointer.Int32Ptr(2),
|
||||
BackoffLimit: utilpointer.Int32Ptr(6),
|
||||
Parallelism: pointer.Int32Ptr(2),
|
||||
BackoffLimit: pointer.Int32Ptr(6),
|
||||
CompletionMode: batchv1.NonIndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
expectLabels: true,
|
||||
@@ -116,7 +141,7 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
"Completions explicitly 2 and others unset -> parallelism and BackoffLimit are defaulted": {
|
||||
original: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: utilpointer.Int32Ptr(2),
|
||||
Completions: pointer.Int32Ptr(2),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels},
|
||||
},
|
||||
@@ -124,10 +149,11 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
},
|
||||
expected: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: utilpointer.Int32Ptr(2),
|
||||
Parallelism: utilpointer.Int32Ptr(1),
|
||||
BackoffLimit: utilpointer.Int32Ptr(6),
|
||||
Completions: pointer.Int32Ptr(2),
|
||||
Parallelism: pointer.Int32Ptr(1),
|
||||
BackoffLimit: pointer.Int32Ptr(6),
|
||||
CompletionMode: batchv1.NonIndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
expectLabels: true,
|
||||
@@ -135,7 +161,7 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
"BackoffLimit explicitly 5 and others unset -> parallelism and completions are defaulted": {
|
||||
original: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
BackoffLimit: utilpointer.Int32Ptr(5),
|
||||
BackoffLimit: pointer.Int32Ptr(5),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels},
|
||||
},
|
||||
@@ -143,10 +169,11 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
},
|
||||
expected: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: utilpointer.Int32Ptr(1),
|
||||
Parallelism: utilpointer.Int32Ptr(1),
|
||||
BackoffLimit: utilpointer.Int32Ptr(5),
|
||||
Completions: pointer.Int32Ptr(1),
|
||||
Parallelism: pointer.Int32Ptr(1),
|
||||
BackoffLimit: pointer.Int32Ptr(5),
|
||||
CompletionMode: batchv1.NonIndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
expectLabels: true,
|
||||
@@ -154,10 +181,11 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
"All set -> no change": {
|
||||
original: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: utilpointer.Int32Ptr(8),
|
||||
Parallelism: utilpointer.Int32Ptr(9),
|
||||
BackoffLimit: utilpointer.Int32Ptr(10),
|
||||
Completions: pointer.Int32Ptr(8),
|
||||
Parallelism: pointer.Int32Ptr(9),
|
||||
BackoffLimit: pointer.Int32Ptr(10),
|
||||
CompletionMode: batchv1.NonIndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(true),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels},
|
||||
},
|
||||
@@ -165,10 +193,11 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
},
|
||||
expected: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: utilpointer.Int32Ptr(8),
|
||||
Parallelism: utilpointer.Int32Ptr(9),
|
||||
BackoffLimit: utilpointer.Int32Ptr(10),
|
||||
Completions: pointer.Int32Ptr(8),
|
||||
Parallelism: pointer.Int32Ptr(9),
|
||||
BackoffLimit: pointer.Int32Ptr(10),
|
||||
CompletionMode: batchv1.NonIndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(true),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels},
|
||||
},
|
||||
@@ -179,10 +208,11 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
"All set, flipped -> no change": {
|
||||
original: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: utilpointer.Int32Ptr(11),
|
||||
Parallelism: utilpointer.Int32Ptr(10),
|
||||
BackoffLimit: utilpointer.Int32Ptr(9),
|
||||
Completions: pointer.Int32Ptr(11),
|
||||
Parallelism: pointer.Int32Ptr(10),
|
||||
BackoffLimit: pointer.Int32Ptr(9),
|
||||
CompletionMode: batchv1.IndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(true),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels},
|
||||
},
|
||||
@@ -190,10 +220,11 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
},
|
||||
expected: &batchv1.Job{
|
||||
Spec: batchv1.JobSpec{
|
||||
Completions: utilpointer.Int32Ptr(11),
|
||||
Parallelism: utilpointer.Int32Ptr(10),
|
||||
BackoffLimit: utilpointer.Int32Ptr(9),
|
||||
Completions: pointer.Int32Ptr(11),
|
||||
Parallelism: pointer.Int32Ptr(10),
|
||||
BackoffLimit: pointer.Int32Ptr(9),
|
||||
CompletionMode: batchv1.IndexedCompletion,
|
||||
Suspend: pointer.BoolPtr(true),
|
||||
},
|
||||
},
|
||||
expectLabels: true,
|
||||
@@ -211,6 +242,9 @@ func TestSetDefaultJob(t *testing.T) {
|
||||
t.Fatalf("Unexpected object: %v", actual)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(expected.Spec.Suspend, actual.Spec.Suspend); diff != "" {
|
||||
t.Errorf(".spec.suspend does not match; -want,+got:\n%s", diff)
|
||||
}
|
||||
validateDefaultInt32(t, "Completions", actual.Spec.Completions, expected.Spec.Completions)
|
||||
validateDefaultInt32(t, "Parallelism", actual.Spec.Parallelism, expected.Spec.Parallelism)
|
||||
validateDefaultInt32(t, "BackoffLimit", actual.Spec.BackoffLimit, expected.Spec.BackoffLimit)
|
||||
@@ -271,8 +305,8 @@ func TestSetDefaultCronJob(t *testing.T) {
|
||||
Spec: batchv1.CronJobSpec{
|
||||
ConcurrencyPolicy: batchv1.AllowConcurrent,
|
||||
Suspend: newBool(false),
|
||||
SuccessfulJobsHistoryLimit: utilpointer.Int32Ptr(3),
|
||||
FailedJobsHistoryLimit: utilpointer.Int32Ptr(1),
|
||||
SuccessfulJobsHistoryLimit: pointer.Int32Ptr(3),
|
||||
FailedJobsHistoryLimit: pointer.Int32Ptr(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -281,16 +315,16 @@ func TestSetDefaultCronJob(t *testing.T) {
|
||||
Spec: batchv1.CronJobSpec{
|
||||
ConcurrencyPolicy: batchv1.ForbidConcurrent,
|
||||
Suspend: newBool(true),
|
||||
SuccessfulJobsHistoryLimit: utilpointer.Int32Ptr(5),
|
||||
FailedJobsHistoryLimit: utilpointer.Int32Ptr(5),
|
||||
SuccessfulJobsHistoryLimit: pointer.Int32Ptr(5),
|
||||
FailedJobsHistoryLimit: pointer.Int32Ptr(5),
|
||||
},
|
||||
},
|
||||
expected: &batchv1.CronJob{
|
||||
Spec: batchv1.CronJobSpec{
|
||||
ConcurrencyPolicy: batchv1.ForbidConcurrent,
|
||||
Suspend: newBool(true),
|
||||
SuccessfulJobsHistoryLimit: utilpointer.Int32Ptr(5),
|
||||
FailedJobsHistoryLimit: utilpointer.Int32Ptr(5),
|
||||
SuccessfulJobsHistoryLimit: pointer.Int32Ptr(5),
|
||||
FailedJobsHistoryLimit: pointer.Int32Ptr(5),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
2
pkg/apis/batch/v1/zz_generated.conversion.go
generated
2
pkg/apis/batch/v1/zz_generated.conversion.go
generated
@@ -393,6 +393,7 @@ func autoConvert_v1_JobSpec_To_batch_JobSpec(in *v1.JobSpec, out *batch.JobSpec,
|
||||
}
|
||||
out.TTLSecondsAfterFinished = (*int32)(unsafe.Pointer(in.TTLSecondsAfterFinished))
|
||||
out.CompletionMode = batch.CompletionMode(in.CompletionMode)
|
||||
out.Suspend = (*bool)(unsafe.Pointer(in.Suspend))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -408,6 +409,7 @@ func autoConvert_batch_JobSpec_To_v1_JobSpec(in *batch.JobSpec, out *v1.JobSpec,
|
||||
}
|
||||
out.TTLSecondsAfterFinished = (*int32)(unsafe.Pointer(in.TTLSecondsAfterFinished))
|
||||
out.CompletionMode = v1.CompletionMode(in.CompletionMode)
|
||||
out.Suspend = (*bool)(unsafe.Pointer(in.Suspend))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
5
pkg/apis/batch/zz_generated.deepcopy.go
generated
5
pkg/apis/batch/zz_generated.deepcopy.go
generated
@@ -271,6 +271,11 @@ func (in *JobSpec) DeepCopyInto(out *JobSpec) {
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
if in.Suspend != nil {
|
||||
in, out := &in.Suspend, &out.Suspend
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user