Move internal types of job from pkg/apis/extensions to pkg/apis/batch

pull/6/head
Maciej Szulik 2016-04-18 17:44:19 +02:00
parent c5df0bf23e
commit a3b4447305
40 changed files with 1004 additions and 812 deletions

View File

@ -32,7 +32,7 @@ import (
var (
test = flag.BoolP("test", "t", false, "set this flag to generate the client code for the testdata")
inputVersions = flag.StringSlice("input", []string{"api/", "extensions/"}, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions\"")
inputVersions = flag.StringSlice("input", []string{"api/", "extensions/", "batch/"}, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,batch/\"")
clientsetName = flag.StringP("clientset-name", "n", "internalclientset", "the name of the generated clientset package.")
clientsetPath = flag.String("clientset-path", "k8s.io/kubernetes/pkg/client/clientset_generated/", "the generated clientset will be output to <clientset-path>/<clientset-name>. Default to \"k8s.io/kubernetes/pkg/client/clientset_generated/\"")
clientsetOnly = flag.Bool("clientset-only", false, "when set, client-gen only generates the clientset shell, without generating the individual typed clients")

View File

@ -60,6 +60,7 @@ func DefaultNameSystem() string {
var fallbackPackages = []string{
"k8s.io/kubernetes/pkg/api/unversioned",
"k8s.io/kubernetes/pkg/apis/extensions",
"k8s.io/kubernetes/pkg/apis/batch",
}
func getInternalTypeFor(context *generator.Context, t *types.Type) (*types.Type, bool) {

View File

@ -29,6 +29,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
expvalidation "k8s.io/kubernetes/pkg/apis/extensions/validation"
"k8s.io/kubernetes/pkg/capabilities"
@ -109,7 +110,7 @@ func validateObject(obj runtime.Object) (errors field.ErrorList) {
t.Namespace = api.NamespaceDefault
}
errors = expvalidation.ValidateDeployment(t)
case *extensions.Job:
case *batch.Job:
if t.Namespace == "" {
t.Namespace = api.NamespaceDefault
}
@ -237,7 +238,7 @@ func TestExampleObjectSchemas(t *testing.T) {
"../docs/user-guide": {
"multi-pod": nil,
"pod": &api.Pod{},
"job": &extensions.Job{},
"job": &batch.Job{},
"ingress": &extensions.Ingress{},
"nginx-deployment": &extensions.Deployment{},
"new-nginx-deployment": &extensions.Deployment{},
@ -405,12 +406,12 @@ func TestExampleObjectSchemas(t *testing.T) {
"javaweb-2": &api.Pod{},
},
"../examples/job/work-queue-1": {
"job": &extensions.Job{},
"job": &batch.Job{},
},
"../examples/job/work-queue-2": {
"redis-pod": &api.Pod{},
"redis-service": &api.Service{},
"job": &extensions.Job{},
"job": &batch.Job{},
},
"../examples/azure_file": {
"azure": &api.Pod{},

View File

@ -28,6 +28,7 @@ import (
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
@ -156,7 +157,7 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source)
j.RollingUpdate = &rollingUpdate
}
},
func(j *extensions.JobSpec, c fuzz.Continue) {
func(j *batch.JobSpec, c fuzz.Continue) {
c.FuzzNoCustom(j) // fuzz self without calling this function again
completions := int(c.Rand.Int31())
parallelism := int(c.Rand.Int31())

View File

@ -19,7 +19,6 @@ package batch
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/runtime"
)
@ -47,8 +46,11 @@ func AddToScheme(scheme *runtime.Scheme) {
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) {
scheme.AddKnownTypes(SchemeGroupVersion,
&extensions.Job{},
&extensions.JobList{},
&Job{},
&JobList{},
&api.ListOptions{},
)
}
func (obj *Job) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *JobList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }

143
pkg/apis/batch/types.go Normal file
View File

@ -0,0 +1,143 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package batch
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
)
// +genclient=true
// Job represents the configuration of a single job.
type Job struct {
unversioned.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
api.ObjectMeta `json:"metadata,omitempty"`
// Spec is a structure defining the expected behavior of a job.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
Spec JobSpec `json:"spec,omitempty"`
// Status is a structure describing current status of a job.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
Status JobStatus `json:"status,omitempty"`
}
// JobList is a collection of jobs.
type JobList struct {
unversioned.TypeMeta `json:",inline"`
// Standard list metadata
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
unversioned.ListMeta `json:"metadata,omitempty"`
// Items is the list of Job.
Items []Job `json:"items"`
}
// JobSpec describes how the job execution will look like.
type JobSpec struct {
// Parallelism specifies the maximum desired number of pods the job should
// run at any given time. The actual number of pods running in steady state will
// be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism),
// i.e. when the work left to do is less than max parallelism.
Parallelism *int `json:"parallelism,omitempty"`
// Completions specifies the desired number of successfully finished pods the
// job should be run with. Setting to nil means that the success of any
// pod signals the success of all pods, and allows parallelism to have any positive
// value. Setting to 1 means that parallelism is limited to 1 and the success of that
// pod signals the success of the job.
Completions *int `json:"completions,omitempty"`
// Optional duration in seconds relative to the startTime that the job may be active
// before the system tries to terminate it; value must be positive integer
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`
// Selector is a label query over pods that should match the pod count.
// Normally, the system sets this field for you.
Selector *unversioned.LabelSelector `json:"selector,omitempty"`
// 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.
ManualSelector *bool `json:"manualSelector,omitempty"`
// Template is the object that describes the pod that will be created when
// executing a job.
Template api.PodTemplateSpec `json:"template"`
}
// JobStatus represents the current state of a Job.
type JobStatus struct {
// Conditions represent the latest available observations of an object's current state.
Conditions []JobCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// StartTime represents time when the job was acknowledged by the Job Manager.
// It is not guaranteed to be set in happens-before order across separate operations.
// It is represented in RFC3339 form and is in UTC.
StartTime *unversioned.Time `json:"startTime,omitempty"`
// 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.
CompletionTime *unversioned.Time `json:"completionTime,omitempty"`
// Active is the number of actively running pods.
Active int `json:"active,omitempty"`
// Succeeded is the number of pods which reached Phase Succeeded.
Succeeded int `json:"succeeded,omitempty"`
// Failed is the number of pods which reached Phase Failed.
Failed int `json:"failed,omitempty"`
}
type JobConditionType string
// These are valid conditions of a job.
const (
// JobComplete means the job has completed its execution.
JobComplete JobConditionType = "Complete"
// JobFailed means the job has failed its execution.
JobFailed JobConditionType = "Failed"
)
// JobCondition describes current state of a job.
type JobCondition struct {
// Type of job condition, Complete or Failed.
Type JobConditionType `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status api.ConditionStatus `json:"status"`
// Last time the condition was checked.
LastProbeTime unversioned.Time `json:"lastProbeTime,omitempty"`
// Last time the condition transit from one status to another.
LastTransitionTime unversioned.Time `json:"lastTransitionTime,omitempty"`
// (brief) reason for the condition's last transition.
Reason string `json:"reason,omitempty"`
// Human readable message indicating details about last transition.
Message string `json:"message,omitempty"`
}

View File

@ -18,14 +18,22 @@ package v1
import (
"fmt"
"reflect"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
v1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
)
func addConversionFuncs(scheme *runtime.Scheme) {
// Add non-generated conversion functions
err := scheme.AddConversionFuncs()
err := scheme.AddConversionFuncs(
Convert_batch_JobSpec_To_v1_JobSpec,
Convert_v1_JobSpec_To_batch_JobSpec,
)
if err != nil {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
@ -45,3 +53,91 @@ func addConversionFuncs(scheme *runtime.Scheme) {
panic(err)
}
}
func Convert_batch_JobSpec_To_v1_JobSpec(in *batch.JobSpec, out *JobSpec, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*batch.JobSpec))(in)
}
if in.Parallelism != nil {
out.Parallelism = new(int32)
*out.Parallelism = int32(*in.Parallelism)
} else {
out.Parallelism = nil
}
if in.Completions != nil {
out.Completions = new(int32)
*out.Completions = int32(*in.Completions)
} else {
out.Completions = nil
}
if in.ActiveDeadlineSeconds != nil {
out.ActiveDeadlineSeconds = new(int64)
*out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds
} else {
out.ActiveDeadlineSeconds = nil
}
// unable to generate simple pointer conversion for unversioned.LabelSelector -> v1.LabelSelector
if in.Selector != nil {
out.Selector = new(LabelSelector)
if err := Convert_unversioned_LabelSelector_To_v1_LabelSelector(in.Selector, out.Selector, s); err != nil {
return err
}
} else {
out.Selector = nil
}
if in.ManualSelector != nil {
out.ManualSelector = new(bool)
*out.ManualSelector = *in.ManualSelector
} else {
out.ManualSelector = nil
}
if err := v1.Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
return err
}
return nil
}
func Convert_v1_JobSpec_To_batch_JobSpec(in *JobSpec, out *batch.JobSpec, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*JobSpec))(in)
}
if in.Parallelism != nil {
out.Parallelism = new(int)
*out.Parallelism = int(*in.Parallelism)
} else {
out.Parallelism = nil
}
if in.Completions != nil {
out.Completions = new(int)
*out.Completions = int(*in.Completions)
} else {
out.Completions = nil
}
if in.ActiveDeadlineSeconds != nil {
out.ActiveDeadlineSeconds = new(int64)
*out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds
} else {
out.ActiveDeadlineSeconds = nil
}
// unable to generate simple pointer conversion for v1.LabelSelector -> unversioned.LabelSelector
if in.Selector != nil {
out.Selector = new(unversioned.LabelSelector)
if err := Convert_v1_LabelSelector_To_unversioned_LabelSelector(in.Selector, out.Selector, s); err != nil {
return err
}
} else {
out.Selector = nil
}
if in.ManualSelector != nil {
out.ManualSelector = new(bool)
*out.ManualSelector = *in.ManualSelector
} else {
out.ManualSelector = nil
}
if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,157 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package validation
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
unversionedvalidation "k8s.io/kubernetes/pkg/api/unversioned/validation"
apivalidation "k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// TODO: generalize for other controller objects that will follow the same pattern, such as ReplicaSet and DaemonSet, and
// move to new location. Replace batch.Job with an interface.
//
// ValidateGeneratedSelector validates that the generated selector on a controller object match the controller object
// metadata, and the labels on the pod template are as generated.
func ValidateGeneratedSelector(obj *batch.Job) field.ErrorList {
allErrs := field.ErrorList{}
if obj.Spec.ManualSelector != nil && *obj.Spec.ManualSelector {
return allErrs
}
if obj.Spec.Selector == nil {
return allErrs // This case should already have been checked in caller. No need for more errors.
}
// If somehow uid was unset then we would get "controller-uid=" as the selector
// which is bad.
if obj.ObjectMeta.UID == "" {
allErrs = append(allErrs, field.Required(field.NewPath("metadata").Child("uid"), ""))
}
// If somehow uid was unset then we would get "controller-uid=" as the selector
// which is bad.
if obj.ObjectMeta.UID == "" {
allErrs = append(allErrs, field.Required(field.NewPath("metadata").Child("uid"), ""))
}
// If selector generation was requested, then expected labels must be
// present on pod template, and much match job's uid and name. The
// generated (not-manual) selectors/labels ensure no overlap with other
// controllers. The manual mode allows orphaning, adoption,
// backward-compatibility, and experimentation with new
// labeling/selection schemes. Automatic selector generation should
// have placed certain labels on the pod, but this could have failed if
// the user added coflicting labels. Validate that the expected
// generated ones are there.
allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), "controller-uid", string(obj.UID))...)
allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), "job-name", string(obj.Name))...)
expectedLabels := make(map[string]string)
expectedLabels["controller-uid"] = string(obj.UID)
expectedLabels["job-name"] = string(obj.Name)
// Whether manually or automatically generated, the selector of the job must match the pods it will produce.
if selector, err := unversioned.LabelSelectorAsSelector(obj.Spec.Selector); err == nil {
if !selector.Matches(labels.Set(expectedLabels)) {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("selector"), obj.Spec.Selector, "`selector` not auto-generated"))
}
}
return allErrs
}
func ValidateJob(job *batch.Job) field.ErrorList {
// Jobs and rcs have the same name validation
allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateGeneratedSelector(job)...)
allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"))...)
return allErrs
}
func ValidateJobSpec(spec *batch.JobSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if spec.Parallelism != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.Parallelism), fldPath.Child("parallelism"))...)
}
if spec.Completions != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.Completions), fldPath.Child("completions"))...)
}
if spec.ActiveDeadlineSeconds != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.ActiveDeadlineSeconds), fldPath.Child("activeDeadlineSeconds"))...)
}
if spec.Selector == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
} else {
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
}
// Whether manually or automatically generated, the selector of the job must match the pods it will produce.
if selector, err := unversioned.LabelSelectorAsSelector(spec.Selector); err == nil {
labels := labels.Set(spec.Template.Labels)
if !selector.Matches(labels) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("template", "metadata", "labels"), spec.Template.Labels, "`selector` does not match template `labels`"))
}
}
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"))...)
if spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure &&
spec.Template.Spec.RestartPolicy != api.RestartPolicyNever {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"),
spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyOnFailure), string(api.RestartPolicyNever)}))
}
return allErrs
}
func ValidateJobStatus(status *batch.JobStatus, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Active), fldPath.Child("active"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Succeeded), fldPath.Child("succeeded"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Failed), fldPath.Child("failed"))...)
return allErrs
}
func ValidateJobUpdate(job, oldJob *batch.Job) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&oldJob.ObjectMeta, &job.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateJobSpecUpdate(job.Spec, oldJob.Spec, field.NewPath("spec"))...)
return allErrs
}
func ValidateJobUpdateStatus(job, oldJob *batch.Job) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&oldJob.ObjectMeta, &job.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateJobStatusUpdate(job.Status, oldJob.Status)...)
return allErrs
}
func ValidateJobSpecUpdate(spec, oldSpec batch.JobSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateJobSpec(&spec, fldPath)...)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Completions, oldSpec.Completions, fldPath.Child("completions"))...)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Selector, oldSpec.Selector, fldPath.Child("selector"))...)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Template, oldSpec.Template, fldPath.Child("template"))...)
return allErrs
}
func ValidateJobStatusUpdate(status, oldStatus batch.JobStatus) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateJobStatus(&status, field.NewPath("status"))...)
return allErrs
}

