New Job resource

pull/6/head
Maciej Szulik 2015-08-18 16:39:49 +02:00
parent 90ba96d486
commit e55c59e8c2
15 changed files with 1338 additions and 0 deletions

View File

@ -986,6 +986,121 @@ func deepCopy_expapi_HorizontalPodAutoscalerStatus(in HorizontalPodAutoscalerSta
return nil
}
func deepCopy_expapi_Job(in Job, out *Job, c *conversion.Cloner) error {
if err := deepCopy_api_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := deepCopy_expapi_JobSpec(in.Spec, &out.Spec, c); err != nil {
return err
}
if err := deepCopy_expapi_JobStatus(in.Status, &out.Status, c); err != nil {
return err
}
return nil
}
func deepCopy_expapi_JobCondition(in JobCondition, out *JobCondition, c *conversion.Cloner) error {
out.Type = in.Type
out.Status = in.Status
if err := deepCopy_util_Time(in.LastProbeTime, &out.LastProbeTime, c); err != nil {
return err
}
if err := deepCopy_util_Time(in.LastTransitionTime, &out.LastTransitionTime, c); err != nil {
return err
}
out.Reason = in.Reason
out.Message = in.Message
return nil
}
func deepCopy_expapi_JobList(in JobList, out *JobList, c *conversion.Cloner) error {
if err := deepCopy_api_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := deepCopy_api_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
out.Items = make([]Job, len(in.Items))
for i := range in.Items {
if err := deepCopy_expapi_Job(in.Items[i], &out.Items[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func deepCopy_expapi_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner) error {
if in.Parallelism != nil {
out.Parallelism = new(int)
*out.Parallelism = *in.Parallelism
} else {
out.Parallelism = nil
}
if in.Completions != nil {
out.Completions = new(int)
*out.Completions = *in.Completions
} else {
out.Completions = nil
}
if in.Selector != nil {
out.Selector = make(map[string]string)
for key, val := range in.Selector {
out.Selector[key] = val
}
} else {
out.Selector = nil
}
if in.Template != nil {
out.Template = new(api.PodTemplateSpec)
if err := deepCopy_api_PodTemplateSpec(*in.Template, out.Template, c); err != nil {
return err
}
} else {
out.Template = nil
}
return nil
}
func deepCopy_expapi_JobStatus(in JobStatus, out *JobStatus, c *conversion.Cloner) error {
if in.Conditions != nil {
out.Conditions = make([]JobCondition, len(in.Conditions))
for i := range in.Conditions {
if err := deepCopy_expapi_JobCondition(in.Conditions[i], &out.Conditions[i], c); err != nil {
return err
}
}
} else {
out.Conditions = nil
}
if in.StartTime != nil {
out.StartTime = new(util.Time)
if err := deepCopy_util_Time(*in.StartTime, out.StartTime, c); err != nil {
return err
}
} else {
out.StartTime = nil
}
if in.CompletionTime != nil {
out.CompletionTime = new(util.Time)
if err := deepCopy_util_Time(*in.CompletionTime, out.CompletionTime, c); err != nil {
return err
}
} else {
out.CompletionTime = nil
}
out.Active = in.Active
out.Successful = in.Successful
out.Unsuccessful = in.Unsuccessful
return nil
}
func deepCopy_expapi_ReplicationControllerDummy(in ReplicationControllerDummy, out *ReplicationControllerDummy, c *conversion.Cloner) error {
if err := deepCopy_api_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
@ -1206,6 +1321,11 @@ func init() {
deepCopy_expapi_HorizontalPodAutoscalerList,
deepCopy_expapi_HorizontalPodAutoscalerSpec,
deepCopy_expapi_HorizontalPodAutoscalerStatus,
deepCopy_expapi_Job,
deepCopy_expapi_JobCondition,
deepCopy_expapi_JobList,
deepCopy_expapi_JobSpec,
deepCopy_expapi_JobStatus,
deepCopy_expapi_ReplicationControllerDummy,
deepCopy_expapi_ResourceConsumption,
deepCopy_expapi_RollingUpdateDeployment,

View File

@ -32,6 +32,8 @@ func addKnownTypes() {
&DeploymentList{},
&HorizontalPodAutoscaler{},
&HorizontalPodAutoscalerList{},
&Job{},
&JobList{},
&ReplicationControllerDummy{},
&Scale{},
&ThirdPartyResource{},
@ -47,6 +49,8 @@ func (*Deployment) IsAnAPIObject() {}
func (*DeploymentList) IsAnAPIObject() {}
func (*HorizontalPodAutoscaler) IsAnAPIObject() {}
func (*HorizontalPodAutoscalerList) IsAnAPIObject() {}
func (*Job) IsAnAPIObject() {}
func (*JobList) IsAnAPIObject() {}
func (*ReplicationControllerDummy) IsAnAPIObject() {}
func (*Scale) IsAnAPIObject() {}
func (*ThirdPartyResource) IsAnAPIObject() {}

View File

@ -362,3 +362,102 @@ type ThirdPartyResourceDataList struct {
// Items is a list of third party objects
Items []ThirdPartyResourceData `json:"items"`
}
// Job represents the configuration of a single job.
type Job struct {
api.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 {
api.TypeMeta `json:",inline"`
// Standard list metadata
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
api.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. Defaults to 1.
Completions *int `json:"completions,omitempty"`
// Selector is a label query over pods that should match the pod count.
Selector map[string]string `json:"selector"`
// 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 *util.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 *util.Time `json:"completionTime,omitempty"`
// Active is the number of actively running pods.
Active int `json:"active,omitempty"`
// Successful is the number of pods which reached Phase Succeeded.
Successful int `json:"successful,omitempty"`
// Unsuccessful is the number of pods failures, this applies only to jobs
// created with RestartPolicyNever, otherwise this value will always be 0.
Unsuccessful int `json:"unsuccessful,omitempty"`
}
type JobConditionType string
// These are valid conditions of a job.
const (
// JobComplete means the job has completed its execution.
JobComplete JobConditionType = "Complete"
)
// JobCondition describes current state of a job.
type JobCondition struct {
// Type of job condition, currently only Complete.
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 util.Time `json:"lastProbeTime,omitempty"`
// Last time the condition transit from one status to another.
LastTransitionTime util.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

@ -1788,6 +1788,134 @@ func convert_expapi_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutoscalerS
return nil
}
func convert_expapi_Job_To_v1_Job(in *expapi.Job, out *Job, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.Job))(in)
}
if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
if err := convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
return err
}
if err := convert_expapi_JobSpec_To_v1_JobSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := convert_expapi_JobStatus_To_v1_JobStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
func convert_expapi_JobCondition_To_v1_JobCondition(in *expapi.JobCondition, out *JobCondition, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.JobCondition))(in)
}
out.Type = JobConditionType(in.Type)
out.Status = v1.ConditionStatus(in.Status)
if err := s.Convert(&in.LastProbeTime, &out.LastProbeTime, 0); err != nil {
return err
}
if err := s.Convert(&in.LastTransitionTime, &out.LastTransitionTime, 0); err != nil {
return err
}
out.Reason = in.Reason
out.Message = in.Message
return nil
}
func convert_expapi_JobList_To_v1_JobList(in *expapi.JobList, out *JobList, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.JobList))(in)
}
if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
if err := convert_api_ListMeta_To_v1_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
return err
}
if in.Items != nil {
out.Items = make([]Job, len(in.Items))
for i := range in.Items {
if err := convert_expapi_Job_To_v1_Job(&in.Items[i], &out.Items[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func convert_expapi_JobSpec_To_v1_JobSpec(in *expapi.JobSpec, out *JobSpec, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.JobSpec))(in)
}
if in.Parallelism != nil {
out.Parallelism = new(int)
*out.Parallelism = *in.Parallelism
} else {
out.Parallelism = nil
}
if in.Completions != nil {
out.Completions = new(int)
*out.Completions = *in.Completions
} else {
out.Completions = nil
}
if in.Selector != nil {
out.Selector = make(map[string]string)
for key, val := range in.Selector {
out.Selector[key] = val
}
} else {
out.Selector = nil
}
if in.Template != nil {
out.Template = new(v1.PodTemplateSpec)
if err := convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(in.Template, out.Template, s); err != nil {
return err
}
} else {
out.Template = nil
}
return nil
}
func convert_expapi_JobStatus_To_v1_JobStatus(in *expapi.JobStatus, out *JobStatus, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.JobStatus))(in)
}
if in.Conditions != nil {
out.Conditions = make([]JobCondition, len(in.Conditions))
for i := range in.Conditions {
if err := convert_expapi_JobCondition_To_v1_JobCondition(&in.Conditions[i], &out.Conditions[i], s); err != nil {
return err
}
}
} else {
out.Conditions = nil
}
if in.StartTime != nil {
if err := s.Convert(&in.StartTime, &out.StartTime, 0); err != nil {
return err
}
} else {
out.StartTime = nil
}
if in.CompletionTime != nil {
if err := s.Convert(&in.CompletionTime, &out.CompletionTime, 0); err != nil {
return err
}
} else {
out.CompletionTime = nil
}
out.Active = in.Active
out.Successful = in.Successful
out.Unsuccessful = in.Unsuccessful
return nil
}
func convert_expapi_ReplicationControllerDummy_To_v1_ReplicationControllerDummy(in *expapi.ReplicationControllerDummy, out *ReplicationControllerDummy, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*expapi.ReplicationControllerDummy))(in)
@ -2176,6 +2304,134 @@ func convert_v1_HorizontalPodAutoscalerStatus_To_expapi_HorizontalPodAutoscalerS
return nil
}
func convert_v1_Job_To_expapi_Job(in *Job, out *expapi.Job, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*Job))(in)
}
if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
if err := convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
return err
}
if err := convert_v1_JobSpec_To_expapi_JobSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := convert_v1_JobStatus_To_expapi_JobStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
func convert_v1_JobCondition_To_expapi_JobCondition(in *JobCondition, out *expapi.JobCondition, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*JobCondition))(in)
}
out.Type = expapi.JobConditionType(in.Type)
out.Status = api.ConditionStatus(in.Status)
if err := s.Convert(&in.LastProbeTime, &out.LastProbeTime, 0); err != nil {
return err
}
if err := s.Convert(&in.LastTransitionTime, &out.LastTransitionTime, 0); err != nil {
return err
}
out.Reason = in.Reason
out.Message = in.Message
return nil
}
func convert_v1_JobList_To_expapi_JobList(in *JobList, out *expapi.JobList, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*JobList))(in)
}
if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
return err
}
if err := convert_v1_ListMeta_To_api_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
return err
}
if in.Items != nil {
out.Items = make([]expapi.Job, len(in.Items))
for i := range in.Items {
if err := convert_v1_Job_To_expapi_Job(&in.Items[i], &out.Items[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func convert_v1_JobSpec_To_expapi_JobSpec(in *JobSpec, out *expapi.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 = *in.Parallelism
} else {
out.Parallelism = nil
}
if in.Completions != nil {
out.Completions = new(int)
*out.Completions = *in.Completions
} else {
out.Completions = nil
}
if in.Selector != nil {
out.Selector = make(map[string]string)
for key, val := range in.Selector {
out.Selector[key] = val
}
} else {
out.Selector = nil
}
if in.Template != nil {
out.Template = new(api.PodTemplateSpec)
if err := convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(in.Template, out.Template, s); err != nil {
return err
}
} else {
out.Template = nil
}
return nil
}
func convert_v1_JobStatus_To_expapi_JobStatus(in *JobStatus, out *expapi.JobStatus, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*JobStatus))(in)
}
if in.Conditions != nil {
out.Conditions = make([]expapi.JobCondition, len(in.Conditions))
for i := range in.Conditions {
if err := convert_v1_JobCondition_To_expapi_JobCondition(&in.Conditions[i], &out.Conditions[i], s); err != nil {
return err
}
}
} else {
out.Conditions = nil
}
if in.StartTime != nil {
if err := s.Convert(&in.StartTime, &out.StartTime, 0); err != nil {
return err
}
} else {
out.StartTime = nil
}
if in.CompletionTime != nil {
if err := s.Convert(&in.CompletionTime, &out.CompletionTime, 0); err != nil {
return err
}
} else {
out.CompletionTime = nil
}
out.Active = in.Active
out.Successful = in.Successful
out.Unsuccessful = in.Unsuccessful
return nil
}
func convert_v1_ReplicationControllerDummy_To_expapi_ReplicationControllerDummy(in *ReplicationControllerDummy, out *expapi.ReplicationControllerDummy, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*ReplicationControllerDummy))(in)
@ -2390,6 +2646,11 @@ func init() {
convert_expapi_HorizontalPodAutoscalerSpec_To_v1_HorizontalPodAutoscalerSpec,
convert_expapi_HorizontalPodAutoscalerStatus_To_v1_HorizontalPodAutoscalerStatus,
convert_expapi_HorizontalPodAutoscaler_To_v1_HorizontalPodAutoscaler,
convert_expapi_JobCondition_To_v1_JobCondition,
convert_expapi_JobList_To_v1_JobList,
convert_expapi_JobSpec_To_v1_JobSpec,
convert_expapi_JobStatus_To_v1_JobStatus,
convert_expapi_Job_To_v1_Job,
convert_expapi_ReplicationControllerDummy_To_v1_ReplicationControllerDummy,
convert_expapi_ResourceConsumption_To_v1_ResourceConsumption,
convert_expapi_ScaleSpec_To_v1_ScaleSpec,
@ -2431,6 +2692,11 @@ func init() {
convert_v1_HorizontalPodAutoscaler_To_expapi_HorizontalPodAutoscaler,
convert_v1_HostPathVolumeSource_To_api_HostPathVolumeSource,
convert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource,
convert_v1_JobCondition_To_expapi_JobCondition,
convert_v1_JobList_To_expapi_JobList,
convert_v1_JobSpec_To_expapi_JobSpec,
convert_v1_JobStatus_To_expapi_JobStatus,
convert_v1_Job_To_expapi_Job,
convert_v1_Lifecycle_To_api_Lifecycle,
convert_v1_ListMeta_To_api_ListMeta,
convert_v1_LocalObjectReference_To_api_LocalObjectReference,

View File

@ -998,6 +998,121 @@ func deepCopy_v1_HorizontalPodAutoscalerStatus(in HorizontalPodAutoscalerStatus,
return nil
}
func deepCopy_v1_Job(in Job, out *Job, c *conversion.Cloner) error {
if err := deepCopy_v1_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := deepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil {
return err
}
if err := deepCopy_v1_JobSpec(in.Spec, &out.Spec, c); err != nil {
return err
}
if err := deepCopy_v1_JobStatus(in.Status, &out.Status, c); err != nil {
return err
}
return nil
}
func deepCopy_v1_JobCondition(in JobCondition, out *JobCondition, c *conversion.Cloner) error {
out.Type = in.Type
out.Status = in.Status
if err := deepCopy_util_Time(in.LastProbeTime, &out.LastProbeTime, c); err != nil {
return err
}
if err := deepCopy_util_Time(in.LastTransitionTime, &out.LastTransitionTime, c); err != nil {
return err
}
out.Reason = in.Reason
out.Message = in.Message
return nil
}
func deepCopy_v1_JobList(in JobList, out *JobList, c *conversion.Cloner) error {
if err := deepCopy_v1_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
}
if err := deepCopy_v1_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil {
return err
}
if in.Items != nil {
out.Items = make([]Job, len(in.Items))
for i := range in.Items {
if err := deepCopy_v1_Job(in.Items[i], &out.Items[i], c); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
func deepCopy_v1_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner) error {
if in.Parallelism != nil {
out.Parallelism = new(int)
*out.Parallelism = *in.Parallelism
} else {
out.Parallelism = nil
}
if in.Completions != nil {
out.Completions = new(int)
*out.Completions = *in.Completions
} else {
out.Completions = nil
}
if in.Selector != nil {
out.Selector = make(map[string]string)
for key, val := range in.Selector {
out.Selector[key] = val
}
} else {
out.Selector = nil
}
if in.Template != nil {
out.Template = new(v1.PodTemplateSpec)
if err := deepCopy_v1_PodTemplateSpec(*in.Template, out.Template, c); err != nil {
return err
}
} else {
out.Template = nil
}
return nil
}
func deepCopy_v1_JobStatus(in JobStatus, out *JobStatus, c *conversion.Cloner) error {
if in.Conditions != nil {
out.Conditions = make([]JobCondition, len(in.Conditions))
for i := range in.Conditions {
if err := deepCopy_v1_JobCondition(in.Conditions[i], &out.Conditions[i], c); err != nil {
return err
}
}
} else {
out.Conditions = nil
}
if in.StartTime != nil {
out.StartTime = new(util.Time)
if err := deepCopy_util_Time(*in.StartTime, out.StartTime, c); err != nil {
return err
}
} else {
out.StartTime = nil
}
if in.CompletionTime != nil {
out.CompletionTime = new(util.Time)
if err := deepCopy_util_Time(*in.CompletionTime, out.CompletionTime, c); err != nil {
return err
}
} else {
out.CompletionTime = nil
}
out.Active = in.Active
out.Successful = in.Successful
out.Unsuccessful = in.Unsuccessful
return nil
}
func deepCopy_v1_ReplicationControllerDummy(in ReplicationControllerDummy, out *ReplicationControllerDummy, c *conversion.Cloner) error {
if err := deepCopy_v1_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil {
return err
@ -1228,6 +1343,11 @@ func init() {
deepCopy_v1_HorizontalPodAutoscalerList,
deepCopy_v1_HorizontalPodAutoscalerSpec,
deepCopy_v1_HorizontalPodAutoscalerStatus,
deepCopy_v1_Job,
deepCopy_v1_JobCondition,
deepCopy_v1_JobList,
deepCopy_v1_JobSpec,
deepCopy_v1_JobStatus,
deepCopy_v1_ReplicationControllerDummy,
deepCopy_v1_ResourceConsumption,
deepCopy_v1_RollingUpdateDeployment,

View File

@ -36,6 +36,8 @@ func addKnownTypes() {
&DeploymentList{},
&HorizontalPodAutoscaler{},
&HorizontalPodAutoscalerList{},
&Job{},
&JobList{},
&ReplicationControllerDummy{},
&Scale{},
&ThirdPartyResource{},
@ -51,6 +53,8 @@ func (*Deployment) IsAnAPIObject() {}
func (*DeploymentList) IsAnAPIObject() {}
func (*HorizontalPodAutoscaler) IsAnAPIObject() {}
func (*HorizontalPodAutoscalerList) IsAnAPIObject() {}
func (*Job) IsAnAPIObject() {}
func (*JobList) IsAnAPIObject() {}
func (*ReplicationControllerDummy) IsAnAPIObject() {}
func (*Scale) IsAnAPIObject() {}
func (*ThirdPartyResource) IsAnAPIObject() {}

View File

@ -363,3 +363,102 @@ type ThirdPartyResourceDataList struct {
// Items is the list of ThirdpartyResourceData.
Items []ThirdPartyResourceData `json:"items"`
}
// Job represents the configuration of a single job.
type Job struct {
v1.TypeMeta `json:",inline"`
// Standard object's metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
v1.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 {
v1.TypeMeta `json:",inline"`
// Standard list metadata
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
v1.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. Defaults to 1.
Completions *int `json:"completions,omitempty"`
// Selector is a label query over pods that should match the pod count.
Selector map[string]string `json:"selector"`
// Template is the object that describes the pod that will be created when
// executing a job.
Template *v1.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 *util.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 *util.Time `json:"completionTime,omitempty"`
// Active is the number of actively running pods.
Active int `json:"active,omitempty"`
// Successful is the number of pods which reached Phase Succeeded.
Successful int `json:"successful,omitempty"`
// Unsuccessful is the number of pods failures, this applies only to jobs
// created with RestartPolicyNever, otherwise this value will always be 0.
Unsuccessful int `json:"unsuccessful,omitempty"`
}
type JobConditionType string
// These are valid conditions of a job.
const (
// JobComplete means the job has completed its execution.
JobComplete JobConditionType = "Complete"
)
// JobCondition describes current state of a job.
type JobCondition struct {
// Type of job condition, currently only Complete.
Type JobConditionType `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status v1.ConditionStatus `json:"status"`
// Last time the condition was checked.
LastProbeTime util.Time `json:"lastProbeTime,omitempty"`
// Last time the condition transit from one status to another.
LastTransitionTime util.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

@ -28,6 +28,8 @@ import (
"k8s.io/kubernetes/pkg/util/sets"
)
const isNegativeErrorMsg string = `must be non-negative`
// ValidateHorizontalPodAutoscaler can be used to check whether the given autoscaler name is valid.
// Prefix indicates this name will be used as part of generation, in which case trailing dashes are allowed.
func ValidateHorizontalPodAutoscalerName(name string, prefix bool) (bool, string) {
@ -268,3 +270,50 @@ func ValidateThirdPartyResourceData(obj *expapi.ThirdPartyResourceData) errs.Val
}
return allErrs
}
func ValidateJob(job *expapi.Job) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{}
// Jobs and rcs have the same name validation
allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&job.ObjectMeta, true, apivalidation.ValidateReplicationControllerName).Prefix("metadata")...)
allErrs = append(allErrs, ValidateJobSpec(&job.Spec).Prefix("spec")...)
return allErrs
}
func ValidateJobSpec(spec *expapi.JobSpec) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{}
if spec.Parallelism != nil && *spec.Parallelism < 0 {
allErrs = append(allErrs, errs.NewFieldInvalid("parallelism", spec.Parallelism, isNegativeErrorMsg))
}
if spec.Completions != nil && *spec.Completions < 0 {
allErrs = append(allErrs, errs.NewFieldInvalid("completions", spec.Completions, isNegativeErrorMsg))
}
selector := labels.Set(spec.Selector).AsSelector()
if selector.Empty() {
allErrs = append(allErrs, errs.NewFieldRequired("selector"))
}
if spec.Template == nil {
allErrs = append(allErrs, errs.NewFieldRequired("template"))
} else {
labels := labels.Set(spec.Template.Labels)
if !selector.Matches(labels) {
allErrs = append(allErrs, errs.NewFieldInvalid("template.labels", spec.Template.Labels, "selector does not match template"))
}
allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(spec.Template).Prefix("template")...)
if spec.Template.Spec.RestartPolicy != api.RestartPolicyOnFailure &&
spec.Template.Spec.RestartPolicy != api.RestartPolicyNever {
allErrs = append(allErrs, errs.NewFieldValueNotSupported("template.spec.restartPolicy",
spec.Template.Spec.RestartPolicy, []string{string(api.RestartPolicyOnFailure), string(api.RestartPolicyNever)}))
}
}
return allErrs
}
func ValidateJobUpdate(oldJob, job *expapi.Job) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{}
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&oldJob.ObjectMeta, &job.ObjectMeta).Prefix("metadata")...)
allErrs = append(allErrs, ValidateJobSpec(&job.Spec).Prefix("spec")...)
return allErrs
}

View File

@ -658,3 +658,129 @@ func TestValidateDeployment(t *testing.T) {
}
}
}
func TestValidateJob(t *testing.T) {
validSelector := map[string]string{"a": "b"}
validPodTemplateSpec := api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validSelector,
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
}
successCases := []expapi.Job{
{
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
},
Spec: expapi.JobSpec{
Selector: validSelector,
Template: &validPodTemplateSpec,
},
},
}
for _, successCase := range successCases {
if errs := ValidateJob(&successCase); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
negative := -1
errorCases := map[string]expapi.Job{
"spec.parallelism:must be non-negative": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
},
Spec: expapi.JobSpec{
Parallelism: &negative,
Selector: validSelector,
Template: &validPodTemplateSpec,
},
},
"spec.completions:must be non-negative": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
},
Spec: expapi.JobSpec{
Completions: &negative,
Selector: validSelector,
Template: &validPodTemplateSpec,
},
},
"spec.selector:required value": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
},
Spec: expapi.JobSpec{
Selector: map[string]string{},
Template: &validPodTemplateSpec,
},
},
"spec.template:required value": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
},
Spec: expapi.JobSpec{
Selector: validSelector,
},
},
"spec.template.labels:selector does not match template": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
},
Spec: expapi.JobSpec{
Selector: validSelector,
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.spec.restartPolicy:unsupported value": {
ObjectMeta: api.ObjectMeta{
Name: "myjob",
Namespace: api.NamespaceDefault,
},
Spec: expapi.JobSpec{
Selector: validSelector,
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: validSelector,
},
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].(*errors.ValidationError)
if err.Field != s[0] || !strings.Contains(err.Error(), s[1]) {
t.Errorf("unexpected error: %v, expected: %s", errs[0], k)
}
}
}
}

View File

@ -55,6 +55,7 @@ import (
endpointsetcd "k8s.io/kubernetes/pkg/registry/endpoint/etcd"
eventetcd "k8s.io/kubernetes/pkg/registry/event/etcd"
expcontrolleretcd "k8s.io/kubernetes/pkg/registry/experimental/controller/etcd"
jobetcd "k8s.io/kubernetes/pkg/registry/job/etcd"
limitrangeetcd "k8s.io/kubernetes/pkg/registry/limitrange/etcd"
"k8s.io/kubernetes/pkg/registry/namespace"
namespaceetcd "k8s.io/kubernetes/pkg/registry/namespace/etcd"
@ -827,6 +828,7 @@ func (m *Master) expapi(c *Config) *apiserver.APIGroupVersion {
thirdPartyResourceStorage := thirdpartyresourceetcd.NewREST(c.ExpDatabaseStorage)
daemonSetStorage := daemonetcd.NewREST(c.ExpDatabaseStorage)
deploymentStorage := deploymentetcd.NewREST(c.ExpDatabaseStorage)
jobStorage := jobetcd.NewREST(c.ExpDatabaseStorage)
storage := map[string]rest.Storage{
strings.ToLower("replicationControllers"): controllerStorage.ReplicationController,
@ -835,6 +837,7 @@ func (m *Master) expapi(c *Config) *apiserver.APIGroupVersion {
strings.ToLower("thirdpartyresources"): thirdPartyResourceStorage,
strings.ToLower("daemonsets"): daemonSetStorage,
strings.ToLower("deployments"): deploymentStorage,
strings.ToLower("jobs"): jobStorage,
}
return &apiserver.APIGroupVersion{

19
pkg/registry/job/doc.go Normal file
View File

@ -0,0 +1,19 @@
/*
Copyright 2015 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 controller provides Registry interface and it's RESTStorage
// implementation for storing Job api objects.
package job

View File

@ -0,0 +1,77 @@
/*
Copyright 2015 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 etcd
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
etcdgeneric "k8s.io/kubernetes/pkg/registry/generic/etcd"
"k8s.io/kubernetes/pkg/registry/job"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage"
)
// rest implements a RESTStorage for jobs against etcd
type REST struct {
*etcdgeneric.Etcd
}
// jobPrefix is the location for jobs in etcd, only exposed
// for testing
var jobPrefix = "/jobs"
// NewREST returns a RESTStorage object that will work against Jobs.
func NewREST(s storage.Interface) *REST {
store := &etcdgeneric.Etcd{
NewFunc: func() runtime.Object { return &expapi.Job{} },
// NewListFunc returns an object capable of storing results of an etcd list.
NewListFunc: func() runtime.Object { return &expapi.JobList{} },
// Produces a path that etcd understands, to the root of the resource
// by combining the namespace in the context with the given prefix
KeyRootFunc: func(ctx api.Context) string {
return etcdgeneric.NamespaceKeyRootFunc(ctx, jobPrefix)
},
// Produces a path that etcd understands, to the resource by combining
// the namespace in the context with the given prefix
KeyFunc: func(ctx api.Context, name string) (string, error) {
return etcdgeneric.NamespaceKeyFunc(ctx, jobPrefix, name)
},
// Retrieve the name field of a job
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*expapi.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)
},
EndpointName: "jobs",
// Used to validate job creation
CreateStrategy: job.Strategy,
// Used to validate job updates
UpdateStrategy: job.Strategy,
Storage: s,
}
return &REST{store}
}

View File

@ -0,0 +1,148 @@
/*
Copyright 2015 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 etcd
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/expapi"
// Ensure that expapi/v1 package is initialized.
_ "k8s.io/kubernetes/pkg/expapi/v1"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/tools"
)
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
etcdStorage, fakeClient := registrytest.NewEtcdStorage(t, "experimental")
return NewREST(etcdStorage), fakeClient
}
func validNewJob() *expapi.Job {
completions := 1
parallelism := 1
return &expapi.Job{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: "default",
},
Spec: expapi.JobSpec{
Completions: &completions,
Parallelism: &parallelism,
Selector: map[string]string{"a": "b"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"a": "b"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "test",
Image: "test_image",
ImagePullPolicy: api.PullIfNotPresent,
},
},
RestartPolicy: api.RestartPolicyOnFailure,
DNSPolicy: api.DNSClusterFirst,
},
},
},
}
}
func TestCreate(t *testing.T) {
storage, fakeClient := newStorage(t)
test := registrytest.New(t, fakeClient, storage.Etcd)
validJob := validNewJob()
validJob.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
validJob,
// invalid (empty selector)
&expapi.Job{
Spec: expapi.JobSpec{
Completions: validJob.Spec.Completions,
Selector: map[string]string{},
Template: validJob.Spec.Template,
},
},
)
}
func TestUpdate(t *testing.T) {
storage, fakeClient := newStorage(t)
test := registrytest.New(t, fakeClient, storage.Etcd)
completions := 2
test.TestUpdate(
// valid
validNewJob(),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*expapi.Job)
object.Spec.Completions = &completions
return object
},
// invalid updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*expapi.Job)
object.Spec.Selector = map[string]string{}
return object
},
)
}
func TestDelete(t *testing.T) {
storage, fakeClient := newStorage(t)
test := registrytest.New(t, fakeClient, storage.Etcd)
test.TestDelete(validNewJob())
}
func TestGet(t *testing.T) {
storage, fakeClient := newStorage(t)
test := registrytest.New(t, fakeClient, storage.Etcd)
test.TestGet(validNewJob())
}
func TestList(t *testing.T) {
storage, fakeClient := newStorage(t)
test := registrytest.New(t, fakeClient, storage.Etcd)
test.TestList(validNewJob())
}
func TestWatch(t *testing.T) {
storage, fakeClient := newStorage(t)
test := registrytest.New(t, fakeClient, storage.Etcd)
test.TestWatch(
validNewJob(),
// matching labels
[]labels.Set{},
// not matching labels
[]labels.Set{
{"x": "y"},
},
// matching fields
[]fields.Set{},
// not matching fields
[]fields.Set{
{"metadata.name": "xyz"},
{"name": "foo"},
},
)
}

View File

@ -0,0 +1,99 @@
/*
Copyright 2015 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 job
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/watch"
)
// Registry is an interface for things that know how to store Jobs.
type Registry interface {
// ListJobs obtains a list of Jobs having labels and fields which match selector.
ListJobs(ctx api.Context, label labels.Selector, field fields.Selector) (*expapi.JobList, error)
// WatchJobs watch for new/changed/deleted Jobs.
WatchJobs(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
// GetJobs gets a specific Job.
GetJob(ctx api.Context, name string) (*expapi.Job, error)
// CreateJob creates a Job based on a specification.
CreateJob(ctx api.Context, job *expapi.Job) (*expapi.Job, error)
// UpdateJob updates an existing Job.
UpdateJob(ctx api.Context, job *expapi.Job) (*expapi.Job, error)
// DeleteJob deletes an existing Job.
DeleteJob(ctx api.Context, name string) error
}
// storage puts strong typing around storage calls
type storage struct {
rest.StandardStorage
}
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
// types will panic.
func NewRegistry(s rest.StandardStorage) Registry {
return &storage{s}
}
func (s *storage) ListJobs(ctx api.Context, label labels.Selector, field fields.Selector) (*expapi.JobList, error) {
if !field.Empty() {
return nil, fmt.Errorf("field selector not supported yet")
}
obj, err := s.List(ctx, label, field)
if err != nil {
return nil, err
}
return obj.(*expapi.JobList), err
}
func (s *storage) WatchJobs(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
return s.Watch(ctx, label, field, resourceVersion)
}
func (s *storage) GetJob(ctx api.Context, name string) (*expapi.Job, error) {
obj, err := s.Get(ctx, name)
if err != nil {
return nil, err
}
return obj.(*expapi.Job), nil
}
func (s *storage) CreateJob(ctx api.Context, job *expapi.Job) (*expapi.Job, error) {
obj, err := s.Create(ctx, job)
if err != nil {
return nil, err
}
return obj.(*expapi.Job), nil
}
func (s *storage) UpdateJob(ctx api.Context, job *expapi.Job) (*expapi.Job, error) {
obj, _, err := s.Update(ctx, job)
if err != nil {
return nil, err
}
return obj.(*expapi.Job), nil
}
func (s *storage) DeleteJob(ctx api.Context, name string) error {
_, err := s.Delete(ctx, name, nil)
return err
}

View File

@ -0,0 +1,105 @@
/*
Copyright 2015 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 job
import (
"fmt"
"strconv"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/expapi"
"k8s.io/kubernetes/pkg/expapi/validation"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/fielderrors"
)
// jobStrategy implements verification logic for Replication Controllers.
type jobStrategy struct {
runtime.ObjectTyper
api.NameGenerator
}
// Strategy is the default logic that applies when creating and updating Replication Controller objects.
var Strategy = jobStrategy{api.Scheme, api.SimpleNameGenerator}
// NamespaceScoped returns true because all jobs need to be within a namespace.
func (jobStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears the status of a job before creation.
func (jobStrategy) PrepareForCreate(obj runtime.Object) {
job := obj.(*expapi.Job)
job.Status = expapi.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.(*expapi.Job)
oldJob := old.(*expapi.Job)
newJob.Status = oldJob.Status
}
// Validate validates a new job.
func (jobStrategy) Validate(ctx api.Context, obj runtime.Object) fielderrors.ValidationErrorList {
job := obj.(*expapi.Job)
return validation.ValidateJob(job)
}
func (jobStrategy) AllowUnconditionalUpdate() bool {
return true
}
// AllowCreateOnUpdate is false for jobs; this means a POST is needed to create one.
func (jobStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (jobStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
validationErrorList := validation.ValidateJob(obj.(*expapi.Job))
updateErrorList := validation.ValidateJobUpdate(old.(*expapi.Job), obj.(*expapi.Job))
return append(validationErrorList, updateErrorList...)
}
// JobSelectableFields returns a field set that represents the object for matching purposes.
func JobToSelectableFields(job *expapi.Job) fields.Set {
return fields.Set{
"metadata.name": job.Name,
"status.successful": strconv.Itoa(job.Status.Successful),
}
}
// MatchJob is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific
// labels/fields.
func MatchJob(label labels.Selector, field fields.Selector) generic.Matcher {
return &generic.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
job, ok := obj.(*expapi.Job)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a job.")
}
return labels.Set(job.ObjectMeta.Labels), JobToSelectableFields(job), nil
},
}
}