New Job resource

This commit is contained in:
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

@@ -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)
}
}
}
}