View File

@ -0,0 +1,297 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package validation
import (
"strings"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/types"
)
func TestValidateJob(t *testing.T) {
validManualSelector := &unversioned.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
}
validGeneratedSelector := &unversioned.LabelSelector{
MatchLabels: map[string]string{"controller-uid": "1a2b3c", "job-name": "myjob"},
}
validPodTemplateSpecForManual := api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validManualSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
validPodTemplateSpecForGenerated := api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validGeneratedSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
successCases := map[string]batch.Job{
"manual selector": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.JobSpec{
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: validPodTemplateSpecForManual,
},
},
"generated selector": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.JobSpec{
Selector: validGeneratedSelector,
ManualSelector: newBool(false),
Template: validPodTemplateSpecForGenerated,
},
},
}
for k, v := range successCases {
if errs := ValidateJob(&v); len(errs) != 0 {
t.Errorf("expected success for %s: %v", k, errs)
}
}
negative := -1
negative64 := int64(-1)
errorCases := map[string]batch.Job{
"spec.parallelism:must be greater than or equal to 0": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.JobSpec{
Parallelism: &negative,
ManualSelector: newBool(true),
Template: validPodTemplateSpecForGenerated,
},
},
"spec.completions:must be greater than or equal to 0": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.JobSpec{
Completions: &negative,
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: validPodTemplateSpecForGenerated,
},
},
"spec.activeDeadlineSeconds:must be greater than or equal to 0": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.JobSpec{
ActiveDeadlineSeconds: &negative64,
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: validPodTemplateSpecForGenerated,
},
},
"spec.selector:Required value": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.JobSpec{
Template: validPodTemplateSpecForGenerated,
},
},
"spec.template.metadata.labels: Invalid value: {\"y\":\"z\"}: `selector` does not match template `labels`": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.JobSpec{
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"y": "z"},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
},
},
"spec.template.metadata.labels: Invalid value: {\"controller-uid\":\"4d5e6f\"}: `selector` does not match template `labels`": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.JobSpec{
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"controller-uid": "4d5e6f"},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
},
},
"spec.template.spec.restartPolicy: Unsupported value": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: batch.JobSpec{
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validManualSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
},
},
}
for k, v := range errorCases {
errs := ValidateJob(&v)
if len(errs) == 0 {
t.Errorf("expected failure for %s", k)
} else {
s := strings.Split(k, ":")
err := errs[0]
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
t.Errorf("unexpected error: %v, expected: %s", err, k)
}
}
}
}
func TestValidateJobUpdateStatus(t *testing.T) {
type testcase struct {
old batch.Job
update batch.Job
}
successCases := []testcase{
{
old: batch.Job{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
Status: batch.JobStatus{
Active: 1,
Succeeded: 2,
Failed: 3,
},
},
update: batch.Job{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
Status: batch.JobStatus{
Active: 1,
Succeeded: 1,
Failed: 3,
},
},
},
}
for _, successCase := range successCases {
successCase.old.ObjectMeta.ResourceVersion = "1"
successCase.update.ObjectMeta.ResourceVersion = "1"
if errs := ValidateJobUpdateStatus(&successCase.update, &successCase.old); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
errorCases := map[string]testcase{
"[status.active: Invalid value: -1: must be greater than or equal to 0, status.succeeded: Invalid value: -2: must be greater than or equal to 0]": {
old: batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "abc",
Namespace: api.NamespaceDefault,
ResourceVersion: "10",
},
Status: batch.JobStatus{
Active: 1,
Succeeded: 2,
Failed: 3,
},
},
update: batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "abc",
Namespace: api.NamespaceDefault,
ResourceVersion: "10",
},
Status: batch.JobStatus{
Active: -1,
Succeeded: -2,
Failed: 3,
},
},
},
}
for testName, errorCase := range errorCases {
errs := ValidateJobUpdateStatus(&errorCase.update, &errorCase.old)
if len(errs) == 0 {
t.Errorf("expected failure: %s", testName)
continue
}
if errs.ToAggregate().Error() != testName {
t.Errorf("expected '%s' got '%s'", errs.ToAggregate().Error(), testName)
}
}
}
func newBool(val bool) *bool {
p := new(bool)
*p = val
return p
}

View File

@ -19,6 +19,7 @@ package extensions
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/runtime"
)
@ -52,8 +53,8 @@ func addKnownTypes(scheme *runtime.Scheme) {
&DeploymentRollback{},
&HorizontalPodAutoscaler{},
&HorizontalPodAutoscalerList{},
&Job{},
&JobList{},
&batch.Job{},
&batch.JobList{},
&ReplicationControllerDummy{},
&Scale{},
&ThirdPartyResource{},
@ -78,8 +79,6 @@ func (obj *DeploymentList) GetObjectKind() unversioned.ObjectKind {
func (obj *DeploymentRollback) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *HorizontalPodAutoscaler) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *HorizontalPodAutoscalerList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *Job) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *JobList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *ReplicationControllerDummy) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *Scale) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *ThirdPartyResource) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }

View File

@ -496,127 +496,6 @@ type ThirdPartyResourceDataList struct {
// +genclient=true
// Job represents the configuration of a single job.
type Job struct {
unversioned.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
api.ObjectMeta `json:"metadata,omitempty"`
// Spec is a structure defining the expected behavior of a job.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
Spec JobSpec `json:"spec,omitempty"`
// Status is a structure describing current status of a job.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
Status JobStatus `json:"status,omitempty"`
}
// JobList is a collection of jobs.
type JobList struct {
unversioned.TypeMeta `json:",inline"`
// Standard list metadata
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
unversioned.ListMeta `json:"metadata,omitempty"`
// Items is the list of Job.
Items []Job `json:"items"`
}
// JobSpec describes how the job execution will look like.
type JobSpec struct {
// Parallelism specifies the maximum desired number of pods the job should
// run at any given time. The actual number of pods running in steady state will
// be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism),
// i.e. when the work left to do is less than max parallelism.
Parallelism *int `json:"parallelism,omitempty"`
// Completions specifies the desired number of successfully finished pods the
// job should be run with. Setting to nil means that the success of any
// pod signals the success of all pods, and allows parallelism to have any positive
// value. Setting to 1 means that parallelism is limited to 1 and the success of that
// pod signals the success of the job.
Completions *int `json:"completions,omitempty"`
// Optional duration in seconds relative to the startTime that the job may be active
// before the system tries to terminate it; value must be positive integer
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`
// Selector is a label query over pods that should match the pod count.
// Normally, the system sets this field for you.
Selector *unversioned.LabelSelector `json:"selector,omitempty"`
// 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.
ManualSelector *bool `json:"manualSelector,omitempty"`
// Template is the object that describes the pod that will be created when
// executing a job.
Template api.PodTemplateSpec `json:"template"`
}
// JobStatus represents the current state of a Job.
type JobStatus struct {
// Conditions represent the latest available observations of an object's current state.
Conditions []JobCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
// StartTime represents time when the job was acknowledged by the Job Manager.
// It is not guaranteed to be set in happens-before order across separate operations.
// It is represented in RFC3339 form and is in UTC.
StartTime *unversioned.Time `json:"startTime,omitempty"`
// 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.
CompletionTime *unversioned.Time `json:"completionTime,omitempty"`
// Active is the number of actively running pods.
Active int `json:"active,omitempty"`
// Succeeded is the number of pods which reached Phase Succeeded.
Succeeded int `json:"succeeded,omitempty"`
// Failed is the number of pods which reached Phase Failed.
Failed int `json:"failed,omitempty"`
}
type JobConditionType string
// These are valid conditions of a job.
const (
// JobComplete means the job has completed its execution.
JobComplete JobConditionType = "Complete"
// JobFailed means the job has failed its execution.
JobFailed JobConditionType = "Failed"
)
// JobCondition describes current state of a job.
type JobCondition struct {
// Type of job condition, Complete or Failed.
Type JobConditionType `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status api.ConditionStatus `json:"status"`
// Last time the condition was checked.
LastProbeTime unversioned.Time `json:"lastProbeTime,omitempty"`
// Last time the condition transit from one status to another.
LastTransitionTime unversioned.Time `json:"lastTransitionTime,omitempty"`
// (brief) reason for the condition's last transition.
Reason string `json:"reason,omitempty"`
// Human readable message indicating details about last transition.
Message string `json:"message,omitempty"`
}
// +genclient=true
// Ingress is a collection of rules that allow inbound connections to reach the
// endpoints defined by a backend. An Ingress can be configured to give services
// externally-reachable urls, load balance traffic, terminate SSL, offer name

View File

@ -23,6 +23,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
v1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
@ -42,8 +43,9 @@ func addConversionFuncs(scheme *runtime.Scheme) {
Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment,
Convert_extensions_ReplicaSetSpec_To_v1beta1_ReplicaSetSpec,
Convert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec,
Convert_extensions_JobSpec_To_v1beta1_JobSpec,
Convert_v1beta1_JobSpec_To_extensions_JobSpec,
// batch
Convert_batch_JobSpec_To_v1beta1_JobSpec,
Convert_v1beta1_JobSpec_To_batch_JobSpec,
)
if err != nil {
// If one of the conversion functions is malformed, detect it immediately.
@ -312,9 +314,9 @@ func Convert_v1beta1_ReplicaSetSpec_To_extensions_ReplicaSetSpec(in *ReplicaSetS
return nil
}
func Convert_extensions_JobSpec_To_v1beta1_JobSpec(in *extensions.JobSpec, out *JobSpec, s conversion.Scope) error {
func Convert_batch_JobSpec_To_v1beta1_JobSpec(in *batch.JobSpec, out *JobSpec, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*extensions.JobSpec))(in)
defaulting.(func(*batch.JobSpec))(in)
}
if in.Parallelism != nil {
out.Parallelism = new(int32)
@ -364,7 +366,7 @@ func Convert_extensions_JobSpec_To_v1beta1_JobSpec(in *extensions.JobSpec, out *
return nil
}
func Convert_v1beta1_JobSpec_To_extensions_JobSpec(in *JobSpec, out *extensions.JobSpec, s conversion.Scope) error {
func Convert_v1beta1_JobSpec_To_batch_JobSpec(in *JobSpec, out *batch.JobSpec, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*JobSpec))(in)
}

View File

@ -21,7 +21,7 @@ import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/batch"
versioned "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)
@ -55,7 +55,7 @@ func TestJobSpecConversion(t *testing.T) {
// Test internal -> v1beta1.
for _, test := range tests {
i := &extensions.JobSpec{
i := &batch.JobSpec{
ManualSelector: test.in,
}
v := versioned.JobSpec{}
@ -72,7 +72,7 @@ func TestJobSpecConversion(t *testing.T) {
i := &versioned.JobSpec{
AutoSelector: test.in,
}
e := extensions.JobSpec{}
e := batch.JobSpec{}
if err := api.Scheme.Convert(i, &e); err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@ -382,136 +382,6 @@ func ValidateThirdPartyResourceData(obj *extensions.ThirdPartyResourceData) fiel
return allErrs
}
// TODO: generalize for other controller objects that will follow the same pattern, such as ReplicaSet and DaemonSet, and
// move to new location. Replace extensions.Job with an interface.
//
// ValidateGeneratedSelector validates that the generated selector on a controller object match the controller object
// metadata, and the labels on the pod template are as generated.
func ValidateGeneratedSelector(obj *extensions.Job) field.ErrorList {
allErrs := field.ErrorList{}
if obj.Spec.ManualSelector != nil && *obj.Spec.ManualSelector {
return allErrs
}
if obj.Spec.Selector == nil {
return allErrs // This case should already have been checked in caller. No need for more errors.
}
// If somehow uid was unset then we would get "controller-uid=" as the selector
// which is bad.
if obj.ObjectMeta.UID == "" {
allErrs = append(allErrs, field.Required(field.NewPath("metadata").Child("uid"), ""))
}
// If somehow uid was unset then we would get "controller-uid=" as the selector
// which is bad.
if obj.ObjectMeta.UID == "" {
allErrs = append(allErrs, field.Required(field.NewPath("metadata").Child("uid"), ""))
}
// If selector generation was requested, then expected labels must be
// present on pod template, and much match job's uid and name. The
// generated (not-manual) selectors/labels ensure no overlap with other
// controllers. The manual mode allows orphaning, adoption,
// backward-compatibility, and experimentation with new
// labeling/selection schemes. Automatic selector generation should
// have placed certain labels on the pod, but this could have failed if
// the user added coflicting labels. Validate that the expected
// generated ones are there.
allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), "controller-uid", string(obj.UID))...)
allErrs = append(allErrs, apivalidation.ValidateHasLabel(obj.Spec.Template.ObjectMeta, field.NewPath("spec").Child("template").Child("metadata"), "job-name", string(obj.Name))...)
expectedLabels := make(map[string]string)
expectedLabels["controller-uid"] = string(obj.UID)
expectedLabels["job-name"] = string(obj.Name)
// Whether manually or automatically generated, the selector of the job must match the pods it will produce.
if selector, err := unversioned.LabelSelectorAsSelector(obj.Spec.Selector); err == nil {
if !selector.Matches(labels.Set(expectedLabels)) {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("selector"), obj.Spec.Selector, "`selector` not auto-generated"))
}
}
return allErrs
}
func ValidateJob(job *extensions.Job) field.ErrorList {
// Jobs and rcs have the same name validation
allErrs := apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateGeneratedSelector(job)...)
allErrs = append(allErrs, ValidateJobSpec(&job.Spec, field.NewPath("spec"))...)
return allErrs
}
func ValidateJobSpec(spec *extensions.JobSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if spec.Parallelism != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.Parallelism), fldPath.Child("parallelism"))...)
}
if spec.Completions != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.Completions), fldPath.Child("completions"))...)
}
if spec.ActiveDeadlineSeconds != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.ActiveDeadlineSeconds), fldPath.Child("activeDeadlineSeconds"))...)
}
if spec.Selector == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
} else {
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
}
// Whether manually or automatically generated, the selector of the job must match the pods it will produce.
if selector, err := unversioned.LabelSelectorAsSelector(spec.Selector); err == nil {
labels := labels.Set(spec.Template.Labels)
if !selector.Matches(labels) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("template", "metadata", "labels"), spec.Template.Labels, "`selector` does not match template `labels`"))
}
}
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(&spec.Template, fldPath.Child("template"))...)
if spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure &&
spec.Template.Spec.RestartPolicy != api.RestartPolicyNever {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("template", "spec", "restartPolicy"),
spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyOnFailure), string(api.RestartPolicyNever)}))
}
return allErrs
}
func ValidateJobStatus(status *extensions.JobStatus, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Active), fldPath.Child("active"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Succeeded), fldPath.Child("succeeded"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Failed), fldPath.Child("failed"))...)
return allErrs
}
func ValidateJobUpdate(job, oldJob *extensions.Job) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&oldJob.ObjectMeta, &job.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateJobSpecUpdate(job.Spec, oldJob.Spec, field.NewPath("spec"))...)
return allErrs
}
func ValidateJobUpdateStatus(job, oldJob *extensions.Job) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&oldJob.ObjectMeta, &job.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateJobStatusUpdate(job.Status, oldJob.Status)...)
return allErrs
}
func ValidateJobSpecUpdate(spec, oldSpec extensions.JobSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateJobSpec(&spec, fldPath)...)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Completions, oldSpec.Completions, fldPath.Child("completions"))...)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Selector, oldSpec.Selector, fldPath.Child("selector"))...)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(spec.Template, oldSpec.Template, fldPath.Child("template"))...)
return allErrs
}
func ValidateJobStatusUpdate(status, oldStatus extensions.JobStatus) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateJobStatus(&status, field.NewPath("status"))...)
return allErrs
}
// ValidateIngress tests if required fields in the Ingress are set.
func ValidateIngress(ingress *extensions.Ingress) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata"))

View File

