diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index 2d033e31c10..8264146562e 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -167,7 +167,7 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes util.StringSet) errs if len(mnt.Name) == 0 { mErrs = append(mErrs, errs.NewFieldRequired("name", mnt.Name)) } else if !volumes.Has(mnt.Name) { - mErrs = append(mErrs, errs.NewNotFound("name", mnt.Name)) + mErrs = append(mErrs, errs.NewFieldNotFound("name", mnt.Name)) } if len(mnt.MountPath) == 0 { mErrs = append(mErrs, errs.NewFieldRequired("mountPath", mnt.MountPath)) @@ -293,6 +293,7 @@ var supportedManifestVersions = util.NewStringSet("v1beta1", "v1beta2") // This includes checking formatting and uniqueness. It also canonicalizes the // structure by setting default values and implementing any backwards-compatibility // tricks. +// TODO: replaced by ValidatePodSpec func ValidateManifest(manifest *api.ContainerManifest) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} @@ -348,6 +349,21 @@ func ValidatePod(pod *api.Pod) errs.ValidationErrorList { return allErrs } +// ValidatePodSpec tests that the specified PodSpec has valid data. +// This includes checking formatting and uniqueness. It also canonicalizes the +// structure by setting default values and implementing any backwards-compatibility +// tricks. +func ValidatePodSpec(spec *api.PodSpec) errs.ValidationErrorList { + allErrs := errs.ValidationErrorList{} + + allVolumes, vErrs := validateVolumes(spec.Volumes) + allErrs = append(allErrs, vErrs.Prefix("volumes")...) + allErrs = append(allErrs, validateContainers(spec.Containers, allVolumes).Prefix("containers")...) + allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy).Prefix("restartPolicy")...) + allErrs = append(allErrs, validateLabels(spec.NodeSelector).Prefix("nodeSelector")...) + return allErrs +} + func validateLabels(labels map[string]string) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} for k := range labels { @@ -435,30 +451,44 @@ func ValidateReplicationController(controller *api.ReplicationController) errs.V if !util.IsDNSSubdomain(controller.Namespace) { allErrs = append(allErrs, errs.NewFieldInvalid("namespace", controller.Namespace)) } - allErrs = append(allErrs, ValidateReplicationControllerState(&controller.DesiredState).Prefix("desiredState")...) + allErrs = append(allErrs, ValidateReplicationControllerSpec(&controller.Spec).Prefix("spec")...) allErrs = append(allErrs, validateLabels(controller.Labels)...) return allErrs } -// ValidateReplicationControllerState tests if required fields in the replication controller state are set. -func ValidateReplicationControllerState(state *api.ReplicationControllerState) errs.ValidationErrorList { +// ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set. +func ValidateReplicationControllerSpec(spec *api.ReplicationControllerSpec) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} - if labels.Set(state.ReplicaSelector).AsSelector().Empty() { - allErrs = append(allErrs, errs.NewFieldRequired("replicaSelector", state.ReplicaSelector)) + + selector := labels.Set(spec.Selector).AsSelector() + if selector.Empty() { + allErrs = append(allErrs, errs.NewFieldRequired("selector", spec.Selector)) } - selector := labels.Set(state.ReplicaSelector).AsSelector() - labels := labels.Set(state.PodTemplate.Labels) - if !selector.Matches(labels) { - allErrs = append(allErrs, errs.NewFieldInvalid("podTemplate.labels", state.PodTemplate)) + if spec.Replicas < 0 { + allErrs = append(allErrs, errs.NewFieldInvalid("replicas", spec.Replicas)) } - allErrs = append(allErrs, validateLabels(labels)...) - if state.Replicas < 0 { - allErrs = append(allErrs, errs.NewFieldInvalid("replicas", state.Replicas)) + + if spec.Template == nil { + allErrs = append(allErrs, errs.NewFieldRequired("template", spec.Template)) + } else { + labels := labels.Set(spec.Template.Labels) + if !selector.Matches(labels) { + allErrs = append(allErrs, errs.NewFieldInvalid("template.labels", spec.Template.Labels)) + } + allErrs = append(allErrs, validateLabels(spec.Template.Labels).Prefix("template.labels")...) + allErrs = append(allErrs, ValidatePodTemplateSpec(spec.Template).Prefix("template")...) } - allErrs = append(allErrs, ValidateManifest(&state.PodTemplate.DesiredState.Manifest).Prefix("podTemplate.desiredState.manifest")...) - allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(state.PodTemplate.DesiredState.Manifest.Volumes).Prefix("podTemplate.desiredState.manifest")...) return allErrs } + +// ValidatePodTemplateSpec validates the spec of a pod template +func ValidatePodTemplateSpec(spec *api.PodTemplateSpec) errs.ValidationErrorList { + allErrs := errs.ValidationErrorList{} + allErrs = append(allErrs, ValidatePodSpec(&spec.Spec).Prefix("spec")...) + allErrs = append(allErrs, ValidateReadOnlyPersistentDisks(spec.Spec.Volumes).Prefix("spec.volumes")...) + return allErrs +} + func ValidateReadOnlyPersistentDisks(volumes []api.Volume) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} for _, vol := range volumes { diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index a82a05ca32c..e1d08c37b4a 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -862,43 +862,41 @@ func TestValidateService(t *testing.T) { func TestValidateReplicationController(t *testing.T) { validSelector := map[string]string{"a": "b"} validPodTemplate := api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Version: "v1beta1", + Spec: api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: validSelector, }, }, - Labels: validSelector, } invalidVolumePodTemplate := api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Version: "v1beta1", + Spec: api.PodTemplateSpec{ + Spec: api.PodSpec{ Volumes: []api.Volume{{Name: "gcepd", Source: &api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}}, }, }, } invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} invalidPodTemplate := api.PodTemplate{ - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Version: "v1beta1", + Spec: api.PodTemplateSpec{ + Spec: api.PodSpec{}, + ObjectMeta: api.ObjectMeta{ + Labels: invalidSelector, }, }, - Labels: invalidSelector, } successCases := []api.ReplicationController{ { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, { ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, } @@ -911,49 +909,49 @@ func TestValidateReplicationController(t *testing.T) { errorCases := map[string]api.ReplicationController{ "zero-length ID": { ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, "missing-namespace": { ObjectMeta: api.ObjectMeta{Name: "abc-123"}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, "empty selector": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Template: &validPodTemplate.Spec, }, }, "selector_doesnt_match": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: map[string]string{"foo": "bar"}, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: map[string]string{"foo": "bar"}, + Template: &validPodTemplate.Spec, }, }, "invalid manifest": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, }, }, "read-write presistent disk": { ObjectMeta: api.ObjectMeta{Name: "abc"}, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: invalidVolumePodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &invalidVolumePodTemplate.Spec, }, }, "negative_replicas": { ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, - DesiredState: api.ReplicationControllerState{ - Replicas: -1, - ReplicaSelector: validSelector, + Spec: api.ReplicationControllerSpec{ + Replicas: -1, + Selector: validSelector, }, }, "invalid_label": { @@ -964,9 +962,9 @@ func TestValidateReplicationController(t *testing.T) { "NoUppercaseOrSpecialCharsLike=Equals": "bar", }, }, - DesiredState: api.ReplicationControllerState{ - ReplicaSelector: validSelector, - PodTemplate: validPodTemplate, + Spec: api.ReplicationControllerSpec{ + Selector: validSelector, + Template: &validPodTemplate.Spec, }, }, "invalid_label 2": { @@ -977,8 +975,8 @@ func TestValidateReplicationController(t *testing.T) { "NoUppercaseOrSpecialCharsLike=Equals": "bar", }, }, - DesiredState: api.ReplicationControllerState{ - PodTemplate: invalidPodTemplate, + Spec: api.ReplicationControllerSpec{ + Template: &invalidPodTemplate.Spec, }, }, } @@ -989,13 +987,14 @@ func TestValidateReplicationController(t *testing.T) { } for i := range errs { field := errs[i].(errors.ValidationError).Field - if !strings.HasPrefix(field, "desiredState.podTemplate.") && + if !strings.HasPrefix(field, "spec.template.") && field != "name" && field != "namespace" && - field != "desiredState.replicaSelector" && + field != "spec.selector" && + field != "spec.template" && field != "GCEPersistentDisk.ReadOnly" && - field != "desiredState.replicas" && - field != "desiredState.label" && + field != "spec.replicas" && + field != "spec.template.label" && field != "label" { t.Errorf("%s: missing prefix for: %v", k, errs[i]) }