Merge pull request #108032 from deejross/kep3140-cronjob-timezone

KEP 3140: TimeZone support for CronJob
This commit is contained in:
Kubernetes Prow Robot
2022-03-29 17:34:20 -07:00
committed by GitHub
35 changed files with 971 additions and 167 deletions

View File

@@ -4079,6 +4079,10 @@
"suspend": { "suspend": {
"description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.", "description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.",
"type": "boolean" "type": "boolean"
},
"timeZone": {
"description": "The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will rely on the time zone of the kube-controller-manager process. ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.",
"type": "string"
} }
}, },
"required": [ "required": [
@@ -4454,6 +4458,10 @@
"suspend": { "suspend": {
"description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.", "description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.",
"type": "boolean" "type": "boolean"
},
"timeZone": {
"description": "The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will rely on the time zone of the kube-controller-manager process. ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.",
"type": "string"
} }
}, },
"required": [ "required": [

View File

@@ -114,6 +114,10 @@
"suspend": { "suspend": {
"description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.", "description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.",
"type": "boolean" "type": "boolean"
},
"timeZone": {
"description": "The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will rely on the time zone of the kube-controller-manager process. ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.",
"type": "string"
} }
}, },
"required": [ "required": [

View File

@@ -164,6 +164,10 @@
"suspend": { "suspend": {
"description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.", "description": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.",
"type": "boolean" "type": "boolean"
},
"timeZone": {
"description": "The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will rely on the time zone of the kube-controller-manager process. ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.",
"type": "string"
} }
}, },
"required": [ "required": [

View File

@@ -20,6 +20,7 @@ package main
import ( import (
"os" "os"
_ "time/tzdata" // for timeZone support in CronJob
"k8s.io/component-base/cli" "k8s.io/component-base/cli"
_ "k8s.io/component-base/logs/json/register" // for JSON log format registration _ "k8s.io/component-base/logs/json/register" // for JSON log format registration

View File

@@ -22,6 +22,7 @@ package main
import ( import (
"os" "os"
_ "time/tzdata" // for CronJob Time Zone support
"k8s.io/component-base/cli" "k8s.io/component-base/cli"
_ "k8s.io/component-base/logs/json/register" // for JSON log format registration _ "k8s.io/component-base/logs/json/register" // for JSON log format registration

View File

@@ -376,6 +376,12 @@ type CronJobSpec struct {
// The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
Schedule string Schedule string
// The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
// If not specified, this will rely on the time zone of the kube-controller-manager process.
// ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.
// +optional
TimeZone *string
// Optional deadline in seconds for starting the job if it misses scheduled // Optional deadline in seconds for starting the job if it misses scheduled
// time for any reason. Missed jobs executions will be counted as failed ones. // time for any reason. Missed jobs executions will be counted as failed ones.
// +optional // +optional

View File

@@ -231,6 +231,7 @@ func Convert_batch_CronJobList_To_v1_CronJobList(in *batch.CronJobList, out *v1.
func autoConvert_v1_CronJobSpec_To_batch_CronJobSpec(in *v1.CronJobSpec, out *batch.CronJobSpec, s conversion.Scope) error { func autoConvert_v1_CronJobSpec_To_batch_CronJobSpec(in *v1.CronJobSpec, out *batch.CronJobSpec, s conversion.Scope) error {
out.Schedule = in.Schedule out.Schedule = in.Schedule
out.TimeZone = (*string)(unsafe.Pointer(in.TimeZone))
out.StartingDeadlineSeconds = (*int64)(unsafe.Pointer(in.StartingDeadlineSeconds)) out.StartingDeadlineSeconds = (*int64)(unsafe.Pointer(in.StartingDeadlineSeconds))
out.ConcurrencyPolicy = batch.ConcurrencyPolicy(in.ConcurrencyPolicy) out.ConcurrencyPolicy = batch.ConcurrencyPolicy(in.ConcurrencyPolicy)
out.Suspend = (*bool)(unsafe.Pointer(in.Suspend)) out.Suspend = (*bool)(unsafe.Pointer(in.Suspend))
@@ -249,6 +250,7 @@ func Convert_v1_CronJobSpec_To_batch_CronJobSpec(in *v1.CronJobSpec, out *batch.
func autoConvert_batch_CronJobSpec_To_v1_CronJobSpec(in *batch.CronJobSpec, out *v1.CronJobSpec, s conversion.Scope) error { func autoConvert_batch_CronJobSpec_To_v1_CronJobSpec(in *batch.CronJobSpec, out *v1.CronJobSpec, s conversion.Scope) error {
out.Schedule = in.Schedule out.Schedule = in.Schedule
out.TimeZone = (*string)(unsafe.Pointer(in.TimeZone))
out.StartingDeadlineSeconds = (*int64)(unsafe.Pointer(in.StartingDeadlineSeconds)) out.StartingDeadlineSeconds = (*int64)(unsafe.Pointer(in.StartingDeadlineSeconds))
out.ConcurrencyPolicy = v1.ConcurrencyPolicy(in.ConcurrencyPolicy) out.ConcurrencyPolicy = v1.ConcurrencyPolicy(in.ConcurrencyPolicy)
out.Suspend = (*bool)(unsafe.Pointer(in.Suspend)) out.Suspend = (*bool)(unsafe.Pointer(in.Suspend))

View File

@@ -180,6 +180,7 @@ func Convert_batch_CronJobList_To_v1beta1_CronJobList(in *batch.CronJobList, out
func autoConvert_v1beta1_CronJobSpec_To_batch_CronJobSpec(in *v1beta1.CronJobSpec, out *batch.CronJobSpec, s conversion.Scope) error { func autoConvert_v1beta1_CronJobSpec_To_batch_CronJobSpec(in *v1beta1.CronJobSpec, out *batch.CronJobSpec, s conversion.Scope) error {
out.Schedule = in.Schedule out.Schedule = in.Schedule
out.TimeZone = (*string)(unsafe.Pointer(in.TimeZone))
out.StartingDeadlineSeconds = (*int64)(unsafe.Pointer(in.StartingDeadlineSeconds)) out.StartingDeadlineSeconds = (*int64)(unsafe.Pointer(in.StartingDeadlineSeconds))
out.ConcurrencyPolicy = batch.ConcurrencyPolicy(in.ConcurrencyPolicy) out.ConcurrencyPolicy = batch.ConcurrencyPolicy(in.ConcurrencyPolicy)
out.Suspend = (*bool)(unsafe.Pointer(in.Suspend)) out.Suspend = (*bool)(unsafe.Pointer(in.Suspend))
@@ -198,6 +199,7 @@ func Convert_v1beta1_CronJobSpec_To_batch_CronJobSpec(in *v1beta1.CronJobSpec, o
func autoConvert_batch_CronJobSpec_To_v1beta1_CronJobSpec(in *batch.CronJobSpec, out *v1beta1.CronJobSpec, s conversion.Scope) error { func autoConvert_batch_CronJobSpec_To_v1beta1_CronJobSpec(in *batch.CronJobSpec, out *v1beta1.CronJobSpec, s conversion.Scope) error {
out.Schedule = in.Schedule out.Schedule = in.Schedule
out.TimeZone = (*string)(unsafe.Pointer(in.TimeZone))
out.StartingDeadlineSeconds = (*int64)(unsafe.Pointer(in.StartingDeadlineSeconds)) out.StartingDeadlineSeconds = (*int64)(unsafe.Pointer(in.StartingDeadlineSeconds))
out.ConcurrencyPolicy = v1beta1.ConcurrencyPolicy(in.ConcurrencyPolicy) out.ConcurrencyPolicy = v1beta1.ConcurrencyPolicy(in.ConcurrencyPolicy)
out.Suspend = (*bool)(unsafe.Pointer(in.Suspend)) out.Suspend = (*bool)(unsafe.Pointer(in.Suspend))

View File

@@ -18,6 +18,8 @@ package validation
import ( import (
"fmt" "fmt"
"strings"
"time"
"github.com/robfig/cron/v3" "github.com/robfig/cron/v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -29,6 +31,7 @@ import (
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/utils/pointer"
) )
// maxParallelismForIndexJob is the maximum parallelism that an Indexed Job // maxParallelismForIndexJob is the maximum parallelism that an Indexed Job
@@ -277,11 +280,11 @@ func ValidateJobStatusUpdate(status, oldStatus batch.JobStatus) field.ErrorList
return allErrs return allErrs
} }
// ValidateCronJob validates a CronJob and returns an ErrorList with any errors. // ValidateCronJobCreate validates a CronJob on creation and returns an ErrorList with any errors.
func ValidateCronJob(cronJob *batch.CronJob, opts apivalidation.PodValidationOptions) field.ErrorList { func ValidateCronJobCreate(cronJob *batch.CronJob, opts apivalidation.PodValidationOptions) field.ErrorList {
// CronJobs and rcs have the same name validation // CronJobs and rcs have the same name validation
allErrs := apivalidation.ValidateObjectMeta(&cronJob.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata")) allErrs := apivalidation.ValidateObjectMeta(&cronJob.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateCronJobSpec(&cronJob.Spec, field.NewPath("spec"), opts)...) allErrs = append(allErrs, validateCronJobSpec(&cronJob.Spec, nil, field.NewPath("spec"), opts)...)
if len(cronJob.ObjectMeta.Name) > apimachineryvalidation.DNS1035LabelMaxLength-11 { if len(cronJob.ObjectMeta.Name) > apimachineryvalidation.DNS1035LabelMaxLength-11 {
// The cronjob controller appends a 11-character suffix to the cronjob (`-$TIMESTAMP`) when // The cronjob controller appends a 11-character suffix to the cronjob (`-$TIMESTAMP`) when
// creating a job. The job name length limit is 63 characters. // creating a job. The job name length limit is 63 characters.
@@ -295,24 +298,31 @@ func ValidateCronJob(cronJob *batch.CronJob, opts apivalidation.PodValidationOpt
// ValidateCronJobUpdate validates an update to a CronJob and returns an ErrorList with any errors. // ValidateCronJobUpdate validates an update to a CronJob and returns an ErrorList with any errors.
func ValidateCronJobUpdate(job, oldJob *batch.CronJob, opts apivalidation.PodValidationOptions) field.ErrorList { func ValidateCronJobUpdate(job, oldJob *batch.CronJob, opts apivalidation.PodValidationOptions) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta, field.NewPath("metadata")) allErrs := apivalidation.ValidateObjectMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateCronJobSpec(&job.Spec, field.NewPath("spec"), opts)...) allErrs = append(allErrs, validateCronJobSpec(&job.Spec, &oldJob.Spec, field.NewPath("spec"), opts)...)
// skip the 52-character name validation limit on update validation // skip the 52-character name validation limit on update validation
// to allow old cronjobs with names > 52 chars to be updated/deleted // to allow old cronjobs with names > 52 chars to be updated/deleted
return allErrs return allErrs
} }
// ValidateCronJobSpec validates a CronJobSpec and returns an ErrorList with any errors. // validateCronJobSpec validates a CronJobSpec and returns an ErrorList with any errors.
func ValidateCronJobSpec(spec *batch.CronJobSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList { func validateCronJobSpec(spec, oldSpec *batch.CronJobSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(spec.Schedule) == 0 { if len(spec.Schedule) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("schedule"), "")) allErrs = append(allErrs, field.Required(fldPath.Child("schedule"), ""))
} else { } else {
allErrs = append(allErrs, validateScheduleFormat(spec.Schedule, fldPath.Child("schedule"))...) allErrs = append(allErrs, validateScheduleFormat(spec.Schedule, spec.TimeZone, fldPath.Child("schedule"))...)
} }
if spec.StartingDeadlineSeconds != nil { if spec.StartingDeadlineSeconds != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.StartingDeadlineSeconds), fldPath.Child("startingDeadlineSeconds"))...) allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.StartingDeadlineSeconds), fldPath.Child("startingDeadlineSeconds"))...)
} }
if oldSpec == nil || !pointer.StringEqual(oldSpec.TimeZone, spec.TimeZone) {
allErrs = append(allErrs, validateTimeZone(spec.TimeZone, fldPath.Child("timeZone"))...)
}
allErrs = append(allErrs, validateConcurrencyPolicy(&spec.ConcurrencyPolicy, fldPath.Child("concurrencyPolicy"))...) allErrs = append(allErrs, validateConcurrencyPolicy(&spec.ConcurrencyPolicy, fldPath.Child("concurrencyPolicy"))...)
allErrs = append(allErrs, ValidateJobTemplateSpec(&spec.JobTemplate, fldPath.Child("jobTemplate"), opts)...) allErrs = append(allErrs, ValidateJobTemplateSpec(&spec.JobTemplate, fldPath.Child("jobTemplate"), opts)...)
@@ -343,11 +353,36 @@ func validateConcurrencyPolicy(concurrencyPolicy *batch.ConcurrencyPolicy, fldPa
return allErrs return allErrs
} }
func validateScheduleFormat(schedule string, fldPath *field.Path) field.ErrorList { func validateScheduleFormat(schedule string, timeZone *string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if _, err := cron.ParseStandard(schedule); err != nil { if _, err := cron.ParseStandard(schedule); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, schedule, err.Error())) allErrs = append(allErrs, field.Invalid(fldPath, schedule, err.Error()))
} }
if strings.Contains(schedule, "TZ") && timeZone != nil {
allErrs = append(allErrs, field.Invalid(fldPath, schedule, "cannot use both timeZone field and TZ or CRON_TZ in schedule"))
}
return allErrs
}
func validateTimeZone(timeZone *string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if timeZone == nil {
return allErrs
}
if len(*timeZone) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath, timeZone, "timeZone must be nil or non-empty string"))
return allErrs
}
if strings.EqualFold(*timeZone, "Local") {
allErrs = append(allErrs, field.Invalid(fldPath, timeZone, "timeZone must be an explicit time zone as defined in https://www.iana.org/time-zones"))
}
if _, err := time.LoadLocation(*timeZone); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, timeZone, err.Error()))
}
return allErrs return allErrs
} }

View File

@@ -31,6 +31,18 @@ import (
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
) )
var (
timeZoneEmpty = ""
timeZoneLocal = "LOCAL"
timeZoneUTC = "UTC"
timeZoneCorrectCasing = "America/New_York"
timeZoneBadCasing = "AMERICA/new_york"
timeZoneBadPrefix = " America/New_York"
timeZoneBadSuffix = "America/New_York "
timeZoneBadName = "America/New York"
timeZoneEmptySpace = " "
)
var ignoreErrValueDetail = cmpopts.IgnoreFields(field.Error{}, "BadValue", "Detail") var ignoreErrValueDetail = cmpopts.IgnoreFields(field.Error{}, "BadValue", "Detail")
func getValidManualSelector() *metav1.LabelSelector { func getValidManualSelector() *metav1.LabelSelector {
@@ -902,9 +914,26 @@ func TestValidateCronJob(t *testing.T) {
}, },
}, },
}, },
"correct timeZone value casing": {
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: &timeZoneCorrectCasing,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
} }
for k, v := range successCases { for k, v := range successCases {
if errs := ValidateCronJob(&v, corevalidation.PodValidationOptions{}); len(errs) != 0 { if errs := ValidateCronJobCreate(&v, corevalidation.PodValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success for %s: %v", k, errs) t.Errorf("expected success for %s: %v", k, errs)
} }
@@ -953,6 +982,142 @@ func TestValidateCronJob(t *testing.T) {
}, },
}, },
}, },
"spec.schedule: cannot use both timeZone field and TZ or CRON_TZ in schedule": {
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.CronJobSpec{
Schedule: "TZ=UTC 0 * * * *",
TimeZone: &timeZoneUTC,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"spec.timeZone: timeZone must be nil or non-empty string": {
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: &timeZoneEmpty,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"spec.timeZone: timeZone must be an explicit time zone as defined in https://www.iana.org/time-zones": {
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: &timeZoneLocal,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"spec.timeZone: Invalid value: \"AMERICA/new_york\": unknown time zone AMERICA/new_york": {
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: &timeZoneBadCasing,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"spec.timeZone: Invalid value: \" America/New_York\": unknown time zone America/New_York": {
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: &timeZoneBadPrefix,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"spec.timeZone: Invalid value: \"America/New_York \": unknown time zone America/New_York ": {
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: &timeZoneBadSuffix,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"spec.timeZone: Invalid value: \"America/New York\": unknown time zone America/New York": {
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: &timeZoneBadName,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"spec.timeZone: Invalid value: \" \": unknown time zone ": {
ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob",
Namespace: metav1.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: &timeZoneEmptySpace,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"spec.startingDeadlineSeconds:must be greater than or equal to 0": { "spec.startingDeadlineSeconds:must be greater than or equal to 0": {
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "mycronjob", Name: "mycronjob",
@@ -1185,7 +1350,7 @@ func TestValidateCronJob(t *testing.T) {
} }
for k, v := range errorCases { for k, v := range errorCases {
errs := ValidateCronJob(&v, corevalidation.PodValidationOptions{}) errs := ValidateCronJobCreate(&v, corevalidation.PodValidationOptions{})
if len(errs) == 0 { if len(errs) == 0 {
t.Errorf("expected failure for %s", k) t.Errorf("expected failure for %s", k)
} else { } else {
@@ -1198,9 +1363,14 @@ func TestValidateCronJob(t *testing.T) {
// Update validation should fail all failure cases other than the 52 character name limit // Update validation should fail all failure cases other than the 52 character name limit
// copy to avoid polluting the testcase object, set a resourceVersion to allow validating update, and test a no-op update // copy to avoid polluting the testcase object, set a resourceVersion to allow validating update, and test a no-op update
v = *v.DeepCopy() oldSpec := *v.DeepCopy()
v.ResourceVersion = "1" oldSpec.ResourceVersion = "1"
errs = ValidateCronJobUpdate(&v, &v, corevalidation.PodValidationOptions{}) oldSpec.Spec.TimeZone = nil
newSpec := *v.DeepCopy()
newSpec.ResourceVersion = "2"
errs = ValidateCronJobUpdate(&newSpec, &oldSpec, corevalidation.PodValidationOptions{})
if len(errs) == 0 { if len(errs) == 0 {
if k == "metadata.name: must be no more than 52 characters" { if k == "metadata.name: must be no more than 52 characters" {
continue continue
@@ -1216,6 +1386,208 @@ func TestValidateCronJob(t *testing.T) {
} }
} }
func TestValidateCronJobSpec(t *testing.T) {
validPodTemplateSpec := getValidPodTemplateSpecForGenerated(getValidGeneratedSelector())
validPodTemplateSpec.Labels = map[string]string{}
type testCase struct {
old *batch.CronJobSpec
new *batch.CronJobSpec
expectErr bool
}
cases := map[string]testCase{
"no validation because timeZone is nil for old and new": {
old: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: nil,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
new: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: nil,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"check validation because timeZone is different for new": {
old: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: nil,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
new: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("America/New_York"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"check validation because timeZone is different for new and invalid": {
old: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: nil,
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
new: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("broken"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
expectErr: true,
},
"old timeZone and new timeZone are valid": {
old: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("America/New_York"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
new: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("America/Chicago"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"old timeZone is valid, but new timeZone is invalid": {
old: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("America/New_York"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
new: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("broken"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
expectErr: true,
},
"old timeZone and new timeZone are invalid, but unchanged": {
old: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("broken"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
new: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("broken"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
"old timeZone and new timeZone are invalid, but different": {
old: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("broken"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
new: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("still broken"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
expectErr: true,
},
"old timeZone is invalid, but new timeZone is valid": {
old: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("broken"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
new: &batch.CronJobSpec{
Schedule: "0 * * * *",
TimeZone: pointer.String("America/New_York"),
ConcurrencyPolicy: batch.AllowConcurrent,
JobTemplate: batch.JobTemplateSpec{
Spec: batch.JobSpec{
Template: validPodTemplateSpec,
},
},
},
},
}
for k, v := range cases {
errs := validateCronJobSpec(v.new, v.old, field.NewPath("spec"), corevalidation.PodValidationOptions{})
if len(errs) > 0 && !v.expectErr {
t.Errorf("unexpected error for %s: %v", k, errs)
} else if len(errs) == 0 && v.expectErr {
t.Errorf("expected error for %s but got nil", k)
}
}
}
func completionModePtr(m batch.CompletionMode) *batch.CompletionMode { func completionModePtr(m batch.CompletionMode) *batch.CompletionMode {
return &m return &m
} }

View File

@@ -92,6 +92,11 @@ func (in *CronJobList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CronJobSpec) DeepCopyInto(out *CronJobSpec) { func (in *CronJobSpec) DeepCopyInto(out *CronJobSpec) {
*out = *in *out = *in
if in.TimeZone != nil {
in, out := &in.TimeZone, &out.TimeZone
*out = new(string)
**out = **in
}
if in.StartingDeadlineSeconds != nil { if in.StartingDeadlineSeconds != nil {
in, out := &in.StartingDeadlineSeconds, &out.StartingDeadlineSeconds in, out := &in.StartingDeadlineSeconds, &out.StartingDeadlineSeconds
*out = new(int64) *out = new(int64)

View File

@@ -35,6 +35,8 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
batchv1informers "k8s.io/client-go/informers/batch/v1" batchv1informers "k8s.io/client-go/informers/batch/v1"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
@@ -48,6 +50,7 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/controller/cronjob/metrics" "k8s.io/kubernetes/pkg/controller/cronjob/metrics"
"k8s.io/utils/pointer"
) )
var ( var (
@@ -371,6 +374,7 @@ func (jm *ControllerV2) enqueueControllerAfter(obj interface{}, t time.Duration)
// updateCronJob re-queues the CronJob for next scheduled time if there is a // updateCronJob re-queues the CronJob for next scheduled time if there is a
// change in spec.schedule otherwise it re-queues it now // change in spec.schedule otherwise it re-queues it now
func (jm *ControllerV2) updateCronJob(old interface{}, curr interface{}) { func (jm *ControllerV2) updateCronJob(old interface{}, curr interface{}) {
timeZoneEnabled := utilfeature.DefaultFeatureGate.Enabled(features.CronJobTimeZone)
oldCJ, okOld := old.(*batchv1.CronJob) oldCJ, okOld := old.(*batchv1.CronJob)
newCJ, okNew := curr.(*batchv1.CronJob) newCJ, okNew := curr.(*batchv1.CronJob)
@@ -381,9 +385,9 @@ func (jm *ControllerV2) updateCronJob(old interface{}, curr interface{}) {
// if the change in schedule results in next requeue having to be sooner than it already was, // if the change in schedule results in next requeue having to be sooner than it already was,
// it will be handled here by the queue. If the next requeue is further than previous schedule, // it will be handled here by the queue. If the next requeue is further than previous schedule,
// the sync loop will essentially be a no-op for the already queued key with old schedule. // the sync loop will essentially be a no-op for the already queued key with old schedule.
if oldCJ.Spec.Schedule != newCJ.Spec.Schedule { if oldCJ.Spec.Schedule != newCJ.Spec.Schedule || (timeZoneEnabled && !pointer.StringEqual(oldCJ.Spec.TimeZone, newCJ.Spec.TimeZone)) {
// schedule changed, change the requeue time // schedule changed, change the requeue time, pass nil recorder so that syncCronJob will output any warnings
sched, err := cron.ParseStandard(newCJ.Spec.Schedule) sched, err := cron.ParseStandard(formatSchedule(timeZoneEnabled, newCJ, nil))
if err != nil { if err != nil {
// this is likely a user error in defining the spec value // this is likely a user error in defining the spec value
// we should log the error and not reconcile this cronjob until an update to spec // we should log the error and not reconcile this cronjob until an update to spec
@@ -420,6 +424,7 @@ func (jm *ControllerV2) syncCronJob(
cronJob = cronJob.DeepCopy() cronJob = cronJob.DeepCopy()
now := jm.now() now := jm.now()
updateStatus := false updateStatus := false
timeZoneEnabled := utilfeature.DefaultFeatureGate.Enabled(features.CronJobTimeZone)
childrenJobs := make(map[types.UID]bool) childrenJobs := make(map[types.UID]bool)
for _, j := range jobs { for _, j := range jobs {
@@ -487,12 +492,21 @@ func (jm *ControllerV2) syncCronJob(
return cronJob, nil, updateStatus, nil return cronJob, nil, updateStatus, nil
} }
if timeZoneEnabled && cronJob.Spec.TimeZone != nil {
if _, err := time.LoadLocation(*cronJob.Spec.TimeZone); err != nil {
timeZone := pointer.StringDeref(cronJob.Spec.TimeZone, "")
klog.V(4).InfoS("Not starting job because timeZone is invalid", "cronjob", klog.KRef(cronJob.GetNamespace(), cronJob.GetName()), "timeZone", timeZone, "err", err)
jm.recorder.Eventf(cronJob, corev1.EventTypeWarning, "UnknownTimeZone", "invalid timeZone: %q: %s", timeZone, err)
return cronJob, nil, updateStatus, nil
}
}
if cronJob.Spec.Suspend != nil && *cronJob.Spec.Suspend { if cronJob.Spec.Suspend != nil && *cronJob.Spec.Suspend {
klog.V(4).InfoS("Not starting job because the cron is suspended", "cronjob", klog.KRef(cronJob.GetNamespace(), cronJob.GetName())) klog.V(4).InfoS("Not starting job because the cron is suspended", "cronjob", klog.KRef(cronJob.GetNamespace(), cronJob.GetName()))
return cronJob, nil, updateStatus, nil return cronJob, nil, updateStatus, nil
} }
sched, err := cron.ParseStandard(cronJob.Spec.Schedule) sched, err := cron.ParseStandard(formatSchedule(timeZoneEnabled, cronJob, jm.recorder))
if err != nil { if err != nil {
// this is likely a user error in defining the spec value // this is likely a user error in defining the spec value
// we should log the error and not reconcile this cronjob until an update to spec // we should log the error and not reconcile this cronjob until an update to spec
@@ -501,10 +515,6 @@ func (jm *ControllerV2) syncCronJob(
return cronJob, nil, updateStatus, nil return cronJob, nil, updateStatus, nil
} }
if strings.Contains(cronJob.Spec.Schedule, "TZ") {
jm.recorder.Eventf(cronJob, corev1.EventTypeWarning, "UnsupportedSchedule", "CRON_TZ or TZ used in schedule %q is not officially supported, see https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/ for more details", cronJob.Spec.Schedule)
}
scheduledTime, err := getNextScheduleTime(*cronJob, now, sched, jm.recorder) scheduledTime, err := getNextScheduleTime(*cronJob, now, sched, jm.recorder)
if err != nil { if err != nil {
// this is likely a user error in defining the spec value // this is likely a user error in defining the spec value
@@ -739,3 +749,23 @@ func deleteJob(cj *batchv1.CronJob, job *batchv1.Job, jc jobControlInterface, re
func getRef(object runtime.Object) (*corev1.ObjectReference, error) { func getRef(object runtime.Object) (*corev1.ObjectReference, error) {
return ref.GetReference(scheme.Scheme, object) return ref.GetReference(scheme.Scheme, object)
} }
func formatSchedule(timeZoneEnabled bool, cj *batchv1.CronJob, recorder record.EventRecorder) string {
if strings.Contains(cj.Spec.Schedule, "TZ") {
if recorder != nil {
recorder.Eventf(cj, corev1.EventTypeWarning, "UnsupportedSchedule", "CRON_TZ or TZ used in schedule %q is not officially supported, see https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/ for more details", cj.Spec.Schedule)
}
return cj.Spec.Schedule
}
if timeZoneEnabled && cj.Spec.TimeZone != nil {
if _, err := time.LoadLocation(*cj.Spec.TimeZone); err != nil {
return cj.Spec.Schedule
}
return fmt.Sprintf("TZ=%s %s", *cj.Spec.TimeZone, cj.Spec.Schedule)
}
return cj.Spec.Schedule
}

View File

@@ -32,10 +32,13 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue" "k8s.io/client-go/util/workqueue"
featuregatetesting "k8s.io/component-base/featuregate/testing"
_ "k8s.io/kubernetes/pkg/apis/batch/install" _ "k8s.io/kubernetes/pkg/apis/batch/install"
_ "k8s.io/kubernetes/pkg/apis/core/install" _ "k8s.io/kubernetes/pkg/apis/core/install"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
@@ -50,6 +53,9 @@ var (
errorSchedule = "obvious error schedule" errorSchedule = "obvious error schedule"
// schedule is hourly on the hour // schedule is hourly on the hour
onTheHour = "0 * * * ?" onTheHour = "0 * * * ?"
errorTimeZone = "bad timezone"
newYork = "America/New_York"
) )
// returns a cronJob with some fields filled in. // returns a cronJob with some fields filled in.
@@ -127,6 +133,19 @@ func justAfterTheHour() *time.Time {
return &T1 return &T1
} }
func justAfterTheHourInZone(tz string) time.Time {
location, err := time.LoadLocation(tz)
if err != nil {
panic("tz error: " + err.Error())
}
T1, err := time.ParseInLocation(time.RFC3339, "2016-05-19T10:01:00Z", location)
if err != nil {
panic("test setup error: " + err.Error())
}
return T1
}
func justBeforeTheHour() time.Time { func justBeforeTheHour() time.Time {
T1, err := time.Parse(time.RFC3339, "2016-05-19T09:59:00Z") T1, err := time.Parse(time.RFC3339, "2016-05-19T09:59:00Z")
if err != nil { if err != nil {
@@ -162,6 +181,7 @@ func TestControllerV2SyncCronJob(t *testing.T) {
concurrencyPolicy batchv1.ConcurrencyPolicy concurrencyPolicy batchv1.ConcurrencyPolicy
suspend bool suspend bool
schedule string schedule string
timeZone *string
deadline int64 deadline int64
// cj status // cj status
@@ -173,6 +193,7 @@ func TestControllerV2SyncCronJob(t *testing.T) {
now time.Time now time.Time
jobCreateError error jobCreateError error
jobGetErr error jobGetErr error
enableTimeZone bool
// expectations // expectations
expectCreate bool expectCreate bool
@@ -212,6 +233,17 @@ func TestControllerV2SyncCronJob(t *testing.T) {
expectedWarnings: 1, expectedWarnings: 1,
jobPresentInCJActiveStatus: true, jobPresentInCJActiveStatus: true,
}, },
"never ran, not valid time zone": {
concurrencyPolicy: "Allow",
schedule: onTheHour,
timeZone: &errorTimeZone,
deadline: noDead,
jobCreationTime: justAfterThePriorHour(),
now: justBeforeTheHour(),
enableTimeZone: true,
expectedWarnings: 1,
jobPresentInCJActiveStatus: true,
},
"never ran, not time, A": { "never ran, not time, A": {
concurrencyPolicy: "Allow", concurrencyPolicy: "Allow",
schedule: onTheHour, schedule: onTheHour,
@@ -238,6 +270,17 @@ func TestControllerV2SyncCronJob(t *testing.T) {
expectRequeueAfter: true, expectRequeueAfter: true,
jobPresentInCJActiveStatus: true, jobPresentInCJActiveStatus: true,
}, },
"never ran, not time in zone": {
concurrencyPolicy: "Allow",
schedule: onTheHour,
timeZone: &newYork,
deadline: noDead,
jobCreationTime: justAfterThePriorHour(),
now: justBeforeTheHour(),
enableTimeZone: true,
expectRequeueAfter: true,
jobPresentInCJActiveStatus: true,
},
"never ran, is time, A": { "never ran, is time, A": {
concurrencyPolicy: "Allow", concurrencyPolicy: "Allow",
schedule: onTheHour, schedule: onTheHour,
@@ -274,6 +317,48 @@ func TestControllerV2SyncCronJob(t *testing.T) {
expectUpdateStatus: true, expectUpdateStatus: true,
jobPresentInCJActiveStatus: true, jobPresentInCJActiveStatus: true,
}, },
"never ran, is time in zone, but time zone disabled": {
concurrencyPolicy: "Allow",
schedule: onTheHour,
timeZone: &newYork,
deadline: noDead,
jobCreationTime: justAfterThePriorHour(),
now: justAfterTheHourInZone(newYork),
enableTimeZone: false,
expectCreate: true,
expectActive: 1,
expectRequeueAfter: true,
expectUpdateStatus: true,
jobPresentInCJActiveStatus: true,
},
"never ran, is time in zone": {
concurrencyPolicy: "Allow",
schedule: onTheHour,
timeZone: &newYork,
deadline: noDead,
jobCreationTime: justAfterThePriorHour(),
now: justAfterTheHourInZone(newYork),
enableTimeZone: true,
expectCreate: true,
expectActive: 1,
expectRequeueAfter: true,
expectUpdateStatus: true,
jobPresentInCJActiveStatus: true,
},
"never ran, is time in zone, but TZ is also set in schedule": {
concurrencyPolicy: "Allow",
schedule: "TZ=UTC " + onTheHour,
timeZone: &newYork,
deadline: noDead,
jobCreationTime: justAfterThePriorHour(),
now: justAfterTheHourInZone(newYork),
enableTimeZone: true,
expectCreate: true,
expectedWarnings: 1,
expectRequeueAfter: true,
expectUpdateStatus: true,
jobPresentInCJActiveStatus: true,
},
"never ran, is time, suspended": { "never ran, is time, suspended": {
concurrencyPolicy: "Allow", concurrencyPolicy: "Allow",
suspend: true, suspend: true,
@@ -815,11 +900,15 @@ func TestControllerV2SyncCronJob(t *testing.T) {
for name, tc := range testCases { for name, tc := range testCases {
name := name name := name
tc := tc tc := tc
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.CronJobTimeZone, tc.enableTimeZone)()
cj := cronJob() cj := cronJob()
cj.Spec.ConcurrencyPolicy = tc.concurrencyPolicy cj.Spec.ConcurrencyPolicy = tc.concurrencyPolicy
cj.Spec.Suspend = &tc.suspend cj.Spec.Suspend = &tc.suspend
cj.Spec.Schedule = tc.schedule cj.Spec.Schedule = tc.schedule
cj.Spec.TimeZone = tc.timeZone
if tc.deadline != noDead { if tc.deadline != noDead {
cj.Spec.StartingDeadlineSeconds = &tc.deadline cj.Spec.StartingDeadlineSeconds = &tc.deadline
} }
@@ -1058,6 +1147,63 @@ func TestControllerV2UpdateCronJob(t *testing.T) {
}, },
expectedDelay: 1*time.Second + nextScheduleDelta, expectedDelay: 1*time.Second + nextScheduleDelta,
}, },
{
name: "spec.timeZone not changed",
oldCronJob: &batchv1.CronJob{
Spec: batchv1.CronJobSpec{
TimeZone: &newYork,
JobTemplate: batchv1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b"},
Annotations: map[string]string{"x": "y"},
},
Spec: jobSpec(),
},
},
},
newCronJob: &batchv1.CronJob{
Spec: batchv1.CronJobSpec{
TimeZone: &newYork,
JobTemplate: batchv1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "foo"},
Annotations: map[string]string{"x": "y"},
},
Spec: jobSpec(),
},
},
},
expectedDelay: 0 * time.Second,
},
{
name: "spec.timeZone changed",
oldCronJob: &batchv1.CronJob{
Spec: batchv1.CronJobSpec{
TimeZone: &newYork,
JobTemplate: batchv1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "b"},
Annotations: map[string]string{"x": "y"},
},
Spec: jobSpec(),
},
},
},
newCronJob: &batchv1.CronJob{
Spec: batchv1.CronJobSpec{
TimeZone: nil,
JobTemplate: batchv1.JobTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"a": "foo"},
Annotations: map[string]string{"x": "y"},
},
Spec: jobSpec(),
},
},
},
expectedDelay: 0 * time.Second,
},
// TODO: Add more test cases for updating scheduling. // TODO: Add more test cases for updating scheduling.
} }
for _, tt := range tests { for _, tt := range tests {

View File

@@ -12682,6 +12682,13 @@ func schema_k8sio_api_batch_v1_CronJobSpec(ref common.ReferenceCallback) common.
Format: "", Format: "",
}, },
}, },
"timeZone": {
SchemaProps: spec.SchemaProps{
Description: "The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will rely on the time zone of the kube-controller-manager process. ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.",
Type: []string{"string"},
Format: "",
},
},
"startingDeadlineSeconds": { "startingDeadlineSeconds": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.", Description: "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.",
@@ -13312,6 +13319,13 @@ func schema_k8sio_api_batch_v1beta1_CronJobSpec(ref common.ReferenceCallback) co
Format: "", Format: "",
}, },
}, },
"timeZone": {
SchemaProps: spec.SchemaProps{
Description: "The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will rely on the time zone of the kube-controller-manager process. ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.",
Type: []string{"string"},
Format: "",
},
},
"startingDeadlineSeconds": { "startingDeadlineSeconds": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.", Description: "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.",

View File

@@ -27,8 +27,10 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/features"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/pod" "k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
@@ -88,6 +90,10 @@ func (cronJobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object)
cronJob.Generation = 1 cronJob.Generation = 1
if !utilfeature.DefaultFeatureGate.Enabled(features.CronJobTimeZone) {
cronJob.Spec.TimeZone = nil
}
pod.DropDisabledTemplateFields(&cronJob.Spec.JobTemplate.Spec.Template, nil) pod.DropDisabledTemplateFields(&cronJob.Spec.JobTemplate.Spec.Template, nil)
} }
@@ -97,6 +103,10 @@ func (cronJobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Ob
oldCronJob := old.(*batch.CronJob) oldCronJob := old.(*batch.CronJob)
newCronJob.Status = oldCronJob.Status newCronJob.Status = oldCronJob.Status
if !utilfeature.DefaultFeatureGate.Enabled(features.CronJobTimeZone) && oldCronJob.Spec.TimeZone == nil {
newCronJob.Spec.TimeZone = nil
}
pod.DropDisabledTemplateFields(&newCronJob.Spec.JobTemplate.Spec.Template, &oldCronJob.Spec.JobTemplate.Spec.Template) pod.DropDisabledTemplateFields(&newCronJob.Spec.JobTemplate.Spec.Template, &oldCronJob.Spec.JobTemplate.Spec.Template)
// Any changes to the spec increment the generation number. // Any changes to the spec increment the generation number.
@@ -110,7 +120,7 @@ func (cronJobStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Ob
func (cronJobStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { func (cronJobStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
cronJob := obj.(*batch.CronJob) cronJob := obj.(*batch.CronJob)
opts := pod.GetValidationOptionsFromPodTemplate(&cronJob.Spec.JobTemplate.Spec.Template, nil) opts := pod.GetValidationOptionsFromPodTemplate(&cronJob.Spec.JobTemplate.Spec.Template, nil)
return validation.ValidateCronJob(cronJob, opts) return validation.ValidateCronJobCreate(cronJob, opts)
} }
// WarningsOnCreate returns warnings for the creation of the given object. // WarningsOnCreate returns warnings for the creation of the given object.

View File

@@ -375,96 +375,97 @@ func init() {
} }
var fileDescriptor_3b52da57c93de713 = []byte{ var fileDescriptor_3b52da57c93de713 = []byte{
// 1416 bytes of a gzipped FileDescriptorProto // 1431 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xc1, 0x6f, 0x1b, 0x45, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x41, 0x6f, 0x1b, 0xc5,
0x17, 0xcf, 0x26, 0x71, 0x6c, 0x8f, 0x93, 0xd4, 0x9d, 0x7e, 0x6d, 0xfd, 0xf9, 0xab, 0xbc, 0xa9, 0x17, 0xcf, 0xc6, 0x71, 0x6c, 0x8f, 0x93, 0xd4, 0x9d, 0xfe, 0xdb, 0xfa, 0x6f, 0x2a, 0x6f, 0x6a,
0xbf, 0x82, 0x02, 0x2a, 0x6b, 0x12, 0x22, 0x04, 0x08, 0x90, 0xb2, 0xa9, 0x0a, 0x0d, 0x8e, 0x1a, 0x0a, 0x0a, 0xa8, 0xac, 0x49, 0x88, 0x10, 0x20, 0x40, 0xca, 0xa6, 0x2a, 0x34, 0x38, 0x6a, 0x18,
0xc6, 0x8e, 0x90, 0xa0, 0x20, 0xd6, 0xbb, 0x63, 0x67, 0x9b, 0xdd, 0x1d, 0x6b, 0x67, 0x1c, 0xe1, 0x3b, 0x42, 0x2a, 0x05, 0xb1, 0xde, 0x1d, 0x3b, 0xdb, 0xac, 0x77, 0xac, 0x9d, 0x71, 0x84, 0x6f,
0x1b, 0x12, 0xff, 0x00, 0xfc, 0x13, 0x88, 0x13, 0x42, 0x82, 0x33, 0x47, 0xd4, 0x63, 0x8f, 0x3d, 0x48, 0x7c, 0x01, 0xf8, 0x12, 0x1c, 0x11, 0x12, 0x9c, 0x39, 0xa2, 0x1e, 0x2b, 0x4e, 0x3d, 0xad,
0xad, 0xe8, 0xf2, 0x07, 0x70, 0x0f, 0x17, 0x34, 0xb3, 0xe3, 0xdd, 0xb5, 0xbd, 0x1b, 0xd2, 0x1e, 0xe8, 0xf2, 0x01, 0xb8, 0x87, 0x0b, 0x9a, 0xd9, 0xf1, 0xee, 0xda, 0xde, 0x0d, 0x49, 0x0f, 0x15,
0x2a, 0x6e, 0xdd, 0x37, 0xbf, 0xf7, 0x9b, 0xe7, 0xf7, 0x7e, 0xf3, 0xde, 0x4b, 0xc1, 0xbb, 0x27, 0xb7, 0xcc, 0x9b, 0xdf, 0xfb, 0xcd, 0xdb, 0x79, 0xbf, 0x79, 0xef, 0x39, 0xe0, 0xfd, 0xe3, 0x77,
0x6f, 0x51, 0xcd, 0x26, 0xad, 0x93, 0x51, 0x0f, 0xfb, 0x1e, 0x66, 0x98, 0xb6, 0x4e, 0xb1, 0x67, 0xa8, 0x66, 0x93, 0xe6, 0xf1, 0xa8, 0x8b, 0x3d, 0x17, 0x33, 0x4c, 0x9b, 0x27, 0xd8, 0xb5, 0x88,
0x11, 0xbf, 0x25, 0x0f, 0x8c, 0xa1, 0xdd, 0xea, 0x19, 0xcc, 0x3c, 0x6e, 0x9d, 0x6e, 0xb5, 0x06, 0xd7, 0x94, 0x1b, 0xc6, 0xd0, 0x6e, 0x76, 0x0d, 0x66, 0x1e, 0x35, 0x4f, 0x36, 0x9b, 0x7d, 0xec,
0xd8, 0xc3, 0xbe, 0xc1, 0xb0, 0xa5, 0x0d, 0x7d, 0xc2, 0x08, 0xbc, 0x12, 0x81, 0x34, 0x63, 0x68, 0x62, 0xcf, 0x60, 0xd8, 0xd2, 0x86, 0x1e, 0x61, 0x04, 0x5e, 0x09, 0x41, 0x9a, 0x31, 0xb4, 0x35,
0x6b, 0x02, 0xa4, 0x9d, 0x6e, 0xd5, 0x5f, 0x1b, 0xd8, 0xec, 0x78, 0xd4, 0xd3, 0x4c, 0xe2, 0xb6, 0x01, 0xd2, 0x4e, 0x36, 0x6b, 0x6f, 0xf4, 0x6d, 0x76, 0x34, 0xea, 0x6a, 0x26, 0x19, 0x34, 0xfb,
0x06, 0x64, 0x40, 0x5a, 0x02, 0xdb, 0x1b, 0xf5, 0xc5, 0x97, 0xf8, 0x10, 0xff, 0x8a, 0x38, 0xea, 0xa4, 0x4f, 0x9a, 0x02, 0xdb, 0x1d, 0xf5, 0xc4, 0x4a, 0x2c, 0xc4, 0x5f, 0x21, 0x47, 0xad, 0x91,
0xcd, 0xd4, 0x45, 0x26, 0xf1, 0x71, 0xc6, 0x3d, 0xf5, 0x9d, 0x04, 0xe3, 0x1a, 0xe6, 0xb1, 0xed, 0x38, 0xc8, 0x24, 0x1e, 0x4e, 0x39, 0xa7, 0xb6, 0x1d, 0x63, 0x06, 0x86, 0x79, 0x64, 0xbb, 0xd8,
0x61, 0x7f, 0xdc, 0x1a, 0x9e, 0x0c, 0xb8, 0x81, 0xb6, 0x5c, 0xcc, 0x8c, 0x2c, 0xaf, 0x56, 0x9e, 0x1b, 0x37, 0x87, 0xc7, 0x7d, 0x6e, 0xa0, 0xcd, 0x01, 0x66, 0x46, 0x9a, 0x57, 0x33, 0xcb, 0xcb,
0x97, 0x3f, 0xf2, 0x98, 0xed, 0xe2, 0x39, 0x87, 0x37, 0xff, 0xc9, 0x81, 0x9a, 0xc7, 0xd8, 0x35, 0x1b, 0xb9, 0xcc, 0x1e, 0xe0, 0x39, 0x87, 0xb7, 0xff, 0xcd, 0x81, 0x9a, 0x47, 0x78, 0x60, 0xcc,
0x66, 0xfd, 0x9a, 0x7f, 0x29, 0xa0, 0xb8, 0xe7, 0x13, 0x6f, 0x9f, 0xf4, 0xe0, 0x97, 0xa0, 0xc4, 0xfa, 0x35, 0xfe, 0x56, 0x40, 0x61, 0xd7, 0x23, 0xee, 0x1e, 0xe9, 0xc2, 0xaf, 0x40, 0x91, 0xc7,
0xe3, 0xb1, 0x0c, 0x66, 0xd4, 0x94, 0x0d, 0x65, 0xb3, 0xb2, 0xfd, 0xba, 0x96, 0x64, 0x29, 0xa6, 0x63, 0x19, 0xcc, 0xa8, 0x2a, 0xeb, 0xca, 0x46, 0x79, 0xeb, 0x4d, 0x2d, 0xbe, 0xa5, 0x88, 0x56,
0xd5, 0x86, 0x27, 0x03, 0x6e, 0xa0, 0x1a, 0x47, 0x6b, 0xa7, 0x5b, 0xda, 0xfd, 0xde, 0x43, 0x6c, 0x1b, 0x1e, 0xf7, 0xb9, 0x81, 0x6a, 0x1c, 0xad, 0x9d, 0x6c, 0x6a, 0xf7, 0xbb, 0x8f, 0xb0, 0xc9,
0xb2, 0x03, 0xcc, 0x0c, 0x1d, 0x3e, 0x0a, 0xd4, 0x85, 0x30, 0x50, 0x41, 0x62, 0x43, 0x31, 0x2b, 0xf6, 0x31, 0x33, 0x74, 0xf8, 0xd8, 0x57, 0x17, 0x02, 0x5f, 0x05, 0xb1, 0x0d, 0x45, 0xac, 0x50,
0xd4, 0xc1, 0x32, 0x1d, 0x62, 0xb3, 0xb6, 0x28, 0xd8, 0x37, 0xb4, 0x8c, 0x1a, 0x68, 0x32, 0x9a, 0x07, 0x4b, 0x74, 0x88, 0xcd, 0xea, 0xa2, 0x60, 0x5f, 0xd7, 0x52, 0x72, 0xa0, 0xc9, 0x68, 0xda,
0xce, 0x10, 0x9b, 0xfa, 0xaa, 0x64, 0x5b, 0xe6, 0x5f, 0x48, 0xf8, 0xc2, 0x7d, 0xb0, 0x42, 0x99, 0x43, 0x6c, 0xea, 0x2b, 0x92, 0x6d, 0x89, 0xaf, 0x90, 0xf0, 0x85, 0x7b, 0x60, 0x99, 0x32, 0x83,
0xc1, 0x46, 0xb4, 0xb6, 0x24, 0x58, 0x9a, 0xe7, 0xb2, 0x08, 0xa4, 0xbe, 0x2e, 0x79, 0x56, 0xa2, 0x8d, 0x68, 0x35, 0x27, 0x58, 0x1a, 0x67, 0xb2, 0x08, 0xa4, 0xbe, 0x26, 0x79, 0x96, 0xc3, 0x35,
0x6f, 0x24, 0x19, 0x9a, 0x3f, 0x2a, 0xa0, 0x22, 0x91, 0x6d, 0x9b, 0x32, 0xf8, 0x60, 0x2e, 0x03, 0x92, 0x0c, 0x8d, 0x1f, 0x15, 0x50, 0x96, 0xc8, 0x96, 0x4d, 0x19, 0x7c, 0x38, 0x77, 0x03, 0xda,
0xda, 0xc5, 0x32, 0xc0, 0xbd, 0xc5, 0xef, 0xaf, 0xca, 0x9b, 0x4a, 0x13, 0x4b, 0xea, 0xd7, 0xef, 0xf9, 0x6e, 0x80, 0x7b, 0x8b, 0xef, 0xaf, 0xc8, 0x93, 0x8a, 0x13, 0x4b, 0xe2, 0xeb, 0x77, 0x40,
0x82, 0x82, 0xcd, 0xb0, 0x4b, 0x6b, 0x8b, 0x1b, 0x4b, 0x9b, 0x95, 0xed, 0x1b, 0xe7, 0x05, 0xae, 0xde, 0x66, 0x78, 0x40, 0xab, 0x8b, 0xeb, 0xb9, 0x8d, 0xf2, 0xd6, 0x8d, 0xb3, 0x02, 0xd7, 0x57,
0xaf, 0x49, 0xa2, 0xc2, 0x3d, 0xee, 0x82, 0x22, 0xcf, 0xe6, 0x0f, 0xcb, 0x71, 0xc0, 0x3c, 0x25, 0x25, 0x51, 0xfe, 0x1e, 0x77, 0x41, 0xa1, 0x67, 0xe3, 0xf7, 0xa5, 0x28, 0x60, 0x7e, 0x25, 0xf0,
0xf0, 0x36, 0x28, 0xf1, 0xc2, 0x5a, 0x23, 0x07, 0x8b, 0x80, 0xcb, 0x49, 0x00, 0x1d, 0x69, 0x47, 0x36, 0x28, 0xf2, 0xc4, 0x5a, 0x23, 0x07, 0x8b, 0x80, 0x4b, 0x71, 0x00, 0x6d, 0x69, 0x47, 0x11,
0x31, 0x02, 0x1e, 0x81, 0xeb, 0x94, 0x19, 0x3e, 0xb3, 0xbd, 0xc1, 0x1d, 0x6c, 0x58, 0x8e, 0xed, 0x02, 0x6e, 0x80, 0x22, 0xd7, 0xc2, 0x03, 0xe2, 0xe2, 0x6a, 0x51, 0xa0, 0x57, 0x38, 0xb2, 0x23,
0xe1, 0x0e, 0x36, 0x89, 0x67, 0x51, 0x51, 0x91, 0x25, 0xfd, 0x7f, 0x61, 0xa0, 0x5e, 0xef, 0x64, 0x6d, 0x28, 0xda, 0x85, 0x87, 0xe0, 0x3a, 0x65, 0x86, 0xc7, 0x6c, 0xb7, 0x7f, 0x07, 0x1b, 0x96,
0x43, 0x50, 0x9e, 0x2f, 0x7c, 0x00, 0x2e, 0x9b, 0xc4, 0x33, 0x47, 0xbe, 0x8f, 0x3d, 0x73, 0x7c, 0x63, 0xbb, 0xb8, 0x8d, 0x4d, 0xe2, 0x5a, 0x54, 0xe4, 0x2e, 0xa7, 0xbf, 0x14, 0xf8, 0xea, 0xf5,
0x48, 0x1c, 0xdb, 0x1c, 0x8b, 0xe2, 0x94, 0x75, 0x4d, 0x46, 0x73, 0x79, 0x6f, 0x16, 0x70, 0x96, 0x76, 0x3a, 0x04, 0x65, 0xf9, 0xc2, 0x87, 0xe0, 0xb2, 0x49, 0x5c, 0x73, 0xe4, 0x79, 0xd8, 0x35,
0x65, 0x44, 0xf3, 0x44, 0xf0, 0x25, 0x50, 0xa4, 0x23, 0x3a, 0xc4, 0x9e, 0x55, 0x5b, 0xde, 0x50, 0xc7, 0x07, 0xc4, 0xb1, 0xcd, 0xb1, 0x48, 0x63, 0x49, 0xd7, 0x64, 0xdc, 0x97, 0x77, 0x67, 0x01,
0x36, 0x4b, 0x7a, 0x25, 0x0c, 0xd4, 0x62, 0x27, 0x32, 0xa1, 0xc9, 0x19, 0xfc, 0x0c, 0x54, 0x1e, 0xa7, 0x69, 0x46, 0x34, 0x4f, 0x04, 0x5f, 0x01, 0x05, 0x3a, 0xa2, 0x43, 0xec, 0x5a, 0xd5, 0xa5,
0x92, 0x5e, 0x17, 0xbb, 0x43, 0xc7, 0x60, 0xb8, 0x56, 0x10, 0xd5, 0xbb, 0x95, 0x99, 0xe2, 0xfd, 0x75, 0x65, 0xa3, 0xa8, 0x97, 0x03, 0x5f, 0x2d, 0xb4, 0x43, 0x13, 0x9a, 0xec, 0xc1, 0xcf, 0x41,
0x04, 0x27, 0x54, 0x76, 0x45, 0x06, 0x59, 0x49, 0x1d, 0xa0, 0x34, 0x1b, 0xfc, 0x02, 0xd4, 0xe9, 0xf9, 0x11, 0xe9, 0x76, 0xf0, 0x60, 0xe8, 0x18, 0x0c, 0x57, 0xf3, 0x22, 0xcf, 0xb7, 0x52, 0x93,
0xc8, 0x34, 0x31, 0xa5, 0xfd, 0x91, 0xb3, 0x4f, 0x7a, 0xf4, 0x43, 0x9b, 0x32, 0xe2, 0x8f, 0xdb, 0xb1, 0x17, 0xe3, 0x84, 0x1e, 0xaf, 0xc8, 0x20, 0xcb, 0x89, 0x0d, 0x94, 0x64, 0x83, 0x5f, 0x82,
0xb6, 0x6b, 0xb3, 0xda, 0xca, 0x86, 0xb2, 0x59, 0xd0, 0x1b, 0x61, 0xa0, 0xd6, 0x3b, 0xb9, 0x28, 0x1a, 0x1d, 0x99, 0x26, 0xa6, 0xb4, 0x37, 0x72, 0xf6, 0x48, 0x97, 0x7e, 0x6c, 0x53, 0x46, 0xbc,
0x74, 0x0e, 0x03, 0x44, 0xe0, 0x5a, 0xdf, 0xb0, 0x1d, 0x6c, 0xcd, 0x71, 0x17, 0x05, 0x77, 0x3d, 0x71, 0xcb, 0x1e, 0xd8, 0xac, 0xba, 0xbc, 0xae, 0x6c, 0xe4, 0xf5, 0x7a, 0xe0, 0xab, 0xb5, 0x76,
0x0c, 0xd4, 0x6b, 0x77, 0x33, 0x11, 0x28, 0xc7, 0xb3, 0xf9, 0xeb, 0x22, 0x58, 0x9b, 0x7a, 0x05, 0x26, 0x0a, 0x9d, 0xc1, 0x00, 0x11, 0xb8, 0xd6, 0x33, 0x6c, 0x07, 0x5b, 0x73, 0xdc, 0x05, 0xc1,
0xf0, 0x23, 0xb0, 0x62, 0x98, 0xcc, 0x3e, 0xe5, 0x52, 0xe1, 0x02, 0xfc, 0x7f, 0x3a, 0x3b, 0xbc, 0x5d, 0x0b, 0x7c, 0xf5, 0xda, 0xdd, 0x54, 0x04, 0xca, 0xf0, 0x6c, 0xfc, 0xba, 0x08, 0x56, 0xa7,
0x7f, 0x25, 0x6f, 0x19, 0xe1, 0x3e, 0xe6, 0x45, 0xc0, 0xc9, 0xd3, 0xd9, 0x15, 0xae, 0x48, 0x52, 0xde, 0x0b, 0xfc, 0x04, 0x2c, 0x1b, 0x26, 0xb3, 0x4f, 0xb8, 0xa8, 0xb8, 0x54, 0x5f, 0x4e, 0xde,
0x40, 0x07, 0x54, 0x1d, 0x83, 0xb2, 0x89, 0xca, 0xba, 0xb6, 0x8b, 0x45, 0x7d, 0x2a, 0xdb, 0xaf, 0x0e, 0xaf, 0x74, 0xf1, 0xab, 0x47, 0xb8, 0x87, 0x79, 0x12, 0x70, 0xfc, 0xc8, 0x76, 0x84, 0x2b,
0x5e, 0xec, 0xc9, 0x70, 0x0f, 0xfd, 0x3f, 0x61, 0xa0, 0x56, 0xdb, 0x33, 0x3c, 0x68, 0x8e, 0x19, 0x92, 0x14, 0xd0, 0x01, 0x15, 0xc7, 0xa0, 0x6c, 0xa2, 0x47, 0xae, 0x36, 0x91, 0x9f, 0xf2, 0xd6,
0xfa, 0x00, 0x0a, 0x5b, 0x9c, 0x42, 0x71, 0x5f, 0xe1, 0x99, 0xef, 0xbb, 0x16, 0x06, 0x2a, 0x6c, 0xeb, 0xe7, 0x7b, 0x5c, 0xdc, 0x43, 0xff, 0x5f, 0xe0, 0xab, 0x95, 0xd6, 0x0c, 0x0f, 0x9a, 0x63,
0xcf, 0x31, 0xa1, 0x0c, 0xf6, 0xe6, 0x9f, 0x0a, 0x58, 0x7a, 0x31, 0x6d, 0xf1, 0xfd, 0xa9, 0xb6, 0x86, 0x1e, 0x80, 0xc2, 0x16, 0x5d, 0xa1, 0x38, 0x2f, 0x7f, 0xe1, 0xf3, 0xae, 0x05, 0xbe, 0x0a,
0x78, 0x23, 0x4f, 0xb4, 0xb9, 0x2d, 0xf1, 0xee, 0x4c, 0x4b, 0x6c, 0xe4, 0x32, 0x9c, 0xdf, 0x0e, 0x5b, 0x73, 0x4c, 0x28, 0x85, 0xbd, 0xf1, 0x97, 0x02, 0x72, 0x2f, 0xa6, 0x80, 0x7e, 0x38, 0x55,
0x7f, 0x5b, 0x02, 0xab, 0xfb, 0xa4, 0xb7, 0x47, 0x3c, 0xcb, 0x66, 0x36, 0xf1, 0xe0, 0x0e, 0x58, 0x40, 0x6f, 0x64, 0x89, 0x36, 0xb3, 0x78, 0xde, 0x9d, 0x29, 0x9e, 0xf5, 0x4c, 0x86, 0xb3, 0x0b,
0x66, 0xe3, 0xe1, 0xa4, 0xb5, 0x6c, 0x4c, 0xae, 0xee, 0x8e, 0x87, 0xf8, 0x2c, 0x50, 0xab, 0x69, 0xe7, 0x6f, 0x39, 0xb0, 0xb2, 0x47, 0xba, 0xbb, 0xc4, 0xb5, 0x6c, 0x66, 0x13, 0x17, 0x6e, 0x83,
0x2c, 0xb7, 0x21, 0x81, 0x86, 0xed, 0x38, 0x9c, 0x45, 0xe1, 0xb7, 0x33, 0x7d, 0xdd, 0x59, 0xa0, 0x25, 0x36, 0x1e, 0x4e, 0x8a, 0xd0, 0xfa, 0xe4, 0xe8, 0xce, 0x78, 0x88, 0x4f, 0x7d, 0xb5, 0x92,
0x66, 0x0c, 0x4e, 0x2d, 0x66, 0x9a, 0x0e, 0x0a, 0x0e, 0xc0, 0x1a, 0x2f, 0xce, 0xa1, 0x4f, 0x7a, 0xc4, 0x72, 0x1b, 0x12, 0x68, 0xd8, 0x8a, 0xc2, 0x59, 0x14, 0x7e, 0xdb, 0xd3, 0xc7, 0x9d, 0xfa,
0x91, 0xca, 0x96, 0x9e, 0xb9, 0xea, 0x57, 0x65, 0x00, 0x6b, 0xed, 0x34, 0x11, 0x9a, 0xe6, 0x85, 0x6a, 0x4a, 0x8b, 0xd5, 0x22, 0xa6, 0xe9, 0xa0, 0x60, 0x1f, 0xac, 0xf2, 0xe4, 0x1c, 0x78, 0xa4,
0xa7, 0x91, 0xc6, 0xba, 0xbe, 0xe1, 0xd1, 0xe8, 0x27, 0x3d, 0x9f, 0xa6, 0xeb, 0xf2, 0x36, 0xa1, 0x1b, 0xaa, 0x2c, 0x77, 0xe1, 0xac, 0x5f, 0x95, 0x01, 0xac, 0xb6, 0x92, 0x44, 0x68, 0x9a, 0x17,
0xb3, 0x69, 0x36, 0x94, 0x71, 0x03, 0x7c, 0x19, 0xac, 0xf8, 0xd8, 0xa0, 0xc4, 0x13, 0x7a, 0x2e, 0x9e, 0x84, 0x1a, 0xeb, 0x78, 0x86, 0x4b, 0xc3, 0x4f, 0x7a, 0x3e, 0x4d, 0xd7, 0xe4, 0x69, 0x42,
0x27, 0xd5, 0x41, 0xc2, 0x8a, 0xe4, 0x29, 0x7c, 0x05, 0x14, 0x5d, 0x4c, 0xa9, 0x31, 0xc0, 0xa2, 0x67, 0xd3, 0x6c, 0x28, 0xe5, 0x04, 0xf8, 0x2a, 0x58, 0xf6, 0xb0, 0x41, 0x89, 0x2b, 0xf4, 0x5c,
0xe3, 0x94, 0xf5, 0x4b, 0x12, 0x58, 0x3c, 0x88, 0xcc, 0x68, 0x72, 0xde, 0xfc, 0x5e, 0x01, 0xc5, 0x8a, 0xb3, 0x83, 0x84, 0x15, 0xc9, 0x5d, 0xf8, 0x1a, 0x28, 0x0c, 0x30, 0xa5, 0x46, 0x1f, 0x8b,
0x17, 0x33, 0xd3, 0xde, 0x9b, 0x9e, 0x69, 0xb5, 0x3c, 0xe5, 0xe5, 0xcc, 0xb3, 0x9f, 0x0a, 0x22, 0x8a, 0x53, 0xd2, 0x2f, 0x49, 0x60, 0x61, 0x3f, 0x34, 0xa3, 0xc9, 0x7e, 0xe3, 0x07, 0x05, 0x14,
0x50, 0x31, 0xcb, 0xb6, 0x40, 0x65, 0x68, 0xf8, 0x86, 0xe3, 0x60, 0xc7, 0xa6, 0xae, 0x88, 0xb5, 0x5e, 0x4c, 0xf7, 0xfb, 0x60, 0xba, 0xfb, 0x55, 0xb3, 0x94, 0x97, 0xd1, 0xf9, 0x7e, 0xca, 0x8b,
0xa0, 0x5f, 0xe2, 0x7d, 0xf9, 0x30, 0x31, 0xa3, 0x34, 0x86, 0xbb, 0x98, 0xc4, 0x1d, 0x3a, 0x98, 0x40, 0x45, 0xd7, 0xdb, 0x04, 0xe5, 0xa1, 0xe1, 0x19, 0x8e, 0x83, 0x1d, 0x9b, 0x0e, 0x44, 0xac,
0x27, 0x33, 0x92, 0x9b, 0x74, 0xd9, 0x4b, 0xcc, 0x28, 0x8d, 0x81, 0xf7, 0xc1, 0xd5, 0xa8, 0x83, 0x79, 0xfd, 0x12, 0xaf, 0xcb, 0x07, 0xb1, 0x19, 0x25, 0x31, 0xdc, 0xc5, 0x24, 0x83, 0xa1, 0x83,
0xcd, 0x4e, 0xc0, 0x25, 0x31, 0x01, 0xff, 0x1b, 0x06, 0xea, 0xd5, 0xdd, 0x2c, 0x00, 0xca, 0xf6, 0xf9, 0x65, 0x86, 0x72, 0x93, 0x2e, 0xbb, 0xb1, 0x19, 0x25, 0x31, 0xf0, 0x3e, 0xb8, 0x1a, 0x56,
0x83, 0x3b, 0x60, 0xb5, 0x67, 0x98, 0x27, 0xa4, 0xdf, 0x4f, 0x77, 0xec, 0x6a, 0x18, 0xa8, 0xab, 0xb0, 0xd9, 0x0e, 0x98, 0x13, 0x1d, 0xf0, 0xff, 0x81, 0xaf, 0x5e, 0xdd, 0x49, 0x03, 0xa0, 0x74,
0x7a, 0xca, 0x8e, 0xa6, 0x50, 0xf0, 0x73, 0x50, 0xa2, 0xd8, 0xc1, 0x26, 0x23, 0xbe, 0x94, 0xd8, 0x3f, 0xb8, 0x0d, 0x56, 0xba, 0x86, 0x79, 0x4c, 0x7a, 0xbd, 0x64, 0xc5, 0xae, 0x04, 0xbe, 0xba,
0x1b, 0x17, 0xac, 0x8a, 0xd1, 0xc3, 0x4e, 0x47, 0xba, 0xea, 0xab, 0x62, 0xd2, 0xcb, 0x2f, 0x14, 0xa2, 0x27, 0xec, 0x68, 0x0a, 0x05, 0xbf, 0x00, 0x45, 0x8a, 0x1d, 0x6c, 0x32, 0xe2, 0x49, 0x89,
0x53, 0xc2, 0x77, 0xc0, 0xba, 0x6b, 0x78, 0x23, 0x23, 0x46, 0x0a, 0x6d, 0x95, 0x74, 0x18, 0x06, 0xbd, 0x75, 0xce, 0xac, 0x18, 0x5d, 0xec, 0xb4, 0xa5, 0x6b, 0xd8, 0xe9, 0x27, 0x2b, 0x14, 0x51,
0xea, 0xfa, 0xc1, 0xd4, 0x09, 0x9a, 0x41, 0xc2, 0x8f, 0x41, 0x89, 0x4d, 0xc6, 0xe8, 0x8a, 0x08, 0xc2, 0xf7, 0xc0, 0xda, 0xc0, 0x70, 0x47, 0x46, 0x84, 0x14, 0xda, 0x2a, 0xea, 0x30, 0xf0, 0xd5,
0x2d, 0x73, 0x50, 0x1c, 0x12, 0x6b, 0x6a, 0x8a, 0xc6, 0x2a, 0x89, 0x47, 0x68, 0x4c, 0xc3, 0x17, 0xb5, 0xfd, 0xa9, 0x1d, 0x34, 0x83, 0x84, 0x9f, 0x82, 0x22, 0x9b, 0xb4, 0xd1, 0x65, 0x11, 0x5a,
0x0f, 0xc6, 0x1c, 0x99, 0xb1, 0xdd, 0x3e, 0xc3, 0xfe, 0x5d, 0xdb, 0xb3, 0xe9, 0x31, 0xb6, 0x6a, 0x6a, 0xa3, 0x38, 0x20, 0xd6, 0x54, 0x17, 0x8d, 0x54, 0x12, 0xb5, 0xd0, 0x88, 0x86, 0x0f, 0x1e,
0x25, 0x91, 0x2e, 0xb1, 0x78, 0x74, 0xbb, 0xed, 0x2c, 0x08, 0xca, 0xf3, 0x85, 0x6d, 0xb0, 0x9e, 0x8c, 0x39, 0xf2, 0xc6, 0x76, 0x7a, 0x0c, 0x7b, 0x77, 0x6d, 0xd7, 0xa6, 0x47, 0xd8, 0x12, 0x13,
0x94, 0xf6, 0x80, 0x58, 0xb8, 0x56, 0x16, 0x0f, 0xe3, 0x16, 0xff, 0x95, 0x7b, 0x53, 0x27, 0x67, 0x4b, 0x3e, 0x1c, 0x3c, 0x3a, 0x9d, 0x56, 0x1a, 0x04, 0x65, 0xf9, 0xc2, 0x16, 0x58, 0x8b, 0x53,
0x73, 0x16, 0x34, 0xe3, 0x9b, 0x5e, 0x34, 0x40, 0xfe, 0xa2, 0xd1, 0xfc, 0xae, 0x00, 0xca, 0xc9, 0xbb, 0x4f, 0x2c, 0x5c, 0x2d, 0x89, 0x87, 0x71, 0x8b, 0x7f, 0xe5, 0xee, 0xd4, 0xce, 0xe9, 0x9c,
0x4c, 0x3d, 0x02, 0xc0, 0x9c, 0x34, 0x2e, 0x2a, 0xe7, 0xea, 0xcd, 0xbc, 0x47, 0x10, 0xb7, 0xb8, 0x05, 0xcd, 0xf8, 0x26, 0x07, 0x0d, 0x90, 0x3d, 0x68, 0x34, 0xbe, 0xcf, 0x83, 0x52, 0xdc, 0x53,
0x64, 0x1e, 0xc4, 0x26, 0x8a, 0x52, 0x44, 0xf0, 0x13, 0x50, 0x16, 0xdb, 0x96, 0x68, 0x41, 0x8b, 0x0f, 0x01, 0x30, 0x27, 0x85, 0x8b, 0xca, 0xbe, 0x7a, 0x33, 0xeb, 0x11, 0x44, 0x25, 0x2e, 0xee,
0xcf, 0xdc, 0x82, 0xd6, 0xc2, 0x40, 0x2d, 0x77, 0x26, 0x04, 0x28, 0xe1, 0x82, 0xfd, 0x74, 0xca, 0x07, 0x91, 0x89, 0xa2, 0x04, 0x11, 0xfc, 0x0c, 0x94, 0xc4, 0xb4, 0x25, 0x4a, 0xd0, 0xe2, 0x85,
0x9e, 0xb3, 0x9d, 0xc2, 0xe9, 0xf4, 0x8a, 0x2b, 0x66, 0x58, 0x79, 0x53, 0x93, 0xbb, 0xc6, 0xb2, 0x4b, 0xd0, 0x6a, 0xe0, 0xab, 0xa5, 0xf6, 0x84, 0x00, 0xc5, 0x5c, 0xb0, 0x97, 0xbc, 0xb2, 0xe7,
0x28, 0x70, 0xde, 0x1a, 0xd1, 0x02, 0x65, 0xb1, 0x17, 0x61, 0x0b, 0x5b, 0x42, 0xa3, 0x05, 0xfd, 0x2c, 0xa7, 0x70, 0xfa, 0x7a, 0xc5, 0x11, 0x33, 0xac, 0xbc, 0xa8, 0xc9, 0x59, 0x63, 0x49, 0x24,
0xb2, 0x84, 0x96, 0x3b, 0x93, 0x03, 0x94, 0x60, 0x38, 0x71, 0xb4, 0xf0, 0xc8, 0xb5, 0x2b, 0x26, 0x38, 0x6b, 0x8c, 0x68, 0x82, 0x92, 0x98, 0x8b, 0xb0, 0x85, 0x2d, 0xa1, 0xd1, 0xbc, 0x7e, 0x59,
0x8e, 0xd6, 0x23, 0x24, 0x4f, 0xe1, 0x1d, 0x50, 0x95, 0x21, 0x61, 0xeb, 0x9e, 0x67, 0xe1, 0xaf, 0x42, 0x4b, 0xed, 0xc9, 0x06, 0x8a, 0x31, 0x9c, 0x38, 0x1c, 0x78, 0xe4, 0xd8, 0x15, 0x11, 0x87,
0x30, 0x15, 0x4f, 0xb3, 0xac, 0xd7, 0xa4, 0x47, 0x75, 0x6f, 0xe6, 0x1c, 0xcd, 0x79, 0xc0, 0x6f, 0xe3, 0x11, 0x92, 0xbb, 0xf0, 0x0e, 0xa8, 0xc8, 0x90, 0xb0, 0x75, 0xcf, 0xb5, 0xf0, 0xd7, 0x98,
0x14, 0x70, 0x7d, 0xe4, 0x99, 0x64, 0xe4, 0x31, 0x6c, 0x75, 0xb1, 0xef, 0xda, 0x1e, 0xff, 0xe3, 0x8a, 0xa7, 0x59, 0xd2, 0xab, 0xd2, 0xa3, 0xb2, 0x3b, 0xb3, 0x8f, 0xe6, 0x3c, 0xe0, 0xb7, 0x0a,
0xe9, 0x90, 0x58, 0x54, 0x28, 0xb7, 0xb2, 0x7d, 0x3b, 0xb3, 0xd8, 0x47, 0xd9, 0x3e, 0x91, 0xce, 0xb8, 0x3e, 0x72, 0x4d, 0x32, 0x72, 0x19, 0xb6, 0x3a, 0xd8, 0x1b, 0xd8, 0x2e, 0xff, 0x99, 0x75,
0x73, 0x0e, 0x51, 0xde, 0x4d, 0x50, 0x05, 0x05, 0x1f, 0x1b, 0xd6, 0x58, 0xc8, 0xbb, 0xa0, 0x97, 0x40, 0x2c, 0x2a, 0x94, 0x5b, 0xde, 0xba, 0x9d, 0x9a, 0xec, 0xc3, 0x74, 0x9f, 0x50, 0xe7, 0x19,
0x79, 0x1b, 0x45, 0xdc, 0x80, 0x22, 0x7b, 0xf3, 0x67, 0x05, 0x5c, 0x9a, 0xd9, 0x6a, 0xff, 0xfd, 0x9b, 0x28, 0xeb, 0x24, 0xa8, 0x82, 0xbc, 0x87, 0x0d, 0x6b, 0x2c, 0xe4, 0x9d, 0xd7, 0x4b, 0xbc,
0x6b, 0x4b, 0xf3, 0x17, 0x05, 0xe4, 0xe5, 0x02, 0x1e, 0xa6, 0x75, 0xc1, 0x9f, 0x55, 0x59, 0xdf, 0x8c, 0x22, 0x6e, 0x40, 0xa1, 0xbd, 0xf1, 0xb3, 0x02, 0x2e, 0xcd, 0x4c, 0xb5, 0xff, 0xfd, 0xb1,
0x9e, 0xd2, 0xc4, 0x59, 0xa0, 0xde, 0xcc, 0xfb, 0x9b, 0x97, 0x6f, 0x21, 0x54, 0x3b, 0xba, 0x77, 0xa5, 0xf1, 0x8b, 0x02, 0xb2, 0xee, 0x02, 0x1e, 0x24, 0x75, 0xc1, 0x9f, 0x55, 0x49, 0xdf, 0x9a,
0x27, 0x2d, 0x9c, 0x0f, 0x62, 0xe1, 0x2c, 0x0a, 0xba, 0x56, 0x22, 0x9a, 0x8b, 0x71, 0x49, 0x77, 0xd2, 0xc4, 0xa9, 0xaf, 0xde, 0xcc, 0xfa, 0x75, 0xcc, 0xa7, 0x10, 0xaa, 0x1d, 0xde, 0xbb, 0x93,
0xfd, 0xed, 0x47, 0x4f, 0x1b, 0x0b, 0x8f, 0x9f, 0x36, 0x16, 0x9e, 0x3c, 0x6d, 0x2c, 0x7c, 0x1d, 0x14, 0xce, 0x47, 0x91, 0x70, 0x16, 0x05, 0x5d, 0x33, 0x16, 0xcd, 0xf9, 0xb8, 0xa4, 0xbb, 0xfe,
0x36, 0x94, 0x47, 0x61, 0x43, 0x79, 0x1c, 0x36, 0x94, 0x27, 0x61, 0x43, 0xf9, 0x3d, 0x6c, 0x28, 0xee, 0xe3, 0x67, 0xf5, 0x85, 0x27, 0xcf, 0xea, 0x0b, 0x4f, 0x9f, 0xd5, 0x17, 0xbe, 0x09, 0xea,
0xdf, 0xfe, 0xd1, 0x58, 0xf8, 0xf4, 0x4a, 0xc6, 0x7f, 0x42, 0xfc, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xca, 0xe3, 0xa0, 0xae, 0x3c, 0x09, 0xea, 0xca, 0xd3, 0xa0, 0xae, 0xfc, 0x11, 0xd4, 0x95, 0xef,
0x07, 0x84, 0xfd, 0x6a, 0xb3, 0x10, 0x00, 0x00, 0xfe, 0xac, 0x2f, 0x3c, 0xb8, 0x92, 0xf2, 0xef, 0x8a, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xae,
0xfd, 0x54, 0xb2, 0xdd, 0x10, 0x00, 0x00,
} }
func (m *CronJob) Marshal() (dAtA []byte, err error) { func (m *CronJob) Marshal() (dAtA []byte, err error) {
@@ -587,6 +588,13 @@ func (m *CronJobSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if m.TimeZone != nil {
i -= len(*m.TimeZone)
copy(dAtA[i:], *m.TimeZone)
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.TimeZone)))
i--
dAtA[i] = 0x42
}
if m.FailedJobsHistoryLimit != nil { if m.FailedJobsHistoryLimit != nil {
i = encodeVarintGenerated(dAtA, i, uint64(*m.FailedJobsHistoryLimit)) i = encodeVarintGenerated(dAtA, i, uint64(*m.FailedJobsHistoryLimit))
i-- i--
@@ -1199,6 +1207,10 @@ func (m *CronJobSpec) Size() (n int) {
if m.FailedJobsHistoryLimit != nil { if m.FailedJobsHistoryLimit != nil {
n += 1 + sovGenerated(uint64(*m.FailedJobsHistoryLimit)) n += 1 + sovGenerated(uint64(*m.FailedJobsHistoryLimit))
} }
if m.TimeZone != nil {
l = len(*m.TimeZone)
n += 1 + l + sovGenerated(uint64(l))
}
return n return n
} }
@@ -1433,6 +1445,7 @@ func (this *CronJobSpec) String() string {
`JobTemplate:` + strings.Replace(strings.Replace(this.JobTemplate.String(), "JobTemplateSpec", "JobTemplateSpec", 1), `&`, ``, 1) + `,`, `JobTemplate:` + strings.Replace(strings.Replace(this.JobTemplate.String(), "JobTemplateSpec", "JobTemplateSpec", 1), `&`, ``, 1) + `,`,
`SuccessfulJobsHistoryLimit:` + valueToStringGenerated(this.SuccessfulJobsHistoryLimit) + `,`, `SuccessfulJobsHistoryLimit:` + valueToStringGenerated(this.SuccessfulJobsHistoryLimit) + `,`,
`FailedJobsHistoryLimit:` + valueToStringGenerated(this.FailedJobsHistoryLimit) + `,`, `FailedJobsHistoryLimit:` + valueToStringGenerated(this.FailedJobsHistoryLimit) + `,`,
`TimeZone:` + valueToStringGenerated(this.TimeZone) + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@@ -2042,6 +2055,39 @@ func (m *CronJobSpec) Unmarshal(dAtA []byte) error {
} }
} }
m.FailedJobsHistoryLimit = &v m.FailedJobsHistoryLimit = &v
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field TimeZone", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.TimeZone = &s
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:]) skippy, err := skipGenerated(dAtA[iNdEx:])

View File

@@ -63,6 +63,12 @@ message CronJobSpec {
// The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
optional string schedule = 1; optional string schedule = 1;
// The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
// If not specified, this will rely on the time zone of the kube-controller-manager process.
// ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.
// +optional
optional string timeZone = 8;
// Optional deadline in seconds for starting the job if it misses scheduled // Optional deadline in seconds for starting the job if it misses scheduled
// time for any reason. Missed jobs executions will be counted as failed ones. // time for any reason. Missed jobs executions will be counted as failed ones.
// +optional // +optional

View File

@@ -17,7 +17,7 @@ limitations under the License.
package v1 package v1
import ( import (
"k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
) )
@@ -146,7 +146,7 @@ type JobSpec struct {
// Describes the pod that will be created when executing a job. // Describes the pod that will be created when executing a job.
// More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/ // More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/
Template v1.PodTemplateSpec `json:"template" protobuf:"bytes,6,opt,name=template"` Template corev1.PodTemplateSpec `json:"template" protobuf:"bytes,6,opt,name=template"`
// ttlSecondsAfterFinished limits the lifetime of a Job that has finished // ttlSecondsAfterFinished limits the lifetime of a Job that has finished
// execution (either Complete or Failed). If this field is set, // execution (either Complete or Failed). If this field is set,
@@ -304,7 +304,7 @@ type JobCondition struct {
// Type of job condition, Complete or Failed. // Type of job condition, Complete or Failed.
Type JobConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=JobConditionType"` Type JobConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=JobConditionType"`
// Status of the condition, one of True, False, Unknown. // Status of the condition, one of True, False, Unknown.
Status v1.ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=k8s.io/api/core/v1.ConditionStatus"` Status corev1.ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=k8s.io/api/core/v1.ConditionStatus"`
// Last time the condition was checked. // Last time the condition was checked.
// +optional // +optional
LastProbeTime metav1.Time `json:"lastProbeTime,omitempty" protobuf:"bytes,3,opt,name=lastProbeTime"` LastProbeTime metav1.Time `json:"lastProbeTime,omitempty" protobuf:"bytes,3,opt,name=lastProbeTime"`
@@ -375,6 +375,12 @@ type CronJobSpec struct {
// The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
Schedule string `json:"schedule" protobuf:"bytes,1,opt,name=schedule"` Schedule string `json:"schedule" protobuf:"bytes,1,opt,name=schedule"`
// The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
// If not specified, this will rely on the time zone of the kube-controller-manager process.
// ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.
// +optional
TimeZone *string `json:"timeZone,omitempty" protobuf:"bytes,8,opt,name=timeZone"`
// Optional deadline in seconds for starting the job if it misses scheduled // Optional deadline in seconds for starting the job if it misses scheduled
// time for any reason. Missed jobs executions will be counted as failed ones. // time for any reason. Missed jobs executions will be counted as failed ones.
// +optional // +optional
@@ -431,7 +437,7 @@ type CronJobStatus struct {
// A list of pointers to currently running jobs. // A list of pointers to currently running jobs.
// +optional // +optional
// +listType=atomic // +listType=atomic
Active []v1.ObjectReference `json:"active,omitempty" protobuf:"bytes,1,rep,name=active"` Active []corev1.ObjectReference `json:"active,omitempty" protobuf:"bytes,1,rep,name=active"`
// Information when was the last time the job was successfully scheduled. // Information when was the last time the job was successfully scheduled.
// +optional // +optional

View File

@@ -51,6 +51,7 @@ func (CronJobList) SwaggerDoc() map[string]string {
var map_CronJobSpec = map[string]string{ var map_CronJobSpec = map[string]string{
"": "CronJobSpec describes how the job execution will look like and when it will actually run.", "": "CronJobSpec describes how the job execution will look like and when it will actually run.",
"schedule": "The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.", "schedule": "The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.",
"timeZone": "The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will rely on the time zone of the kube-controller-manager process. ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.",
"startingDeadlineSeconds": "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.", "startingDeadlineSeconds": "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.",
"concurrencyPolicy": "Specifies how to treat concurrent executions of a Job. Valid values are: - \"Allow\" (default): allows CronJobs to run concurrently; - \"Forbid\": forbids concurrent runs, skipping next run if previous run hasn't finished yet; - \"Replace\": cancels currently running job and replaces it with a new one", "concurrencyPolicy": "Specifies how to treat concurrent executions of a Job. Valid values are: - \"Allow\" (default): allows CronJobs to run concurrently; - \"Forbid\": forbids concurrent runs, skipping next run if previous run hasn't finished yet; - \"Replace\": cancels currently running job and replaces it with a new one",
"suspend": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.", "suspend": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.",

View File

@@ -92,6 +92,11 @@ func (in *CronJobList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CronJobSpec) DeepCopyInto(out *CronJobSpec) { func (in *CronJobSpec) DeepCopyInto(out *CronJobSpec) {
*out = *in *out = *in
if in.TimeZone != nil {
in, out := &in.TimeZone, &out.TimeZone
*out = new(string)
**out = **in
}
if in.StartingDeadlineSeconds != nil { if in.StartingDeadlineSeconds != nil {
in, out := &in.StartingDeadlineSeconds, &out.StartingDeadlineSeconds in, out := &in.StartingDeadlineSeconds, &out.StartingDeadlineSeconds
*out = new(int64) *out = new(int64)

View File

@@ -227,57 +227,58 @@ func init() {
} }
var fileDescriptor_e57b277b05179ae7 = []byte{ var fileDescriptor_e57b277b05179ae7 = []byte{
// 796 bytes of a gzipped FileDescriptorProto // 814 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x96, 0x41, 0x6f, 0xdc, 0x44, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x96, 0x41, 0x6f, 0x1b, 0x45,
0x14, 0xc7, 0xd7, 0x9b, 0x6c, 0xb2, 0x9d, 0xa5, 0x90, 0x0e, 0x28, 0xb5, 0x16, 0x64, 0x87, 0xad, 0x14, 0xc7, 0xbd, 0x4e, 0x9c, 0xb8, 0xe3, 0x16, 0xd2, 0x01, 0xa5, 0x2b, 0x83, 0xd6, 0xc1, 0x55,
0x2a, 0x02, 0x82, 0x31, 0x89, 0x10, 0xe2, 0x54, 0xa9, 0x2e, 0x2a, 0x10, 0x82, 0x8a, 0x66, 0x8b, 0x85, 0x41, 0x30, 0x4b, 0x22, 0x84, 0x38, 0x55, 0xea, 0x16, 0x15, 0x08, 0x41, 0x45, 0xe3, 0x22,
0x90, 0x50, 0x85, 0x3a, 0x1e, 0xbf, 0x6c, 0xa6, 0xb1, 0x3d, 0x96, 0x67, 0x1c, 0x29, 0x37, 0x2e, 0xa4, 0xaa, 0x42, 0x9d, 0x1d, 0xbf, 0x38, 0xd3, 0x78, 0x77, 0x56, 0x3b, 0xb3, 0x91, 0x72, 0xe3,
0xdc, 0xf9, 0x22, 0x9c, 0xb8, 0x73, 0xce, 0xb1, 0xc7, 0x9e, 0x2c, 0x62, 0xbe, 0x05, 0x27, 0xe4, 0xc2, 0x9d, 0xef, 0xc2, 0x9d, 0x73, 0x8e, 0xbd, 0xd1, 0xd3, 0x8a, 0x2c, 0xdf, 0x82, 0x13, 0x9a,
0x59, 0xc7, 0xbb, 0xdd, 0xf5, 0x36, 0xed, 0x25, 0xb7, 0xcc, 0x9b, 0xff, 0xff, 0x37, 0xcf, 0xef, 0xf1, 0x7a, 0xed, 0xda, 0xeb, 0xa6, 0xbd, 0xf4, 0xe6, 0x79, 0xf3, 0xff, 0xff, 0xe6, 0xed, 0x7b,
0xbd, 0x99, 0x2c, 0xba, 0x7f, 0xf2, 0x95, 0x22, 0x42, 0x7a, 0x27, 0x79, 0x00, 0x59, 0x02, 0x1a, 0x6f, 0x67, 0x8d, 0xee, 0x9d, 0x7e, 0xad, 0x88, 0x90, 0xfe, 0x69, 0x16, 0x42, 0x1a, 0x83, 0x06,
0x94, 0x77, 0x0a, 0x49, 0x28, 0x33, 0xaf, 0xde, 0x60, 0xa9, 0xf0, 0x02, 0xa6, 0xf9, 0xb1, 0x77, 0xe5, 0x9f, 0x41, 0x3c, 0x92, 0xa9, 0x5f, 0x6e, 0xb0, 0x44, 0xf8, 0x21, 0xd3, 0xfc, 0xc4, 0x3f,
0xba, 0x17, 0x80, 0x66, 0x7b, 0xde, 0x04, 0x12, 0xc8, 0x98, 0x86, 0x90, 0xa4, 0x99, 0xd4, 0x12, 0xdb, 0x0f, 0x41, 0xb3, 0x7d, 0x7f, 0x0c, 0x31, 0xa4, 0x4c, 0xc3, 0x88, 0x24, 0xa9, 0xd4, 0x12,
0xdb, 0x53, 0x25, 0x61, 0xa9, 0x20, 0x46, 0x49, 0x6a, 0xe5, 0xf0, 0xb3, 0x89, 0xd0, 0xc7, 0x79, 0xbb, 0x53, 0x25, 0x61, 0x89, 0x20, 0x56, 0x49, 0x4a, 0x65, 0xf7, 0xf3, 0xb1, 0xd0, 0x27, 0x59,
0x40, 0xb8, 0x8c, 0xbd, 0x89, 0x9c, 0x48, 0xcf, 0x18, 0x82, 0xfc, 0xc8, 0xac, 0xcc, 0xc2, 0xfc, 0x48, 0xb8, 0x8c, 0xfc, 0xb1, 0x1c, 0x4b, 0xdf, 0x1a, 0xc2, 0xec, 0xd8, 0xae, 0xec, 0xc2, 0xfe,
0x35, 0x05, 0x0d, 0xef, 0xb4, 0x1c, 0xb9, 0x78, 0xda, 0x70, 0x34, 0x27, 0xe2, 0x32, 0x83, 0x36, 0x9a, 0x82, 0xba, 0xb7, 0x6b, 0x8e, 0x5c, 0x3e, 0xad, 0xdb, 0x5f, 0x10, 0x71, 0x99, 0x42, 0x9d,
0xcd, 0x17, 0x33, 0x4d, 0xcc, 0xf8, 0xb1, 0x48, 0x20, 0x3b, 0xf3, 0xd2, 0x93, 0x49, 0x15, 0x50, 0xe6, 0xcb, 0xb9, 0x26, 0x62, 0xfc, 0x44, 0xc4, 0x90, 0x9e, 0xfb, 0xc9, 0xe9, 0xd8, 0x04, 0x94,
0x5e, 0x0c, 0x9a, 0xb5, 0xb9, 0xbc, 0x55, 0xae, 0x2c, 0x4f, 0xb4, 0x88, 0x61, 0xc9, 0xf0, 0xe5, 0x1f, 0x81, 0x66, 0x75, 0x2e, 0x7f, 0x9d, 0x2b, 0xcd, 0x62, 0x2d, 0x22, 0x58, 0x31, 0x7c, 0x75,
0x55, 0x06, 0xc5, 0x8f, 0x21, 0x66, 0x8b, 0xbe, 0xd1, 0xef, 0x5d, 0xb4, 0xf9, 0x20, 0x93, 0xc9, 0x95, 0x41, 0xf1, 0x13, 0x88, 0xd8, 0xb2, 0xaf, 0xff, 0x7b, 0x13, 0x6d, 0xdf, 0x4f, 0x65, 0x7c,
0x81, 0x0c, 0xf0, 0x53, 0xd4, 0xaf, 0xf2, 0x09, 0x99, 0x66, 0xb6, 0xb5, 0x63, 0xed, 0x0e, 0xf6, 0x28, 0x43, 0xfc, 0x14, 0xb5, 0x4d, 0x3e, 0x23, 0xa6, 0x99, 0xeb, 0xec, 0x39, 0x83, 0xce, 0xc1,
0x3f, 0x27, 0xb3, 0x7a, 0x36, 0x58, 0x92, 0x9e, 0x4c, 0xaa, 0x80, 0x22, 0x95, 0x9a, 0x9c, 0xee, 0x17, 0x64, 0x5e, 0xcf, 0x0a, 0x4b, 0x92, 0xd3, 0xb1, 0x09, 0x28, 0x62, 0xd4, 0xe4, 0x6c, 0x9f,
0x91, 0x47, 0xc1, 0x33, 0xe0, 0xfa, 0x07, 0xd0, 0xcc, 0xc7, 0xe7, 0x85, 0xdb, 0x29, 0x0b, 0x17, 0x3c, 0x0c, 0x9f, 0x01, 0xd7, 0x3f, 0x82, 0x66, 0x01, 0xbe, 0xc8, 0x7b, 0x8d, 0x22, 0xef, 0xa1,
0xcd, 0x62, 0xb4, 0xa1, 0xe2, 0x6f, 0xd0, 0xba, 0x4a, 0x81, 0xdb, 0x5d, 0x43, 0xbf, 0x4b, 0x56, 0x79, 0x8c, 0x56, 0x54, 0xfc, 0x2d, 0xda, 0x54, 0x09, 0x70, 0xb7, 0x69, 0xe9, 0x77, 0xc8, 0xba,
0x75, 0x8b, 0xd4, 0x29, 0x8d, 0x53, 0xe0, 0xfe, 0x5b, 0x35, 0x72, 0xbd, 0x5a, 0x51, 0x03, 0xc0, 0x6e, 0x91, 0x32, 0xa5, 0x61, 0x02, 0x3c, 0xb8, 0x5e, 0x22, 0x37, 0xcd, 0x8a, 0x5a, 0x00, 0x7e,
0x8f, 0xd0, 0x86, 0xd2, 0x4c, 0xe7, 0xca, 0x5e, 0x33, 0xa8, 0x8f, 0xae, 0x46, 0x19, 0xb9, 0xff, 0x88, 0xb6, 0x94, 0x66, 0x3a, 0x53, 0xee, 0x86, 0x45, 0x7d, 0x7c, 0x35, 0xca, 0xca, 0x83, 0x77,
0x76, 0x0d, 0xdb, 0x98, 0xae, 0x69, 0x8d, 0x19, 0xfd, 0x65, 0xa1, 0x41, 0xad, 0x3c, 0x14, 0x4a, 0x4a, 0xd8, 0xd6, 0x74, 0x4d, 0x4b, 0x4c, 0xff, 0x4f, 0x07, 0x75, 0x4a, 0xe5, 0x91, 0x50, 0x1a,
0xe3, 0x27, 0x4b, 0xb5, 0x20, 0xaf, 0x57, 0x8b, 0xca, 0x6d, 0x2a, 0xb1, 0x55, 0x9f, 0xd4, 0xbf, 0x3f, 0x59, 0xa9, 0x05, 0x79, 0xbd, 0x5a, 0x18, 0xb7, 0xad, 0xc4, 0x4e, 0x79, 0x52, 0x7b, 0x16,
0x8c, 0xcc, 0xd5, 0xe1, 0x21, 0xea, 0x09, 0x0d, 0xb1, 0xb2, 0xbb, 0x3b, 0x6b, 0xbb, 0x83, 0xfd, 0x59, 0xa8, 0xc3, 0x03, 0xd4, 0x12, 0x1a, 0x22, 0xe5, 0x36, 0xf7, 0x36, 0x06, 0x9d, 0x83, 0x8f,
0x0f, 0xaf, 0xcc, 0xde, 0xbf, 0x59, 0xd3, 0x7a, 0xdf, 0x55, 0x3e, 0x3a, 0xb5, 0x8f, 0xfe, 0x5c, 0xae, 0xcc, 0x3e, 0xb8, 0x51, 0xd2, 0x5a, 0xdf, 0x1b, 0x1f, 0x9d, 0xda, 0xfb, 0x7f, 0x6f, 0x56,
0x6f, 0xb2, 0xae, 0x8a, 0x83, 0x3f, 0x45, 0xfd, 0xaa, 0xcf, 0x61, 0x1e, 0x81, 0xc9, 0xfa, 0xc6, 0x59, 0x9b, 0xe2, 0xe0, 0xcf, 0x50, 0xdb, 0xf4, 0x79, 0x94, 0x4d, 0xc0, 0x66, 0x7d, 0x6d, 0x9e,
0x2c, 0x8b, 0x71, 0x1d, 0xa7, 0x8d, 0x02, 0xff, 0x84, 0x6e, 0x2b, 0xcd, 0x32, 0x2d, 0x92, 0xc9, 0xc5, 0xb0, 0x8c, 0xd3, 0x4a, 0x81, 0x07, 0xa8, 0x6d, 0x46, 0xe3, 0xb1, 0x8c, 0xc1, 0x6d, 0x5b,
0xd7, 0xc0, 0xc2, 0x48, 0x24, 0x30, 0x06, 0x2e, 0x93, 0x50, 0x99, 0x06, 0xad, 0xf9, 0xef, 0x97, 0xf5, 0x75, 0xa3, 0x7c, 0x54, 0xc6, 0x68, 0xb5, 0x8b, 0x7f, 0x46, 0xb7, 0x94, 0x66, 0xa9, 0x16,
0x85, 0x7b, 0x7b, 0xdc, 0x2e, 0xa1, 0xab, 0xbc, 0xf8, 0x09, 0xba, 0xc5, 0x65, 0xc2, 0xf3, 0x2c, 0xf1, 0xf8, 0x1b, 0x60, 0xa3, 0x89, 0x88, 0x61, 0x08, 0x5c, 0xc6, 0x23, 0x65, 0x5b, 0xb9, 0x11,
0x83, 0x84, 0x9f, 0xfd, 0x28, 0x23, 0xc1, 0xcf, 0x4c, 0x9b, 0x6e, 0xf8, 0xa4, 0xce, 0xe6, 0xd6, 0x7c, 0x50, 0xe4, 0xbd, 0x5b, 0xc3, 0x7a, 0x09, 0x5d, 0xe7, 0xc5, 0x4f, 0xd0, 0x4d, 0x2e, 0x63,
0x83, 0x45, 0xc1, 0x7f, 0x6d, 0x41, 0xba, 0x0c, 0xc2, 0x77, 0xd1, 0xa6, 0xca, 0x55, 0x0a, 0x49, 0x9e, 0xa5, 0x29, 0xc4, 0xfc, 0xfc, 0x27, 0x39, 0x11, 0xfc, 0xdc, 0x36, 0xf4, 0x5a, 0x40, 0xca,
0x68, 0xaf, 0xef, 0x58, 0xbb, 0x7d, 0x7f, 0x50, 0x16, 0xee, 0xe6, 0x78, 0x1a, 0xa2, 0x97, 0x7b, 0xbc, 0x6f, 0xde, 0x5f, 0x16, 0xfc, 0x57, 0x17, 0xa4, 0xab, 0x20, 0x7c, 0x07, 0x6d, 0xab, 0x4c,
0xf8, 0x29, 0x1a, 0x3c, 0x93, 0xc1, 0x63, 0x88, 0xd3, 0x88, 0x69, 0xb0, 0x7b, 0xa6, 0x85, 0x1f, 0x25, 0x10, 0x8f, 0xdc, 0xcd, 0x3d, 0x67, 0xd0, 0x0e, 0x3a, 0x45, 0xde, 0xdb, 0x1e, 0x4e, 0x43,
0xaf, 0xae, 0xf3, 0xc1, 0x4c, 0x6c, 0x86, 0xee, 0xdd, 0x3a, 0xd3, 0xc1, 0xdc, 0x06, 0x9d, 0x47, 0x74, 0xb6, 0x87, 0x9f, 0xa2, 0xce, 0x33, 0x19, 0x3e, 0x82, 0x28, 0x99, 0x30, 0x0d, 0x6e, 0xcb,
0xe2, 0x5f, 0xd1, 0x50, 0xe5, 0x9c, 0x83, 0x52, 0x47, 0x79, 0x74, 0x20, 0x03, 0xf5, 0xad, 0x50, 0x36, 0xfb, 0x93, 0xf5, 0x1d, 0x39, 0x9c, 0x8b, 0xed, 0x78, 0xbe, 0x57, 0x66, 0xda, 0x59, 0xd8,
0x5a, 0x66, 0x67, 0x87, 0x22, 0x16, 0xda, 0xde, 0xd8, 0xb1, 0x76, 0x7b, 0xbe, 0x53, 0x16, 0xee, 0xa0, 0x8b, 0x48, 0xfc, 0x2b, 0xea, 0xaa, 0x8c, 0x73, 0x50, 0xea, 0x38, 0x9b, 0x1c, 0xca, 0x50,
0x70, 0xbc, 0x52, 0x45, 0x5f, 0x41, 0xc0, 0x14, 0x6d, 0x1f, 0x31, 0x11, 0x41, 0xb8, 0xc4, 0xde, 0x7d, 0x27, 0x94, 0x96, 0xe9, 0xf9, 0x91, 0x88, 0x84, 0x76, 0xb7, 0xf6, 0x9c, 0x41, 0x2b, 0xf0,
0x34, 0xec, 0x61, 0x59, 0xb8, 0xdb, 0x0f, 0x5b, 0x15, 0x74, 0x85, 0x73, 0xf4, 0x77, 0x17, 0xdd, 0x8a, 0xbc, 0xd7, 0x1d, 0xae, 0x55, 0xd1, 0x57, 0x10, 0x30, 0x45, 0xbb, 0xc7, 0x4c, 0x4c, 0x60,
0x7c, 0xe9, 0x3e, 0xe0, 0xef, 0xd1, 0x06, 0xe3, 0x5a, 0x9c, 0x56, 0xf3, 0x52, 0x8d, 0xe2, 0x9d, 0xb4, 0xc2, 0xde, 0xb6, 0xec, 0x6e, 0x91, 0xf7, 0x76, 0x1f, 0xd4, 0x2a, 0xe8, 0x1a, 0x67, 0xff,
0xf9, 0x12, 0x55, 0x6f, 0xda, 0xec, 0x7e, 0x53, 0x38, 0x82, 0xaa, 0x13, 0x30, 0xbb, 0x44, 0xf7, 0xaf, 0x26, 0xba, 0xf1, 0xd2, 0x9b, 0x83, 0x7f, 0x40, 0x5b, 0x8c, 0x6b, 0x71, 0x66, 0x26, 0xcb,
0x8d, 0x95, 0xd6, 0x08, 0x1c, 0xa1, 0xad, 0x88, 0x29, 0x7d, 0x39, 0x6a, 0x8f, 0x45, 0x0c, 0xa6, 0x0c, 0xed, 0xed, 0xc5, 0x12, 0x99, 0xdb, 0x6f, 0x7e, 0x13, 0x50, 0x38, 0x06, 0xd3, 0x09, 0x98,
0x49, 0x83, 0xfd, 0x4f, 0x5e, 0xef, 0xf2, 0x54, 0x0e, 0xff, 0xbd, 0xb2, 0x70, 0xb7, 0x0e, 0x17, 0xbf, 0x6e, 0xf7, 0xac, 0x95, 0x96, 0x08, 0x3c, 0x41, 0x3b, 0x13, 0xa6, 0xf4, 0x6c, 0x28, 0xcd,
0x38, 0x74, 0x89, 0x8c, 0x33, 0x84, 0x4d, 0xac, 0x29, 0xa1, 0x39, 0xaf, 0xf7, 0xc6, 0xe7, 0x6d, 0xc8, 0xd9, 0x26, 0x75, 0x0e, 0x3e, 0x7d, 0xbd, 0xd7, 0xcc, 0x38, 0x82, 0xf7, 0x8b, 0xbc, 0xb7,
0x97, 0x85, 0x8b, 0x0f, 0x97, 0x48, 0xb4, 0x85, 0x3e, 0x3a, 0xb7, 0xd0, 0xfc, 0x44, 0x5c, 0xc3, 0x73, 0xb4, 0xc4, 0xa1, 0x2b, 0x64, 0x9c, 0x22, 0x6c, 0x63, 0x55, 0x09, 0xed, 0x79, 0xad, 0x37,
0x93, 0xf9, 0x33, 0xea, 0xeb, 0xcb, 0x29, 0xee, 0xbe, 0xe9, 0x14, 0x37, 0xb7, 0xbf, 0x19, 0xe1, 0x3e, 0x6f, 0xb7, 0xc8, 0x7b, 0xf8, 0x68, 0x85, 0x44, 0x6b, 0xe8, 0xfd, 0x0b, 0x07, 0x2d, 0x4e,
0x06, 0x56, 0xbd, 0x78, 0xef, 0x2c, 0xe8, 0xaf, 0xe1, 0x73, 0xee, 0xbd, 0xf4, 0x1f, 0xe0, 0x83, 0xc4, 0x5b, 0xb8, 0x5c, 0x7f, 0x41, 0x6d, 0x3d, 0x9b, 0xe2, 0xe6, 0x9b, 0x4e, 0x71, 0x75, 0x4f,
0xb6, 0x4f, 0x21, 0xaf, 0x78, 0xf8, 0xfd, 0x7b, 0xe7, 0x17, 0x4e, 0xe7, 0xf9, 0x85, 0xd3, 0x79, 0x54, 0x23, 0x5c, 0xc1, 0xcc, 0xdd, 0xf8, 0xee, 0x92, 0xfe, 0x2d, 0x3c, 0xce, 0xdd, 0x97, 0xbe,
0x71, 0xe1, 0x74, 0x7e, 0x2b, 0x1d, 0xeb, 0xbc, 0x74, 0xac, 0xe7, 0xa5, 0x63, 0xbd, 0x28, 0x1d, 0x15, 0x1f, 0xd6, 0x3d, 0x0a, 0x79, 0xc5, 0x27, 0x22, 0xb8, 0x7b, 0x71, 0xe9, 0x35, 0x9e, 0x5f,
0xeb, 0x9f, 0xd2, 0xb1, 0xfe, 0xf8, 0xd7, 0xe9, 0xfc, 0x62, 0xaf, 0xfa, 0xc1, 0xf0, 0x7f, 0x00, 0x7a, 0x8d, 0x17, 0x97, 0x5e, 0xe3, 0xb7, 0xc2, 0x73, 0x2e, 0x0a, 0xcf, 0x79, 0x5e, 0x78, 0xce,
0x00, 0x00, 0xff, 0xff, 0x4f, 0x1b, 0x4a, 0x8e, 0x64, 0x08, 0x00, 0x00, 0x8b, 0xc2, 0x73, 0xfe, 0x29, 0x3c, 0xe7, 0x8f, 0x7f, 0xbd, 0xc6, 0x63, 0x77, 0xdd, 0x5f, 0x8b,
0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0xd7, 0xf2, 0x8b, 0xe9, 0x8e, 0x08, 0x00, 0x00,
} }
func (m *CronJob) Marshal() (dAtA []byte, err error) { func (m *CronJob) Marshal() (dAtA []byte, err error) {
@@ -400,6 +401,13 @@ func (m *CronJobSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if m.TimeZone != nil {
i -= len(*m.TimeZone)
copy(dAtA[i:], *m.TimeZone)
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.TimeZone)))
i--
dAtA[i] = 0x42
}
if m.FailedJobsHistoryLimit != nil { if m.FailedJobsHistoryLimit != nil {
i = encodeVarintGenerated(dAtA, i, uint64(*m.FailedJobsHistoryLimit)) i = encodeVarintGenerated(dAtA, i, uint64(*m.FailedJobsHistoryLimit))
i-- i--
@@ -662,6 +670,10 @@ func (m *CronJobSpec) Size() (n int) {
if m.FailedJobsHistoryLimit != nil { if m.FailedJobsHistoryLimit != nil {
n += 1 + sovGenerated(uint64(*m.FailedJobsHistoryLimit)) n += 1 + sovGenerated(uint64(*m.FailedJobsHistoryLimit))
} }
if m.TimeZone != nil {
l = len(*m.TimeZone)
n += 1 + l + sovGenerated(uint64(l))
}
return n return n
} }
@@ -760,6 +772,7 @@ func (this *CronJobSpec) String() string {
`JobTemplate:` + strings.Replace(strings.Replace(this.JobTemplate.String(), "JobTemplateSpec", "JobTemplateSpec", 1), `&`, ``, 1) + `,`, `JobTemplate:` + strings.Replace(strings.Replace(this.JobTemplate.String(), "JobTemplateSpec", "JobTemplateSpec", 1), `&`, ``, 1) + `,`,
`SuccessfulJobsHistoryLimit:` + valueToStringGenerated(this.SuccessfulJobsHistoryLimit) + `,`, `SuccessfulJobsHistoryLimit:` + valueToStringGenerated(this.SuccessfulJobsHistoryLimit) + `,`,
`FailedJobsHistoryLimit:` + valueToStringGenerated(this.FailedJobsHistoryLimit) + `,`, `FailedJobsHistoryLimit:` + valueToStringGenerated(this.FailedJobsHistoryLimit) + `,`,
`TimeZone:` + valueToStringGenerated(this.TimeZone) + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@@ -1284,6 +1297,39 @@ func (m *CronJobSpec) Unmarshal(dAtA []byte) error {
} }
} }
m.FailedJobsHistoryLimit = &v m.FailedJobsHistoryLimit = &v
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field TimeZone", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.TimeZone = &s
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:]) skippy, err := skipGenerated(dAtA[iNdEx:])

View File

@@ -64,6 +64,12 @@ message CronJobSpec {
// The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
optional string schedule = 1; optional string schedule = 1;
// The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
// If not specified, this will rely on the time zone of the kube-controller-manager process.
// ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.
// +optional
optional string timeZone = 8;
// Optional deadline in seconds for starting the job if it misses scheduled // Optional deadline in seconds for starting the job if it misses scheduled
// time for any reason. Missed jobs executions will be counted as failed ones. // time for any reason. Missed jobs executions will be counted as failed ones.
// +optional // +optional

View File

@@ -104,6 +104,12 @@ type CronJobSpec struct {
// The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.
Schedule string `json:"schedule" protobuf:"bytes,1,opt,name=schedule"` Schedule string `json:"schedule" protobuf:"bytes,1,opt,name=schedule"`
// The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
// If not specified, this will rely on the time zone of the kube-controller-manager process.
// ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.
// +optional
TimeZone *string `json:"timeZone,omitempty" protobuf:"bytes,8,opt,name=timeZone"`
// Optional deadline in seconds for starting the job if it misses scheduled // Optional deadline in seconds for starting the job if it misses scheduled
// time for any reason. Missed jobs executions will be counted as failed ones. // time for any reason. Missed jobs executions will be counted as failed ones.
// +optional // +optional

View File

@@ -51,6 +51,7 @@ func (CronJobList) SwaggerDoc() map[string]string {
var map_CronJobSpec = map[string]string{ var map_CronJobSpec = map[string]string{
"": "CronJobSpec describes how the job execution will look like and when it will actually run.", "": "CronJobSpec describes how the job execution will look like and when it will actually run.",
"schedule": "The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.", "schedule": "The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron.",
"timeZone": "The time zone for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will rely on the time zone of the kube-controller-manager process. ALPHA: This field is in alpha and must be enabled via the `CronJobTimeZone` feature gate.",
"startingDeadlineSeconds": "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.", "startingDeadlineSeconds": "Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones.",
"concurrencyPolicy": "Specifies how to treat concurrent executions of a Job. Valid values are: - \"Allow\" (default): allows CronJobs to run concurrently; - \"Forbid\": forbids concurrent runs, skipping next run if previous run hasn't finished yet; - \"Replace\": cancels currently running job and replaces it with a new one", "concurrencyPolicy": "Specifies how to treat concurrent executions of a Job. Valid values are: - \"Allow\" (default): allows CronJobs to run concurrently; - \"Forbid\": forbids concurrent runs, skipping next run if previous run hasn't finished yet; - \"Replace\": cancels currently running job and replaces it with a new one",
"suspend": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.", "suspend": "This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false.",

View File

@@ -90,6 +90,11 @@ func (in *CronJobList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CronJobSpec) DeepCopyInto(out *CronJobSpec) { func (in *CronJobSpec) DeepCopyInto(out *CronJobSpec) {
*out = *in *out = *in
if in.TimeZone != nil {
in, out := &in.TimeZone, &out.TimeZone
*out = new(string)
**out = **in
}
if in.StartingDeadlineSeconds != nil { if in.StartingDeadlineSeconds != nil {
in, out := &in.StartingDeadlineSeconds, &out.StartingDeadlineSeconds in, out := &in.StartingDeadlineSeconds, &out.StartingDeadlineSeconds
*out = new(int64) *out = new(int64)

View File

@@ -46,6 +46,7 @@
}, },
"spec": { "spec": {
"schedule": "scheduleValue", "schedule": "scheduleValue",
"timeZone": "timeZoneValue",
"startingDeadlineSeconds": 2, "startingDeadlineSeconds": 2,
"concurrencyPolicy": "concurrencyPolicyValue", "concurrencyPolicy": "concurrencyPolicyValue",
"suspend": true, "suspend": true,

View File

@@ -1170,6 +1170,7 @@ spec:
startingDeadlineSeconds: 2 startingDeadlineSeconds: 2
successfulJobsHistoryLimit: 6 successfulJobsHistoryLimit: 6
suspend: true suspend: true
timeZone: timeZoneValue
status: status:
active: active:
- apiVersion: apiVersionValue - apiVersion: apiVersionValue

View File

@@ -46,6 +46,7 @@
}, },
"spec": { "spec": {
"schedule": "scheduleValue", "schedule": "scheduleValue",
"timeZone": "timeZoneValue",
"startingDeadlineSeconds": 2, "startingDeadlineSeconds": 2,
"concurrencyPolicy": "concurrencyPolicyValue", "concurrencyPolicy": "concurrencyPolicyValue",
"suspend": true, "suspend": true,

View File

@@ -1170,6 +1170,7 @@ spec:
startingDeadlineSeconds: 2 startingDeadlineSeconds: 2
successfulJobsHistoryLimit: 6 successfulJobsHistoryLimit: 6
suspend: true suspend: true
timeZone: timeZoneValue
status: status:
active: active:
- apiVersion: apiVersionValue - apiVersion: apiVersionValue

View File

@@ -178,6 +178,13 @@ const (
// //
// Enables server-side field validation. // Enables server-side field validation.
ServerSideFieldValidation featuregate.Feature = "ServerSideFieldValidation" ServerSideFieldValidation featuregate.Feature = "ServerSideFieldValidation"
// owner: @deejross
// kep: http://kep.k8s.io/3140
// alpha: v1.24
//
// Enables support for time zones in CronJobs.
CronJobTimeZone featuregate.Feature = "CronJobTimeZone"
) )
func init() { func init() {
@@ -207,4 +214,5 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
CustomResourceValidationExpressions: {Default: false, PreRelease: featuregate.Alpha}, CustomResourceValidationExpressions: {Default: false, PreRelease: featuregate.Alpha},
OpenAPIV3: {Default: false, PreRelease: featuregate.Alpha}, OpenAPIV3: {Default: false, PreRelease: featuregate.Alpha},
ServerSideFieldValidation: {Default: true, PreRelease: featuregate.Beta}, ServerSideFieldValidation: {Default: true, PreRelease: featuregate.Beta},
CronJobTimeZone: {Default: false, PreRelease: featuregate.Alpha},
} }

View File

@@ -26,6 +26,7 @@ import (
// with apply. // with apply.
type CronJobSpecApplyConfiguration struct { type CronJobSpecApplyConfiguration struct {
Schedule *string `json:"schedule,omitempty"` Schedule *string `json:"schedule,omitempty"`
TimeZone *string `json:"timeZone,omitempty"`
StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"` StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"`
ConcurrencyPolicy *v1.ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"` ConcurrencyPolicy *v1.ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`
Suspend *bool `json:"suspend,omitempty"` Suspend *bool `json:"suspend,omitempty"`
@@ -48,6 +49,14 @@ func (b *CronJobSpecApplyConfiguration) WithSchedule(value string) *CronJobSpecA
return b return b
} }
// WithTimeZone sets the TimeZone field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the TimeZone field is set to the value of the last call.
func (b *CronJobSpecApplyConfiguration) WithTimeZone(value string) *CronJobSpecApplyConfiguration {
b.TimeZone = &value
return b
}
// WithStartingDeadlineSeconds sets the StartingDeadlineSeconds field in the declarative configuration to the given value // WithStartingDeadlineSeconds sets the StartingDeadlineSeconds field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations. // and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the StartingDeadlineSeconds field is set to the value of the last call. // If called multiple times, the StartingDeadlineSeconds field is set to the value of the last call.

View File

@@ -26,6 +26,7 @@ import (
// with apply. // with apply.
type CronJobSpecApplyConfiguration struct { type CronJobSpecApplyConfiguration struct {
Schedule *string `json:"schedule,omitempty"` Schedule *string `json:"schedule,omitempty"`
TimeZone *string `json:"timeZone,omitempty"`
StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"` StartingDeadlineSeconds *int64 `json:"startingDeadlineSeconds,omitempty"`
ConcurrencyPolicy *v1beta1.ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"` ConcurrencyPolicy *v1beta1.ConcurrencyPolicy `json:"concurrencyPolicy,omitempty"`
Suspend *bool `json:"suspend,omitempty"` Suspend *bool `json:"suspend,omitempty"`
@@ -48,6 +49,14 @@ func (b *CronJobSpecApplyConfiguration) WithSchedule(value string) *CronJobSpecA
return b return b
} }
// WithTimeZone sets the TimeZone field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the TimeZone field is set to the value of the last call.
func (b *CronJobSpecApplyConfiguration) WithTimeZone(value string) *CronJobSpecApplyConfiguration {
b.TimeZone = &value
return b
}
// WithStartingDeadlineSeconds sets the StartingDeadlineSeconds field in the declarative configuration to the given value // WithStartingDeadlineSeconds sets the StartingDeadlineSeconds field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations. // and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the StartingDeadlineSeconds field is set to the value of the last call. // If called multiple times, the StartingDeadlineSeconds field is set to the value of the last call.

View File

@@ -2956,6 +2956,9 @@ var schemaYAML = typed.YAMLObject(`types:
- name: suspend - name: suspend
type: type:
scalar: boolean scalar: boolean
- name: timeZone
type:
scalar: string
- name: io.k8s.api.batch.v1.CronJobStatus - name: io.k8s.api.batch.v1.CronJobStatus
map: map:
fields: fields:
@@ -3157,6 +3160,9 @@ var schemaYAML = typed.YAMLObject(`types:
- name: suspend - name: suspend
type: type:
scalar: boolean scalar: boolean
- name: timeZone
type:
scalar: string
- name: io.k8s.api.batch.v1beta1.CronJobStatus - name: io.k8s.api.batch.v1beta1.CronJobStatus
map: map:
fields: fields: