From 1bff5705fab60d4a17b0f21adc26f41f677f7dbb Mon Sep 17 00:00:00 2001 From: Michal Wozniak Date: Fri, 1 Mar 2024 18:22:32 +0100 Subject: [PATCH] Use managedBy field instead of managed-by label --- api/openapi-spec/swagger.json | 22 +- .../v3/apis__batch__v1_openapi.json | 22 +- pkg/apis/batch/fuzzer/fuzzer.go | 3 + pkg/apis/batch/types.go | 39 ++- pkg/apis/batch/v1/zz_generated.conversion.go | 2 + pkg/apis/batch/validation/validation.go | 21 +- pkg/apis/batch/validation/validation_test.go | 87 +++--- pkg/apis/batch/zz_generated.deepcopy.go | 5 + pkg/apis/core/validation/validation.go | 8 - pkg/controller/job/job_controller.go | 6 +- pkg/controller/job/job_controller_test.go | 42 +-- pkg/controller/job/metrics/metrics.go | 2 +- pkg/features/kube_features.go | 6 +- pkg/generated/openapi/zz_generated.openapi.go | 25 +- pkg/registry/batch/job/strategy.go | 9 +- pkg/registry/batch/job/strategy_test.go | 160 ++++++----- .../src/k8s.io/api/batch/v1/generated.pb.go | 272 ++++++++++-------- .../src/k8s.io/api/batch/v1/generated.proto | 32 ++- staging/src/k8s.io/api/batch/v1/types.go | 39 ++- .../batch/v1/types_swagger_doc_generated.go | 19 +- .../api/batch/v1/zz_generated.deepcopy.go | 5 + .../api/testdata/HEAD/batch.v1.CronJob.json | 3 +- .../api/testdata/HEAD/batch.v1.CronJob.pb | Bin 11136 -> 11152 bytes .../api/testdata/HEAD/batch.v1.CronJob.yaml | 1 + .../api/testdata/HEAD/batch.v1.Job.json | 3 +- .../k8s.io/api/testdata/HEAD/batch.v1.Job.pb | Bin 10762 -> 10778 bytes .../api/testdata/HEAD/batch.v1.Job.yaml | 1 + .../testdata/HEAD/batch.v1beta1.CronJob.json | 3 +- .../testdata/HEAD/batch.v1beta1.CronJob.pb | Bin 11141 -> 11157 bytes .../testdata/HEAD/batch.v1beta1.CronJob.yaml | 1 + .../applyconfigurations/batch/v1/jobspec.go | 9 + .../applyconfigurations/internal/internal.go | 3 + test/e2e/apps/job.go | 29 -- test/integration/job/job_test.go | 88 +++--- 34 files changed, 548 insertions(+), 419 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 40edbeb3044d6..99460a59d32b7 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -4295,6 +4295,10 @@ "format": "int32", "type": "integer" }, + "managedBy": { + "description": "ManagedBy field indicates the controller that manages a Job. The k8s Job controller reconciles jobs which don't have this field at all or the field value is the reserved string `job-controller.k8s.io`, but skips reconciling Jobs with a custom value for this field. The value must be a valid DNS subdomain name.\n\nThis field is alpha-level. The job controller accepts setting the field when the feature gate JobManagedBy is enabled (disabled by default).", + "type": "string" + }, "manualSelector": { "description": "manualSelector controls generation of pod labels and pod selectors. Leave `manualSelector` unset unless you are certain what you are doing. When false or unset, the system pick labels unique to this job and appends those labels to the pod template. When true, the user is responsible for picking unique labels and specifying the selector. Failure to pick a unique label may cause this and other jobs to not function correctly. However, You may see `manualSelector=true` in jobs that were created with the old `extensions/v1beta1` API. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector", "type": "boolean" @@ -4344,7 +4348,7 @@ "description": "JobStatus represents the current state of a Job.", "properties": { "active": { - "description": "The number of pending and running pods which are not terminating (without a deletionTimestamp).", + "description": "The number of pending and running pods which are not terminating (without a deletionTimestamp). The value is zero for finished jobs.", "format": "int32", "type": "integer" }, @@ -4354,10 +4358,10 @@ }, "completionTime": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", - "description": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is only set when the job finishes successfully." + "description": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is set when the job finishes successfully, and only then. The value cannot be updated or removed. The value is equal or later than startTime." }, "conditions": { - "description": "The latest available observations of an object's current state. When a Job fails, one of the conditions will have type \"Failed\" and status true. When a Job is suspended, one of the conditions will have type \"Suspended\" and status true; when the Job is resumed, the status of this condition will become false. When a Job is completed, one of the conditions will have type \"Complete\" and status true. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "description": "The latest available observations of an object's current state. When a Job fails, one of the conditions will have type \"Failed\" and status true. When a Job is suspended, one of the conditions will have type \"Suspended\" and status true; when the Job is resumed, the status of this condition will become false. When a Job is completed, one of the conditions will have type \"Complete\" and status true.\n\nA job is considered finished when it is either in the \"Complete\" or \"Failed\" condition. Job cannot be in the \"Complete\" condition together we neither \"Failed\" nor \"FailureTarget\" condition. The \"Complete\", \"Failed\" and \"FailureTarget\" conditions cannot be disabled.\n\nMore info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", "items": { "$ref": "#/definitions/io.k8s.api.batch.v1.JobCondition" }, @@ -4367,7 +4371,7 @@ "x-kubernetes-patch-strategy": "merge" }, "failed": { - "description": "The number of pods which reached phase Failed.", + "description": "The number of pods which reached phase Failed. The value increases monotonically.", "format": "int32", "type": "integer" }, @@ -4376,27 +4380,27 @@ "type": "string" }, "ready": { - "description": "The number of pods which have a Ready condition.", + "description": "The number of pods which have a Ready condition. The value is zero (or nil) for finished jobs.", "format": "int32", "type": "integer" }, "startTime": { "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time", - "description": "Represents time when the job controller started processing a job. When a Job is created in the suspended state, this field is not set until the first time it is resumed. This field is reset every time a Job is resumed from suspension. It is represented in RFC3339 form and is in UTC." + "description": "Represents time when the job controller started processing a job. When a Job is created in the suspended state, this field is not set until the first time it is resumed. This field is reset every time a Job is resumed from suspension. It is represented in RFC3339 form and is in UTC.\n\nOnce set, the field can only be removed when the job is suspended. The field cannot be modified while the job is unsuspended or finished." }, "succeeded": { - "description": "The number of pods which reached phase Succeeded.", + "description": "The number of pods which reached phase Succeeded. The value increases monotonically, with the exception of elastic indexed jobs (with completions = parallelism), which can be scaled down.", "format": "int32", "type": "integer" }, "terminating": { - "description": "The number of pods which are terminating (in phase Pending or Running and have a deletionTimestamp).\n\nThis field is beta-level. The job controller populates the field when the feature gate JobPodReplacementPolicy is enabled (enabled by default).", + "description": "The number of pods which are terminating (in phase Pending or Running and have a deletionTimestamp). The value is zero (or nil) for finished jobs.\n\nThis field is beta-level. The job controller populates the field when the feature gate JobPodReplacementPolicy is enabled (enabled by default).", "format": "int32", "type": "integer" }, "uncountedTerminatedPods": { "$ref": "#/definitions/io.k8s.api.batch.v1.UncountedTerminatedPods", - "description": "uncountedTerminatedPods holds the UIDs of Pods that have terminated but the job controller hasn't yet accounted for in the status counters.\n\nThe job controller creates pods with a finalizer. When a pod terminates (succeeded or failed), the controller does three steps to account for it in the job status:\n\n1. Add the pod UID to the arrays in this field. 2. Remove the pod finalizer. 3. Remove the pod UID from the arrays while increasing the corresponding\n counter.\n\nOld jobs might not be tracked using this field, in which case the field remains null." + "description": "uncountedTerminatedPods holds the UIDs of Pods that have terminated but the job controller hasn't yet accounted for in the status counters.\n\nThe job controller creates pods with a finalizer. When a pod terminates (succeeded or failed), the controller does three steps to account for it in the job status:\n\n1. Add the pod UID to the arrays in this field. 2. Remove the pod finalizer. 3. Remove the pod UID from the arrays while increasing the corresponding\n counter.\n\nOld jobs might not be tracked using this field, in which case the field remains null. The structure is empty for finished jobs." } }, "type": "object" diff --git a/api/openapi-spec/v3/apis__batch__v1_openapi.json b/api/openapi-spec/v3/apis__batch__v1_openapi.json index c169e28f99bb0..898ae9a23f188 100644 --- a/api/openapi-spec/v3/apis__batch__v1_openapi.json +++ b/api/openapi-spec/v3/apis__batch__v1_openapi.json @@ -344,6 +344,10 @@ "format": "int32", "type": "integer" }, + "managedBy": { + "description": "ManagedBy field indicates the controller that manages a Job. The k8s Job controller reconciles jobs which don't have this field at all or the field value is the reserved string `job-controller.k8s.io`, but skips reconciling Jobs with a custom value for this field. The value must be a valid DNS subdomain name.\n\nThis field is alpha-level. The job controller accepts setting the field when the feature gate JobManagedBy is enabled (disabled by default).", + "type": "string" + }, "manualSelector": { "description": "manualSelector controls generation of pod labels and pod selectors. Leave `manualSelector` unset unless you are certain what you are doing. When false or unset, the system pick labels unique to this job and appends those labels to the pod template. When true, the user is responsible for picking unique labels and specifying the selector. Failure to pick a unique label may cause this and other jobs to not function correctly. However, You may see `manualSelector=true` in jobs that were created with the old `extensions/v1beta1` API. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/#specifying-your-own-pod-selector", "type": "boolean" @@ -406,7 +410,7 @@ "description": "JobStatus represents the current state of a Job.", "properties": { "active": { - "description": "The number of pending and running pods which are not terminating (without a deletionTimestamp).", + "description": "The number of pending and running pods which are not terminating (without a deletionTimestamp). The value is zero for finished jobs.", "format": "int32", "type": "integer" }, @@ -420,10 +424,10 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" } ], - "description": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is only set when the job finishes successfully." + "description": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is set when the job finishes successfully, and only then. The value cannot be updated or removed. The value is equal or later than startTime." }, "conditions": { - "description": "The latest available observations of an object's current state. When a Job fails, one of the conditions will have type \"Failed\" and status true. When a Job is suspended, one of the conditions will have type \"Suspended\" and status true; when the Job is resumed, the status of this condition will become false. When a Job is completed, one of the conditions will have type \"Complete\" and status true. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "description": "The latest available observations of an object's current state. When a Job fails, one of the conditions will have type \"Failed\" and status true. When a Job is suspended, one of the conditions will have type \"Suspended\" and status true; when the Job is resumed, the status of this condition will become false. When a Job is completed, one of the conditions will have type \"Complete\" and status true.\n\nA job is considered finished when it is either in the \"Complete\" or \"Failed\" condition. Job cannot be in the \"Complete\" condition together we neither \"Failed\" nor \"FailureTarget\" condition. The \"Complete\", \"Failed\" and \"FailureTarget\" conditions cannot be disabled.\n\nMore info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", "items": { "allOf": [ { @@ -438,7 +442,7 @@ "x-kubernetes-patch-strategy": "merge" }, "failed": { - "description": "The number of pods which reached phase Failed.", + "description": "The number of pods which reached phase Failed. The value increases monotonically.", "format": "int32", "type": "integer" }, @@ -447,7 +451,7 @@ "type": "string" }, "ready": { - "description": "The number of pods which have a Ready condition.", + "description": "The number of pods which have a Ready condition. The value is zero (or nil) for finished jobs.", "format": "int32", "type": "integer" }, @@ -457,15 +461,15 @@ "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" } ], - "description": "Represents time when the job controller started processing a job. When a Job is created in the suspended state, this field is not set until the first time it is resumed. This field is reset every time a Job is resumed from suspension. It is represented in RFC3339 form and is in UTC." + "description": "Represents time when the job controller started processing a job. When a Job is created in the suspended state, this field is not set until the first time it is resumed. This field is reset every time a Job is resumed from suspension. It is represented in RFC3339 form and is in UTC.\n\nOnce set, the field can only be removed when the job is suspended. The field cannot be modified while the job is unsuspended or finished." }, "succeeded": { - "description": "The number of pods which reached phase Succeeded.", + "description": "The number of pods which reached phase Succeeded. The value increases monotonically, with the exception of elastic indexed jobs (with completions = parallelism), which can be scaled down.", "format": "int32", "type": "integer" }, "terminating": { - "description": "The number of pods which are terminating (in phase Pending or Running and have a deletionTimestamp).\n\nThis field is beta-level. The job controller populates the field when the feature gate JobPodReplacementPolicy is enabled (enabled by default).", + "description": "The number of pods which are terminating (in phase Pending or Running and have a deletionTimestamp). The value is zero (or nil) for finished jobs.\n\nThis field is beta-level. The job controller populates the field when the feature gate JobPodReplacementPolicy is enabled (enabled by default).", "format": "int32", "type": "integer" }, @@ -475,7 +479,7 @@ "$ref": "#/components/schemas/io.k8s.api.batch.v1.UncountedTerminatedPods" } ], - "description": "uncountedTerminatedPods holds the UIDs of Pods that have terminated but the job controller hasn't yet accounted for in the status counters.\n\nThe job controller creates pods with a finalizer. When a pod terminates (succeeded or failed), the controller does three steps to account for it in the job status:\n\n1. Add the pod UID to the arrays in this field. 2. Remove the pod finalizer. 3. Remove the pod UID from the arrays while increasing the corresponding\n counter.\n\nOld jobs might not be tracked using this field, in which case the field remains null." + "description": "uncountedTerminatedPods holds the UIDs of Pods that have terminated but the job controller hasn't yet accounted for in the status counters.\n\nThe job controller creates pods with a finalizer. When a pod terminates (succeeded or failed), the controller does three steps to account for it in the job status:\n\n1. Add the pod UID to the arrays in this field. 2. Remove the pod finalizer. 3. Remove the pod UID from the arrays while increasing the corresponding\n counter.\n\nOld jobs might not be tracked using this field, in which case the field remains null. The structure is empty for finished jobs." } }, "type": "object" diff --git a/pkg/apis/batch/fuzzer/fuzzer.go b/pkg/apis/batch/fuzzer/fuzzer.go index 832de7d2f6650..a4704588994e3 100644 --- a/pkg/apis/batch/fuzzer/fuzzer.go +++ b/pkg/apis/batch/fuzzer/fuzzer.go @@ -64,6 +64,9 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { podReplacementPolicy = batch.Failed } j.PodReplacementPolicy = &podReplacementPolicy + if c.RandBool() { + j.ManagedBy = pointer.String(batch.JobControllerName) + } }, func(sj *batch.CronJobSpec, c fuzz.Continue) { c.FuzzNoCustom(sj) diff --git a/pkg/apis/batch/types.go b/pkg/apis/batch/types.go index c5e572170ad2b..9e8a8b2624d37 100644 --- a/pkg/apis/batch/types.go +++ b/pkg/apis/batch/types.go @@ -44,11 +44,6 @@ const ( JobNameLabel = labelPrefix + LegacyJobNameLabel // Controller UID is used for selectors and labels for jobs ControllerUidLabel = labelPrefix + LegacyControllerUidLabel - // Label indicating the controller that manages a Job. The k8s Job controller - // reconciles jobs which don't have this label at all or the label value is - // the reserved string `job-controller.k8s.io`, but skips reconciling Jobs - // with a custom value for this label. - JobManagedByLabel = labelPrefix + "managed-by" // Annotation indicating the number of failures for the index corresponding // to the pod, which are counted towards the backoff limit. JobIndexFailureCountAnnotation = labelPrefix + "job-index-failure-count" @@ -56,7 +51,7 @@ const ( // to the pod, which don't count towards the backoff limit, according to the // pod failure policy. When the annotation is absent zero is implied. JobIndexIgnoredFailureCountAnnotation = labelPrefix + "job-index-ignored-failure-count" - // JobControllerName reserved value for the managed-by label for the built-in + // JobControllerName reserved value for the managedBy field for the built-in // Job controller. JobControllerName = "job-controller.k8s.io" ) @@ -417,6 +412,17 @@ type JobSpec struct { // This is on by default. // +optional PodReplacementPolicy *PodReplacementPolicy + + // ManagedBy field indicates the controller that manages a Job. The k8s Job + // controller reconciles jobs which don't have this field at all or the field + // value is the reserved string `job-controller.k8s.io`, but skips reconciling + // Jobs with a custom value for this field. + // The value must be a valid DNS subdomain name. + // + // This field is alpha-level. The job controller accepts setting the field + // when the feature gate JobManagedBy is enabled (disabled by default). + // +optional + ManagedBy *string } // JobStatus represents the current state of a Job. @@ -428,6 +434,12 @@ type JobStatus struct { // status true; when the Job is resumed, the status of this condition will // become false. When a Job is completed, one of the conditions will have // type "Complete" and status true. + // + // A job is considered finished when it is either in the "Complete" or "Failed" + // condition. Job cannot be in the "Complete" condition together we neither + // "Failed" nor "FailureTarget" condition. + // The "Complete", "Failed" and "FailureTarget" conditions cannot be disabled. + // // +optional Conditions []JobCondition @@ -435,23 +447,31 @@ type JobStatus struct { // Job is created in the suspended state, this field is not set until the // first time it is resumed. This field is reset every time a Job is resumed // from suspension. It is represented in RFC3339 form and is in UTC. + // + // Once set, the field can only be removed when the job is suspended. + // The field cannot be modified while the job is unsuspended or finished. + // // +optional StartTime *metav1.Time // Represents time when the job was completed. It is not guaranteed to // be set in happens-before order across separate operations. // It is represented in RFC3339 form and is in UTC. - // The completion time is only set when the job finishes successfully. + // The completion time is set when the job finishes successfully, and only then. + // The value cannot be updated or removed. The value is equal or later + // than startTime. // +optional CompletionTime *metav1.Time // The number of pending and running pods which are not terminating (without // a deletionTimestamp). + // The value is zero for finished jobs. // +optional Active int32 // The number of pods which are terminating (in phase Pending or Running // and have a deletionTimestamp). + // The value is zero (or nil) for finished jobs. // // This field is beta-level. The job controller populates the field when // the feature gate JobPodReplacementPolicy is enabled (enabled by default). @@ -459,14 +479,18 @@ type JobStatus struct { Terminating *int32 // The number of active pods which have a Ready condition. + // The value is zero (or nil) for finished jobs. // +optional Ready *int32 // The number of pods which reached phase Succeeded. + // The value increases monotonically, with the exception of elastic indexed + // jobs (with completions = parallelism), which can be scaled down. // +optional Succeeded int32 // The number of pods which reached phase Failed. + // The value increases monotonically. // +optional Failed int32 @@ -507,6 +531,7 @@ type JobStatus struct { // // Old jobs might not be tracked using this field, in which case the field // remains null. + // The structure is empty for finished jobs. // +optional UncountedTerminatedPods *UncountedTerminatedPods } diff --git a/pkg/apis/batch/v1/zz_generated.conversion.go b/pkg/apis/batch/v1/zz_generated.conversion.go index 603b7e36975dc..7c7cef8afdabe 100644 --- a/pkg/apis/batch/v1/zz_generated.conversion.go +++ b/pkg/apis/batch/v1/zz_generated.conversion.go @@ -452,6 +452,7 @@ func autoConvert_v1_JobSpec_To_batch_JobSpec(in *v1.JobSpec, out *batch.JobSpec, out.CompletionMode = (*batch.CompletionMode)(unsafe.Pointer(in.CompletionMode)) out.Suspend = (*bool)(unsafe.Pointer(in.Suspend)) out.PodReplacementPolicy = (*batch.PodReplacementPolicy)(unsafe.Pointer(in.PodReplacementPolicy)) + out.ManagedBy = (*string)(unsafe.Pointer(in.ManagedBy)) return nil } @@ -472,6 +473,7 @@ func autoConvert_batch_JobSpec_To_v1_JobSpec(in *batch.JobSpec, out *v1.JobSpec, out.CompletionMode = (*v1.CompletionMode)(unsafe.Pointer(in.CompletionMode)) out.Suspend = (*bool)(unsafe.Pointer(in.Suspend)) out.PodReplacementPolicy = (*v1.PodReplacementPolicy)(unsafe.Pointer(in.PodReplacementPolicy)) + out.ManagedBy = (*string)(unsafe.Pointer(in.ManagedBy)) return nil } diff --git a/pkg/apis/batch/validation/validation.go b/pkg/apis/batch/validation/validation.go index f851a995d5182..8d306020ad4b4 100644 --- a/pkg/apis/batch/validation/validation.go +++ b/pkg/apis/batch/validation/validation.go @@ -208,6 +208,9 @@ func validateJobSpec(spec *batch.JobSpec, fldPath *field.Path, opts apivalidatio allErrs = append(allErrs, field.Required(fldPath.Child("backoffLimitPerIndex"), fmt.Sprintf("when maxFailedIndexes is specified"))) } } + if spec.ManagedBy != nil { + allErrs = append(allErrs, apivalidation.ValidateDNS1123Subdomain(*spec.ManagedBy, fldPath.Child("managedBy"))...) + } if spec.CompletionMode != nil { if *spec.CompletionMode != batch.NonIndexedCompletion && *spec.CompletionMode != batch.IndexedCompletion { allErrs = append(allErrs, field.NotSupported(fldPath.Child("completionMode"), spec.CompletionMode, []batch.CompletionMode{batch.NonIndexedCompletion, batch.IndexedCompletion})) @@ -509,25 +512,18 @@ func validateJobStatus(job *batch.Job, fldPath *field.Path, opts JobStatusValida // ValidateJobUpdate validates an update to a Job and returns an ErrorList with any errors. func ValidateJobUpdate(job, oldJob *batch.Job, opts JobValidationOptions) field.ErrorList { - allErrs := validateJobMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta) + allErrs := apivalidation.ValidateObjectMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta, field.NewPath("metadata")) allErrs = append(allErrs, ValidateJobSpecUpdate(job.Spec, oldJob.Spec, field.NewPath("spec"), opts)...) return allErrs } // ValidateJobUpdateStatus validates an update to the status of a Job and returns an ErrorList with any errors. func ValidateJobUpdateStatus(job, oldJob *batch.Job, opts JobStatusValidationOptions) field.ErrorList { - allErrs := validateJobMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta) + allErrs := apivalidation.ValidateObjectMetaUpdate(&job.ObjectMeta, &oldJob.ObjectMeta, field.NewPath("metadata")) allErrs = append(allErrs, ValidateJobStatusUpdate(job, oldJob, opts)...) return allErrs } -func validateJobMetaUpdate(newMeta, oldMeta *metav1.ObjectMeta) field.ErrorList { - fldPath := field.NewPath("metadata") - allErrs := apivalidation.ValidateObjectMetaUpdate(newMeta, oldMeta, fldPath) - allErrs = append(allErrs, apivalidation.ValidateImmutableLabel(newMeta.Labels[batch.JobManagedByLabel], oldMeta.Labels[batch.JobManagedByLabel], batch.JobManagedByLabel, fldPath)...) - return allErrs -} - // ValidateJobSpecUpdate validates an update to a JobSpec and returns an ErrorList with any errors. func ValidateJobSpecUpdate(spec, oldSpec batch.JobSpec, fldPath *field.Path, opts JobValidationOptions) field.ErrorList { allErrs := field.ErrorList{} @@ -538,6 +534,7 @@ func ValidateJobSpecUpdate(spec, oldSpec batch.JobSpec, fldPath *field.Path, opt allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.CompletionMode, oldSpec.CompletionMode, fldPath.Child("completionMode"))...) allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.PodFailurePolicy, oldSpec.PodFailurePolicy, fldPath.Child("podFailurePolicy"))...) allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.BackoffLimitPerIndex, oldSpec.BackoffLimitPerIndex, fldPath.Child("backoffLimitPerIndex"))...) + allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.ManagedBy, oldSpec.ManagedBy, fldPath.Child("managedBy"))...) return allErrs } @@ -599,8 +596,8 @@ func ValidateJobStatusUpdate(job, oldJob *batch.Job, opts JobStatusValidationOpt allErrs = append(allErrs, field.Invalid(statusFld.Child("completionTime"), job.Status.CompletionTime, "completionTime cannot be mutated")) } } - if opts.RejectRemovingStartTimeForUnsuspendedJob { - if oldJob.Status.StartTime != nil && job.Status.StartTime == nil && !ptr.Deref(job.Spec.Suspend, false) { + if opts.RejectStartTimeUpdateForUnsuspendedJob { + if oldJob.Status.StartTime != nil && !ptr.Equal(oldJob.Status.StartTime, job.Status.StartTime) && !ptr.Deref(job.Spec.Suspend, false) { allErrs = append(allErrs, field.Required(statusFld.Child("startTime"), "startTime cannot be removed for unsuspended job")) } } @@ -868,7 +865,7 @@ type JobStatusValidationOptions struct { RejectFinishedJobWithTerminatingPods bool RejectFinishedJobWithoutStartTime bool RejectFinishedJobWithUncountedTerminatedPods bool - RejectRemovingStartTimeForUnsuspendedJob bool + RejectStartTimeUpdateForUnsuspendedJob bool RejectCompletionTimeBeforeStartTime bool RejectMutatingCompletionTime bool RejectCompleteJobWithoutCompletionTime bool diff --git a/pkg/apis/batch/validation/validation_test.go b/pkg/apis/batch/validation/validation_test.go index 0cbfa7b6b58a1..8d3393985f943 100644 --- a/pkg/apis/batch/validation/validation_test.go +++ b/pkg/apis/batch/validation/validation_test.go @@ -34,6 +34,7 @@ import ( api "k8s.io/kubernetes/pkg/apis/core" corevalidation "k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) var ( @@ -381,6 +382,17 @@ func TestValidateJob(t *testing.T) { }, }, }, + "valid managedBy field": { + opts: JobValidationOptions{RequirePrefixedLabels: true}, + job: batch.Job{ + ObjectMeta: validJobObjectMeta, + Spec: batch.JobSpec{ + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + ManagedBy: ptr.To("custom-job-controller"), + }, + }, + }, } for k, v := range successCases { t.Run(k, func(t *testing.T) { @@ -395,6 +407,17 @@ func TestValidateJob(t *testing.T) { opts JobValidationOptions job batch.Job }{ + `spec.managedBy: Invalid value: "invalid custom controller name": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*`: { + opts: JobValidationOptions{RequirePrefixedLabels: true}, + job: batch.Job{ + ObjectMeta: validJobObjectMeta, + Spec: batch.JobSpec{ + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + ManagedBy: ptr.To("invalid custom controller name"), + }, + }, + }, `spec.podFailurePolicy.rules[0]: Invalid value: specifying one of OnExitCodes and OnPodConditions is required`: { job: batch.Job{ ObjectMeta: validJobObjectMeta, @@ -1350,7 +1373,7 @@ func TestValidateJobUpdate(t *testing.T) { job.Spec.ManualSelector = pointer.Bool(true) }, }, - "invalid attempt to update managed-by label": { + "invalid attempt to set managedBy field": { old: batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "abc", @@ -1363,31 +1386,31 @@ func TestValidateJobUpdate(t *testing.T) { }, }, update: func(job *batch.Job) { - job.Labels[batch.JobManagedByLabel] = "custom-controller" + job.Spec.ManagedBy = ptr.To("custom-controller") }, err: &field.Error{ Type: field.ErrorTypeInvalid, - Field: "metadata.labels.batch.kubernetes.io/managed-by", + Field: "spec.managedBy", }, }, - "update managed-by label; feature enabled": { + "invalid update of the managedBy field": { old: batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "abc", Namespace: metav1.NamespaceDefault, - Labels: map[string]string{}, }, Spec: batch.JobSpec{ - Selector: validGeneratedSelector, - Template: validPodTemplateSpecForGenerated, + Selector: validGeneratedSelector, + Template: validPodTemplateSpecForGenerated, + ManagedBy: ptr.To("custom-controller1"), }, }, update: func(job *batch.Job) { - job.Labels[batch.JobManagedByLabel] = "custom-controller" + job.Spec.ManagedBy = ptr.To("custom-controller2") }, err: &field.Error{ Type: field.ErrorTypeInvalid, - Field: "metadata.labels.batch.kubernetes.io/managed-by", + Field: "spec.managedBy", }, }, "immutable completions for non-indexed jobs": { @@ -2090,52 +2113,6 @@ func TestValidateJobUpdateStatus(t *testing.T) { }, }, }, - "invalid attempt to set managed-by label": { - old: batch.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: "abc", - Namespace: metav1.NamespaceDefault, - Labels: map[string]string{}, - }, - }, - update: batch.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: "abc", - Namespace: metav1.NamespaceDefault, - ResourceVersion: "1", - Labels: map[string]string{ - batch.JobManagedByLabel: "custom-job-controller", - }, - }, - }, - wantErrs: field.ErrorList{ - {Type: field.ErrorTypeInvalid, Field: "metadata.labels.batch.kubernetes.io/managed-by"}, - }, - }, - "invalid attempt to update managed-by label": { - old: batch.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: "abc", - Namespace: metav1.NamespaceDefault, - Labels: map[string]string{ - batch.JobManagedByLabel: "custom-job-controller1", - }, - }, - }, - update: batch.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: "abc", - Namespace: metav1.NamespaceDefault, - ResourceVersion: "1", - Labels: map[string]string{ - batch.JobManagedByLabel: "custom-job-controller2", - }, - }, - }, - wantErrs: field.ErrorList{ - {Type: field.ErrorTypeInvalid, Field: "metadata.labels.batch.kubernetes.io/managed-by"}, - }, - }, "nil ready and terminating": { old: batch.Job{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/apis/batch/zz_generated.deepcopy.go b/pkg/apis/batch/zz_generated.deepcopy.go index f34516f7b4acc..e29334dadeddd 100644 --- a/pkg/apis/batch/zz_generated.deepcopy.go +++ b/pkg/apis/batch/zz_generated.deepcopy.go @@ -308,6 +308,11 @@ func (in *JobSpec) DeepCopyInto(out *JobSpec) { *out = new(PodReplacementPolicy) **out = **in } + if in.ManagedBy != nil { + in, out := &in.ManagedBy, &out.ManagedBy + *out = new(string) + **out = **in + } return } diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index adc1f959fbafa..61b543cc58764 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -352,14 +352,6 @@ func ValidateImmutableField(newVal, oldVal interface{}, fldPath *field.Path) fie return apimachineryvalidation.ValidateImmutableField(newVal, oldVal, fldPath) } -func ValidateImmutableLabel(newVal string, oldVal string, label string, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - if oldVal != newVal { - allErrs = append(allErrs, field.Invalid(fldPath.Child("labels", label), newVal, fieldImmutableErrorMsg)) - } - return allErrs -} - func ValidateImmutableAnnotation(newVal string, oldVal string, annotation string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} diff --git a/pkg/controller/job/job_controller.go b/pkg/controller/job/job_controller.go index 33ce0579420c8..146d0f5e77dd7 100644 --- a/pkg/controller/job/job_controller.go +++ b/pkg/controller/job/job_controller.go @@ -1968,9 +1968,9 @@ func recordJobPodsCreationTotal(job *batch.Job, jobCtx *syncJobCtx, succeeded, f } func managedByExternalController(jobObj *batch.Job) *string { - if feature.DefaultFeatureGate.Enabled(features.JobManagedByLabel) { - if controllerName, found := jobObj.Labels[batch.JobManagedByLabel]; found && controllerName != batch.JobControllerName { - return &controllerName + if feature.DefaultFeatureGate.Enabled(features.JobManagedBy) { + if controllerName := jobObj.Spec.ManagedBy; controllerName != nil && *controllerName != batch.JobControllerName { + return controllerName } } return nil diff --git a/pkg/controller/job/job_controller_test.go b/pkg/controller/job/job_controller_test.go index f9dd23f2885f1..5f3f1ba7add59 100644 --- a/pkg/controller/job/job_controller_test.go +++ b/pkg/controller/job/job_controller_test.go @@ -2292,7 +2292,7 @@ func TestSyncJobDeleted(t *testing.T) { } } -func TestSyncJobWhenManagedByLabel(t *testing.T) { +func TestSyncJobWhenManagedBy(t *testing.T) { _, ctx := ktesting.NewTestContext(t) now := metav1.Now() baseJob := batch.Job{ @@ -2327,24 +2327,24 @@ func TestSyncJobWhenManagedByLabel(t *testing.T) { } testCases := map[string]struct { - enableJobManagedByLabel bool - job batch.Job - wantStatus batch.JobStatus + enableJobManagedBy bool + job batch.Job + wantStatus batch.JobStatus }{ - "job with custom value of managed-by label; feature enabled; the status is unchanged": { - enableJobManagedByLabel: true, + "job with custom value of managedBy; feature enabled; the status is unchanged": { + enableJobManagedBy: true, job: func() batch.Job { job := baseJob.DeepCopy() - job.Labels[batch.JobManagedByLabel] = "custom-managed-by" + job.Spec.ManagedBy = ptr.To("custom-managed-by") return *job }(), wantStatus: baseJob.Status, }, - "job with managed-by label equal job-controller.k8s.io; feature enabled; the status is updated": { - enableJobManagedByLabel: true, + "job with well known value of the managedBy; feature enabled; the status is updated": { + enableJobManagedBy: true, job: func() batch.Job { job := baseJob.DeepCopy() - job.Labels[batch.JobManagedByLabel] = "job-controller.k8s.io" + job.Spec.ManagedBy = ptr.To(batch.JobControllerName) return *job }(), wantStatus: batch.JobStatus{ @@ -2355,10 +2355,10 @@ func TestSyncJobWhenManagedByLabel(t *testing.T) { UncountedTerminatedPods: &batch.UncountedTerminatedPods{}, }, }, - "job with custom value of managed-by label; feature disabled; the status is updated": { + "job with custom value of managedBy; feature disabled; the status is updated": { job: func() batch.Job { job := baseJob.DeepCopy() - job.Labels[batch.JobManagedByLabel] = "custom-managed-by" + job.Spec.ManagedBy = ptr.To("custom-managed-by") return *job }(), wantStatus: batch.JobStatus{ @@ -2369,9 +2369,9 @@ func TestSyncJobWhenManagedByLabel(t *testing.T) { UncountedTerminatedPods: &batch.UncountedTerminatedPods{}, }, }, - "job without the managed-by label; feature enabled; the status is updated": { - enableJobManagedByLabel: true, - job: baseJob, + "job without the managedBy; feature enabled; the status is updated": { + enableJobManagedBy: true, + job: baseJob, wantStatus: batch.JobStatus{ Active: 2, Ready: ptr.To[int32](0), @@ -2383,7 +2383,7 @@ func TestSyncJobWhenManagedByLabel(t *testing.T) { } for name, tc := range testCases { t.Run(name, func(t *testing.T) { - defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobManagedByLabel, tc.enableJobManagedByLabel)() + defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobManagedBy, tc.enableJobManagedBy)() clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: "", ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}) manager, sharedInformerFactory := newControllerFromClient(ctx, t, clientset, controller.NoResyncPeriodFunc) @@ -4084,7 +4084,7 @@ func TestUpdateJobRequeue(t *testing.T) { logger, ctx := ktesting.NewTestContext(t) clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: "", ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}) cases := map[string]struct { - enableJobManagedByLabel bool + enableJobManagedBy bool oldJob *batch.Job updateFn func(job *batch.Job) wantRequeuedImmediately bool @@ -4097,11 +4097,11 @@ func TestUpdateJobRequeue(t *testing.T) { }, wantRequeuedImmediately: true, }, - "spec update; managed-by label used": { - enableJobManagedByLabel: true, + "spec update; managedBy used": { + enableJobManagedBy: true, oldJob: func() *batch.Job { job := newJob(1, 1, 1, batch.IndexedCompletion) - job.Labels = map[string]string{batch.JobManagedByLabel: "custom-job-controller"} + job.Spec.ManagedBy = ptr.To("custom-job-controller") return job }(), updateFn: func(job *batch.Job) { @@ -4120,7 +4120,7 @@ func TestUpdateJobRequeue(t *testing.T) { } for name, tc := range cases { t.Run(name, func(t *testing.T) { - defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobManagedByLabel, tc.enableJobManagedByLabel)() + defer featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.JobManagedBy, tc.enableJobManagedBy)() manager, sharedInformerFactory := newControllerFromClient(ctx, t, clientset, controller.NoResyncPeriodFunc) manager.podStoreSynced = alwaysReady manager.jobStoreSynced = alwaysReady diff --git a/pkg/controller/job/metrics/metrics.go b/pkg/controller/job/metrics/metrics.go index 626a79d46d073..c6e137525b8c7 100644 --- a/pkg/controller/job/metrics/metrics.go +++ b/pkg/controller/job/metrics/metrics.go @@ -74,7 +74,7 @@ var ( // JobByExternalControllerTotal tracks the number of Jobs that were created // as managed by an external controller. // The value of the label controllerName corresponds to the value of the - // managed-by label. + // managedBy field. JobByExternalControllerTotal = metrics.NewCounterVec( &metrics.CounterOpts{ Subsystem: JobControllerSubsystem, diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 54eee33ced4a0..239dda2963e3b 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -350,10 +350,10 @@ const ( // owner: @mimowo // kep: https://kep.k8s.io/4368 - // beta: v1.30 + // alpha: v1.30 // // Allows to delegate reconciliation of a Job object to an external controller. - JobManagedByLabel featuregate.Feature = "JobManagedByLabel" + JobManagedBy featuregate.Feature = "JobManagedBy" // owner: @mimowo // kep: https://kep.k8s.io/3329 @@ -1034,7 +1034,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS JobBackoffLimitPerIndex: {Default: true, PreRelease: featuregate.Beta}, - JobManagedByLabel: {Default: true, PreRelease: featuregate.Beta}, + JobManagedBy: {Default: false, PreRelease: featuregate.Alpha}, JobPodFailurePolicy: {Default: true, PreRelease: featuregate.Beta}, diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 7b6c8463902e3..d2b16ef3eb40d 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -16236,6 +16236,13 @@ func schema_k8sio_api_batch_v1_JobSpec(ref common.ReferenceCallback) common.Open Enum: []interface{}{"Failed", "TerminatingOrFailed"}, }, }, + "managedBy": { + SchemaProps: spec.SchemaProps{ + Description: "ManagedBy field indicates the controller that manages a Job. The k8s Job controller reconciles jobs which don't have this field at all or the field value is the reserved string `job-controller.k8s.io`, but skips reconciling Jobs with a custom value for this field. The value must be a valid DNS subdomain name.\n\nThis field is alpha-level. The job controller accepts setting the field when the feature gate JobManagedBy is enabled (disabled by default).", + Type: []string{"string"}, + Format: "", + }, + }, }, Required: []string{"template"}, }, @@ -16261,7 +16268,7 @@ func schema_k8sio_api_batch_v1_JobStatus(ref common.ReferenceCallback) common.Op }, }, SchemaProps: spec.SchemaProps{ - Description: "The latest available observations of an object's current state. When a Job fails, one of the conditions will have type \"Failed\" and status true. When a Job is suspended, one of the conditions will have type \"Suspended\" and status true; when the Job is resumed, the status of this condition will become false. When a Job is completed, one of the conditions will have type \"Complete\" and status true. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + Description: "The latest available observations of an object's current state. When a Job fails, one of the conditions will have type \"Failed\" and status true. When a Job is suspended, one of the conditions will have type \"Suspended\" and status true; when the Job is resumed, the status of this condition will become false. When a Job is completed, one of the conditions will have type \"Complete\" and status true.\n\nA job is considered finished when it is either in the \"Complete\" or \"Failed\" condition. Job cannot be in the \"Complete\" condition together we neither \"Failed\" nor \"FailureTarget\" condition. The \"Complete\", \"Failed\" and \"FailureTarget\" conditions cannot be disabled.\n\nMore info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ @@ -16275,40 +16282,40 @@ func schema_k8sio_api_batch_v1_JobStatus(ref common.ReferenceCallback) common.Op }, "startTime": { SchemaProps: spec.SchemaProps{ - Description: "Represents time when the job controller started processing a job. When a Job is created in the suspended state, this field is not set until the first time it is resumed. This field is reset every time a Job is resumed from suspension. It is represented in RFC3339 form and is in UTC.", + Description: "Represents time when the job controller started processing a job. When a Job is created in the suspended state, this field is not set until the first time it is resumed. This field is reset every time a Job is resumed from suspension. It is represented in RFC3339 form and is in UTC.\n\nOnce set, the field can only be removed when the job is suspended. The field cannot be modified while the job is unsuspended or finished.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, "completionTime": { SchemaProps: spec.SchemaProps{ - Description: "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is only set when the job finishes successfully.", + Description: "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is set when the job finishes successfully, and only then. The value cannot be updated or removed. The value is equal or later than startTime.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, "active": { SchemaProps: spec.SchemaProps{ - Description: "The number of pending and running pods which are not terminating (without a deletionTimestamp).", + Description: "The number of pending and running pods which are not terminating (without a deletionTimestamp). The value is zero for finished jobs.", Type: []string{"integer"}, Format: "int32", }, }, "succeeded": { SchemaProps: spec.SchemaProps{ - Description: "The number of pods which reached phase Succeeded.", + Description: "The number of pods which reached phase Succeeded. The value increases monotonically, with the exception of elastic indexed jobs (with completions = parallelism), which can be scaled down.", Type: []string{"integer"}, Format: "int32", }, }, "failed": { SchemaProps: spec.SchemaProps{ - Description: "The number of pods which reached phase Failed.", + Description: "The number of pods which reached phase Failed. The value increases monotonically.", Type: []string{"integer"}, Format: "int32", }, }, "terminating": { SchemaProps: spec.SchemaProps{ - Description: "The number of pods which are terminating (in phase Pending or Running and have a deletionTimestamp).\n\nThis field is beta-level. The job controller populates the field when the feature gate JobPodReplacementPolicy is enabled (enabled by default).", + Description: "The number of pods which are terminating (in phase Pending or Running and have a deletionTimestamp). The value is zero (or nil) for finished jobs.\n\nThis field is beta-level. The job controller populates the field when the feature gate JobPodReplacementPolicy is enabled (enabled by default).", Type: []string{"integer"}, Format: "int32", }, @@ -16329,13 +16336,13 @@ func schema_k8sio_api_batch_v1_JobStatus(ref common.ReferenceCallback) common.Op }, "uncountedTerminatedPods": { SchemaProps: spec.SchemaProps{ - Description: "uncountedTerminatedPods holds the UIDs of Pods that have terminated but the job controller hasn't yet accounted for in the status counters.\n\nThe job controller creates pods with a finalizer. When a pod terminates (succeeded or failed), the controller does three steps to account for it in the job status:\n\n1. Add the pod UID to the arrays in this field. 2. Remove the pod finalizer. 3. Remove the pod UID from the arrays while increasing the corresponding\n counter.\n\nOld jobs might not be tracked using this field, in which case the field remains null.", + Description: "uncountedTerminatedPods holds the UIDs of Pods that have terminated but the job controller hasn't yet accounted for in the status counters.\n\nThe job controller creates pods with a finalizer. When a pod terminates (succeeded or failed), the controller does three steps to account for it in the job status:\n\n1. Add the pod UID to the arrays in this field. 2. Remove the pod finalizer. 3. Remove the pod UID from the arrays while increasing the corresponding\n counter.\n\nOld jobs might not be tracked using this field, in which case the field remains null. The structure is empty for finished jobs.", Ref: ref("k8s.io/api/batch/v1.UncountedTerminatedPods"), }, }, "ready": { SchemaProps: spec.SchemaProps{ - Description: "The number of pods which have a Ready condition.", + Description: "The number of pods which have a Ready condition. The value is zero (or nil) for finished jobs.", Type: []string{"integer"}, Format: "int32", }, diff --git a/pkg/registry/batch/job/strategy.go b/pkg/registry/batch/job/strategy.go index 2ca525b97bcb2..148b4fd42a54b 100644 --- a/pkg/registry/batch/job/strategy.go +++ b/pkg/registry/batch/job/strategy.go @@ -101,6 +101,9 @@ func (jobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { if !utilfeature.DefaultFeatureGate.Enabled(features.JobPodFailurePolicy) { job.Spec.PodFailurePolicy = nil } + if !utilfeature.DefaultFeatureGate.Enabled(features.JobManagedBy) { + job.Spec.ManagedBy = nil + } if !utilfeature.DefaultFeatureGate.Enabled(features.JobBackoffLimitPerIndex) { job.Spec.BackoffLimitPerIndex = nil @@ -341,9 +344,9 @@ func (jobStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob // getStatusValidationOptions returns validation options for Job status func getStatusValidationOptions(newJob, oldJob *batch.Job) batchvalidation.JobStatusValidationOptions { - if utilfeature.DefaultFeatureGate.Enabled(features.JobManagedByLabel) { + if utilfeature.DefaultFeatureGate.Enabled(features.JobManagedBy) { // A strengthened validation of the Job status transitions is needed since the - // Job managed-by label let's the Job object be controlled by external + // Job managedBy field let's the Job object be controlled by external // controllers. We want to make sure the transitions done by the external // controllers meet the expectations of the clients of the Job API. // For example, we verify that a Job in terminal state (Failed or Complete) @@ -380,7 +383,7 @@ func getStatusValidationOptions(newJob, oldJob *batch.Job) batchvalidation.JobSt RejectFinishedJobWithTerminatingPods: isJobFinishedChanged || isTerminatingChanged, RejectFinishedJobWithoutStartTime: isJobFinishedChanged || isStartTimeChanged, RejectFinishedJobWithUncountedTerminatedPods: isJobFinishedChanged || isUncountedTerminatedPodsChanged, - RejectRemovingStartTimeForUnsuspendedJob: isStartTimeChanged, + RejectStartTimeUpdateForUnsuspendedJob: isStartTimeChanged, RejectCompletionTimeBeforeStartTime: isStartTimeChanged || isCompletionTimeChanged, RejectMutatingCompletionTime: true, RejectNotCompleteJobWithCompletionTime: isJobCompleteChanged || isCompletionTimeChanged, diff --git a/pkg/registry/batch/job/strategy_test.go b/pkg/registry/batch/job/strategy_test.go index 830c90ce7aa10..2d6d1c3f38362 100644 --- a/pkg/registry/batch/job/strategy_test.go +++ b/pkg/registry/batch/job/strategy_test.go @@ -482,6 +482,7 @@ func TestJobStrategy_PrepareForCreate(t *testing.T) { enableJobPodFailurePolicy bool enableJobBackoffLimitPerIndex bool enableJobPodReplacementPolicy bool + enableJobManageBy bool job batch.Job wantJob batch.Job }{ @@ -753,6 +754,47 @@ func TestJobStrategy_PrepareForCreate(t *testing.T) { }, }, }, + "managedBy field is dropped when the feature gate is disabled": { + enableJobManageBy: false, + job: batch.Job{ + ObjectMeta: getValidObjectMeta(0), + Spec: batch.JobSpec{ + Selector: validSelector, + ManualSelector: pointer.Bool(false), + Template: validPodTemplateSpec, + ManagedBy: ptr.To("custom-controller-name"), + }, + }, + wantJob: batch.Job{ + ObjectMeta: getValidObjectMeta(1), + Spec: batch.JobSpec{ + Selector: validSelector, + ManualSelector: pointer.Bool(false), + Template: expectedPodTemplateSpec, + }, + }, + }, + "managedBy field is set when the feature gate is enabled": { + enableJobManageBy: true, + job: batch.Job{ + ObjectMeta: getValidObjectMeta(0), + Spec: batch.JobSpec{ + Selector: validSelector, + ManualSelector: pointer.Bool(false), + Template: validPodTemplateSpec, + ManagedBy: ptr.To("custom-controller-name"), + }, + }, + wantJob: batch.Job{ + ObjectMeta: getValidObjectMeta(1), + Spec: batch.JobSpec{ + Selector: validSelector, + ManualSelector: pointer.Bool(false), + Template: expectedPodTemplateSpec, + ManagedBy: ptr.To("custom-controller-name"), + }, + }, + }, } for name, tc := range cases { @@ -760,6 +802,7 @@ func TestJobStrategy_PrepareForCreate(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobPodFailurePolicy, tc.enableJobPodFailurePolicy)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobBackoffLimitPerIndex, tc.enableJobBackoffLimitPerIndex)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobPodReplacementPolicy, tc.enableJobPodReplacementPolicy)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobManagedBy, tc.enableJobManageBy)() ctx := genericapirequest.NewDefaultContext() Strategy.PrepareForCreate(ctx, &tc.job) @@ -1870,7 +1913,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { nowPlusMinute := metav1.Time{Time: now.Add(time.Minute)} cases := map[string]struct { - enableJobManagedByLabel bool + enableJobManagedBy bool job *batch.Job newJob *batch.Job @@ -1916,7 +1959,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid addition of both Failed=True and Complete=True; allowed because feature gate disabled": { - enableJobManagedByLabel: false, + enableJobManagedBy: false, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -1939,7 +1982,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid addition of both Failed=True and Complete=True": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -1965,7 +2008,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "completionTime can be removed to fix still running job": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Status: batch.JobStatus{ @@ -1981,7 +2024,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to transition to Failed=True without startTime": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -2001,7 +2044,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to transition to Complete=True without startTime": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -2022,7 +2065,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to transition to Complete=True with active > 0": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -2045,7 +2088,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to transition to Complete=True with terminating > 0": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -2068,7 +2111,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to transition to Failed=True with uncountedTerminatedPods.Failed>0": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -2092,7 +2135,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to transition to Complete=True with uncountedTerminatedPods.Succeeded>0": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -2117,7 +2160,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid addition Complete=True without setting CompletionTime": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -2138,7 +2181,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to remove completionTime": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Status: batch.JobStatus{ @@ -2169,7 +2212,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "verify startTime can be cleared for suspended job": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2190,7 +2233,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "verify startTime cannot be removed for unsuspended job": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Status: batch.JobStatus{ @@ -2207,8 +2250,26 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { {Type: field.ErrorTypeRequired, Field: "status.startTime"}, }, }, + "verify startTime cannot be updated for unsuspended job": { + enableJobManagedBy: true, + job: &batch.Job{ + ObjectMeta: validObjectMeta, + Status: batch.JobStatus{ + StartTime: &now, + }, + }, + newJob: &batch.Job{ + ObjectMeta: validObjectMeta, + Status: batch.JobStatus{ + StartTime: &nowPlusMinute, + }, + }, + wantErrs: field.ErrorList{ + {Type: field.ErrorTypeRequired, Field: "status.startTime"}, + }, + }, "invalid attempt to set completionTime before startTime": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Status: batch.JobStatus{ @@ -2233,7 +2294,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to modify completionTime": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Status: batch.JobStatus{ @@ -2264,7 +2325,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid removal of terminal condition Failed=True": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Status: batch.JobStatus{ @@ -2284,7 +2345,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid removal of terminal condition Complete=True": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Status: batch.JobStatus{ @@ -2304,7 +2365,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid removal of terminal condition FailureTarget=True": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Status: batch.JobStatus{ @@ -2324,7 +2385,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt setting of CompletionTime when there is no Complete condition": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -2339,7 +2400,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid CompletionTime when there is no Complete condition, but allowed": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Status: batch.JobStatus{ @@ -2355,7 +2416,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt setting CompletedIndexes when non-indexed completion mode is used": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2379,7 +2440,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid because CompletedIndexes set when non-indexed completion mode is used; but allowed": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2403,7 +2464,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt setting FailedIndexes when not backoffLimitPerIndex": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2426,7 +2487,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to decrease the failed counter": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2450,7 +2511,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to decrease the succeeded counter": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2474,7 +2535,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to set bad format for CompletedIndexes": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2497,7 +2558,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid format for CompletedIndexes, but allowed": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2521,7 +2582,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid attempt to set bad format for FailedIndexes": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2546,7 +2607,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid format for FailedIndexes, but allowed": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2571,39 +2632,8 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, }, - "invalid attempt to update the managed-by label": { - enableJobManagedByLabel: true, - job: &batch.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: "abc", - Namespace: metav1.NamespaceDefault, - Labels: map[string]string{ - batch.JobManagedByLabel: "custom-value1", - }, - ResourceVersion: "2", - }, - Spec: batch.JobSpec{}, - }, - newJob: &batch.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: "abc", - Namespace: metav1.NamespaceDefault, - Labels: map[string]string{ - batch.JobManagedByLabel: "custom-value2", - }, - ResourceVersion: "2", - }, - Spec: batch.JobSpec{}, - Status: batch.JobStatus{ - Active: 1, - }, - }, - wantErrs: field.ErrorList{ - {Type: field.ErrorTypeInvalid, Field: "metadata.labels.batch.kubernetes.io/managed-by"}, - }, - }, "invalid attempt to set more ready pods than active": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2625,7 +2655,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "more ready pods than active, but allowed": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ @@ -2649,7 +2679,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { }, }, "invalid addition of both FailureTarget=True and Complete=True": { - enableJobManagedByLabel: true, + enableJobManagedBy: true, job: &batch.Job{ ObjectMeta: validObjectMeta, }, @@ -2677,7 +2707,7 @@ func TestStatusStrategy_ValidateUpdate(t *testing.T) { } for name, tc := range cases { t.Run(name, func(t *testing.T) { - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobManagedByLabel, tc.enableJobManagedByLabel)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobManagedBy, tc.enableJobManagedBy)() errs := StatusStrategy.ValidateUpdate(ctx, tc.newJob, tc.job) if diff := cmp.Diff(tc.wantErrs, errs, ignoreErrValueDetail); diff != "" { t.Errorf("Unexpected errors (-want,+got):\n%s", diff) diff --git a/staging/src/k8s.io/api/batch/v1/generated.pb.go b/staging/src/k8s.io/api/batch/v1/generated.pb.go index 0f010b7a10757..989c9a0e71af5 100644 --- a/staging/src/k8s.io/api/batch/v1/generated.pb.go +++ b/staging/src/k8s.io/api/batch/v1/generated.pb.go @@ -495,119 +495,120 @@ func init() { } var fileDescriptor_79228dc2c4001a22 = []byte{ - // 1783 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcd, 0x6f, 0x24, 0x47, - 0x15, 0xf7, 0xd8, 0x1e, 0x7b, 0xa6, 0xc6, 0x1f, 0xb3, 0xb5, 0xde, 0xdd, 0xc1, 0x44, 0xd3, 0xce, - 0x6c, 0x12, 0x39, 0x28, 0xf4, 0x64, 0x9d, 0x15, 0xe1, 0x43, 0xa0, 0x6c, 0x7b, 0xd9, 0xb0, 0x66, - 0x9c, 0x1d, 0x6a, 0xbc, 0x20, 0x85, 0x80, 0xa8, 0xe9, 0xae, 0x19, 0x77, 0xb6, 0xa7, 0xab, 0xe9, - 0xaa, 0xb6, 0xd6, 0x17, 0x84, 0xc4, 0x1f, 0x00, 0x7f, 0x05, 0x47, 0x2e, 0x70, 0x86, 0x1b, 0xf2, - 0x31, 0xe2, 0x14, 0x71, 0x68, 0xb1, 0xcd, 0x1f, 0xc0, 0xdd, 0x08, 0x09, 0x55, 0x75, 0x4d, 0x7f, - 0x4d, 0xb7, 0xf1, 0x46, 0x62, 0x95, 0x9b, 0xfb, 0xbd, 0xdf, 0xfb, 0xd5, 0xc7, 0x7b, 0xf5, 0x7b, - 0xcf, 0x03, 0xee, 0x3e, 0xfb, 0x26, 0xd3, 0x6d, 0xda, 0xc7, 0x9e, 0xdd, 0x1f, 0x63, 0x6e, 0x9e, - 0xf6, 0xcf, 0xee, 0xf5, 0xa7, 0xc4, 0x25, 0x3e, 0xe6, 0xc4, 0xd2, 0x3d, 0x9f, 0x72, 0x0a, 0x6f, - 0xc6, 0x20, 0x1d, 0x7b, 0xb6, 0x2e, 0x41, 0xfa, 0xd9, 0xbd, 0xdd, 0xaf, 0x4f, 0x6d, 0x7e, 0x1a, - 0x8c, 0x75, 0x93, 0xce, 0xfa, 0x53, 0x3a, 0xa5, 0x7d, 0x89, 0x1d, 0x07, 0x13, 0xf9, 0x25, 0x3f, - 0xe4, 0x5f, 0x31, 0xc7, 0x6e, 0x2f, 0xb3, 0x90, 0x49, 0x7d, 0x52, 0xb2, 0xce, 0xee, 0xfd, 0x14, - 0x33, 0xc3, 0xe6, 0xa9, 0xed, 0x12, 0xff, 0xbc, 0xef, 0x3d, 0x9b, 0x0a, 0x03, 0xeb, 0xcf, 0x08, - 0xc7, 0x65, 0x51, 0xfd, 0xaa, 0x28, 0x3f, 0x70, 0xb9, 0x3d, 0x23, 0x0b, 0x01, 0xdf, 0xf8, 0x5f, - 0x01, 0xcc, 0x3c, 0x25, 0x33, 0x5c, 0x8c, 0xeb, 0xfd, 0xbb, 0x06, 0xd6, 0x0f, 0x7d, 0xea, 0x1e, - 0xd1, 0x31, 0xfc, 0x05, 0x68, 0x88, 0xfd, 0x58, 0x98, 0xe3, 0x4e, 0x6d, 0xaf, 0xb6, 0xdf, 0x3a, - 0x78, 0x57, 0x4f, 0x6f, 0x29, 0xa1, 0xd5, 0xbd, 0x67, 0x53, 0x61, 0x60, 0xba, 0x40, 0xeb, 0x67, - 0xf7, 0xf4, 0x27, 0xe3, 0x4f, 0x89, 0xc9, 0x8f, 0x09, 0xc7, 0x06, 0xbc, 0x08, 0xb5, 0xa5, 0x28, - 0xd4, 0x40, 0x6a, 0x43, 0x09, 0x2b, 0x34, 0xc0, 0x2a, 0xf3, 0x88, 0xd9, 0x59, 0x96, 0xec, 0x7b, - 0x7a, 0x49, 0x0e, 0x74, 0xb5, 0x9b, 0x91, 0x47, 0x4c, 0x63, 0x43, 0xb1, 0xad, 0x8a, 0x2f, 0x24, - 0x63, 0xe1, 0x11, 0x58, 0x63, 0x1c, 0xf3, 0x80, 0x75, 0x56, 0x24, 0x4b, 0xef, 0x4a, 0x16, 0x89, - 0x34, 0xb6, 0x14, 0xcf, 0x5a, 0xfc, 0x8d, 0x14, 0x43, 0xef, 0x0f, 0x35, 0xd0, 0x52, 0xc8, 0x81, - 0xcd, 0x38, 0xfc, 0x64, 0xe1, 0x06, 0xf4, 0xeb, 0xdd, 0x80, 0x88, 0x96, 0xe7, 0x6f, 0xab, 0x95, - 0x1a, 0x73, 0x4b, 0xe6, 0xf4, 0x0f, 0x40, 0xdd, 0xe6, 0x64, 0xc6, 0x3a, 0xcb, 0x7b, 0x2b, 0xfb, - 0xad, 0x83, 0xd7, 0xae, 0xda, 0xb8, 0xb1, 0xa9, 0x88, 0xea, 0x8f, 0x45, 0x08, 0x8a, 0x23, 0x7b, - 0x7f, 0x5b, 0x4d, 0x36, 0x2c, 0xae, 0x04, 0xbe, 0x03, 0x1a, 0x22, 0xb1, 0x56, 0xe0, 0x10, 0xb9, - 0xe1, 0x66, 0xba, 0x81, 0x91, 0xb2, 0xa3, 0x04, 0x01, 0xf7, 0x41, 0x43, 0xd4, 0xc2, 0xc7, 0xd4, - 0x25, 0x9d, 0x86, 0x44, 0x6f, 0x08, 0xe4, 0x89, 0xb2, 0xa1, 0xc4, 0x0b, 0x9f, 0x82, 0x3b, 0x8c, - 0x63, 0x9f, 0xdb, 0xee, 0xf4, 0x21, 0xc1, 0x96, 0x63, 0xbb, 0x64, 0x44, 0x4c, 0xea, 0x5a, 0x4c, - 0xe6, 0x6e, 0xc5, 0xf8, 0x6a, 0x14, 0x6a, 0x77, 0x46, 0xe5, 0x10, 0x54, 0x15, 0x0b, 0x3f, 0x01, - 0x37, 0x4c, 0xea, 0x9a, 0x81, 0xef, 0x13, 0xd7, 0x3c, 0x1f, 0x52, 0xc7, 0x36, 0xcf, 0x65, 0x1a, - 0x9b, 0x86, 0xae, 0xf6, 0x7d, 0xe3, 0xb0, 0x08, 0xb8, 0x2c, 0x33, 0xa2, 0x45, 0x22, 0xf8, 0x26, - 0x58, 0x67, 0x01, 0xf3, 0x88, 0x6b, 0x75, 0x56, 0xf7, 0x6a, 0xfb, 0x0d, 0xa3, 0x15, 0x85, 0xda, - 0xfa, 0x28, 0x36, 0xa1, 0xb9, 0x0f, 0xfe, 0x14, 0xb4, 0x3e, 0xa5, 0xe3, 0x13, 0x32, 0xf3, 0x1c, - 0xcc, 0x49, 0xa7, 0x2e, 0xf3, 0xfc, 0x46, 0x69, 0x32, 0x8e, 0x52, 0x9c, 0xac, 0xc7, 0x9b, 0x6a, - 0x93, 0xad, 0x8c, 0x03, 0x65, 0xd9, 0xe0, 0xcf, 0xc1, 0x2e, 0x0b, 0x4c, 0x93, 0x30, 0x36, 0x09, - 0x9c, 0x23, 0x3a, 0x66, 0x3f, 0xb0, 0x19, 0xa7, 0xfe, 0xf9, 0xc0, 0x9e, 0xd9, 0xbc, 0xb3, 0xb6, - 0x57, 0xdb, 0xaf, 0x1b, 0xdd, 0x28, 0xd4, 0x76, 0x47, 0x95, 0x28, 0x74, 0x05, 0x03, 0x44, 0xe0, - 0xf6, 0x04, 0xdb, 0x0e, 0xb1, 0x16, 0xb8, 0xd7, 0x25, 0xf7, 0x6e, 0x14, 0x6a, 0xb7, 0x1f, 0x95, - 0x22, 0x50, 0x45, 0x64, 0xef, 0xcf, 0xcb, 0x60, 0x33, 0xf7, 0x5e, 0xe0, 0x0f, 0xc1, 0x1a, 0x36, - 0xb9, 0x7d, 0x26, 0x8a, 0x4a, 0x94, 0xea, 0xdd, 0xec, 0xed, 0x08, 0xa5, 0x4b, 0x5f, 0x3d, 0x22, - 0x13, 0x22, 0x92, 0x40, 0xd2, 0x47, 0xf6, 0x40, 0x86, 0x22, 0x45, 0x01, 0x1d, 0xd0, 0x76, 0x30, - 0xe3, 0xf3, 0x7a, 0x14, 0xd5, 0x26, 0xf3, 0xd3, 0x3a, 0xf8, 0xda, 0xf5, 0x1e, 0x97, 0x88, 0x30, - 0x76, 0xa2, 0x50, 0x6b, 0x0f, 0x0a, 0x3c, 0x68, 0x81, 0x19, 0xfa, 0x00, 0x4a, 0x5b, 0x72, 0x85, - 0x72, 0xbd, 0xfa, 0x4b, 0xaf, 0x77, 0x3b, 0x0a, 0x35, 0x38, 0x58, 0x60, 0x42, 0x25, 0xec, 0xbd, - 0x7f, 0xd5, 0xc0, 0xca, 0xab, 0x11, 0xd0, 0xef, 0xe5, 0x04, 0xf4, 0xb5, 0xaa, 0xa2, 0xad, 0x14, - 0xcf, 0x47, 0x05, 0xf1, 0xec, 0x56, 0x32, 0x5c, 0x2d, 0x9c, 0x7f, 0x5d, 0x01, 0x1b, 0x47, 0x74, - 0x7c, 0x48, 0x5d, 0xcb, 0xe6, 0x36, 0x75, 0xe1, 0x7d, 0xb0, 0xca, 0xcf, 0xbd, 0xb9, 0x08, 0xed, - 0xcd, 0x97, 0x3e, 0x39, 0xf7, 0xc8, 0x65, 0xa8, 0xb5, 0xb3, 0x58, 0x61, 0x43, 0x12, 0x0d, 0x07, - 0xc9, 0x76, 0x96, 0x65, 0xdc, 0xfd, 0xfc, 0x72, 0x97, 0xa1, 0x56, 0xd2, 0x62, 0xf5, 0x84, 0x29, - 0xbf, 0x29, 0x38, 0x05, 0x9b, 0x22, 0x39, 0x43, 0x9f, 0x8e, 0xe3, 0x2a, 0x5b, 0x79, 0xe9, 0xac, - 0xdf, 0x52, 0x1b, 0xd8, 0x1c, 0x64, 0x89, 0x50, 0x9e, 0x17, 0x9e, 0xc5, 0x35, 0x76, 0xe2, 0x63, - 0x97, 0xc5, 0x47, 0xfa, 0x62, 0x35, 0xbd, 0xab, 0x56, 0x93, 0x75, 0x96, 0x67, 0x43, 0x25, 0x2b, - 0xc0, 0xb7, 0xc0, 0x9a, 0x4f, 0x30, 0xa3, 0xae, 0xac, 0xe7, 0x66, 0x9a, 0x1d, 0x24, 0xad, 0x48, - 0x79, 0xe1, 0xdb, 0x60, 0x7d, 0x46, 0x18, 0xc3, 0x53, 0x22, 0x15, 0xa7, 0x69, 0x6c, 0x2b, 0xe0, - 0xfa, 0x71, 0x6c, 0x46, 0x73, 0x7f, 0xef, 0xf7, 0x35, 0xb0, 0xfe, 0x6a, 0xba, 0xdf, 0x77, 0xf3, - 0xdd, 0xaf, 0x53, 0x55, 0x79, 0x15, 0x9d, 0xef, 0xb7, 0x0d, 0xb9, 0x51, 0xd9, 0xf5, 0xee, 0x81, - 0x96, 0x87, 0x7d, 0xec, 0x38, 0xc4, 0xb1, 0xd9, 0x4c, 0xee, 0xb5, 0x6e, 0x6c, 0x0b, 0x5d, 0x1e, - 0xa6, 0x66, 0x94, 0xc5, 0x88, 0x10, 0x93, 0xce, 0x3c, 0x87, 0x88, 0xcb, 0x8c, 0xcb, 0x4d, 0x85, - 0x1c, 0xa6, 0x66, 0x94, 0xc5, 0xc0, 0x27, 0xe0, 0x56, 0xac, 0x60, 0xc5, 0x0e, 0xb8, 0x22, 0x3b, - 0xe0, 0x57, 0xa2, 0x50, 0xbb, 0xf5, 0xa0, 0x0c, 0x80, 0xca, 0xe3, 0xe0, 0x14, 0xb4, 0x3d, 0x6a, - 0x09, 0x71, 0x0e, 0x7c, 0xa2, 0x9a, 0x5f, 0x4b, 0xde, 0xf3, 0x9b, 0xa5, 0x97, 0x31, 0x2c, 0x80, - 0x63, 0x0d, 0x2c, 0x5a, 0xd1, 0x02, 0x29, 0xbc, 0x0f, 0x36, 0xc6, 0xd8, 0x7c, 0x46, 0x27, 0x93, - 0x6c, 0x6b, 0x68, 0x47, 0xa1, 0xb6, 0x61, 0x64, 0xec, 0x28, 0x87, 0x82, 0x03, 0xb0, 0x93, 0xfd, - 0x1e, 0x12, 0xff, 0xb1, 0x6b, 0x91, 0xe7, 0x9d, 0x0d, 0x19, 0xdd, 0x89, 0x42, 0x6d, 0xc7, 0x28, - 0xf1, 0xa3, 0xd2, 0x28, 0xf8, 0x01, 0x68, 0xcf, 0xf0, 0xf3, 0xb8, 0x13, 0x49, 0x0b, 0x61, 0x9d, - 0x4d, 0xc9, 0x24, 0x4f, 0x71, 0x5c, 0xf0, 0xa1, 0x05, 0x34, 0xfc, 0x19, 0x68, 0x30, 0xe2, 0x10, - 0x93, 0x53, 0x5f, 0xbd, 0xad, 0xf7, 0xae, 0x59, 0x8e, 0x78, 0x4c, 0x9c, 0x91, 0x0a, 0x8d, 0x47, - 0x9c, 0xf9, 0x17, 0x4a, 0x28, 0xe1, 0xb7, 0xc1, 0xd6, 0x0c, 0xbb, 0x01, 0x4e, 0x90, 0xf2, 0x51, - 0x35, 0x0c, 0x18, 0x85, 0xda, 0xd6, 0x71, 0xce, 0x83, 0x0a, 0x48, 0xf8, 0x23, 0xd0, 0xe0, 0xf3, - 0xf9, 0x61, 0x4d, 0x6e, 0xad, 0xb4, 0x43, 0x0e, 0xa9, 0x95, 0x1b, 0x1f, 0x92, 0xe7, 0x91, 0xcc, - 0x0e, 0x09, 0x8d, 0x98, 0xb8, 0x38, 0x77, 0x54, 0xa9, 0x3c, 0x98, 0x70, 0xe2, 0x3f, 0xb2, 0x5d, - 0x9b, 0x9d, 0x12, 0x4b, 0x8e, 0x6a, 0xf5, 0x78, 0xe2, 0x3a, 0x39, 0x19, 0x94, 0x41, 0x50, 0x55, - 0x2c, 0x1c, 0x80, 0xad, 0xb4, 0xa6, 0x8f, 0xa9, 0x45, 0x3a, 0x4d, 0xa9, 0x08, 0x6f, 0x88, 0x53, - 0x1e, 0xe6, 0x3c, 0x97, 0x0b, 0x16, 0x54, 0x88, 0xcd, 0x4e, 0x58, 0xe0, 0x8a, 0x09, 0xcb, 0x02, - 0x3b, 0x1e, 0xb5, 0x10, 0xf1, 0x1c, 0x6c, 0x92, 0x19, 0x71, 0xb9, 0x2a, 0xf6, 0x2d, 0xb9, 0xf4, - 0xbb, 0xa2, 0x92, 0x86, 0x25, 0xfe, 0xcb, 0x0a, 0x3b, 0x2a, 0x65, 0xeb, 0xfd, 0xa7, 0x0e, 0x9a, - 0xe9, 0xc8, 0xf2, 0x14, 0x00, 0x73, 0xde, 0x17, 0x98, 0x1a, 0x5b, 0x5e, 0xaf, 0xd2, 0x98, 0xa4, - 0x83, 0xa4, 0xed, 0x36, 0x31, 0x31, 0x94, 0x21, 0x82, 0x3f, 0x01, 0x4d, 0x39, 0xcc, 0x4a, 0x85, - 0x5f, 0x7e, 0x69, 0x85, 0xdf, 0x8c, 0x42, 0xad, 0x39, 0x9a, 0x13, 0xa0, 0x94, 0x0b, 0x4e, 0xb2, - 0x89, 0xf9, 0x82, 0xdd, 0x0a, 0xe6, 0x93, 0x28, 0x97, 0x28, 0xb0, 0x8a, 0x9e, 0xa1, 0x46, 0xb9, - 0x55, 0x59, 0x46, 0x55, 0x53, 0x5a, 0x1f, 0x34, 0xe5, 0xd8, 0x49, 0x2c, 0x62, 0xc9, 0x97, 0x50, - 0x37, 0x6e, 0x28, 0x68, 0x73, 0x34, 0x77, 0xa0, 0x14, 0x23, 0x88, 0xe3, 0x79, 0x52, 0x4d, 0xb5, - 0x09, 0x71, 0xfc, 0x8a, 0x91, 0xf2, 0x0a, 0xe5, 0xe5, 0xc4, 0x9f, 0xd9, 0x2e, 0x16, 0xff, 0x11, - 0x48, 0xc1, 0x53, 0xca, 0x7b, 0x92, 0x9a, 0x51, 0x16, 0x03, 0x1f, 0x82, 0xb6, 0x3a, 0x45, 0xaa, - 0x1d, 0xeb, 0xb2, 0x76, 0x3a, 0x6a, 0x91, 0xf6, 0x61, 0xc1, 0x8f, 0x16, 0x22, 0xe0, 0xfb, 0x60, - 0x73, 0x92, 0x93, 0x1f, 0x20, 0x29, 0x6e, 0x88, 0xf6, 0x9e, 0xd7, 0x9e, 0x3c, 0x0e, 0xfe, 0xa6, - 0x06, 0xee, 0x04, 0xae, 0x49, 0x03, 0x97, 0x13, 0x6b, 0xbe, 0x49, 0x62, 0x0d, 0xa9, 0xc5, 0xe4, - 0x5b, 0x6c, 0x1d, 0xbc, 0x53, 0x5a, 0x58, 0x4f, 0xcb, 0x63, 0xe2, 0x97, 0x5b, 0xe1, 0x44, 0x55, - 0x2b, 0x41, 0x0d, 0xd4, 0x7d, 0x82, 0xad, 0x73, 0xf9, 0x60, 0xeb, 0x46, 0x53, 0x74, 0x44, 0x24, - 0x0c, 0x28, 0xb6, 0xf7, 0xfe, 0x58, 0x03, 0xdb, 0x85, 0x7f, 0x50, 0xbe, 0xfc, 0x13, 0x68, 0x6f, - 0x0c, 0x16, 0x3a, 0x18, 0xfc, 0x08, 0xd4, 0xfd, 0xc0, 0x21, 0xf3, 0x67, 0xfb, 0xf6, 0xb5, 0xba, - 0x21, 0x0a, 0x1c, 0x92, 0xce, 0x0a, 0xe2, 0x8b, 0xa1, 0x98, 0xa6, 0xf7, 0xf7, 0x1a, 0x78, 0xab, - 0x08, 0x7f, 0xe2, 0x7e, 0xff, 0xb9, 0xcd, 0x0f, 0xa9, 0x45, 0x18, 0x22, 0xbf, 0x0c, 0x6c, 0x5f, - 0x4a, 0x89, 0x28, 0x12, 0x93, 0xba, 0x1c, 0x8b, 0x6b, 0xf9, 0x08, 0xcf, 0xe6, 0x03, 0xac, 0x2c, - 0x92, 0xc3, 0xac, 0x03, 0xe5, 0x71, 0x70, 0x04, 0x1a, 0xd4, 0x23, 0x3e, 0x16, 0x8d, 0x23, 0x1e, - 0x5e, 0xdf, 0x9f, 0xab, 0xfb, 0x13, 0x65, 0xbf, 0x0c, 0xb5, 0xbb, 0x57, 0x6c, 0x63, 0x0e, 0x43, - 0x09, 0x11, 0xec, 0x81, 0xb5, 0x33, 0xec, 0x04, 0x44, 0xcc, 0x18, 0x2b, 0xfb, 0x75, 0x03, 0x88, - 0xf7, 0xf4, 0x63, 0x69, 0x41, 0xca, 0xd3, 0xfb, 0x4b, 0xe9, 0xe1, 0x86, 0xd4, 0x4a, 0x15, 0x6c, - 0x88, 0x39, 0x27, 0xbe, 0x0b, 0x3f, 0xcc, 0x0d, 0xe5, 0xef, 0x15, 0x86, 0xf2, 0xbb, 0x25, 0xa3, - 0x75, 0x96, 0xe6, 0xff, 0x35, 0xa7, 0xf7, 0x2e, 0x96, 0xc1, 0x4e, 0x59, 0x36, 0xe1, 0x07, 0xb1, - 0x56, 0x51, 0x57, 0xed, 0x78, 0x3f, 0xab, 0x55, 0xd4, 0xbd, 0x0c, 0xb5, 0xdb, 0xc5, 0xb8, 0xd8, - 0x83, 0x54, 0x1c, 0x74, 0x41, 0x8b, 0xa6, 0x37, 0xac, 0x8a, 0xf4, 0x3b, 0xd7, 0xaa, 0xa7, 0xf2, - 0x02, 0x89, 0x95, 0x2a, 0xeb, 0xcb, 0x2e, 0x00, 0x7f, 0x05, 0xb6, 0x69, 0xfe, 0xee, 0x65, 0xe6, - 0xae, 0xbf, 0x66, 0x59, 0xde, 0x8c, 0x3b, 0xea, 0xdc, 0xdb, 0x05, 0x3f, 0x2a, 0x2e, 0xd6, 0xfb, - 0x53, 0x0d, 0x54, 0x29, 0x0b, 0x1c, 0x66, 0x15, 0x5d, 0xbc, 0xac, 0xa6, 0x71, 0x90, 0x53, 0xf3, - 0xcb, 0x50, 0x7b, 0xbd, 0xea, 0x67, 0x43, 0x91, 0x76, 0xa6, 0x3f, 0x7d, 0xfc, 0x30, 0x2b, 0xf9, - 0x1f, 0x26, 0x92, 0xbf, 0x2c, 0xe9, 0xfa, 0xa9, 0xdc, 0x5f, 0x8f, 0x4b, 0x85, 0x1b, 0xdf, 0xba, - 0x78, 0xd1, 0x5d, 0xfa, 0xec, 0x45, 0x77, 0xe9, 0xf3, 0x17, 0xdd, 0xa5, 0x5f, 0x47, 0xdd, 0xda, - 0x45, 0xd4, 0xad, 0x7d, 0x16, 0x75, 0x6b, 0x9f, 0x47, 0xdd, 0xda, 0x3f, 0xa2, 0x6e, 0xed, 0x77, - 0xff, 0xec, 0x2e, 0x7d, 0x7c, 0xb3, 0xe4, 0x77, 0xdc, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x6d, - 0xdd, 0x3b, 0x38, 0xdd, 0x15, 0x00, 0x00, + // 1804 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x6f, 0xe4, 0x48, + 0x15, 0x4f, 0x27, 0xe9, 0xa4, 0xbb, 0x3a, 0x7f, 0x7a, 0x6a, 0x32, 0x33, 0x4d, 0x58, 0xb5, 0xb3, + 0x3d, 0xbb, 0xab, 0x2c, 0x2c, 0xee, 0x9d, 0xec, 0x88, 0xe5, 0x8f, 0x40, 0x3b, 0xce, 0x30, 0xcb, + 0x84, 0xce, 0x4e, 0x53, 0x9d, 0x01, 0x69, 0x59, 0x10, 0xd5, 0x76, 0x75, 0xc7, 0x3b, 0xb6, 0xcb, + 0xd8, 0xe5, 0x68, 0x72, 0x41, 0x48, 0x7c, 0x01, 0x3e, 0x05, 0x47, 0x2e, 0x70, 0x44, 0x70, 0x43, + 0x39, 0xae, 0x38, 0xad, 0x38, 0x58, 0x8c, 0xf9, 0x00, 0xdc, 0x83, 0x90, 0x50, 0x95, 0xcb, 0x7f, + 0xdb, 0x0e, 0x99, 0x95, 0x18, 0x71, 0x8b, 0xdf, 0xfb, 0xbd, 0xdf, 0x7b, 0x55, 0xef, 0xd5, 0x7b, + 0x2f, 0x0d, 0xee, 0x3e, 0xfb, 0x86, 0xaf, 0x9a, 0x74, 0x88, 0x5d, 0x73, 0x38, 0xc5, 0x4c, 0x3f, + 0x1d, 0x9e, 0xdd, 0x1b, 0xce, 0x89, 0x43, 0x3c, 0xcc, 0x88, 0xa1, 0xba, 0x1e, 0x65, 0x14, 0xde, + 0x8c, 0x41, 0x2a, 0x76, 0x4d, 0x55, 0x80, 0xd4, 0xb3, 0x7b, 0xbb, 0x5f, 0x9b, 0x9b, 0xec, 0x34, + 0x98, 0xaa, 0x3a, 0xb5, 0x87, 0x73, 0x3a, 0xa7, 0x43, 0x81, 0x9d, 0x06, 0x33, 0xf1, 0x25, 0x3e, + 0xc4, 0x5f, 0x31, 0xc7, 0xee, 0x20, 0xe7, 0x48, 0xa7, 0x1e, 0xa9, 0xf0, 0xb3, 0x7b, 0x3f, 0xc3, + 0xd8, 0x58, 0x3f, 0x35, 0x1d, 0xe2, 0x9d, 0x0f, 0xdd, 0x67, 0x73, 0x2e, 0xf0, 0x87, 0x36, 0x61, + 0xb8, 0xca, 0x6a, 0x58, 0x67, 0xe5, 0x05, 0x0e, 0x33, 0x6d, 0xb2, 0x60, 0xf0, 0xf5, 0xff, 0x66, + 0xe0, 0xeb, 0xa7, 0xc4, 0xc6, 0x65, 0xbb, 0xc1, 0xbf, 0x1a, 0x60, 0xfd, 0xd0, 0xa3, 0xce, 0x11, + 0x9d, 0xc2, 0x9f, 0x83, 0x16, 0x8f, 0xc7, 0xc0, 0x0c, 0xf7, 0x1a, 0x7b, 0x8d, 0xfd, 0xce, 0xc1, + 0xbb, 0x6a, 0x76, 0x4b, 0x29, 0xad, 0xea, 0x3e, 0x9b, 0x73, 0x81, 0xaf, 0x72, 0xb4, 0x7a, 0x76, + 0x4f, 0x7d, 0x32, 0xfd, 0x94, 0xe8, 0xec, 0x98, 0x30, 0xac, 0xc1, 0x8b, 0x50, 0x59, 0x8a, 0x42, + 0x05, 0x64, 0x32, 0x94, 0xb2, 0x42, 0x0d, 0xac, 0xfa, 0x2e, 0xd1, 0x7b, 0xcb, 0x82, 0x7d, 0x4f, + 0xad, 0xc8, 0x81, 0x2a, 0xa3, 0x99, 0xb8, 0x44, 0xd7, 0x36, 0x24, 0xdb, 0x2a, 0xff, 0x42, 0xc2, + 0x16, 0x1e, 0x81, 0x35, 0x9f, 0x61, 0x16, 0xf8, 0xbd, 0x15, 0xc1, 0x32, 0xb8, 0x92, 0x45, 0x20, + 0xb5, 0x2d, 0xc9, 0xb3, 0x16, 0x7f, 0x23, 0xc9, 0x30, 0xf8, 0x5d, 0x03, 0x74, 0x24, 0x72, 0x64, + 0xfa, 0x0c, 0x7e, 0xb2, 0x70, 0x03, 0xea, 0xf5, 0x6e, 0x80, 0x5b, 0x8b, 0xf3, 0x77, 0xa5, 0xa7, + 0x56, 0x22, 0xc9, 0x9d, 0xfe, 0x01, 0x68, 0x9a, 0x8c, 0xd8, 0x7e, 0x6f, 0x79, 0x6f, 0x65, 0xbf, + 0x73, 0xf0, 0xda, 0x55, 0x81, 0x6b, 0x9b, 0x92, 0xa8, 0xf9, 0x98, 0x9b, 0xa0, 0xd8, 0x72, 0xf0, + 0xd7, 0xd5, 0x34, 0x60, 0x7e, 0x25, 0xf0, 0x1d, 0xd0, 0xe2, 0x89, 0x35, 0x02, 0x8b, 0x88, 0x80, + 0xdb, 0x59, 0x00, 0x13, 0x29, 0x47, 0x29, 0x02, 0xee, 0x83, 0x16, 0xaf, 0x85, 0x8f, 0xa9, 0x43, + 0x7a, 0x2d, 0x81, 0xde, 0xe0, 0xc8, 0x13, 0x29, 0x43, 0xa9, 0x16, 0x3e, 0x05, 0x77, 0x7c, 0x86, + 0x3d, 0x66, 0x3a, 0xf3, 0x87, 0x04, 0x1b, 0x96, 0xe9, 0x90, 0x09, 0xd1, 0xa9, 0x63, 0xf8, 0x22, + 0x77, 0x2b, 0xda, 0x97, 0xa3, 0x50, 0xb9, 0x33, 0xa9, 0x86, 0xa0, 0x3a, 0x5b, 0xf8, 0x09, 0xb8, + 0xa1, 0x53, 0x47, 0x0f, 0x3c, 0x8f, 0x38, 0xfa, 0xf9, 0x98, 0x5a, 0xa6, 0x7e, 0x2e, 0xd2, 0xd8, + 0xd6, 0x54, 0x19, 0xf7, 0x8d, 0xc3, 0x32, 0xe0, 0xb2, 0x4a, 0x88, 0x16, 0x89, 0xe0, 0x9b, 0x60, + 0xdd, 0x0f, 0x7c, 0x97, 0x38, 0x46, 0x6f, 0x75, 0xaf, 0xb1, 0xdf, 0xd2, 0x3a, 0x51, 0xa8, 0xac, + 0x4f, 0x62, 0x11, 0x4a, 0x74, 0xf0, 0x27, 0xa0, 0xf3, 0x29, 0x9d, 0x9e, 0x10, 0xdb, 0xb5, 0x30, + 0x23, 0xbd, 0xa6, 0xc8, 0xf3, 0x1b, 0x95, 0xc9, 0x38, 0xca, 0x70, 0xa2, 0x1e, 0x6f, 0xca, 0x20, + 0x3b, 0x39, 0x05, 0xca, 0xb3, 0xc1, 0x9f, 0x81, 0x5d, 0x3f, 0xd0, 0x75, 0xe2, 0xfb, 0xb3, 0xc0, + 0x3a, 0xa2, 0x53, 0xff, 0xfb, 0xa6, 0xcf, 0xa8, 0x77, 0x3e, 0x32, 0x6d, 0x93, 0xf5, 0xd6, 0xf6, + 0x1a, 0xfb, 0x4d, 0xad, 0x1f, 0x85, 0xca, 0xee, 0xa4, 0x16, 0x85, 0xae, 0x60, 0x80, 0x08, 0xdc, + 0x9e, 0x61, 0xd3, 0x22, 0xc6, 0x02, 0xf7, 0xba, 0xe0, 0xde, 0x8d, 0x42, 0xe5, 0xf6, 0xa3, 0x4a, + 0x04, 0xaa, 0xb1, 0x1c, 0xfc, 0x69, 0x19, 0x6c, 0x16, 0xde, 0x0b, 0xfc, 0x01, 0x58, 0xc3, 0x3a, + 0x33, 0xcf, 0x78, 0x51, 0xf1, 0x52, 0xbd, 0x9b, 0xbf, 0x1d, 0xde, 0xe9, 0xb2, 0x57, 0x8f, 0xc8, + 0x8c, 0xf0, 0x24, 0x90, 0xec, 0x91, 0x3d, 0x10, 0xa6, 0x48, 0x52, 0x40, 0x0b, 0x74, 0x2d, 0xec, + 0xb3, 0xa4, 0x1e, 0x79, 0xb5, 0x89, 0xfc, 0x74, 0x0e, 0xbe, 0x72, 0xbd, 0xc7, 0xc5, 0x2d, 0xb4, + 0x9d, 0x28, 0x54, 0xba, 0xa3, 0x12, 0x0f, 0x5a, 0x60, 0x86, 0x1e, 0x80, 0x42, 0x96, 0x5e, 0xa1, + 0xf0, 0xd7, 0x7c, 0x69, 0x7f, 0xb7, 0xa3, 0x50, 0x81, 0xa3, 0x05, 0x26, 0x54, 0xc1, 0x3e, 0xf8, + 0x67, 0x03, 0xac, 0xbc, 0x9a, 0x06, 0xfa, 0xdd, 0x42, 0x03, 0x7d, 0xad, 0xae, 0x68, 0x6b, 0x9b, + 0xe7, 0xa3, 0x52, 0xf3, 0xec, 0xd7, 0x32, 0x5c, 0xdd, 0x38, 0xff, 0xb2, 0x02, 0x36, 0x8e, 0xe8, + 0xf4, 0x90, 0x3a, 0x86, 0xc9, 0x4c, 0xea, 0xc0, 0xfb, 0x60, 0x95, 0x9d, 0xbb, 0x49, 0x13, 0xda, + 0x4b, 0x5c, 0x9f, 0x9c, 0xbb, 0xe4, 0x32, 0x54, 0xba, 0x79, 0x2c, 0x97, 0x21, 0x81, 0x86, 0xa3, + 0x34, 0x9c, 0x65, 0x61, 0x77, 0xbf, 0xe8, 0xee, 0x32, 0x54, 0x2a, 0x46, 0xac, 0x9a, 0x32, 0x15, + 0x83, 0x82, 0x73, 0xb0, 0xc9, 0x93, 0x33, 0xf6, 0xe8, 0x34, 0xae, 0xb2, 0x95, 0x97, 0xce, 0xfa, + 0x2d, 0x19, 0xc0, 0xe6, 0x28, 0x4f, 0x84, 0x8a, 0xbc, 0xf0, 0x2c, 0xae, 0xb1, 0x13, 0x0f, 0x3b, + 0x7e, 0x7c, 0xa4, 0x2f, 0x56, 0xd3, 0xbb, 0xd2, 0x9b, 0xa8, 0xb3, 0x22, 0x1b, 0xaa, 0xf0, 0x00, + 0xdf, 0x02, 0x6b, 0x1e, 0xc1, 0x3e, 0x75, 0x44, 0x3d, 0xb7, 0xb3, 0xec, 0x20, 0x21, 0x45, 0x52, + 0x0b, 0xdf, 0x06, 0xeb, 0x36, 0xf1, 0x7d, 0x3c, 0x27, 0xa2, 0xe3, 0xb4, 0xb5, 0x6d, 0x09, 0x5c, + 0x3f, 0x8e, 0xc5, 0x28, 0xd1, 0x0f, 0x7e, 0xdb, 0x00, 0xeb, 0xaf, 0x66, 0xfa, 0x7d, 0xa7, 0x38, + 0xfd, 0x7a, 0x75, 0x95, 0x57, 0x33, 0xf9, 0xfe, 0xd8, 0x12, 0x81, 0x8a, 0xa9, 0x77, 0x0f, 0x74, + 0x5c, 0xec, 0x61, 0xcb, 0x22, 0x96, 0xe9, 0xdb, 0x22, 0xd6, 0xa6, 0xb6, 0xcd, 0xfb, 0xf2, 0x38, + 0x13, 0xa3, 0x3c, 0x86, 0x9b, 0xe8, 0xd4, 0x76, 0x2d, 0xc2, 0x2f, 0x33, 0x2e, 0x37, 0x69, 0x72, + 0x98, 0x89, 0x51, 0x1e, 0x03, 0x9f, 0x80, 0x5b, 0x71, 0x07, 0x2b, 0x4f, 0xc0, 0x15, 0x31, 0x01, + 0xbf, 0x14, 0x85, 0xca, 0xad, 0x07, 0x55, 0x00, 0x54, 0x6d, 0x07, 0xe7, 0xa0, 0xeb, 0x52, 0x83, + 0x37, 0xe7, 0xc0, 0x23, 0x72, 0xf8, 0x75, 0xc4, 0x3d, 0xbf, 0x59, 0x79, 0x19, 0xe3, 0x12, 0x38, + 0xee, 0x81, 0x65, 0x29, 0x5a, 0x20, 0x85, 0xf7, 0xc1, 0xc6, 0x14, 0xeb, 0xcf, 0xe8, 0x6c, 0x96, + 0x1f, 0x0d, 0xdd, 0x28, 0x54, 0x36, 0xb4, 0x9c, 0x1c, 0x15, 0x50, 0x70, 0x04, 0x76, 0xf2, 0xdf, + 0x63, 0xe2, 0x3d, 0x76, 0x0c, 0xf2, 0xbc, 0xb7, 0x21, 0xac, 0x7b, 0x51, 0xa8, 0xec, 0x68, 0x15, + 0x7a, 0x54, 0x69, 0x05, 0x3f, 0x00, 0x5d, 0x1b, 0x3f, 0x8f, 0x27, 0x91, 0x90, 0x10, 0xbf, 0xb7, + 0x29, 0x98, 0xc4, 0x29, 0x8e, 0x4b, 0x3a, 0xb4, 0x80, 0x86, 0x3f, 0x05, 0x2d, 0x9f, 0x58, 0x44, + 0x67, 0xd4, 0x93, 0x6f, 0xeb, 0xbd, 0x6b, 0x96, 0x23, 0x9e, 0x12, 0x6b, 0x22, 0x4d, 0xe3, 0x15, + 0x27, 0xf9, 0x42, 0x29, 0x25, 0xfc, 0x16, 0xd8, 0xb2, 0xb1, 0x13, 0xe0, 0x14, 0x29, 0x1e, 0x55, + 0x4b, 0x83, 0x51, 0xa8, 0x6c, 0x1d, 0x17, 0x34, 0xa8, 0x84, 0x84, 0x3f, 0x04, 0x2d, 0x96, 0xec, + 0x0f, 0x6b, 0x22, 0xb4, 0xca, 0x09, 0x39, 0xa6, 0x46, 0x61, 0x7d, 0x48, 0x9f, 0x47, 0xba, 0x3b, + 0xa4, 0x34, 0x7c, 0xe3, 0x62, 0xcc, 0x92, 0xa5, 0xf2, 0x60, 0xc6, 0x88, 0xf7, 0xc8, 0x74, 0x4c, + 0xff, 0x94, 0x18, 0x62, 0x55, 0x6b, 0xc6, 0x1b, 0xd7, 0xc9, 0xc9, 0xa8, 0x0a, 0x82, 0xea, 0x6c, + 0xe1, 0x08, 0x6c, 0x65, 0x35, 0x7d, 0x4c, 0x0d, 0xd2, 0x6b, 0x8b, 0x8e, 0xf0, 0x06, 0x3f, 0xe5, + 0x61, 0x41, 0x73, 0xb9, 0x20, 0x41, 0x25, 0xdb, 0xfc, 0x86, 0x05, 0xae, 0xd8, 0xb0, 0x0c, 0xb0, + 0xe3, 0x52, 0x03, 0x11, 0xd7, 0xc2, 0x3a, 0xb1, 0x89, 0xc3, 0x64, 0xb1, 0x6f, 0x09, 0xd7, 0xef, + 0xf2, 0x4a, 0x1a, 0x57, 0xe8, 0x2f, 0x6b, 0xe4, 0xa8, 0x92, 0x0d, 0x7e, 0x15, 0xb4, 0x6d, 0xec, + 0xe0, 0x39, 0x31, 0xb4, 0xf3, 0xde, 0xb6, 0xa0, 0xde, 0x8c, 0x42, 0xa5, 0x7d, 0x9c, 0x08, 0x51, + 0xa6, 0x1f, 0xfc, 0xbb, 0x09, 0xda, 0xd9, 0x7e, 0xf3, 0x14, 0x00, 0x3d, 0x19, 0x22, 0xbe, 0xdc, + 0x71, 0x5e, 0xaf, 0x6b, 0x48, 0xe9, 0xb8, 0xc9, 0x66, 0x73, 0x2a, 0xf2, 0x51, 0x8e, 0x08, 0xfe, + 0x18, 0xb4, 0xc5, 0xe6, 0x2b, 0xc6, 0xc1, 0xf2, 0x4b, 0x8f, 0x03, 0x11, 0xfd, 0x24, 0x21, 0x40, + 0x19, 0x17, 0x9c, 0xe5, 0xb3, 0xf8, 0x05, 0x47, 0x1b, 0x2c, 0x66, 0x5c, 0xb8, 0x28, 0xb1, 0xf2, + 0x01, 0x23, 0xf7, 0xbe, 0x55, 0x51, 0x73, 0x75, 0x2b, 0xdd, 0x10, 0xb4, 0xc5, 0x8e, 0x4a, 0x0c, + 0x62, 0x88, 0x67, 0xd3, 0xd4, 0x6e, 0x48, 0x68, 0x7b, 0x92, 0x28, 0x50, 0x86, 0xe1, 0xc4, 0xf1, + 0xf2, 0x29, 0x57, 0xe0, 0x94, 0x38, 0x7e, 0xf2, 0x48, 0x6a, 0x79, 0x9b, 0x66, 0xc4, 0xb3, 0x4d, + 0x07, 0xf3, 0x7f, 0x1f, 0x44, 0x77, 0x94, 0x6d, 0xfa, 0x24, 0x13, 0xa3, 0x3c, 0x06, 0x3e, 0x04, + 0x5d, 0x79, 0x8a, 0xac, 0xd1, 0xac, 0x8b, 0x6a, 0xe8, 0x49, 0x27, 0xdd, 0xc3, 0x92, 0x1e, 0x2d, + 0x58, 0xc0, 0xf7, 0xc1, 0xe6, 0xac, 0xd0, 0xab, 0x80, 0xa0, 0xb8, 0xc1, 0x77, 0x81, 0x62, 0xa3, + 0x2a, 0xe2, 0xe0, 0xaf, 0x1b, 0xe0, 0x4e, 0xe0, 0xe8, 0x34, 0x70, 0x18, 0x31, 0x92, 0x20, 0x89, + 0x31, 0xa6, 0x86, 0x2f, 0x1e, 0x6e, 0xe7, 0xe0, 0x9d, 0xca, 0xc2, 0x7a, 0x5a, 0x6d, 0x13, 0x3f, + 0xf3, 0x1a, 0x25, 0xaa, 0xf3, 0x04, 0x15, 0xd0, 0xf4, 0x08, 0x36, 0xce, 0xc5, 0xeb, 0x6e, 0x6a, + 0x6d, 0x3e, 0x3e, 0x11, 0x17, 0xa0, 0x58, 0x3e, 0xf8, 0x7d, 0x03, 0x6c, 0x97, 0xfe, 0x9b, 0xf9, + 0xff, 0x5f, 0x57, 0x07, 0x53, 0xb0, 0x30, 0xee, 0xe0, 0x47, 0xa0, 0xe9, 0x05, 0x16, 0x49, 0x9e, + 0xed, 0xdb, 0xd7, 0x1a, 0x9d, 0x28, 0xb0, 0x48, 0xb6, 0x58, 0xf0, 0x2f, 0x1f, 0xc5, 0x34, 0x83, + 0xbf, 0x35, 0xc0, 0x5b, 0x65, 0xf8, 0x13, 0xe7, 0x7b, 0xcf, 0x4d, 0x76, 0x48, 0x0d, 0xe2, 0x23, + 0xf2, 0x8b, 0xc0, 0xf4, 0x44, 0xdf, 0xe1, 0x45, 0xa2, 0x53, 0x87, 0x61, 0x7e, 0x2d, 0x1f, 0x61, + 0x3b, 0xd9, 0x76, 0x45, 0x91, 0x1c, 0xe6, 0x15, 0xa8, 0x88, 0x83, 0x13, 0xd0, 0xa2, 0x2e, 0xf1, + 0x30, 0x9f, 0x32, 0xf1, 0xa6, 0xfb, 0x7e, 0x32, 0x0a, 0x9e, 0x48, 0xf9, 0x65, 0xa8, 0xdc, 0xbd, + 0x22, 0x8c, 0x04, 0x86, 0x52, 0x22, 0x38, 0x00, 0x6b, 0x67, 0xd8, 0x0a, 0x08, 0x5f, 0x48, 0x56, + 0xf6, 0x9b, 0x1a, 0xe0, 0xef, 0xe9, 0x47, 0x42, 0x82, 0xa4, 0x66, 0xf0, 0xe7, 0xca, 0xc3, 0x8d, + 0xa9, 0x91, 0x75, 0xb0, 0x31, 0x66, 0x8c, 0x78, 0x0e, 0xfc, 0xb0, 0xb0, 0xc1, 0xbf, 0x57, 0xda, + 0xe0, 0xef, 0x56, 0xec, 0xe1, 0x79, 0x9a, 0xff, 0xd5, 0x52, 0x3f, 0xb8, 0x58, 0x06, 0x3b, 0x55, + 0xd9, 0x84, 0x1f, 0xc4, 0xbd, 0x8a, 0x3a, 0x32, 0xe2, 0xfd, 0x7c, 0xaf, 0xa2, 0xce, 0x65, 0xa8, + 0xdc, 0x2e, 0xdb, 0xc5, 0x1a, 0x24, 0xed, 0xa0, 0x03, 0x3a, 0x34, 0xbb, 0x61, 0x59, 0xa4, 0xdf, + 0xbe, 0x56, 0x3d, 0x55, 0x17, 0x48, 0xdc, 0xa9, 0xf2, 0xba, 0xbc, 0x03, 0xf8, 0x4b, 0xb0, 0x4d, + 0x8b, 0x77, 0x2f, 0x32, 0x77, 0x7d, 0x9f, 0x55, 0x79, 0xd3, 0xee, 0xc8, 0x73, 0x6f, 0x97, 0xf4, + 0xa8, 0xec, 0x6c, 0xf0, 0x87, 0x06, 0xa8, 0xeb, 0x2c, 0x70, 0x9c, 0xef, 0xe8, 0xfc, 0x65, 0xb5, + 0xb5, 0x83, 0x42, 0x37, 0xbf, 0x0c, 0x95, 0xd7, 0xeb, 0x7e, 0x63, 0xe4, 0x69, 0xf7, 0xd5, 0xa7, + 0x8f, 0x1f, 0xe6, 0x5b, 0xfe, 0x87, 0x69, 0xcb, 0x5f, 0x16, 0x74, 0xc3, 0xac, 0xdd, 0x5f, 0x8f, + 0x4b, 0x9a, 0x6b, 0xdf, 0xbc, 0x78, 0xd1, 0x5f, 0xfa, 0xec, 0x45, 0x7f, 0xe9, 0xf3, 0x17, 0xfd, + 0xa5, 0x5f, 0x45, 0xfd, 0xc6, 0x45, 0xd4, 0x6f, 0x7c, 0x16, 0xf5, 0x1b, 0x9f, 0x47, 0xfd, 0xc6, + 0xdf, 0xa3, 0x7e, 0xe3, 0x37, 0xff, 0xe8, 0x2f, 0x7d, 0x7c, 0xb3, 0xe2, 0x47, 0xdf, 0xff, 0x04, + 0x00, 0x00, 0xff, 0xff, 0xe0, 0x48, 0x1b, 0x03, 0x0a, 0x16, 0x00, 0x00, } func (m *CronJob) Marshal() (dAtA []byte, err error) { @@ -1029,6 +1030,13 @@ func (m *JobSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.ManagedBy != nil { + i -= len(*m.ManagedBy) + copy(dAtA[i:], *m.ManagedBy) + i = encodeVarintGenerated(dAtA, i, uint64(len(*m.ManagedBy))) + i-- + dAtA[i] = 0x7a + } if m.PodReplacementPolicy != nil { i -= len(*m.PodReplacementPolicy) copy(dAtA[i:], *m.PodReplacementPolicy) @@ -1690,6 +1698,10 @@ func (m *JobSpec) Size() (n int) { l = len(*m.PodReplacementPolicy) n += 1 + l + sovGenerated(uint64(l)) } + if m.ManagedBy != nil { + l = len(*m.ManagedBy) + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -1968,6 +1980,7 @@ func (this *JobSpec) String() string { `BackoffLimitPerIndex:` + valueToStringGenerated(this.BackoffLimitPerIndex) + `,`, `MaxFailedIndexes:` + valueToStringGenerated(this.MaxFailedIndexes) + `,`, `PodReplacementPolicy:` + valueToStringGenerated(this.PodReplacementPolicy) + `,`, + `ManagedBy:` + valueToStringGenerated(this.ManagedBy) + `,`, `}`, }, "") return s @@ -3657,6 +3670,39 @@ func (m *JobSpec) Unmarshal(dAtA []byte) error { s := PodReplacementPolicy(dAtA[iNdEx:postIndex]) m.PodReplacementPolicy = &s iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ManagedBy", 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.ManagedBy = &s + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/batch/v1/generated.proto b/staging/src/k8s.io/api/batch/v1/generated.proto index f050072b7c15a..d5169a86914f3 100644 --- a/staging/src/k8s.io/api/batch/v1/generated.proto +++ b/staging/src/k8s.io/api/batch/v1/generated.proto @@ -330,6 +330,17 @@ message JobSpec { // This is on by default. // +optional optional string podReplacementPolicy = 14; + + // ManagedBy field indicates the controller that manages a Job. The k8s Job + // controller reconciles jobs which don't have this field at all or the field + // value is the reserved string `job-controller.k8s.io`, but skips reconciling + // Jobs with a custom value for this field. + // The value must be a valid DNS subdomain name. + // + // This field is alpha-level. The job controller accepts setting the field + // when the feature gate JobManagedBy is enabled (disabled by default). + // +optional + optional string managedBy = 15; } // JobStatus represents the current state of a Job. @@ -340,6 +351,12 @@ message JobStatus { // status true; when the Job is resumed, the status of this condition will // become false. When a Job is completed, one of the conditions will have // type "Complete" and status true. + // + // A job is considered finished when it is either in the "Complete" or "Failed" + // condition. Job cannot be in the "Complete" condition together we neither + // "Failed" nor "FailureTarget" condition. + // The "Complete", "Failed" and "FailureTarget" conditions cannot be disabled. + // // More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/ // +optional // +patchMergeKey=type @@ -351,31 +368,42 @@ message JobStatus { // Job is created in the suspended state, this field is not set until the // first time it is resumed. This field is reset every time a Job is resumed // from suspension. It is represented in RFC3339 form and is in UTC. + // + // Once set, the field can only be removed when the job is suspended. + // The field cannot be modified while the job is unsuspended or finished. + // // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.Time startTime = 2; // Represents time when the job was completed. It is not guaranteed to // be set in happens-before order across separate operations. // It is represented in RFC3339 form and is in UTC. - // The completion time is only set when the job finishes successfully. + // The completion time is set when the job finishes successfully, and only then. + // The value cannot be updated or removed. The value is equal or later + // than startTime. // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.Time completionTime = 3; // The number of pending and running pods which are not terminating (without // a deletionTimestamp). + // The value is zero for finished jobs. // +optional optional int32 active = 4; // The number of pods which reached phase Succeeded. + // The value increases monotonically, with the exception of elastic indexed + // jobs (with completions = parallelism), which can be scaled down. // +optional optional int32 succeeded = 5; // The number of pods which reached phase Failed. + // The value increases monotonically. // +optional optional int32 failed = 6; // The number of pods which are terminating (in phase Pending or Running // and have a deletionTimestamp). + // The value is zero (or nil) for finished jobs. // // This field is beta-level. The job controller populates the field when // the feature gate JobPodReplacementPolicy is enabled (enabled by default). @@ -419,10 +447,12 @@ message JobStatus { // // Old jobs might not be tracked using this field, in which case the field // remains null. + // The structure is empty for finished jobs. // +optional optional UncountedTerminatedPods uncountedTerminatedPods = 8; // The number of pods which have a Ready condition. + // The value is zero (or nil) for finished jobs. // +optional optional int32 ready = 9; } diff --git a/staging/src/k8s.io/api/batch/v1/types.go b/staging/src/k8s.io/api/batch/v1/types.go index 70eb5a8dee4e1..af303ab115144 100644 --- a/staging/src/k8s.io/api/batch/v1/types.go +++ b/staging/src/k8s.io/api/batch/v1/types.go @@ -47,11 +47,6 @@ const ( // Historically the job controller uses unprefixed labels for job-name and controller-uid and // Kubernetes continutes to recognize those unprefixed labels for consistency. JobNameLabel = labelPrefix + "job-name" - // Label indicating the controller that manages a Job. The k8s Job controller - // reconciles jobs which don't have this label at all or the label value is - // the reserved string `job-controller.k8s.io`, but skips reconciling Jobs - // with a custom value for this label. - JobManagedByLabel = labelPrefix + "managed-by" // ControllerUid is used to programatically get pods corresponding to a Job. // There is a corresponding label without the batch.kubernetes.io that we support for legacy reasons. ControllerUidLabel = labelPrefix + "controller-uid" @@ -62,7 +57,7 @@ const ( // to the pod, which don't count towards the backoff limit, according to the // pod failure policy. When the annotation is absent zero is implied. JobIndexIgnoredFailureCountAnnotation = labelPrefix + "job-index-ignored-failure-count" - // JobControllerName reserved value for the managed-by label for the built-in + // JobControllerName reserved value for the managedBy field for the built-in // Job controller. JobControllerName = "job-controller.k8s.io" ) @@ -418,6 +413,17 @@ type JobSpec struct { // This is on by default. // +optional PodReplacementPolicy *PodReplacementPolicy `json:"podReplacementPolicy,omitempty" protobuf:"bytes,14,opt,name=podReplacementPolicy,casttype=podReplacementPolicy"` + + // ManagedBy field indicates the controller that manages a Job. The k8s Job + // controller reconciles jobs which don't have this field at all or the field + // value is the reserved string `job-controller.k8s.io`, but skips reconciling + // Jobs with a custom value for this field. + // The value must be a valid DNS subdomain name. + // + // This field is alpha-level. The job controller accepts setting the field + // when the feature gate JobManagedBy is enabled (disabled by default). + // +optional + ManagedBy *string `json:"managedBy,omitempty" protobuf:"bytes,15,opt,name=managedBy"` } // JobStatus represents the current state of a Job. @@ -428,6 +434,12 @@ type JobStatus struct { // status true; when the Job is resumed, the status of this condition will // become false. When a Job is completed, one of the conditions will have // type "Complete" and status true. + // + // A job is considered finished when it is either in the "Complete" or "Failed" + // condition. Job cannot be in the "Complete" condition together we neither + // "Failed" nor "FailureTarget" condition. + // The "Complete", "Failed" and "FailureTarget" conditions cannot be disabled. + // // More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/ // +optional // +patchMergeKey=type @@ -439,31 +451,42 @@ type JobStatus struct { // Job is created in the suspended state, this field is not set until the // first time it is resumed. This field is reset every time a Job is resumed // from suspension. It is represented in RFC3339 form and is in UTC. + // + // Once set, the field can only be removed when the job is suspended. + // The field cannot be modified while the job is unsuspended or finished. + // // +optional StartTime *metav1.Time `json:"startTime,omitempty" protobuf:"bytes,2,opt,name=startTime"` // Represents time when the job was completed. It is not guaranteed to // be set in happens-before order across separate operations. // It is represented in RFC3339 form and is in UTC. - // The completion time is only set when the job finishes successfully. + // The completion time is set when the job finishes successfully, and only then. + // The value cannot be updated or removed. The value is equal or later + // than startTime. // +optional CompletionTime *metav1.Time `json:"completionTime,omitempty" protobuf:"bytes,3,opt,name=completionTime"` // The number of pending and running pods which are not terminating (without // a deletionTimestamp). + // The value is zero for finished jobs. // +optional Active int32 `json:"active,omitempty" protobuf:"varint,4,opt,name=active"` // The number of pods which reached phase Succeeded. + // The value increases monotonically, with the exception of elastic indexed + // jobs (with completions = parallelism), which can be scaled down. // +optional Succeeded int32 `json:"succeeded,omitempty" protobuf:"varint,5,opt,name=succeeded"` // The number of pods which reached phase Failed. + // The value increases monotonically. // +optional Failed int32 `json:"failed,omitempty" protobuf:"varint,6,opt,name=failed"` // The number of pods which are terminating (in phase Pending or Running // and have a deletionTimestamp). + // The value is zero (or nil) for finished jobs. // // This field is beta-level. The job controller populates the field when // the feature gate JobPodReplacementPolicy is enabled (enabled by default). @@ -507,10 +530,12 @@ type JobStatus struct { // // Old jobs might not be tracked using this field, in which case the field // remains null. + // The structure is empty for finished jobs. // +optional UncountedTerminatedPods *UncountedTerminatedPods `json:"uncountedTerminatedPods,omitempty" protobuf:"bytes,8,opt,name=uncountedTerminatedPods"` // The number of pods which have a Ready condition. + // The value is zero (or nil) for finished jobs. // +optional Ready *int32 `json:"ready,omitempty" protobuf:"varint,9,opt,name=ready"` } 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 911c436f8d47e..a68c6a861cdb1 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 @@ -126,6 +126,7 @@ var map_JobSpec = map[string]string{ "completionMode": "completionMode specifies how Pod completions are tracked. It can be `NonIndexed` (default) or `Indexed`.\n\n`NonIndexed` means that the Job is considered complete when there have been .spec.completions successfully completed Pods. Each Pod completion is homologous to each other.\n\n`Indexed` means that the Pods of a Job get an associated completion index from 0 to (.spec.completions - 1), available in the annotation batch.kubernetes.io/job-completion-index. The Job is considered complete when there is one successfully completed Pod for each index. When value is `Indexed`, .spec.completions must be specified and `.spec.parallelism` must be less than or equal to 10^5. In addition, The Pod name takes the form `$(job-name)-$(index)-$(random-string)`, the Pod hostname takes the form `$(job-name)-$(index)`.\n\nMore completion modes can be added in the future. If the Job controller observes a mode that it doesn't recognize, which is possible during upgrades due to version skew, the controller skips updates for the Job.", "suspend": "suspend specifies whether the Job controller should create Pods or not. If a Job is created with suspend set to true, no Pods are created by the Job controller. If a Job is suspended after creation (i.e. the flag goes from false to true), the Job controller will delete all active Pods associated with this Job. Users must design their workload to gracefully handle this. Suspending a Job will reset the StartTime field of the Job, effectively resetting the ActiveDeadlineSeconds timer too. Defaults to false.", "podReplacementPolicy": "podReplacementPolicy specifies when to create replacement Pods. Possible values are: - TerminatingOrFailed means that we recreate pods\n when they are terminating (has a metadata.deletionTimestamp) or failed.\n- Failed means to wait until a previously created Pod is fully terminated (has phase\n Failed or Succeeded) before creating a replacement Pod.\n\nWhen using podFailurePolicy, Failed is the the only allowed value. TerminatingOrFailed and Failed are allowed values when podFailurePolicy is not in use. This is an beta field. To use this, enable the JobPodReplacementPolicy feature toggle. This is on by default.", + "managedBy": "ManagedBy field indicates the controller that manages a Job. The k8s Job controller reconciles jobs which don't have this field at all or the field value is the reserved string `job-controller.k8s.io`, but skips reconciling Jobs with a custom value for this field. The value must be a valid DNS subdomain name.\n\nThis field is alpha-level. The job controller accepts setting the field when the feature gate JobManagedBy is enabled (disabled by default).", } func (JobSpec) SwaggerDoc() map[string]string { @@ -134,17 +135,17 @@ func (JobSpec) SwaggerDoc() map[string]string { var map_JobStatus = map[string]string{ "": "JobStatus represents the current state of a Job.", - "conditions": "The latest available observations of an object's current state. When a Job fails, one of the conditions will have type \"Failed\" and status true. When a Job is suspended, one of the conditions will have type \"Suspended\" and status true; when the Job is resumed, the status of this condition will become false. When a Job is completed, one of the conditions will have type \"Complete\" and status true. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", - "startTime": "Represents time when the job controller started processing a job. When a Job is created in the suspended state, this field is not set until the first time it is resumed. This field is reset every time a Job is resumed from suspension. It is represented in RFC3339 form and is in UTC.", - "completionTime": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is only set when the job finishes successfully.", - "active": "The number of pending and running pods which are not terminating (without a deletionTimestamp).", - "succeeded": "The number of pods which reached phase Succeeded.", - "failed": "The number of pods which reached phase Failed.", - "terminating": "The number of pods which are terminating (in phase Pending or Running and have a deletionTimestamp).\n\nThis field is beta-level. The job controller populates the field when the feature gate JobPodReplacementPolicy is enabled (enabled by default).", + "conditions": "The latest available observations of an object's current state. When a Job fails, one of the conditions will have type \"Failed\" and status true. When a Job is suspended, one of the conditions will have type \"Suspended\" and status true; when the Job is resumed, the status of this condition will become false. When a Job is completed, one of the conditions will have type \"Complete\" and status true.\n\nA job is considered finished when it is either in the \"Complete\" or \"Failed\" condition. Job cannot be in the \"Complete\" condition together we neither \"Failed\" nor \"FailureTarget\" condition. The \"Complete\", \"Failed\" and \"FailureTarget\" conditions cannot be disabled.\n\nMore info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/", + "startTime": "Represents time when the job controller started processing a job. When a Job is created in the suspended state, this field is not set until the first time it is resumed. This field is reset every time a Job is resumed from suspension. It is represented in RFC3339 form and is in UTC.\n\nOnce set, the field can only be removed when the job is suspended. The field cannot be modified while the job is unsuspended or finished.", + "completionTime": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC. The completion time is set when the job finishes successfully, and only then. The value cannot be updated or removed. The value is equal or later than startTime.", + "active": "The number of pending and running pods which are not terminating (without a deletionTimestamp). The value is zero for finished jobs.", + "succeeded": "The number of pods which reached phase Succeeded. The value increases monotonically, with the exception of elastic indexed jobs (with completions = parallelism), which can be scaled down.", + "failed": "The number of pods which reached phase Failed. The value increases monotonically.", + "terminating": "The number of pods which are terminating (in phase Pending or Running and have a deletionTimestamp). The value is zero (or nil) for finished jobs.\n\nThis field is beta-level. The job controller populates the field when the feature gate JobPodReplacementPolicy is enabled (enabled by default).", "completedIndexes": "completedIndexes holds the completed indexes when .spec.completionMode = \"Indexed\" in a text format. The indexes are represented as decimal integers separated by commas. The numbers are listed in increasing order. Three or more consecutive numbers are compressed and represented by the first and last element of the series, separated by a hyphen. For example, if the completed indexes are 1, 3, 4, 5 and 7, they are represented as \"1,3-5,7\".", "failedIndexes": "FailedIndexes holds the failed indexes when backoffLimitPerIndex=true. The indexes are represented in the text format analogous as for the `completedIndexes` field, ie. they are kept as decimal integers separated by commas. The numbers are listed in increasing order. Three or more consecutive numbers are compressed and represented by the first and last element of the series, separated by a hyphen. For example, if the failed indexes are 1, 3, 4, 5 and 7, they are represented as \"1,3-5,7\". This field is beta-level. It can be used when the `JobBackoffLimitPerIndex` feature gate is enabled (enabled by default).", - "uncountedTerminatedPods": "uncountedTerminatedPods holds the UIDs of Pods that have terminated but the job controller hasn't yet accounted for in the status counters.\n\nThe job controller creates pods with a finalizer. When a pod terminates (succeeded or failed), the controller does three steps to account for it in the job status:\n\n1. Add the pod UID to the arrays in this field. 2. Remove the pod finalizer. 3. Remove the pod UID from the arrays while increasing the corresponding\n counter.\n\nOld jobs might not be tracked using this field, in which case the field remains null.", - "ready": "The number of pods which have a Ready condition.", + "uncountedTerminatedPods": "uncountedTerminatedPods holds the UIDs of Pods that have terminated but the job controller hasn't yet accounted for in the status counters.\n\nThe job controller creates pods with a finalizer. When a pod terminates (succeeded or failed), the controller does three steps to account for it in the job status:\n\n1. Add the pod UID to the arrays in this field. 2. Remove the pod finalizer. 3. Remove the pod UID from the arrays while increasing the corresponding\n counter.\n\nOld jobs might not be tracked using this field, in which case the field remains null. The structure is empty for finished jobs.", + "ready": "The number of pods which have a Ready condition. The value is zero (or nil) for finished jobs.", } func (JobStatus) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/batch/v1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/batch/v1/zz_generated.deepcopy.go index 43fc41515be1f..bf3682968e160 100644 --- a/staging/src/k8s.io/api/batch/v1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/batch/v1/zz_generated.deepcopy.go @@ -308,6 +308,11 @@ func (in *JobSpec) DeepCopyInto(out *JobSpec) { *out = new(PodReplacementPolicy) **out = **in } + if in.ManagedBy != nil { + in, out := &in.ManagedBy, &out.ManagedBy + *out = new(string) + **out = **in + } return } diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.json b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.json index f50501671e649..3a303983ac8cc 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.json +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.json @@ -1822,7 +1822,8 @@ "ttlSecondsAfterFinished": 8, "completionMode": "completionModeValue", "suspend": true, - "podReplacementPolicy": "podReplacementPolicyValue" + "podReplacementPolicy": "podReplacementPolicyValue", + "managedBy": "managedByValue" } }, "successfulJobsHistoryLimit": 6, diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.pb b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.pb index 4cf754ea21e3bbb0e641367c7f705c6ba91a868a..4e407827b451c1874305bffd9da4376bad8bf3d6 100644 GIT binary patch delta 61 zcmV-D0K)%(SCChb8UpZEv#-uc~uZ@#QAwC8npQI8FA^+y?;YM-3SO delta 23 fcmbOg(iJj6jPcz@@l-~}E1PQ>uc}W@(%b<6Z_Ej3 diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml index 7f5b79c7cf9db..4b95df6f01279 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml @@ -38,6 +38,7 @@ spec: backoffLimitPerIndex: 12 completionMode: completionModeValue completions: 2 + managedBy: managedByValue manualSelector: true maxFailedIndexes: 13 parallelism: 1 diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json index f5a5741578a5c..b6f8275eab16d 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json +++ b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json @@ -1822,7 +1822,8 @@ "ttlSecondsAfterFinished": 8, "completionMode": "completionModeValue", "suspend": true, - "podReplacementPolicy": "podReplacementPolicyValue" + "podReplacementPolicy": "podReplacementPolicyValue", + "managedBy": "managedByValue" } }, "successfulJobsHistoryLimit": 6, diff --git a/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.pb b/staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.pb index 2032e0b40a52e479fe643e8f18d7f44cd8f84c1b..54a9b31ba9865c1c25388555f678facf2b119542 100644 GIT binary patch delta 48 zcmZn-pBg?vp7Fy*g;YkS?%>Joj5