@ -25,7 +25,6 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/controller/podautoscaler"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/intstr"
)
@ -989,270 +988,6 @@ func TestValidateDeploymentRollback(t *testing.T) {
}
}
func TestValidateJob(t *testing.T) {
validManualSelector := &unversioned.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
}
validGeneratedSelector := &unversioned.LabelSelector{
MatchLabels: map[string]string{"controller-uid": "1a2b3c", "job-name": "myjob"},
}
validPodTemplateSpecForManual := api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validManualSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
validPodTemplateSpecForGenerated := api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validGeneratedSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
successCases := map[string]extensions.Job{
"manual selector": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: extensions.JobSpec{
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: validPodTemplateSpecForManual,
},
},
"generated selector": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: extensions.JobSpec{
Selector: validGeneratedSelector,
ManualSelector: newBool(false),
Template: validPodTemplateSpecForGenerated,
},
},
}
for k, v := range successCases {
if errs := ValidateJob(&v); len(errs) != 0 {
t.Errorf("expected success for %s: %v", k, errs)
}
}
negative := -1
negative64 := int64(-1)
errorCases := map[string]extensions.Job{
"spec.parallelism:must be greater than or equal to 0": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: extensions.JobSpec{
Parallelism: &negative,
ManualSelector: newBool(true),
Template: validPodTemplateSpecForGenerated,
},
},
"spec.completions:must be greater than or equal to 0": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: extensions.JobSpec{
Completions: &negative,
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: validPodTemplateSpecForGenerated,
},
},
"spec.activeDeadlineSeconds:must be greater than or equal to 0": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: extensions.JobSpec{
ActiveDeadlineSeconds: &negative64,
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: validPodTemplateSpecForGenerated,
},
},
"spec.selector:Required value": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: extensions.JobSpec{
Template: validPodTemplateSpecForGenerated,
},
},
"spec.template.metadata.labels: Invalid value: {\"y\":\"z\"}: `selector` does not match template `labels`": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: extensions.JobSpec{
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"y": "z"},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
},
},
"spec.template.metadata.labels: Invalid value: {\"controller-uid\":\"4d5e6f\"}: `selector` does not match template `labels`": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: extensions.JobSpec{
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"controller-uid": "4d5e6f"},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
},
},
"spec.template.spec.restartPolicy: Unsupported value": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
UID: types.UID("1a2b3c"),
},
Spec: extensions.JobSpec{
Selector: validManualSelector,
ManualSelector: newBool(true),
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validManualSelector.MatchLabels,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
},
},
}
for k, v := range errorCases {
errs := ValidateJob(&v)
if len(errs) == 0 {
t.Errorf("expected failure for %s", k)
} else {
s := strings.Split(k, ":")
err := errs[0]
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
t.Errorf("unexpected error: %v, expected: %s", err, k)
}
}
}
}
func TestValidateJobUpdateStatus(t *testing.T) {
type testcase struct {
old extensions.Job
update extensions.Job
}
successCases := []testcase{
{
old: extensions.Job{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
Status: extensions.JobStatus{
Active: 1,
Succeeded: 2,
Failed: 3,
},
},
update: extensions.Job{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
Status: extensions.JobStatus{
Active: 1,
Succeeded: 1,
Failed: 3,
},
},
},
}
for _, successCase := range successCases {
successCase.old.ObjectMeta.ResourceVersion = "1"
successCase.update.ObjectMeta.ResourceVersion = "1"
if errs := ValidateJobUpdateStatus(&successCase.update, &successCase.old); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
errorCases := map[string]testcase{
"[status.active: Invalid value: -1: must be greater than or equal to 0, status.succeeded: Invalid value: -2: must be greater than or equal to 0]": {
old: extensions.Job{
ObjectMeta: api.ObjectMeta{
Name: "abc",
Namespace: api.NamespaceDefault,
ResourceVersion: "10",
},
Status: extensions.JobStatus{
Active: 1,
Succeeded: 2,
Failed: 3,
},
},
update: extensions.Job{
ObjectMeta: api.ObjectMeta{
Name: "abc",
Namespace: api.NamespaceDefault,
ResourceVersion: "10",
},
Status: extensions.JobStatus{
Active: -1,
Succeeded: -2,
Failed: 3,
},
},
},
}
for testName, errorCase := range errorCases {
errs := ValidateJobUpdateStatus(&errorCase.update, &errorCase.old)
if len(errs) == 0 {
t.Errorf("expected failure: %s", testName)
continue
}
if errs.ToAggregate().Error() != testName {
t.Errorf("expected '%s' got '%s'", errs.ToAggregate().Error(), testName)
}
}
}
type ingressRules map[string]string
func TestValidateIngress(t *testing.T) {

View File

@ -22,6 +22,7 @@ import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/labels"
)
@ -474,7 +475,7 @@ type StoreToJobLister struct {
}
// Exists checks if the given job exists in the store.
func (s *StoreToJobLister) Exists(job *extensions.Job) (bool, error) {
func (s *StoreToJobLister) Exists(job *batch.Job) (bool, error) {
_, exists, err := s.Store.Get(job)
if err != nil {
return false, err
@ -483,17 +484,17 @@ func (s *StoreToJobLister) Exists(job *extensions.Job) (bool, error) {
}
// StoreToJobLister lists all jobs in the store.
func (s *StoreToJobLister) List() (jobs extensions.JobList, err error) {
func (s *StoreToJobLister) List() (jobs batch.JobList, err error) {
for _, c := range s.Store.List() {
jobs.Items = append(jobs.Items, *(c.(*extensions.Job)))
jobs.Items = append(jobs.Items, *(c.(*batch.Job)))
}
return jobs, nil
}
// GetPodJobs returns a list of jobs managing a pod. Returns an error only if no matching jobs are found.
func (s *StoreToJobLister) GetPodJobs(pod *api.Pod) (jobs []extensions.Job, err error) {
func (s *StoreToJobLister) GetPodJobs(pod *api.Pod) (jobs []batch.Job, err error) {
var selector labels.Selector
var job extensions.Job
var job batch.Job
if len(pod.Labels) == 0 {
err = fmt.Errorf("no jobs found for pod %v because it has no labels", pod.Name)
@ -501,7 +502,7 @@ func (s *StoreToJobLister) GetPodJobs(pod *api.Pod) (jobs []extensions.Job, err
}
for _, m := range s.Store.List() {
job = *m.(*extensions.Job)
job = *m.(*batch.Job)
if job.Namespace != pod.Namespace {
continue
}

View File

@ -21,6 +21,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/util/sets"
@ -476,18 +477,18 @@ func TestStoreToJobLister(t *testing.T) {
store := NewStore(MetaNamespaceKeyFunc)
lister := StoreToJobLister{store}
testCases := []struct {
inJobs []*extensions.Job
list func() ([]extensions.Job, error)
inJobs []*batch.Job
list func() ([]batch.Job, error)
outJobNames sets.String
expectErr bool
msg string
}{
// Basic listing
{
inJobs: []*extensions.Job{
inJobs: []*batch.Job{
{ObjectMeta: api.ObjectMeta{Name: "basic"}},
},
list: func() ([]extensions.Job, error) {
list: func() ([]batch.Job, error) {
list, err := lister.List()
return list.Items, err
},
@ -496,12 +497,12 @@ func TestStoreToJobLister(t *testing.T) {
},
// Listing multiple jobs
{
inJobs: []*extensions.Job{
inJobs: []*batch.Job{
{ObjectMeta: api.ObjectMeta{Name: "basic"}},
{ObjectMeta: api.ObjectMeta{Name: "complex"}},
{ObjectMeta: api.ObjectMeta{Name: "complex2"}},
},
list: func() ([]extensions.Job, error) {
list: func() ([]batch.Job, error) {
list, err := lister.List()
return list.Items, err
},
@ -510,17 +511,17 @@ func TestStoreToJobLister(t *testing.T) {
},
// No pod labels
{
inJobs: []*extensions.Job{
inJobs: []*batch.Job{
{
ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"foo": "baz"},
},
},
},
},
list: func() ([]extensions.Job, error) {
list: func() ([]batch.Job, error) {
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{Name: "pod", Namespace: "ns"},
}
@ -532,12 +533,12 @@ func TestStoreToJobLister(t *testing.T) {
},
// No Job selectors
{
inJobs: []*extensions.Job{
inJobs: []*batch.Job{
{
ObjectMeta: api.ObjectMeta{Name: "basic", Namespace: "ns"},
},
},
list: func() ([]extensions.Job, error) {
list: func() ([]batch.Job, error) {
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod",
@ -553,10 +554,10 @@ func TestStoreToJobLister(t *testing.T) {
},
// Matching labels to selectors and namespace
{
inJobs: []*extensions.Job{
inJobs: []*batch.Job{
{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
@ -564,14 +565,14 @@ func TestStoreToJobLister(t *testing.T) {
},
{
ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "ns"},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
},
},
},
list: func() ([]extensions.Job, error) {
list: func() ([]batch.Job, error) {
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod",
@ -586,10 +587,10 @@ func TestStoreToJobLister(t *testing.T) {
},
// Matching labels to selectors and namespace, error case
{
inJobs: []*extensions.Job{
inJobs: []*batch.Job{
{
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "foo"},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
@ -597,14 +598,14 @@ func TestStoreToJobLister(t *testing.T) {
},
{
ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "bar"},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
},
},
},
list: func() ([]extensions.Job, error) {
list: func() ([]batch.Job, error) {
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod",

View File

@ -23,6 +23,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/util/wait"
"k8s.io/kubernetes/pkg/watch"
@ -129,7 +130,7 @@ func ReplicaSetHasDesiredReplicas(c ExtensionsInterface, replicaSet *extensions.
// JobHasDesiredParallelism returns a condition that will be true if the desired parallelism count
// for a job equals the current active counts or is less by an appropriate successful/unsuccessful count.
func JobHasDesiredParallelism(c ExtensionsInterface, job *extensions.Job) wait.ConditionFunc {
func JobHasDesiredParallelism(c BatchInterface, job *batch.Job) wait.ConditionFunc {
return func() (bool, error) {
job, err := c.Jobs(job.Namespace).Get(job.Name)

View File

@ -18,7 +18,7 @@ package unversioned
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/watch"
)
@ -29,13 +29,13 @@ type JobsNamespacer interface {
// JobInterface exposes methods to work on Job resources.
type JobInterface interface {
List(opts api.ListOptions) (*extensions.JobList, error)
Get(name string) (*extensions.Job, error)
Create(job *extensions.Job) (*extensions.Job, error)
Update(job *extensions.Job) (*extensions.Job, error)
List(opts api.ListOptions) (*batch.JobList, error)
Get(name string) (*batch.Job, error)
Create(job *batch.Job) (*batch.Job, error)
Update(job *batch.Job) (*batch.Job, error)
Delete(name string, options *api.DeleteOptions) error
Watch(opts api.ListOptions) (watch.Interface, error)
UpdateStatus(job *extensions.Job) (*extensions.Job, error)
UpdateStatus(job *batch.Job) (*batch.Job, error)
}
// jobs implements JobsNamespacer interface
@ -53,29 +53,29 @@ func newJobs(c *ExtensionsClient, namespace string) *jobs {
var _ JobInterface = &jobs{}
// List returns a list of jobs that match the label and field selectors.
func (c *jobs) List(opts api.ListOptions) (result *extensions.JobList, err error) {
result = &extensions.JobList{}
func (c *jobs) List(opts api.ListOptions) (result *batch.JobList, err error) {
result = &batch.JobList{}
err = c.r.Get().Namespace(c.ns).Resource("jobs").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular job.
func (c *jobs) Get(name string) (result *extensions.Job, err error) {
result = &extensions.Job{}
func (c *jobs) Get(name string) (result *batch.Job, err error) {
result = &batch.Job{}
err = c.r.Get().Namespace(c.ns).Resource("jobs").Name(name).Do().Into(result)
return
}
// Create creates a new job.
func (c *jobs) Create(job *extensions.Job) (result *extensions.Job, err error) {
result = &extensions.Job{}
func (c *jobs) Create(job *batch.Job) (result *batch.Job, err error) {
result = &batch.Job{}
err = c.r.Post().Namespace(c.ns).Resource("jobs").Body(job).Do().Into(result)
return
}
// Update updates an existing job.
func (c *jobs) Update(job *extensions.Job) (result *extensions.Job, err error) {
result = &extensions.Job{}
func (c *jobs) Update(job *batch.Job) (result *batch.Job, err error) {
result = &batch.Job{}
err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).Body(job).Do().Into(result)
return
}
@ -96,8 +96,8 @@ func (c *jobs) Watch(opts api.ListOptions) (watch.Interface, error) {
}
// UpdateStatus takes the name of the job and the new status. Returns the server's representation of the job, and an error, if it occurs.
func (c *jobs) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) {
result = &extensions.Job{}
func (c *jobs) UpdateStatus(job *batch.Job) (result *batch.Job, err error) {
result = &batch.Job{}
err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).SubResource("status").Body(job).Do().Into(result)
return
}
@ -117,29 +117,29 @@ func newJobsV1(c *BatchClient, namespace string) *jobsV1 {
var _ JobInterface = &jobsV1{}
// List returns a list of jobs that match the label and field selectors.
func (c *jobsV1) List(opts api.ListOptions) (result *extensions.JobList, err error) {
result = &extensions.JobList{}
func (c *jobsV1) List(opts api.ListOptions) (result *batch.JobList, err error) {
result = &batch.JobList{}
err = c.r.Get().Namespace(c.ns).Resource("jobs").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular job.
func (c *jobsV1) Get(name string) (result *extensions.Job, err error) {
result = &extensions.Job{}
func (c *jobsV1) Get(name string) (result *batch.Job, err error) {
result = &batch.Job{}
err = c.r.Get().Namespace(c.ns).Resource("jobs").Name(name).Do().Into(result)
return
}
// Create creates a new job.
func (c *jobsV1) Create(job *extensions.Job) (result *extensions.Job, err error) {
result = &extensions.Job{}
func (c *jobsV1) Create(job *batch.Job) (result *batch.Job, err error) {
result = &batch.Job{}
err = c.r.Post().Namespace(c.ns).Resource("jobs").Body(job).Do().Into(result)
return
}
// Update updates an existing job.
func (c *jobsV1) Update(job *extensions.Job) (result *extensions.Job, err error) {
result = &extensions.Job{}
func (c *jobsV1) Update(job *batch.Job) (result *batch.Job, err error) {
result = &batch.Job{}
err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).Body(job).Do().Into(result)
return
}
@ -160,8 +160,8 @@ func (c *jobsV1) Watch(opts api.ListOptions) (watch.Interface, error) {
}
// UpdateStatus takes the name of the job and the new status. Returns the server's representation of the job, and an error, if it occurs.
func (c *jobsV1) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) {
result = &extensions.Job{}
func (c *jobsV1) UpdateStatus(job *batch.Job) (result *batch.Job, err error) {
result = &batch.Job{}
err = c.r.Put().Namespace(c.ns).Resource("jobs").Name(job.Name).SubResource("status").Body(job).Do().Into(result)
return
}

View File

@ -51,8 +51,8 @@ func testListJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
Path: group.ResourcePath(getJobsResourceName(), ns, ""),
},
Response: simple.Response{StatusCode: 200,
Body: &extensions.JobList{
Items: []extensions.Job{
Body: &batch.JobList{
Items: []batch.Job{
{
ObjectMeta: api.ObjectMeta{
Name: "foo",
@ -61,7 +61,7 @@ func testListJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
"name": "baz",
},
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{},
},
},
@ -90,7 +90,7 @@ func testGetJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
},
Response: simple.Response{
StatusCode: 200,
Body: &extensions.Job{
Body: &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
@ -98,7 +98,7 @@ func testGetJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
"name": "baz",
},
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{},
},
},
@ -117,7 +117,7 @@ func TestGetJob(t *testing.T) {
func testUpdateJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
requestJob := &extensions.Job{
requestJob := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: ns,
@ -132,7 +132,7 @@ func testUpdateJob(t *testing.T, group testapi.TestGroup, resourceGroup string)
},
Response: simple.Response{
StatusCode: 200,
Body: &extensions.Job{
Body: &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
@ -140,7 +140,7 @@ func testUpdateJob(t *testing.T, group testapi.TestGroup, resourceGroup string)
"name": "baz",
},
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{},
},
},
@ -159,7 +159,7 @@ func TestUpdateJob(t *testing.T) {
func testUpdateJobStatus(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
requestJob := &extensions.Job{
requestJob := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: ns,
@ -174,7 +174,7 @@ func testUpdateJobStatus(t *testing.T, group testapi.TestGroup, resourceGroup st
},
Response: simple.Response{
StatusCode: 200,
Body: &extensions.Job{
Body: &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
@ -182,10 +182,10 @@ func testUpdateJobStatus(t *testing.T, group testapi.TestGroup, resourceGroup st
"name": "baz",
},
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{},
},
Status: extensions.JobStatus{
Status: batch.JobStatus{
Active: 1,
},
},
@ -225,7 +225,7 @@ func TestDeleteJob(t *testing.T) {
func testCreateJob(t *testing.T, group testapi.TestGroup, resourceGroup string) {
ns := api.NamespaceDefault
requestJob := &extensions.Job{
requestJob := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: ns,
@ -240,7 +240,7 @@ func testCreateJob(t *testing.T, group testapi.TestGroup, resourceGroup string)
},
Response: simple.Response{
StatusCode: 200,
Body: &extensions.Job{
Body: &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
@ -248,7 +248,7 @@ func testCreateJob(t *testing.T, group testapi.TestGroup, resourceGroup string)
"name": "baz",
},
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Template: api.PodTemplateSpec{},
},
},

View File

@ -18,7 +18,7 @@ package testclient
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/watch"
)
@ -29,44 +29,44 @@ type FakeJobs struct {
Namespace string
}
func (c *FakeJobs) Get(name string) (*extensions.Job, error) {
obj, err := c.Fake.Invokes(NewGetAction("jobs", c.Namespace, name), &extensions.Job{})
func (c *FakeJobs) Get(name string) (*batch.Job, error) {
obj, err := c.Fake.Invokes(NewGetAction("jobs", c.Namespace, name), &batch.Job{})
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
return obj.(*batch.Job), err
}
func (c *FakeJobs) List(opts api.ListOptions) (*extensions.JobList, error) {
obj, err := c.Fake.Invokes(NewListAction("jobs", c.Namespace, opts), &extensions.JobList{})
func (c *FakeJobs) List(opts api.ListOptions) (*batch.JobList, error) {
obj, err := c.Fake.Invokes(NewListAction("jobs", c.Namespace, opts), &batch.JobList{})
if obj == nil {
return nil, err
}
return obj.(*extensions.JobList), err
return obj.(*batch.JobList), err
}
func (c *FakeJobs) Create(job *extensions.Job) (*extensions.Job, error) {
func (c *FakeJobs) Create(job *batch.Job) (*batch.Job, error) {
obj, err := c.Fake.Invokes(NewCreateAction("jobs", c.Namespace, job), job)
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
return obj.(*batch.Job), err
}
func (c *FakeJobs) Update(job *extensions.Job) (*extensions.Job, error) {
func (c *FakeJobs) Update(job *batch.Job) (*batch.Job, error) {
obj, err := c.Fake.Invokes(NewUpdateAction("jobs", c.Namespace, job), job)
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
return obj.(*batch.Job), err
}
func (c *FakeJobs) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.Invokes(NewDeleteAction("jobs", c.Namespace, name), &extensions.Job{})
_, err := c.Fake.Invokes(NewDeleteAction("jobs", c.Namespace, name), &batch.Job{})
return err
}
@ -74,13 +74,13 @@ func (c *FakeJobs) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.InvokesWatch(NewWatchAction("jobs", c.Namespace, opts))
}
func (c *FakeJobs) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) {
func (c *FakeJobs) UpdateStatus(job *batch.Job) (result *batch.Job, err error) {
obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("jobs", "status", c.Namespace, job), job)
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
return obj.(*batch.Job), err
}
// FakeJobs implements JobInterface. Meant to be embedded into a struct to get a default
@ -92,44 +92,44 @@ type FakeJobsV1 struct {
Namespace string
}
func (c *FakeJobsV1) Get(name string) (*extensions.Job, error) {
obj, err := c.Fake.Invokes(NewGetAction("jobs", c.Namespace, name), &extensions.Job{})
func (c *FakeJobsV1) Get(name string) (*batch.Job, error) {
obj, err := c.Fake.Invokes(NewGetAction("jobs", c.Namespace, name), &batch.Job{})
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
return obj.(*batch.Job), err
}
func (c *FakeJobsV1) List(opts api.ListOptions) (*extensions.JobList, error) {
obj, err := c.Fake.Invokes(NewListAction("jobs", c.Namespace, opts), &extensions.JobList{})
func (c *FakeJobsV1) List(opts api.ListOptions) (*batch.JobList, error) {
obj, err := c.Fake.Invokes(NewListAction("jobs", c.Namespace, opts), &batch.JobList{})
if obj == nil {
return nil, err
}
return obj.(*extensions.JobList), err
return obj.(*batch.JobList), err
}
func (c *FakeJobsV1) Create(job *extensions.Job) (*extensions.Job, error) {
func (c *FakeJobsV1) Create(job *batch.Job) (*batch.Job, error) {
obj, err := c.Fake.Invokes(NewCreateAction("jobs", c.Namespace, job), job)
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
return obj.(*batch.Job), err
}
func (c *FakeJobsV1) Update(job *extensions.Job) (*extensions.Job, error) {
func (c *FakeJobsV1) Update(job *batch.Job) (*batch.Job, error) {
obj, err := c.Fake.Invokes(NewUpdateAction("jobs", c.Namespace, job), job)
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
return obj.(*batch.Job), err
}
func (c *FakeJobsV1) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.Invokes(NewDeleteAction("jobs", c.Namespace, name), &extensions.Job{})
_, err := c.Fake.Invokes(NewDeleteAction("jobs", c.Namespace, name), &batch.Job{})
return err
}
@ -137,11 +137,11 @@ func (c *FakeJobsV1) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.InvokesWatch(NewWatchAction("jobs", c.Namespace, opts))
}
func (c *FakeJobsV1) UpdateStatus(job *extensions.Job) (result *extensions.Job, err error) {
func (c *FakeJobsV1) UpdateStatus(job *batch.Job) (result *batch.Job, err error) {
obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("jobs", "status", c.Namespace, job), job)
if obj == nil {
return nil, err
}
return obj.(*extensions.Job), err
return obj.(*batch.Job), err
}

View File

@ -25,7 +25,7 @@ import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/client/cache"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
unversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
@ -53,7 +53,7 @@ type JobController struct {
internalPodInformer framework.SharedInformer
// To allow injection of updateJobStatus for testing.
updateHandler func(job *extensions.Job) error
updateHandler func(job *batch.Job) error
syncHandler func(jobKey string) error
// podStoreSynced returns true if the pod store has been synced at least once.
// Added as a member to the struct to allow injection for testing.
@ -96,19 +96,19 @@ func NewJobController(podInformer framework.SharedInformer, kubeClient clientset
jm.jobStore.Store, jm.jobController = framework.NewInformer(
&cache.ListWatch{
ListFunc: func(options api.ListOptions) (runtime.Object, error) {
return jm.kubeClient.Extensions().Jobs(api.NamespaceAll).List(options)
return jm.kubeClient.Batch().Jobs(api.NamespaceAll).List(options)
},
WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
return jm.kubeClient.Extensions().Jobs(api.NamespaceAll).Watch(options)
return jm.kubeClient.Batch().Jobs(api.NamespaceAll).Watch(options)
},
},
&extensions.Job{},
&batch.Job{},
// TODO: Can we have much longer period here?
replicationcontroller.FullControllerResyncPeriod,
framework.ResourceEventHandlerFuncs{
AddFunc: jm.enqueueController,
UpdateFunc: func(old, cur interface{}) {
if job := cur.(*extensions.Job); !isJobFinished(job) {
if job := cur.(*batch.Job); !isJobFinished(job) {
jm.enqueueController(job)
}
},
@ -155,7 +155,7 @@ func (jm *JobController) Run(workers int, stopCh <-chan struct{}) {
}
// getPodJob returns the job managing the given pod.
func (jm *JobController) getPodJob(pod *api.Pod) *extensions.Job {
func (jm *JobController) getPodJob(pod *api.Pod) *batch.Job {
jobs, err := jm.jobStore.GetPodJobs(pod)
if err != nil {
glog.V(4).Infof("No jobs found for pod %v, job controller will avoid syncing", pod.Name)
@ -251,7 +251,7 @@ func (jm *JobController) deletePod(obj interface{}) {
}
}
// obj could be an *extensions.Job, or a DeletionFinalStateUnknown marker item.
// obj could be an *batch.Job, or a DeletionFinalStateUnknown marker item.
func (jm *JobController) enqueueController(obj interface{}) {
key, err := controller.KeyFunc(obj)
if err != nil {
@ -314,7 +314,7 @@ func (jm *JobController) syncJob(key string) error {
jm.queue.Add(key)
return err
}
job := *obj.(*extensions.Job)
job := *obj.(*batch.Job)
// Check the expectations of the job before counting active pods, otherwise a new pod can sneak in
// and update the expectations after we've retrieved active pods from the store. If a new pod enters
@ -366,7 +366,7 @@ func (jm *JobController) syncJob(key string) error {
// update status values accordingly
failed += active
active = 0
job.Status.Conditions = append(job.Status.Conditions, newCondition(extensions.JobFailed, "DeadlineExceeded", "Job was active longer than specified deadline"))
job.Status.Conditions = append(job.Status.Conditions, newCondition(batch.JobFailed, "DeadlineExceeded", "Job was active longer than specified deadline"))
jm.recorder.Event(&job, api.EventTypeNormal, "DeadlineExceeded", "Job was active longer than specified deadline")
} else {
if jobNeedsSync {
@ -400,7 +400,7 @@ func (jm *JobController) syncJob(key string) error {
}
}
if complete {
job.Status.Conditions = append(job.Status.Conditions, newCondition(extensions.JobComplete, "", ""))
job.Status.Conditions = append(job.Status.Conditions, newCondition(batch.JobComplete, "", ""))
now := unversioned.Now()
job.Status.CompletionTime = &now
}
@ -421,7 +421,7 @@ func (jm *JobController) syncJob(key string) error {
}
// pastActiveDeadline checks if job has ActiveDeadlineSeconds field set and if it is exceeded.
func pastActiveDeadline(job *extensions.Job) bool {
func pastActiveDeadline(job *batch.Job) bool {
if job.Spec.ActiveDeadlineSeconds == nil || job.Status.StartTime == nil {
return false
}
@ -432,8 +432,8 @@ func pastActiveDeadline(job *extensions.Job) bool {
return duration >= allowedDuration
}
func newCondition(conditionType extensions.JobConditionType, reason, message string) extensions.JobCondition {
return extensions.JobCondition{
func newCondition(conditionType batch.JobConditionType, reason, message string) batch.JobCondition {
return batch.JobCondition{
Type: conditionType,
Status: api.ConditionTrue,
LastProbeTime: unversioned.Now(),
@ -452,7 +452,7 @@ func getStatus(pods []api.Pod) (succeeded, failed int) {
// manageJob is the core method responsible for managing the number of running
// pods according to what is specified in the job.Spec.
func (jm *JobController) manageJob(activePods []*api.Pod, succeeded int, job *extensions.Job) int {
func (jm *JobController) manageJob(activePods []*api.Pod, succeeded int, job *batch.Job) int {
var activeLock sync.Mutex
active := len(activePods)
parallelism := *job.Spec.Parallelism
@ -538,8 +538,8 @@ func (jm *JobController) manageJob(activePods []*api.Pod, succeeded int, job *ex
return active
}
func (jm *JobController) updateJobStatus(job *extensions.Job) error {
_, err := jm.kubeClient.Extensions().Jobs(job.Namespace).UpdateStatus(job)
func (jm *JobController) updateJobStatus(job *batch.Job) error {
_, err := jm.kubeClient.Batch().Jobs(job.Namespace).UpdateStatus(job)
return err
}
@ -554,9 +554,9 @@ func filterPods(pods []api.Pod, phase api.PodPhase) int {
return result
}
func isJobFinished(j *extensions.Job) bool {
func isJobFinished(j *batch.Job) bool {
for _, c := range j.Status.Conditions {
if (c.Type == extensions.JobComplete || c.Type == extensions.JobFailed) && c.Status == api.ConditionTrue {
if (c.Type == batch.JobComplete || c.Type == batch.JobFailed) && c.Status == api.ConditionTrue {
return true
}
}
@ -564,7 +564,7 @@ func isJobFinished(j *extensions.Job) bool {
}
// byCreationTimestamp sorts a list by creation timestamp, using their names as a tie breaker.
type byCreationTimestamp []extensions.Job
type byCreationTimestamp []batch.Job
func (o byCreationTimestamp) Len() int { return len(o) }
func (o byCreationTimestamp) Swap(i, j int) { o[i], o[j] = o[j], o[i] }

View File

@ -24,7 +24,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/batch"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/restclient"
@ -37,13 +37,13 @@ import (
var alwaysReady = func() bool { return true }
func newJob(parallelism, completions int) *extensions.Job {
j := &extensions.Job{
func newJob(parallelism, completions int) *batch.Job {
j := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foobar",
Namespace: api.NamespaceDefault,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
@ -76,7 +76,7 @@ func newJob(parallelism, completions int) *extensions.Job {
return j
}
func getKey(job *extensions.Job, t *testing.T) string {
func getKey(job *batch.Job, t *testing.T) string {
if key, err := controller.KeyFunc(job); err != nil {
t.Errorf("Unexpected error getting key for job %v: %v", job.Name, err)
return ""
@ -86,7 +86,7 @@ func getKey(job *extensions.Job, t *testing.T) string {
}
// create count pods with the given phase for the given job
func newPodList(count int, status api.PodPhase, job *extensions.Job) []api.Pod {
func newPodList(count int, status api.PodPhase, job *batch.Job) []api.Pod {
pods := []api.Pod{}
for i := 0; i < count; i++ {
newPod := api.Pod{
@ -211,8 +211,8 @@ func TestControllerSyncJob(t *testing.T) {
fakePodControl := controller.FakePodControl{Err: tc.podControllerError}
manager.podControl = &fakePodControl
manager.podStoreSynced = alwaysReady
var actual *extensions.Job
manager.updateHandler = func(job *extensions.Job) error {
var actual *batch.Job
manager.updateHandler = func(job *batch.Job) error {
actual = job
return nil
}
@ -257,7 +257,7 @@ func TestControllerSyncJob(t *testing.T) {
t.Errorf("%s: .status.startTime was not set", name)
}
// validate conditions
if tc.expectedComplete && !getCondition(actual, extensions.JobComplete) {
if tc.expectedComplete && !getCondition(actual, batch.JobComplete) {
t.Errorf("%s: expected completion condition. Got %#v", name, actual.Status.Conditions)
}
}
@ -306,8 +306,8 @@ func TestSyncJobPastDeadline(t *testing.T) {
fakePodControl := controller.FakePodControl{}
manager.podControl = &fakePodControl
manager.podStoreSynced = alwaysReady
var actual *extensions.Job
manager.updateHandler = func(job *extensions.Job) error {
var actual *batch.Job
manager.updateHandler = func(job *batch.Job) error {
actual = job
return nil
}
@ -355,13 +355,13 @@ func TestSyncJobPastDeadline(t *testing.T) {
t.Errorf("%s: .status.startTime was not set", name)
}
// validate conditions
if !getCondition(actual, extensions.JobFailed) {
if !getCondition(actual, batch.JobFailed) {
t.Errorf("%s: expected fail condition. Got %#v", name, actual.Status.Conditions)
}
}
}
func getCondition(job *extensions.Job, condition extensions.JobConditionType) bool {
func getCondition(job *batch.Job, condition batch.JobConditionType) bool {
for _, v := range job.Status.Conditions {
if v.Type == condition && v.Status == api.ConditionTrue {
return true
@ -376,8 +376,8 @@ func TestSyncPastDeadlineJobFinished(t *testing.T) {
fakePodControl := controller.FakePodControl{}
manager.podControl = &fakePodControl
manager.podStoreSynced = alwaysReady
var actual *extensions.Job
manager.updateHandler = func(job *extensions.Job) error {
var actual *batch.Job
manager.updateHandler = func(job *batch.Job) error {
actual = job
return nil
}
@ -387,7 +387,7 @@ func TestSyncPastDeadlineJobFinished(t *testing.T) {
job.Spec.ActiveDeadlineSeconds = &activeDeadlineSeconds
start := unversioned.Unix(unversioned.Now().Time.Unix()-15, 0)
job.Status.StartTime = &start
job.Status.Conditions = append(job.Status.Conditions, newCondition(extensions.JobFailed, "DeadlineExceeded", "Job was active longer than specified deadline"))
job.Status.Conditions = append(job.Status.Conditions, newCondition(batch.JobFailed, "DeadlineExceeded", "Job was active longer than specified deadline"))
manager.jobStore.Store.Add(job)
err := manager.syncJob(getKey(job, t))
if err != nil {
@ -412,7 +412,7 @@ func TestSyncJobComplete(t *testing.T) {
manager.podStoreSynced = alwaysReady
job := newJob(1, 1)
job.Status.Conditions = append(job.Status.Conditions, newCondition(extensions.JobComplete, "", ""))
job.Status.Conditions = append(job.Status.Conditions, newCondition(batch.JobComplete, "", ""))
manager.jobStore.Store.Add(job)
err := manager.syncJob(getKey(job, t))
if err != nil {
@ -422,7 +422,7 @@ func TestSyncJobComplete(t *testing.T) {
if err != nil {
t.Fatalf("Unexpected error when trying to get job from the store: %v", err)
}
actual := uncastJob.(*extensions.Job)
actual := uncastJob.(*batch.Job)
// Verify that after syncing a complete job, the conditions are the same.
if got, expected := len(actual.Status.Conditions), 1; got != expected {
t.Fatalf("Unexpected job status conditions amount; expected %d, got %d", expected, got)
@ -435,7 +435,7 @@ func TestSyncJobDeleted(t *testing.T) {
fakePodControl := controller.FakePodControl{}
manager.podControl = &fakePodControl
manager.podStoreSynced = alwaysReady
manager.updateHandler = func(job *extensions.Job) error { return nil }
manager.updateHandler = func(job *batch.Job) error { return nil }
job := newJob(2, 2)
err := manager.syncJob(getKey(job, t))
if err != nil {
@ -455,7 +455,7 @@ func TestSyncJobUpdateRequeue(t *testing.T) {
fakePodControl := controller.FakePodControl{}
manager.podControl = &fakePodControl
manager.podStoreSynced = alwaysReady
manager.updateHandler = func(job *extensions.Job) error { return fmt.Errorf("Fake error") }
manager.updateHandler = func(job *batch.Job) error { return fmt.Errorf("Fake error") }
job := newJob(2, 2)
manager.jobStore.Store.Add(job)
err := manager.syncJob(getKey(job, t))
@ -475,14 +475,14 @@ func TestJobPodLookup(t *testing.T) {
manager := NewJobControllerFromClient(clientset, controller.NoResyncPeriodFunc)
manager.podStoreSynced = alwaysReady
testCases := []struct {
job *extensions.Job
job *batch.Job
pod *api.Pod
expectedName string
}{
// pods without labels don't match any job
{
job: &extensions.Job{
job: &batch.Job{
ObjectMeta: api.ObjectMeta{Name: "basic"},
},
pod: &api.Pod{
@ -492,9 +492,9 @@ func TestJobPodLookup(t *testing.T) {
},
// matching labels, different namespace
{
job: &extensions.Job{
job: &batch.Job{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
@ -511,9 +511,9 @@ func TestJobPodLookup(t *testing.T) {
},
// matching ns and labels returns
{
job: &extensions.Job{
job: &batch.Job{
ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "ns"},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchExpressions: []unversioned.LabelSelectorRequirement{
{
@ -566,7 +566,7 @@ func TestSyncJobExpectations(t *testing.T) {
fakePodControl := controller.FakePodControl{}
manager.podControl = &fakePodControl
manager.podStoreSynced = alwaysReady
manager.updateHandler = func(job *extensions.Job) error { return nil }
manager.updateHandler = func(job *batch.Job) error { return nil }
job := newJob(2, 2)
manager.jobStore.Store.Add(job)
@ -602,7 +602,7 @@ func TestWatchJobs(t *testing.T) {
manager := NewJobControllerFromClient(clientset, controller.NoResyncPeriodFunc)
manager.podStoreSynced = alwaysReady
var testJob extensions.Job
var testJob batch.Job
received := make(chan struct{})
// The update sent through the fakeWatcher should make its way into the workqueue,
@ -613,7 +613,7 @@ func TestWatchJobs(t *testing.T) {
if !exists || err != nil {
t.Errorf("Expected to find job under key %v", key)
}
job, ok := obj.(*extensions.Job)
job, ok := obj.(*batch.Job)
if !ok {
t.Fatalf("unexpected type: %v %#v", reflect.TypeOf(obj), obj)
}
@ -637,10 +637,10 @@ func TestWatchJobs(t *testing.T) {
}
func TestIsJobFinished(t *testing.T) {
job := &extensions.Job{
Status: extensions.JobStatus{
Conditions: []extensions.JobCondition{{
Type: extensions.JobComplete,
job := &batch.Job{
Status: batch.JobStatus{
Conditions: []batch.JobCondition{{
Type: batch.JobComplete,
Status: api.ConditionTrue,
}},
},
@ -681,7 +681,7 @@ func TestWatchPods(t *testing.T) {
close(received)
return nil
}
job, ok := obj.(*extensions.Job)
job, ok := obj.(*batch.Job)
if !ok {
t.Errorf("unexpected type: %v %#v", reflect.TypeOf(obj), obj)
close(received)

View File

@ -33,6 +33,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/restclient"
"k8s.io/kubernetes/pkg/client/unversioned/fake"
@ -262,14 +263,14 @@ func TestDrain(t *testing.T) {
},
}
job := extensions.Job{
job := batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "job",
Namespace: "default",
CreationTimestamp: unversioned.Time{Time: time.Now()},
SelfLink: "/apis/extensions/v1beta1/namespaces/default/jobs/job",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{MatchLabels: labels},
},
}

View File

@ -665,7 +665,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
}
pod, _, err := GetFirstPod(client, t.Namespace, selector)
return pod, err
case *extensions.Job:
case *batch.Job:
selector, err := unversioned.LabelSelectorAsSelector(t.Spec.Selector)
if err != nil {
return nil, fmt.Errorf("invalid label selector: %v", err)

View File

@ -1089,7 +1089,7 @@ func (d *JobDescriber) Describe(namespace, name string) (string, error) {
return describeJob(job, events)
}
func describeJob(job *extensions.Job, events *api.EventList) (string, error) {
func describeJob(job *batch.Job, events *api.EventList) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", job.Name)
fmt.Fprintf(out, "Namespace:\t%s\n", job.Namespace)

View File

@ -36,6 +36,7 @@ import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
@ -795,7 +796,7 @@ func printReplicaSetList(list *extensions.ReplicaSetList, w io.Writer, options P
return nil
}
func printJob(job *extensions.Job, w io.Writer, options PrintOptions) error {
func printJob(job *batch.Job, w io.Writer, options PrintOptions) error {
name := job.Name
namespace := job.Namespace
containers := job.Spec.Template.Spec.Containers
@ -848,7 +849,7 @@ func printJob(job *extensions.Job, w io.Writer, options PrintOptions) error {
return nil
}
func printJobList(list *extensions.JobList, w io.Writer, options PrintOptions) error {
func printJobList(list *batch.JobList, w io.Writer, options PrintOptions) error {
for _, job := range list.Items {
if err := printJob(&job, w, options); err != nil {
return err
@ -1784,7 +1785,7 @@ func formatWideHeaders(wide bool, t reflect.Type) []string {
if t.String() == "*api.ReplicationController" || t.String() == "*api.ReplicationControllerList" {
return []string{"CONTAINER(S)", "IMAGE(S)", "SELECTOR"}
}
if t.String() == "*extensions.Job" || t.String() == "*extensions.JobList" {
if t.String() == "*batch.Job" || t.String() == "*batch.JobList" {
return []string{"CONTAINER(S)", "IMAGE(S)", "SELECTOR"}
}
if t.String() == "*api.Service" || t.String() == "*api.ServiceList" {

View File

@ -30,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing"
"k8s.io/kubernetes/pkg/runtime"
@ -1343,34 +1344,34 @@ func TestPrintDaemonSet(t *testing.T) {
func TestPrintJob(t *testing.T) {
completions := 2
tests := []struct {
job extensions.Job
job batch.Job
expect string
}{
{
extensions.Job{
batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "job1",
CreationTimestamp: unversioned.Time{Time: time.Now().Add(1.9e9)},
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Completions: &completions,
},
Status: extensions.JobStatus{
Status: batch.JobStatus{
Succeeded: 1,
},
},
"job1\t2\t1\t0s\n",
},
{
extensions.Job{
batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "job2",
CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Completions: nil,
},
Status: extensions.JobStatus{
Status: batch.JobStatus{
Succeeded: 0,
},
},

View File

@ -25,6 +25,7 @@ import (
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/batch"
batchv1 "k8s.io/kubernetes/pkg/apis/batch/v1"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/runtime"
@ -280,12 +281,12 @@ func (JobV1Beta1) Generate(genericParams map[string]interface{}) (runtime.Object
}
podSpec.RestartPolicy = restartPolicy
job := extensions.Job{
job := batch.Job{
ObjectMeta: api.ObjectMeta{
Name: name,
Labels: labels,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: labels,
},

View File

@ -23,6 +23,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
)
@ -721,7 +722,7 @@ func TestGenerateDeployment(t *testing.T) {
func TestGenerateJob(t *testing.T) {
tests := []struct {
params map[string]interface{}
expected *extensions.Job
expected *batch.Job
expectErr bool
}{
{
@ -740,12 +741,12 @@ func TestGenerateJob(t *testing.T) {
"limits": "cpu=400m,memory=200Mi",
"restart": "OnFailure",
},
expected: &extensions.Job{
expected: &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar", "baz": "blah"},
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"foo": "bar", "baz": "blah"},
},
@ -807,8 +808,8 @@ func TestGenerateJob(t *testing.T) {
if test.expectErr && err != nil {
continue
}
if !reflect.DeepEqual(obj.(*extensions.Job), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*extensions.Job))
if !reflect.DeepEqual(obj.(*batch.Job), test.expected) {
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*batch.Job))
}
}
}

View File

@ -48,7 +48,7 @@ func ScalerFor(kind unversioned.GroupKind, c client.Interface) (Scaler, error) {
case extensions.Kind("ReplicaSet"):
return &ReplicaSetScaler{c.Extensions()}, nil
case extensions.Kind("Job"), batch.Kind("Job"):
return &JobScaler{c.Extensions()}, nil // Either kind of job can be scaled with Extensions interface.
return &JobScaler{c.Batch()}, nil // Either kind of job can be scaled with Batch interface.
case extensions.Kind("Deployment"):
return &DeploymentScaler{c.Extensions()}, nil
}
@ -252,7 +252,7 @@ func (scaler *ReplicaSetScaler) Scale(namespace, name string, newSize uint, prec
}
// ValidateJob ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
func (precondition *ScalePrecondition) ValidateJob(job *extensions.Job) error {
func (precondition *ScalePrecondition) ValidateJob(job *batch.Job) error {
if precondition.Size != -1 && job.Spec.Parallelism == nil {
return PreconditionError{"parallelism", strconv.Itoa(precondition.Size), "nil"}
}
@ -266,7 +266,7 @@ func (precondition *ScalePrecondition) ValidateJob(job *extensions.Job) error {
}
type JobScaler struct {
c client.ExtensionsInterface
c client.BatchInterface
}
// ScaleSimple is responsible for updating job's parallelism.

View File

@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
@ -252,17 +253,17 @@ type ErrorJobs struct {
invalid bool
}
func (c *ErrorJobs) Update(job *extensions.Job) (*extensions.Job, error) {
func (c *ErrorJobs) Update(job *batch.Job) (*batch.Job, error) {
if c.invalid {
return nil, kerrors.NewInvalid(extensions.Kind(job.Kind), job.Name, nil)
return nil, kerrors.NewInvalid(batch.Kind(job.Kind), job.Name, nil)
}
return nil, errors.New("Job update failure")
}
func (c *ErrorJobs) Get(name string) (*extensions.Job, error) {
func (c *ErrorJobs) Get(name string) (*batch.Job, error) {
zero := 0
return &extensions.Job{
Spec: extensions.JobSpec{
return &batch.Job{
Spec: batch.JobSpec{
Parallelism: &zero,
},
}, nil
@ -316,7 +317,7 @@ func TestJobScale(t *testing.T) {
if action, ok := actions[0].(testclient.GetAction); !ok || action.GetResource() != "jobs" || action.GetName() != name {
t.Errorf("unexpected action: %v, expected get-replicationController %s", actions[0], name)
}
if action, ok := actions[1].(testclient.UpdateAction); !ok || action.GetResource() != "jobs" || *action.GetObject().(*extensions.Job).Spec.Parallelism != int(count) {
if action, ok := actions[1].(testclient.UpdateAction); !ok || action.GetResource() != "jobs" || *action.GetObject().(*batch.Job).Spec.Parallelism != int(count) {
t.Errorf("unexpected action %v, expected update-job with parallelism = %d", actions[1], count)
}
}
@ -342,8 +343,8 @@ func TestJobScaleInvalid(t *testing.T) {
func TestJobScaleFailsPreconditions(t *testing.T) {
ten := 10
fake := testclient.NewSimpleFake(&extensions.Job{
Spec: extensions.JobSpec{
fake := testclient.NewSimpleFake(&batch.Job{
Spec: batch.JobSpec{
Parallelism: &ten,
},
})
@ -366,7 +367,7 @@ func TestValidateJob(t *testing.T) {
zero, ten, twenty := 0, 10, 20
tests := []struct {
preconditions ScalePrecondition
job extensions.Job
job batch.Job
expectError bool
test string
}{
@ -377,11 +378,11 @@ func TestValidateJob(t *testing.T) {
},
{
preconditions: ScalePrecondition{-1, ""},
job: extensions.Job{
job: batch.Job{
ObjectMeta: api.ObjectMeta{
ResourceVersion: "foo",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &ten,
},
},
@ -390,11 +391,11 @@ func TestValidateJob(t *testing.T) {
},
{
preconditions: ScalePrecondition{0, ""},
job: extensions.Job{
job: batch.Job{
ObjectMeta: api.ObjectMeta{
ResourceVersion: "foo",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &zero,
},
},
@ -403,11 +404,11 @@ func TestValidateJob(t *testing.T) {
},
{
preconditions: ScalePrecondition{-1, "foo"},
job: extensions.Job{
job: batch.Job{
ObjectMeta: api.ObjectMeta{
ResourceVersion: "foo",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &ten,
},
},
@ -416,11 +417,11 @@ func TestValidateJob(t *testing.T) {
},
{
preconditions: ScalePrecondition{10, "foo"},
job: extensions.Job{
job: batch.Job{
ObjectMeta: api.ObjectMeta{
ResourceVersion: "foo",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &ten,
},
},
@ -429,11 +430,11 @@ func TestValidateJob(t *testing.T) {
},
{
preconditions: ScalePrecondition{10, "foo"},
job: extensions.Job{
job: batch.Job{
ObjectMeta: api.ObjectMeta{
ResourceVersion: "foo",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &twenty,
},
},
@ -442,7 +443,7 @@ func TestValidateJob(t *testing.T) {
},
{
preconditions: ScalePrecondition{10, "foo"},
job: extensions.Job{
job: batch.Job{
ObjectMeta: api.ObjectMeta{
ResourceVersion: "foo",
},
@ -452,11 +453,11 @@ func TestValidateJob(t *testing.T) {
},
{
preconditions: ScalePrecondition{10, "foo"},
job: extensions.Job{
job: batch.Job{
ObjectMeta: api.ObjectMeta{
ResourceVersion: "bar",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &ten,
},
},
@ -465,11 +466,11 @@ func TestValidateJob(t *testing.T) {
},
{
preconditions: ScalePrecondition{10, "foo"},
job: extensions.Job{
job: batch.Job{
ObjectMeta: api.ObjectMeta{
ResourceVersion: "bar",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &twenty,
},
},

View File

@ -314,9 +314,9 @@ func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duratio
}
func (reaper *JobReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
jobs := reaper.Extensions().Jobs(namespace)
jobs := reaper.Batch().Jobs(namespace)
pods := reaper.Pods(namespace)
scaler, err := ScalerFor(extensions.Kind("Job"), *reaper)
scaler, err := ScalerFor(batch.Kind("Job"), *reaper)
if err != nil {
return err
}

View File

@ -25,6 +25,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
@ -388,26 +389,26 @@ func TestJobStop(t *testing.T) {
{
Name: "OnlyOneJob",
Objs: []runtime.Object{
&extensions.Job{ // GET
&batch.Job{ // GET
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &zero,
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"k1": "v1"},
},
},
},
&extensions.JobList{ // LIST
Items: []extensions.Job{
&batch.JobList{ // LIST
Items: []batch.Job{
{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &zero,
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"k1": "v1"},
@ -424,26 +425,26 @@ func TestJobStop(t *testing.T) {
{
Name: "JobWithDeadPods",
Objs: []runtime.Object{
&extensions.Job{ // GET
&batch.Job{ // GET
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &zero,
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"k1": "v1"},
},
},
},
&extensions.JobList{ // LIST
Items: []extensions.Job{
&batch.JobList{ // LIST
Items: []batch.Job{
{
ObjectMeta: api.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &zero,
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"k1": "v1"},

View File

@ -18,7 +18,7 @@ package etcd
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/cachesize"
@ -37,12 +37,12 @@ type REST struct {
func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix := "/jobs"
newListFunc := func() runtime.Object { return &extensions.JobList{} }
newListFunc := func() runtime.Object { return &batch.JobList{} }
storageInterface := opts.Decorator(
opts.Storage, cachesize.GetWatchCacheSizeByResource(cachesize.Jobs), &extensions.Job{}, prefix, job.Strategy, newListFunc)
opts.Storage, cachesize.GetWatchCacheSizeByResource(cachesize.Jobs), &batch.Job{}, prefix, job.Strategy, newListFunc)
store := &etcdgeneric.Etcd{
NewFunc: func() runtime.Object { return &extensions.Job{} },
NewFunc: func() runtime.Object { return &batch.Job{} },
// NewListFunc returns an object capable of storing results of an etcd list.
NewListFunc: newListFunc,
@ -58,13 +58,13 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
},
// Retrieve the name field of a job
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*extensions.Job).Name, nil
return obj.(*batch.Job).Name, nil
},
// Used to match objects based on labels/fields for list and watch
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
return job.MatchJob(label, field)
},
QualifiedResource: extensions.Resource("jobs"),
QualifiedResource: batch.Resource("jobs"),
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
// Used to validate job creation
@ -89,7 +89,7 @@ type StatusREST struct {
}
func (r *StatusREST) New() runtime.Object {
return &extensions.Job{}
return &batch.Job{}
}
// Update alters the status subset of an object.

View File

@ -21,9 +21,8 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/extensions"
// Ensure that extensions/v1beta1 package is initialized.
_ "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
@ -39,15 +38,15 @@ func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer)
return jobStorage, statusStorage, server
}
func validNewJob() *extensions.Job {
func validNewJob() *batch.Job {
completions := 1
parallelism := 1
return &extensions.Job{
return &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: "default",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Completions: &completions,
Parallelism: &parallelism,
Selector: &unversioned.LabelSelector{
@ -84,8 +83,8 @@ func TestCreate(t *testing.T) {
// valid
validJob,
// invalid (empty selector)
&extensions.Job{
Spec: extensions.JobSpec{
&batch.Job{
Spec: batch.JobSpec{
Completions: validJob.Spec.Completions,
Selector: &unversioned.LabelSelector{},
Template: validJob.Spec.Template,
@ -104,18 +103,18 @@ func TestUpdate(t *testing.T) {
validNewJob(),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*extensions.Job)
object := obj.(*batch.Job)
object.Spec.Parallelism = &two
return object
},
// invalid updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*extensions.Job)
object := obj.(*batch.Job)
object.Spec.Selector = &unversioned.LabelSelector{}
return object
},
func(obj runtime.Object) runtime.Object {
object := obj.(*extensions.Job)
object := obj.(*batch.Job)
object.Spec.Completions = &two
return object
},

View File

@ -22,8 +22,8 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/validation"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
@ -47,20 +47,20 @@ func (jobStrategy) NamespaceScoped() bool {
// PrepareForCreate clears the status of a job before creation.
func (jobStrategy) PrepareForCreate(obj runtime.Object) {
job := obj.(*extensions.Job)
job.Status = extensions.JobStatus{}
job := obj.(*batch.Job)
job.Status = batch.JobStatus{}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (jobStrategy) PrepareForUpdate(obj, old runtime.Object) {
newJob := obj.(*extensions.Job)
oldJob := old.(*extensions.Job)
newJob := obj.(*batch.Job)
oldJob := old.(*batch.Job)
newJob.Status = oldJob.Status
}
// Validate validates a new job.
func (jobStrategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
job := obj.(*extensions.Job)
job := obj.(*batch.Job)
// TODO: move UID generation earlier and do this in defaulting logic?
if job.Spec.ManualSelector == nil || *job.Spec.ManualSelector == false {
generateSelector(job)
@ -71,7 +71,7 @@ func (jobStrategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList
// generateSelector adds a selector to a job and labels to its template
// which can be used to uniquely identify the pods created by that job,
// if the user has requested this behavior.
func generateSelector(obj *extensions.Job) {
func generateSelector(obj *batch.Job) {
if obj.Spec.Template.Labels == nil {
obj.Spec.Template.Labels = make(map[string]string)
}
@ -133,8 +133,8 @@ func (jobStrategy) AllowCreateOnUpdate() bool {
// ValidateUpdate is the default update validation for an end user.
func (jobStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateJob(obj.(*extensions.Job))
updateErrorList := validation.ValidateJobUpdate(obj.(*extensions.Job), old.(*extensions.Job))
validationErrorList := validation.ValidateJob(obj.(*batch.Job))
updateErrorList := validation.ValidateJobUpdate(obj.(*batch.Job), old.(*batch.Job))
return append(validationErrorList, updateErrorList...)
}
@ -145,17 +145,17 @@ type jobStatusStrategy struct {
var StatusStrategy = jobStatusStrategy{Strategy}
func (jobStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
newJob := obj.(*extensions.Job)
oldJob := old.(*extensions.Job)
newJob := obj.(*batch.Job)
oldJob := old.(*batch.Job)
newJob.Spec = oldJob.Spec
}
func (jobStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateJobUpdateStatus(obj.(*extensions.Job), old.(*extensions.Job))
return validation.ValidateJobUpdateStatus(obj.(*batch.Job), old.(*batch.Job))
}
// JobSelectableFields returns a field set that represents the object for matching purposes.
func JobToSelectableFields(job *extensions.Job) fields.Set {
func JobToSelectableFields(job *batch.Job) fields.Set {
objectMetaFieldsSet := generic.ObjectMetaFieldsSet(job.ObjectMeta, true)
specificFieldsSet := fields.Set{
"status.successful": strconv.Itoa(job.Status.Succeeded),
@ -171,7 +171,7 @@ func MatchJob(label labels.Selector, field fields.Selector) generic.Matcher {
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
job, ok := obj.(*extensions.Job)
job, ok := obj.(*batch.Job)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a job.")
}

View File

@ -24,7 +24,7 @@ import (
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/types"
)
@ -57,17 +57,17 @@ func TestJobStrategy(t *testing.T) {
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
job := &extensions.Job{
job := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
ManualSelector: newBool(true),
},
Status: extensions.JobStatus{
Status: batch.JobStatus{
Active: 11,
},
}
@ -81,12 +81,12 @@ func TestJobStrategy(t *testing.T) {
t.Errorf("Unexpected error validating %v", errs)
}
parallelism := 10
updatedJob := &extensions.Job{
updatedJob := &batch.Job{
ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "4"},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &parallelism,
},
Status: extensions.JobStatus{
Status: batch.JobStatus{
Active: 11,
},
}
@ -114,13 +114,13 @@ func TestJobStrategyWithGeneration(t *testing.T) {
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
job := &extensions.Job{
job := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "myjob2",
Namespace: api.NamespaceDefault,
UID: theUID,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: nil,
Template: validPodTemplateSpec,
},
@ -175,33 +175,33 @@ func TestJobStatusStrategy(t *testing.T) {
}
oldParallelism := 10
newParallelism := 11
oldJob := &extensions.Job{
oldJob := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
ResourceVersion: "10",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
Parallelism: &oldParallelism,
},
Status: extensions.JobStatus{
Status: batch.JobStatus{
Active: 11,
},
}
newJob := &extensions.Job{
newJob := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
ResourceVersion: "9",
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Selector: validSelector,
Template: validPodTemplateSpec,
Parallelism: &newParallelism,
},
Status: extensions.JobStatus{
Status: batch.JobStatus{
Active: 12,
},
}
@ -226,7 +226,7 @@ func TestSelectableFieldLabelConversions(t *testing.T) {
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
testapi.Extensions.GroupVersion().String(),
"Job",
labels.Set(JobToSelectableFields(&extensions.Job{})),
labels.Set(JobToSelectableFields(&batch.Job{})),
nil,
)
}

View File

@ -25,7 +25,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/batch"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/labels"
@ -128,7 +128,7 @@ var _ = framework.KubeDescribe("V1Job", func() {
Expect(err).NotTo(HaveOccurred())
By("scale job up")
scaler, err := kubectl.ScalerFor(extensions.Kind("Job"), f.Client)
scaler, err := kubectl.ScalerFor(batch.Kind("Job"), f.Client)
Expect(err).NotTo(HaveOccurred())
waitForScale := kubectl.NewRetryParams(5*time.Second, 1*time.Minute)
waitForReplicas := kubectl.NewRetryParams(5*time.Second, 5*time.Minute)
@ -153,7 +153,7 @@ var _ = framework.KubeDescribe("V1Job", func() {
Expect(err).NotTo(HaveOccurred())
By("scale job down")
scaler, err := kubectl.ScalerFor(extensions.Kind("Job"), f.Client)
scaler, err := kubectl.ScalerFor(batch.Kind("Job"), f.Client)
Expect(err).NotTo(HaveOccurred())
waitForScale := kubectl.NewRetryParams(5*time.Second, 1*time.Minute)
waitForReplicas := kubectl.NewRetryParams(5*time.Second, 5*time.Minute)
@ -176,7 +176,7 @@ var _ = framework.KubeDescribe("V1Job", func() {
Expect(err).NotTo(HaveOccurred())
By("delete a job")
reaper, err := kubectl.ReaperFor(extensions.Kind("Job"), f.Client)
reaper, err := kubectl.ReaperFor(batch.Kind("Job"), f.Client)
Expect(err).NotTo(HaveOccurred())
timeout := 1 * time.Minute
err = reaper.Stop(f.Namespace.Name, job.Name, timeout, api.NewDeleteOptions(0))
@ -203,12 +203,12 @@ var _ = framework.KubeDescribe("V1Job", func() {
})
// newTestV1Job returns a job which does one of several testing behaviors.
func newTestV1Job(behavior, name string, rPol api.RestartPolicy, parallelism, completions int) *extensions.Job {
job := &extensions.Job{
func newTestV1Job(behavior, name string, rPol api.RestartPolicy, parallelism, completions int) *batch.Job {
job := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: name,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &parallelism,
Completions: &completions,
Template: api.PodTemplateSpec{
@ -264,7 +264,7 @@ func newTestV1Job(behavior, name string, rPol api.RestartPolicy, parallelism, co
return job
}
func createV1Job(c *client.Client, ns string, job *extensions.Job) (*extensions.Job, error) {
func createV1Job(c *client.Client, ns string, job *batch.Job) (*batch.Job, error) {
return c.Batch().Jobs(ns).Create(job)
}
@ -310,7 +310,7 @@ func waitForV1JobFail(c *client.Client, ns, jobName string) error {
return false, err
}
for _, c := range curr.Status.Conditions {
if c.Type == extensions.JobFailed && c.Status == api.ConditionTrue {
if c.Type == batch.JobFailed && c.Status == api.ConditionTrue {
return true, nil
}
}

View File

@ -21,7 +21,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/batch"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/labels"
@ -124,7 +124,7 @@ var _ = framework.KubeDescribe("Job", func() {
Expect(err).NotTo(HaveOccurred())
By("scale job up")
scaler, err := kubectl.ScalerFor(extensions.Kind("Job"), f.Client)
scaler, err := kubectl.ScalerFor(batch.Kind("Job"), f.Client)
Expect(err).NotTo(HaveOccurred())
waitForScale := kubectl.NewRetryParams(5*time.Second, 1*time.Minute)
waitForReplicas := kubectl.NewRetryParams(5*time.Second, 5*time.Minute)
@ -149,7 +149,7 @@ var _ = framework.KubeDescribe("Job", func() {
Expect(err).NotTo(HaveOccurred())
By("scale job down")
scaler, err := kubectl.ScalerFor(extensions.Kind("Job"), f.Client)
scaler, err := kubectl.ScalerFor(batch.Kind("Job"), f.Client)
Expect(err).NotTo(HaveOccurred())
waitForScale := kubectl.NewRetryParams(5*time.Second, 1*time.Minute)
waitForReplicas := kubectl.NewRetryParams(5*time.Second, 5*time.Minute)
@ -172,7 +172,7 @@ var _ = framework.KubeDescribe("Job", func() {
Expect(err).NotTo(HaveOccurred())
By("delete a job")
reaper, err := kubectl.ReaperFor(extensions.Kind("Job"), f.Client)
reaper, err := kubectl.ReaperFor(batch.Kind("Job"), f.Client)
Expect(err).NotTo(HaveOccurred())
timeout := 1 * time.Minute
err = reaper.Stop(f.Namespace.Name, job.Name, timeout, api.NewDeleteOptions(0))
@ -199,12 +199,12 @@ var _ = framework.KubeDescribe("Job", func() {
})
// newTestJob returns a job which does one of several testing behaviors.
func newTestJob(behavior, name string, rPol api.RestartPolicy, parallelism, completions int) *extensions.Job {
job := &extensions.Job{
func newTestJob(behavior, name string, rPol api.RestartPolicy, parallelism, completions int) *batch.Job {
job := &batch.Job{
ObjectMeta: api.ObjectMeta{
Name: name,
},
Spec: extensions.JobSpec{
Spec: batch.JobSpec{
Parallelism: &parallelism,
Completions: &completions,
ManualSelector: newBool(true),
@ -261,7 +261,7 @@ func newTestJob(behavior, name string, rPol api.RestartPolicy, parallelism, comp
return job
}
func createJob(c *client.Client, ns string, job *extensions.Job) (*extensions.Job, error) {
func createJob(c *client.Client, ns string, job *batch.Job) (*batch.Job, error) {
return c.Extensions().Jobs(ns).Create(job)
}
@ -307,7 +307,7 @@ func waitForJobFail(c *client.Client, ns, jobName string) error {
return false, err
}
for _, c := range curr.Status.Conditions {
if c.Type == extensions.JobFailed && c.Status == api.ConditionTrue {
if c.Type == batch.JobFailed && c.Status == api.ConditionTrue {
return true, nil
}
}