diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 72edd44da41..61bb335c966 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -3542,7 +3542,7 @@ "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.", + "description": "The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will default to the time zone of the kube-controller-manager process. The set of valid time zone names and the time zone offset is loaded from the system-wide time zone database by the API server during CronJob validation and the controller manager during execution. If no system-wide time zone database can be found a bundled version of the database is used instead. If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host configuration, the controller will stop creating new new Jobs and will create a system event with the reason UnknownTimeZone. More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones This is beta field and must be enabled via the `CronJobTimeZone` feature gate.", "type": "string" } }, diff --git a/api/openapi-spec/v3/apis__batch__v1_openapi.json b/api/openapi-spec/v3/apis__batch__v1_openapi.json index dcfb9c4a727..a13afc7534d 100644 --- a/api/openapi-spec/v3/apis__batch__v1_openapi.json +++ b/api/openapi-spec/v3/apis__batch__v1_openapi.json @@ -135,7 +135,7 @@ "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.", + "description": "The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will default to the time zone of the kube-controller-manager process. The set of valid time zone names and the time zone offset is loaded from the system-wide time zone database by the API server during CronJob validation and the controller manager during execution. If no system-wide time zone database can be found a bundled version of the database is used instead. If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host configuration, the controller will stop creating new new Jobs and will create a system event with the reason UnknownTimeZone. More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones This is beta field and must be enabled via the `CronJobTimeZone` feature gate.", "type": "string" } }, diff --git a/pkg/apis/batch/types.go b/pkg/apis/batch/types.go index 5231dc0f303..9bc186af9e6 100644 --- a/pkg/apis/batch/types.go +++ b/pkg/apis/batch/types.go @@ -376,9 +376,16 @@ type CronJobSpec struct { // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. 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. + // The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. + // If not specified, this will default to the time zone of the kube-controller-manager process. + // The set of valid time zone names and the time zone offset is loaded from the system-wide time zone + // database by the API server during CronJob validation and the controller manager during execution. + // If no system-wide time zone database can be found a bundled version of the database is used instead. + // If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host + // configuration, the controller will stop creating new new Jobs and will create a system event with the + // reason UnknownTimeZone. + // More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones + // This is beta field and must be enabled via the `CronJobTimeZone` feature gate. // +optional TimeZone *string diff --git a/pkg/apis/batch/validation/validation_test.go b/pkg/apis/batch/validation/validation_test.go index fd318c0aafc..3eec16fab01 100644 --- a/pkg/apis/batch/validation/validation_test.go +++ b/pkg/apis/batch/validation/validation_test.go @@ -17,6 +17,10 @@ limitations under the License. package validation import ( + "archive/zip" + "io" + "os" + "path/filepath" "runtime" "strings" "testing" @@ -33,15 +37,14 @@ import ( ) 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 = " " + timeZoneEmpty = "" + timeZoneLocal = "LOCAL" + timeZoneUTC = "UTC" + timeZoneCorrect = "Continent/Zone" + timeZoneBadPrefix = " Continent/Zone" + timeZoneBadSuffix = "Continent/Zone " + timeZoneBadName = "Continent/InvalidZone" + timeZoneEmptySpace = " " ) var ignoreErrValueDetail = cmpopts.IgnoreFields(field.Error{}, "BadValue", "Detail") @@ -882,6 +885,12 @@ func TestValidateCronJob(t *testing.T) { validPodTemplateSpec := getValidPodTemplateSpecForGenerated(getValidGeneratedSelector()) validPodTemplateSpec.Labels = map[string]string{} + zoneDir := t.TempDir() + if err := setupFakeTimeZoneDatabase(zoneDir); err != nil { + t.Fatalf("Unexpected error setting up fake timezone database: %v", err) + } + t.Setenv("ZONEINFO", zoneDir) + successCases := map[string]batch.CronJob{ "basic scheduled job": { ObjectMeta: metav1.ObjectMeta{ @@ -915,7 +924,7 @@ func TestValidateCronJob(t *testing.T) { }, }, }, - "correct timeZone value casing": { + "correct timeZone value": { ObjectMeta: metav1.ObjectMeta{ Name: "mycronjob", Namespace: metav1.NamespaceDefault, @@ -923,7 +932,7 @@ func TestValidateCronJob(t *testing.T) { }, Spec: batch.CronJobSpec{ Schedule: "0 * * * *", - TimeZone: &timeZoneCorrectCasing, + TimeZone: &timeZoneCorrect, ConcurrencyPolicy: batch.AllowConcurrent, JobTemplate: batch.JobTemplateSpec{ Spec: batch.JobSpec{ @@ -934,17 +943,19 @@ func TestValidateCronJob(t *testing.T) { }, } for k, v := range successCases { - if errs := ValidateCronJobCreate(&v, corevalidation.PodValidationOptions{}); len(errs) != 0 { - t.Errorf("expected success for %s: %v", k, errs) - } + t.Run(k, func(t *testing.T) { + if errs := ValidateCronJobCreate(&v, corevalidation.PodValidationOptions{}); len(errs) != 0 { + t.Errorf("expected success for %s: %v", k, errs) + } - // Update validation should pass same success cases - // copy to avoid polluting the testcase object, set a resourceVersion to allow validating update, and test a no-op update - v = *v.DeepCopy() - v.ResourceVersion = "1" - if errs := ValidateCronJobUpdate(&v, &v, corevalidation.PodValidationOptions{}); len(errs) != 0 { - t.Errorf("expected success for %s: %v", k, errs) - } + // Update validation should pass same success cases + // copy to avoid polluting the testcase object, set a resourceVersion to allow validating update, and test a no-op update + v = *v.DeepCopy() + v.ResourceVersion = "1" + if errs := ValidateCronJobUpdate(&v, &v, corevalidation.PodValidationOptions{}); len(errs) != 0 { + t.Errorf("expected success for %s: %v", k, errs) + } + }) } negative := int32(-1) @@ -1034,7 +1045,7 @@ func TestValidateCronJob(t *testing.T) { }, }, }, - "spec.timeZone: Invalid value: \" America/New_York\": unknown time zone America/New_York": { + "spec.timeZone: Invalid value: \" Continent/Zone\": unknown time zone Continent/Zone": { ObjectMeta: metav1.ObjectMeta{ Name: "mycronjob", Namespace: metav1.NamespaceDefault, @@ -1051,7 +1062,7 @@ func TestValidateCronJob(t *testing.T) { }, }, }, - "spec.timeZone: Invalid value: \"America/New_York \": unknown time zone America/New_York ": { + "spec.timeZone: Invalid value: \"Continent/Zone \": unknown time zone Continent/Zone ": { ObjectMeta: metav1.ObjectMeta{ Name: "mycronjob", Namespace: metav1.NamespaceDefault, @@ -1068,7 +1079,7 @@ func TestValidateCronJob(t *testing.T) { }, }, }, - "spec.timeZone: Invalid value: \"America/New York\": unknown time zone America/New York": { + "spec.timeZone: Invalid value: \"Continent/InvalidZone\": unknown time zone Continent/InvalidZone": { ObjectMeta: metav1.ObjectMeta{ Name: "mycronjob", Namespace: metav1.NamespaceDefault, @@ -1314,82 +1325,100 @@ func TestValidateCronJob(t *testing.T) { }, }, }, - } - errorCases["spec.jobTemplate.spec.ttlSecondsAfterFinished:must be greater than or equal to 0"] = batch.CronJob{ - ObjectMeta: metav1.ObjectMeta{ - Name: "mycronjob", - Namespace: metav1.NamespaceDefault, - UID: types.UID("1a2b3c"), - }, - Spec: batch.CronJobSpec{ - Schedule: "* * * * ?", - ConcurrencyPolicy: batch.AllowConcurrent, - JobTemplate: batch.JobTemplateSpec{ - Spec: batch.JobSpec{ - TTLSecondsAfterFinished: &negative, - Template: validPodTemplateSpec, - }, - }, - }, - } - if runtime.GOOS != "darwin" { - // Skip this error case on darwin, see https://github.com/golang/go/issues/21512 - errorCases["spec.timeZone: Invalid value: \"AMERICA/new_york\": unknown time zone AMERICA/new_york"] = batch.CronJob{ + "spec.jobTemplate.spec.ttlSecondsAfterFinished:must be greater than or equal to 0": { ObjectMeta: metav1.ObjectMeta{ Name: "mycronjob", Namespace: metav1.NamespaceDefault, UID: types.UID("1a2b3c"), }, Spec: batch.CronJobSpec{ - Schedule: "0 * * * *", - TimeZone: &timeZoneBadCasing, + Schedule: "* * * * ?", ConcurrencyPolicy: batch.AllowConcurrent, JobTemplate: batch.JobTemplateSpec{ Spec: batch.JobSpec{ - Template: validPodTemplateSpec, + TTLSecondsAfterFinished: &negative, + Template: validPodTemplateSpec, }, }, }, - } + }, } for k, v := range errorCases { - errs := ValidateCronJobCreate(&v, corevalidation.PodValidationOptions{}) - if len(errs) == 0 { - t.Errorf("expected failure for %s", k) - } else { - s := strings.Split(k, ":") - err := errs[0] - if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) { - t.Errorf("unexpected error: %v, expected: %s", err, k) + t.Run(k, func(t *testing.T) { + errs := ValidateCronJobCreate(&v, corevalidation.PodValidationOptions{}) + if len(errs) == 0 { + t.Errorf("expected failure for %s", k) + } else { + s := strings.Split(k, ":") + err := errs[0] + if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) { + t.Errorf("unexpected error: %v, expected: %s", err, k) + } } - } - // 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 - oldSpec := *v.DeepCopy() - oldSpec.ResourceVersion = "1" - oldSpec.Spec.TimeZone = nil + // 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 + oldSpec := *v.DeepCopy() + oldSpec.ResourceVersion = "1" + oldSpec.Spec.TimeZone = nil - newSpec := *v.DeepCopy() - newSpec.ResourceVersion = "2" + newSpec := *v.DeepCopy() + newSpec.ResourceVersion = "2" - errs = ValidateCronJobUpdate(&newSpec, &oldSpec, corevalidation.PodValidationOptions{}) - if len(errs) == 0 { - if k == "metadata.name: must be no more than 52 characters" { - continue + errs = ValidateCronJobUpdate(&newSpec, &oldSpec, corevalidation.PodValidationOptions{}) + if len(errs) == 0 { + if k == "metadata.name: must be no more than 52 characters" { + return + } + t.Errorf("expected failure for %s", k) + } else { + s := strings.Split(k, ":") + err := errs[0] + if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) { + t.Errorf("unexpected error: %v, expected: %s", err, k) + } } - t.Errorf("expected failure for %s", k) - } else { - s := strings.Split(k, ":") - err := errs[0] - if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) { - t.Errorf("unexpected error: %v, expected: %s", err, k) - } - } + }) } } +// Sets up fake timezone database in a zoneDir directory with a single valid +// time zone called "Continent/Zone" by copying UTC metadata from golang's +// built-in databse. Returns an error in case of problems. +func setupFakeTimeZoneDatabase(zoneDir string) error { + reader, err := zip.OpenReader(runtime.GOROOT() + "/lib/time/zoneinfo.zip") + if err != nil { + return err + } + defer reader.Close() + + if err := os.Mkdir(filepath.Join(zoneDir, "Continent"), os.ModePerm); err != nil { + return err + } + zoneFile, err := os.OpenFile(filepath.Join(zoneDir, "Continent", "Zone"), os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0666) + if err != nil { + return err + } + defer zoneFile.Close() + + for _, file := range reader.File { + if file.Name != "UTC" { + continue + } + rc, err := file.Open() + if err != nil { + return err + } + if _, err := io.Copy(zoneFile, rc); err != nil { + return err + } + rc.Close() + break + } + return nil +} + func TestValidateCronJobSpec(t *testing.T) { validPodTemplateSpec := getValidPodTemplateSpecForGenerated(getValidGeneratedSelector()) validPodTemplateSpec.Labels = map[string]string{} diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index c6fbfb05bbb..813114cad6f 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -207,9 +207,10 @@ const ( // Enables Leader Migration for kube-controller-manager and cloud-controller-manager ControllerManagerLeaderMigration featuregate.Feature = "ControllerManagerLeaderMigration" - // owner: @deejross + // owner: @deejross, @soltysh // kep: http://kep.k8s.io/3140 // alpha: v1.24 + // beta: v1.25 // // Enables support for time zones in CronJobs. CronJobTimeZone featuregate.Feature = "CronJobTimeZone" @@ -910,7 +911,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS ControllerManagerLeaderMigration: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.26 - CronJobTimeZone: {Default: false, PreRelease: featuregate.Alpha}, + CronJobTimeZone: {Default: true, PreRelease: featuregate.Beta}, DaemonSetUpdateSurge: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.27 diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 4097987d0f1..bf66f52d9c8 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -12703,7 +12703,7 @@ func schema_k8sio_api_batch_v1_CronJobSpec(ref common.ReferenceCallback) common. }, "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.", + Description: "The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will default to the time zone of the kube-controller-manager process. The set of valid time zone names and the time zone offset is loaded from the system-wide time zone database by the API server during CronJob validation and the controller manager during execution. If no system-wide time zone database can be found a bundled version of the database is used instead. If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host configuration, the controller will stop creating new new Jobs and will create a system event with the reason UnknownTimeZone. More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones This is beta field and must be enabled via the `CronJobTimeZone` feature gate.", Type: []string{"string"}, Format: "", }, @@ -13340,7 +13340,7 @@ func schema_k8sio_api_batch_v1beta1_CronJobSpec(ref common.ReferenceCallback) co }, "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.", + Description: "The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will default to the time zone of the kube-controller-manager process. The set of valid time zone names and the time zone offset is loaded from the system-wide time zone database by the API server during CronJob validation and the controller manager during execution. If no system-wide time zone database can be found a bundled version of the database is used instead. If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host configuration, the controller will stop creating new new Jobs and will create a system event with the reason UnknownTimeZone. More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones This is beta field and must be enabled via the `CronJobTimeZone` feature gate.", Type: []string{"string"}, Format: "", }, diff --git a/staging/src/k8s.io/api/batch/v1/generated.proto b/staging/src/k8s.io/api/batch/v1/generated.proto index a57fef0b2f6..d0b1d39e484 100644 --- a/staging/src/k8s.io/api/batch/v1/generated.proto +++ b/staging/src/k8s.io/api/batch/v1/generated.proto @@ -63,9 +63,16 @@ message CronJobSpec { // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. 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. + // The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. + // If not specified, this will default to the time zone of the kube-controller-manager process. + // The set of valid time zone names and the time zone offset is loaded from the system-wide time zone + // database by the API server during CronJob validation and the controller manager during execution. + // If no system-wide time zone database can be found a bundled version of the database is used instead. + // If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host + // configuration, the controller will stop creating new new Jobs and will create a system event with the + // reason UnknownTimeZone. + // More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones + // This is beta field and must be enabled via the `CronJobTimeZone` feature gate. // +optional optional string timeZone = 8; diff --git a/staging/src/k8s.io/api/batch/v1/types.go b/staging/src/k8s.io/api/batch/v1/types.go index 110f974b788..4a31a8cc75e 100644 --- a/staging/src/k8s.io/api/batch/v1/types.go +++ b/staging/src/k8s.io/api/batch/v1/types.go @@ -375,9 +375,16 @@ type CronJobSpec struct { // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. 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. + // The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. + // If not specified, this will default to the time zone of the kube-controller-manager process. + // The set of valid time zone names and the time zone offset is loaded from the system-wide time zone + // database by the API server during CronJob validation and the controller manager during execution. + // If no system-wide time zone database can be found a bundled version of the database is used instead. + // If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host + // configuration, the controller will stop creating new new Jobs and will create a system event with the + // reason UnknownTimeZone. + // More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones + // This is beta field and must be enabled via the `CronJobTimeZone` feature gate. // +optional TimeZone *string `json:"timeZone,omitempty" protobuf:"bytes,8,opt,name=timeZone"` diff --git a/staging/src/k8s.io/api/batch/v1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/batch/v1/types_swagger_doc_generated.go index df24723a82f..e0181922ed9 100644 --- a/staging/src/k8s.io/api/batch/v1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/batch/v1/types_swagger_doc_generated.go @@ -51,7 +51,7 @@ func (CronJobList) SwaggerDoc() map[string]string { var map_CronJobSpec = map[string]string{ "": "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.", - "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.", + "timeZone": "The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will default to the time zone of the kube-controller-manager process. The set of valid time zone names and the time zone offset is loaded from the system-wide time zone database by the API server during CronJob validation and the controller manager during execution. If no system-wide time zone database can be found a bundled version of the database is used instead. If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host configuration, the controller will stop creating new new Jobs and will create a system event with the reason UnknownTimeZone. More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones This is beta field 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.", "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.", diff --git a/staging/src/k8s.io/api/batch/v1beta1/generated.proto b/staging/src/k8s.io/api/batch/v1beta1/generated.proto index c73b7a65403..d8386a8f51d 100644 --- a/staging/src/k8s.io/api/batch/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/batch/v1beta1/generated.proto @@ -64,9 +64,16 @@ message CronJobSpec { // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. 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. + // The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. + // If not specified, this will default to the time zone of the kube-controller-manager process. + // The set of valid time zone names and the time zone offset is loaded from the system-wide time zone + // database by the API server during CronJob validation and the controller manager during execution. + // If no system-wide time zone database can be found a bundled version of the database is used instead. + // If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host + // configuration, the controller will stop creating new new Jobs and will create a system event with the + // reason UnknownTimeZone. + // More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones + // This is beta field and must be enabled via the `CronJobTimeZone` feature gate. // +optional optional string timeZone = 8; diff --git a/staging/src/k8s.io/api/batch/v1beta1/types.go b/staging/src/k8s.io/api/batch/v1beta1/types.go index 54a2d14318f..4c0d69dd6b0 100644 --- a/staging/src/k8s.io/api/batch/v1beta1/types.go +++ b/staging/src/k8s.io/api/batch/v1beta1/types.go @@ -104,9 +104,16 @@ type CronJobSpec struct { // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. 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. + // The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. + // If not specified, this will default to the time zone of the kube-controller-manager process. + // The set of valid time zone names and the time zone offset is loaded from the system-wide time zone + // database by the API server during CronJob validation and the controller manager during execution. + // If no system-wide time zone database can be found a bundled version of the database is used instead. + // If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host + // configuration, the controller will stop creating new new Jobs and will create a system event with the + // reason UnknownTimeZone. + // More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones + // This is beta field and must be enabled via the `CronJobTimeZone` feature gate. // +optional TimeZone *string `json:"timeZone,omitempty" protobuf:"bytes,8,opt,name=timeZone"` diff --git a/staging/src/k8s.io/api/batch/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/batch/v1beta1/types_swagger_doc_generated.go index 87194807631..5716bbb862a 100644 --- a/staging/src/k8s.io/api/batch/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/batch/v1beta1/types_swagger_doc_generated.go @@ -51,7 +51,7 @@ func (CronJobList) SwaggerDoc() map[string]string { var map_CronJobSpec = map[string]string{ "": "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.", - "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.", + "timeZone": "The time zone name for the given schedule, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. If not specified, this will default to the time zone of the kube-controller-manager process. The set of valid time zone names and the time zone offset is loaded from the system-wide time zone database by the API server during CronJob validation and the controller manager during execution. If no system-wide time zone database can be found a bundled version of the database is used instead. If the time zone name becomes invalid during the lifetime of a CronJob or due to a change in host configuration, the controller will stop creating new new Jobs and will create a system event with the reason UnknownTimeZone. More information can be found in https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#time-zones This is beta field 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.", "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.", diff --git a/test/e2e/apps/cronjob.go b/test/e2e/apps/cronjob.go index 2101a953fd8..102b704e2e5 100644 --- a/test/e2e/apps/cronjob.go +++ b/test/e2e/apps/cronjob.go @@ -298,6 +298,17 @@ var _ = SIGDescribe("CronJob", func() { ensureHistoryLimits(f.ClientSet, f.Namespace.Name, cronJob) }) + ginkgo.It("should support timezone", func() { + ginkgo.By("Creating a cronjob with TimeZone") + cronJob := newTestCronJob("cronjob-with-timezone", "*/1 * * * ?", batchv1.AllowConcurrent, + failureCommand, nil, nil) + badTimeZone := "bad-time-zone" + cronJob.Spec.TimeZone = &badTimeZone + _, err := createCronJob(f.ClientSet, f.Namespace.Name, cronJob) + framework.ExpectError(err, "CronJob creation should fail with invalid time zone error") + framework.ExpectEqual(apierrors.IsInvalid(err), true, "CronJob creation should fail with invalid time zone error") + }) + /* Release: v1.21 Testname: CronJob API Operations