Update the validation logic to test PodTemplateSpec

This commit is contained in:
Clayton Coleman 2014-11-06 21:08:46 -05:00
parent a68a493e4b
commit 72bf12c86d
2 changed files with 89 additions and 60 deletions

View File

@ -167,7 +167,7 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes util.StringSet) errs
if len(mnt.Name) == 0 { if len(mnt.Name) == 0 {
mErrs = append(mErrs, errs.NewFieldRequired("name", mnt.Name)) mErrs = append(mErrs, errs.NewFieldRequired("name", mnt.Name))
} else if !volumes.Has(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 { if len(mnt.MountPath) == 0 {
mErrs = append(mErrs, errs.NewFieldRequired("mountPath", mnt.MountPath)) 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 // This includes checking formatting and uniqueness. It also canonicalizes the
// structure by setting default values and implementing any backwards-compatibility // structure by setting default values and implementing any backwards-compatibility
// tricks. // tricks.
// TODO: replaced by ValidatePodSpec
func ValidateManifest(manifest *api.ContainerManifest) errs.ValidationErrorList { func ValidateManifest(manifest *api.ContainerManifest) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{} allErrs := errs.ValidationErrorList{}
@ -348,6 +349,21 @@ func ValidatePod(pod *api.Pod) errs.ValidationErrorList {
return allErrs 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 { func validateLabels(labels map[string]string) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{} allErrs := errs.ValidationErrorList{}
for k := range labels { for k := range labels {
@ -435,30 +451,44 @@ func ValidateReplicationController(controller *api.ReplicationController) errs.V
if !util.IsDNSSubdomain(controller.Namespace) { if !util.IsDNSSubdomain(controller.Namespace) {
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", 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)...) allErrs = append(allErrs, validateLabels(controller.Labels)...)
return allErrs return allErrs
} }
// ValidateReplicationControllerState tests if required fields in the replication controller state are set. // ValidateReplicationControllerSpec tests if required fields in the replication controller spec are set.
func ValidateReplicationControllerState(state *api.ReplicationControllerState) errs.ValidationErrorList { func ValidateReplicationControllerSpec(spec *api.ReplicationControllerSpec) errs.ValidationErrorList {
allErrs := 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() if spec.Replicas < 0 {
labels := labels.Set(state.PodTemplate.Labels) allErrs = append(allErrs, errs.NewFieldInvalid("replicas", spec.Replicas))
if !selector.Matches(labels) {
allErrs = append(allErrs, errs.NewFieldInvalid("podTemplate.labels", state.PodTemplate))
} }
allErrs = append(allErrs, validateLabels(labels)...)
if state.Replicas < 0 { if spec.Template == nil {
allErrs = append(allErrs, errs.NewFieldInvalid("replicas", state.Replicas)) 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 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 { func ValidateReadOnlyPersistentDisks(volumes []api.Volume) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{} allErrs := errs.ValidationErrorList{}
for _, vol := range volumes { for _, vol := range volumes {

View File

@ -862,43 +862,41 @@ func TestValidateService(t *testing.T) {
func TestValidateReplicationController(t *testing.T) { func TestValidateReplicationController(t *testing.T) {
validSelector := map[string]string{"a": "b"} validSelector := map[string]string{"a": "b"}
validPodTemplate := api.PodTemplate{ validPodTemplate := api.PodTemplate{
DesiredState: api.PodState{ Spec: api.PodTemplateSpec{
Manifest: api.ContainerManifest{ ObjectMeta: api.ObjectMeta{
Version: "v1beta1", Labels: validSelector,
}, },
}, },
Labels: validSelector,
} }
invalidVolumePodTemplate := api.PodTemplate{ invalidVolumePodTemplate := api.PodTemplate{
DesiredState: api.PodState{ Spec: api.PodTemplateSpec{
Manifest: api.ContainerManifest{ Spec: api.PodSpec{
Version: "v1beta1",
Volumes: []api.Volume{{Name: "gcepd", Source: &api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}}, Volumes: []api.Volume{{Name: "gcepd", Source: &api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDisk{"my-PD", "ext4", 1, false}}}},
}, },
}, },
} }
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"} invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
invalidPodTemplate := api.PodTemplate{ invalidPodTemplate := api.PodTemplate{
DesiredState: api.PodState{ Spec: api.PodTemplateSpec{
Manifest: api.ContainerManifest{ Spec: api.PodSpec{},
Version: "v1beta1", ObjectMeta: api.ObjectMeta{
Labels: invalidSelector,
}, },
}, },
Labels: invalidSelector,
} }
successCases := []api.ReplicationController{ successCases := []api.ReplicationController{
{ {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
{ {
ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
} }
@ -911,49 +909,49 @@ func TestValidateReplicationController(t *testing.T) {
errorCases := map[string]api.ReplicationController{ errorCases := map[string]api.ReplicationController{
"zero-length ID": { "zero-length ID": {
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"missing-namespace": { "missing-namespace": {
ObjectMeta: api.ObjectMeta{Name: "abc-123"}, ObjectMeta: api.ObjectMeta{Name: "abc-123"},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"empty selector": { "empty selector": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"selector_doesnt_match": { "selector_doesnt_match": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: map[string]string{"foo": "bar"}, Selector: map[string]string{"foo": "bar"},
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"invalid manifest": { "invalid manifest": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
}, },
}, },
"read-write presistent disk": { "read-write presistent disk": {
ObjectMeta: api.ObjectMeta{Name: "abc"}, ObjectMeta: api.ObjectMeta{Name: "abc"},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: invalidVolumePodTemplate, Template: &invalidVolumePodTemplate.Spec,
}, },
}, },
"negative_replicas": { "negative_replicas": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault}, ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
Replicas: -1, Replicas: -1,
ReplicaSelector: validSelector, Selector: validSelector,
}, },
}, },
"invalid_label": { "invalid_label": {
@ -964,9 +962,9 @@ func TestValidateReplicationController(t *testing.T) {
"NoUppercaseOrSpecialCharsLike=Equals": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar",
}, },
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
ReplicaSelector: validSelector, Selector: validSelector,
PodTemplate: validPodTemplate, Template: &validPodTemplate.Spec,
}, },
}, },
"invalid_label 2": { "invalid_label 2": {
@ -977,8 +975,8 @@ func TestValidateReplicationController(t *testing.T) {
"NoUppercaseOrSpecialCharsLike=Equals": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar",
}, },
}, },
DesiredState: api.ReplicationControllerState{ Spec: api.ReplicationControllerSpec{
PodTemplate: invalidPodTemplate, Template: &invalidPodTemplate.Spec,
}, },
}, },
} }
@ -989,13 +987,14 @@ func TestValidateReplicationController(t *testing.T) {
} }
for i := range errs { for i := range errs {
field := errs[i].(errors.ValidationError).Field field := errs[i].(errors.ValidationError).Field
if !strings.HasPrefix(field, "desiredState.podTemplate.") && if !strings.HasPrefix(field, "spec.template.") &&
field != "name" && field != "name" &&
field != "namespace" && field != "namespace" &&
field != "desiredState.replicaSelector" && field != "spec.selector" &&
field != "spec.template" &&
field != "GCEPersistentDisk.ReadOnly" && field != "GCEPersistentDisk.ReadOnly" &&
field != "desiredState.replicas" && field != "spec.replicas" &&
field != "desiredState.label" && field != "spec.template.label" &&
field != "label" { field != "label" {
t.Errorf("%s: missing prefix for: %v", k, errs[i]) t.Errorf("%s: missing prefix for: %v", k, errs[i])
} }