diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index 3f35be6e07d..875e2702954 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -6266,7 +6266,7 @@ func ValidateReplicationControllerSpec(spec, oldSpec *core.ReplicationController allErrs := field.ErrorList{} allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...) allErrs = append(allErrs, ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...) - allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...) + allErrs = append(allErrs, ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas")).WithOrigin("minimum")...) allErrs = append(allErrs, ValidatePodTemplateSpecForRC(spec.Template, spec.Selector, spec.Replicas, fldPath.Child("template"), opts)...) return allErrs } diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index 4c6e0f64425..f269cbaf96b 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -16791,144 +16791,179 @@ func TestValidateReplicationController(t *testing.T) { } } - errorCases := map[string]core.ReplicationController{ + errorCases := map[string]struct { + rc core.ReplicationController + expectedOrigin []string + }{ "zero-length ID": { - ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault}, - Spec: core.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault}, + Spec: core.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Template, + }, }, }, "missing-namespace": { - ObjectMeta: metav1.ObjectMeta{Name: "abc-123"}, - Spec: core.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{Name: "abc-123"}, + Spec: core.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Template, + }, }, }, "empty selector": { - ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, - Spec: core.ReplicationControllerSpec{ - Template: &validPodTemplate.Template, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: core.ReplicationControllerSpec{ + Template: &validPodTemplate.Template, + }, }, }, "selector_doesnt_match": { - ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, - Spec: core.ReplicationControllerSpec{ - Selector: map[string]string{"foo": "bar"}, - Template: &validPodTemplate.Template, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: core.ReplicationControllerSpec{ + Selector: map[string]string{"foo": "bar"}, + Template: &validPodTemplate.Template, + }, }, }, "invalid manifest": { - ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, - Spec: core.ReplicationControllerSpec{ - Selector: validSelector, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: core.ReplicationControllerSpec{ + Selector: validSelector, + }, }, }, "read-write persistent disk with > 1 pod": { - ObjectMeta: metav1.ObjectMeta{Name: "abc"}, - Spec: core.ReplicationControllerSpec{ - Replicas: 2, - Selector: validSelector, - Template: &readWriteVolumePodTemplate.Template, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{Name: "abc"}, + Spec: core.ReplicationControllerSpec{ + Replicas: 2, + Selector: validSelector, + Template: &readWriteVolumePodTemplate.Template, + }, }, }, "negative_replicas": { - ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, - Spec: core.ReplicationControllerSpec{ - Replicas: -1, - Selector: validSelector, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, + Spec: core.ReplicationControllerSpec{ + Replicas: -1, + Selector: validSelector, + }, + }, + expectedOrigin: []string{ + "minimum", }, }, "invalid_label": { - ObjectMeta: metav1.ObjectMeta{ - Name: "abc-123", - Namespace: metav1.NamespaceDefault, - Labels: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{ + Name: "abc-123", + Namespace: metav1.NamespaceDefault, + Labels: map[string]string{ + "NoUppercaseOrSpecialCharsLike=Equals": "bar", + }, + }, + Spec: core.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Template, }, - }, - Spec: core.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, }, }, "invalid_label 2": { - ObjectMeta: metav1.ObjectMeta{ - Name: "abc-123", - Namespace: metav1.NamespaceDefault, - Labels: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{ + Name: "abc-123", + Namespace: metav1.NamespaceDefault, + Labels: map[string]string{ + "NoUppercaseOrSpecialCharsLike=Equals": "bar", + }, + }, + Spec: core.ReplicationControllerSpec{ + Template: &invalidPodTemplate.Template, }, - }, - Spec: core.ReplicationControllerSpec{ - Template: &invalidPodTemplate.Template, }, }, "invalid_annotation": { - ObjectMeta: metav1.ObjectMeta{ - Name: "abc-123", - Namespace: metav1.NamespaceDefault, - Annotations: map[string]string{ - "NoUppercaseOrSpecialCharsLike=Equals": "bar", + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{ + Name: "abc-123", + Namespace: metav1.NamespaceDefault, + Annotations: map[string]string{ + "NoUppercaseOrSpecialCharsLike=Equals": "bar", + }, + }, + Spec: core.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Template, }, - }, - Spec: core.ReplicationControllerSpec{ - Selector: validSelector, - Template: &validPodTemplate.Template, }, }, "invalid restart policy 1": { - ObjectMeta: metav1.ObjectMeta{ - Name: "abc-123", - Namespace: metav1.NamespaceDefault, - }, - Spec: core.ReplicationControllerSpec{ - Selector: validSelector, - Template: &core.PodTemplateSpec{ - Spec: podtest.MakePodSpec(podtest.SetRestartPolicy(core.RestartPolicyOnFailure)), - ObjectMeta: metav1.ObjectMeta{ - Labels: validSelector, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{ + Name: "abc-123", + Namespace: metav1.NamespaceDefault, + }, + Spec: core.ReplicationControllerSpec{ + Selector: validSelector, + Template: &core.PodTemplateSpec{ + Spec: podtest.MakePodSpec(podtest.SetRestartPolicy(core.RestartPolicyOnFailure)), + ObjectMeta: metav1.ObjectMeta{ + Labels: validSelector, + }, }, }, }, }, "invalid restart policy 2": { - ObjectMeta: metav1.ObjectMeta{ - Name: "abc-123", - Namespace: metav1.NamespaceDefault, - }, - Spec: core.ReplicationControllerSpec{ - Selector: validSelector, - Template: &core.PodTemplateSpec{ - Spec: podtest.MakePodSpec(podtest.SetRestartPolicy(core.RestartPolicyNever)), - ObjectMeta: metav1.ObjectMeta{ - Labels: validSelector, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{ + Name: "abc-123", + Namespace: metav1.NamespaceDefault, + }, + Spec: core.ReplicationControllerSpec{ + Selector: validSelector, + Template: &core.PodTemplateSpec{ + Spec: podtest.MakePodSpec(podtest.SetRestartPolicy(core.RestartPolicyNever)), + ObjectMeta: metav1.ObjectMeta{ + Labels: validSelector, + }, }, }, }, }, "template may not contain ephemeral containers": { - ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault}, - Spec: core.ReplicationControllerSpec{ - Replicas: 1, - Selector: validSelector, - Template: &core.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: validSelector, + rc: core.ReplicationController{ + ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault}, + Spec: core.ReplicationControllerSpec{ + Replicas: 1, + Selector: validSelector, + Template: &core.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: validSelector, + }, + Spec: podtest.MakePodSpec( + podtest.SetEphemeralContainers(core.EphemeralContainer{EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}}), + ), }, - Spec: podtest.MakePodSpec( - podtest.SetEphemeralContainers(core.EphemeralContainer{EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}}), - ), }, }, }, } for k, v := range errorCases { - errs := ValidateReplicationController(&v, PodValidationOptions{}) + errs := ValidateReplicationController(&v.rc, PodValidationOptions{}) if len(errs) == 0 { t.Errorf("expected failure for %s", k) } + + expectedOrigins := sets.NewString(v.expectedOrigin...) + for i := range errs { field := errs[i].Field if !strings.HasPrefix(field, "spec.template.") && @@ -16944,6 +16979,16 @@ func TestValidateReplicationController(t *testing.T) { field != "status.replicas" { t.Errorf("%s: missing prefix for: %v", k, errs[i]) } + + if len(v.expectedOrigin) > 0 && errs[i].Origin != "" { + if !expectedOrigins.Has(errs[i].Origin) { + t.Errorf("%s: unexpected origin for: %v, expected one of %v", k, errs[i].Origin, v.expectedOrigin) + } + expectedOrigins.Delete(errs[i].Origin) + } + } + if len(expectedOrigins) > 0 { + t.Errorf("%s: missing errors with origin: %v", k, expectedOrigins.List()) } } }