Convert validation to use FieldPath

Before this change we have a mish-mash of ways to pass field names around for
error generation.  Sometimes string fieldnames, sometimes .Prefix(), sometimes
neither, often wrong names or not indexed when it should be.

Instead of that mess, this is part one of a couple of commits that will make it
more strongly typed and hopefully encourage correct behavior.  At least you
will have to think about field names, which is better than nothing.

It turned out to be really hard to do this incrementally.
This commit is contained in:
Tim Hockin
2015-11-04 13:52:14 -08:00
parent 102eced0b1
commit e6df0b1a24
19 changed files with 986 additions and 807 deletions

View File

@@ -43,12 +43,16 @@ func expectPrefix(t *testing.T, prefix string, errs validation.ErrorList) {
// Ensure custom name functions are allowed
func TestValidateObjectMetaCustomName(t *testing.T) {
errs := ValidateObjectMeta(&api.ObjectMeta{Name: "test", GenerateName: "foo"}, false, func(s string, prefix bool) (bool, string) {
if s == "test" {
return true, ""
}
return false, "name-gen"
})
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", GenerateName: "foo"},
false,
func(s string, prefix bool) (bool, string) {
if s == "test" {
return true, ""
}
return false, "name-gen"
},
validation.NewFieldPath("field"))
if len(errs) != 1 {
t.Fatalf("unexpected errors: %v", errs)
}
@@ -59,9 +63,13 @@ func TestValidateObjectMetaCustomName(t *testing.T) {
// Ensure namespace names follow dns label format
func TestValidateObjectMetaNamespaces(t *testing.T) {
errs := ValidateObjectMeta(&api.ObjectMeta{Name: "test", Namespace: "foo.bar"}, false, func(s string, prefix bool) (bool, string) {
return true, ""
})
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", Namespace: "foo.bar"},
false,
func(s string, prefix bool) (bool, string) {
return true, ""
},
validation.NewFieldPath("field"))
if len(errs) != 1 {
t.Fatalf("unexpected errors: %v", errs)
}
@@ -74,9 +82,13 @@ func TestValidateObjectMetaNamespaces(t *testing.T) {
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
errs = ValidateObjectMeta(&api.ObjectMeta{Name: "test", Namespace: string(b)}, false, func(s string, prefix bool) (bool, string) {
return true, ""
})
errs = ValidateObjectMeta(
&api.ObjectMeta{Name: "test", Namespace: string(b)},
false,
func(s string, prefix bool) (bool, string) {
return true, ""
},
validation.NewFieldPath("field"))
if len(errs) != 1 {
t.Fatalf("unexpected errors: %v", errs)
}
@@ -89,18 +101,21 @@ func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) {
if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1"},
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: unversioned.NewTime(time.Unix(10, 0))},
validation.NewFieldPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: unversioned.NewTime(time.Unix(10, 0))},
&api.ObjectMeta{Name: "test", ResourceVersion: "1"},
validation.NewFieldPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: unversioned.NewTime(time.Unix(10, 0))},
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: unversioned.NewTime(time.Unix(11, 0))},
validation.NewFieldPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
@@ -108,7 +123,11 @@ func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) {
// Ensure trailing slash is allowed in generate name
func TestValidateObjectMetaTrimsTrailingSlash(t *testing.T) {
errs := ValidateObjectMeta(&api.ObjectMeta{Name: "test", GenerateName: "foo-"}, false, NameIsDNSSubdomain)
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", GenerateName: "foo-"},
false,
NameIsDNSSubdomain,
validation.NewFieldPath("field"))
if len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
@@ -132,7 +151,7 @@ func TestValidateLabels(t *testing.T) {
{"goodvalue": "123_-.BaR"},
}
for i := range successCases {
errs := ValidateLabels(successCases[i], "field")
errs := ValidateLabels(successCases[i], validation.NewFieldPath("field"))
if len(errs) != 0 {
t.Errorf("case[%d] expected success, got %#v", i, errs)
}
@@ -145,7 +164,7 @@ func TestValidateLabels(t *testing.T) {
{strings.Repeat("a", 254): "bar"},
}
for i := range labelNameErrorCases {
errs := ValidateLabels(labelNameErrorCases[i], "field")
errs := ValidateLabels(labelNameErrorCases[i], validation.NewFieldPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d] expected failure", i)
} else {
@@ -163,7 +182,7 @@ func TestValidateLabels(t *testing.T) {
{"strangecharsinvalue": "?#$notsogood"},
}
for i := range labelValueErrorCases {
errs := ValidateLabels(labelValueErrorCases[i], "field")
errs := ValidateLabels(labelValueErrorCases[i], validation.NewFieldPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d] expected failure", i)
} else {
@@ -197,7 +216,7 @@ func TestValidateAnnotations(t *testing.T) {
},
}
for i := range successCases {
errs := ValidateAnnotations(successCases[i], "field")
errs := ValidateAnnotations(successCases[i], validation.NewFieldPath("field"))
if len(errs) != 0 {
t.Errorf("case[%d] expected success, got %#v", i, errs)
}
@@ -210,7 +229,7 @@ func TestValidateAnnotations(t *testing.T) {
{strings.Repeat("a", 254): "bar"},
}
for i := range nameErrorCases {
errs := ValidateAnnotations(nameErrorCases[i], "field")
errs := ValidateAnnotations(nameErrorCases[i], validation.NewFieldPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d] expected failure", i)
}
@@ -227,7 +246,7 @@ func TestValidateAnnotations(t *testing.T) {
},
}
for i := range totalSizeErrorCases {
errs := ValidateAnnotations(totalSizeErrorCases[i], "field")
errs := ValidateAnnotations(totalSizeErrorCases[i], validation.NewFieldPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d] expected failure", i)
}
@@ -490,7 +509,7 @@ func TestValidateVolumes(t *testing.T) {
}}}},
{Name: "fc", VolumeSource: api.VolumeSource{FC: &api.FCVolumeSource{[]string{"some_wwn"}, &lun, "ext4", false}}},
}
names, errs := validateVolumes(successCase)
names, errs := validateVolumes(successCase, validation.NewFieldPath("field"))
if len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
@@ -543,46 +562,137 @@ func TestValidateVolumes(t *testing.T) {
F string
D string
}{
"zero-length name": {[]api.Volume{{Name: "", VolumeSource: emptyVS}}, validation.ErrorTypeRequired, "[0].name", ""},
"name > 63 characters": {[]api.Volume{{Name: strings.Repeat("a", 64), VolumeSource: emptyVS}}, validation.ErrorTypeInvalid, "[0].name", "must be a DNS label (at most 63 characters, matching regex [a-z0-9]([-a-z0-9]*[a-z0-9])?): e.g. \"my-name\""},
"name not a DNS label": {[]api.Volume{{Name: "a.b.c", VolumeSource: emptyVS}}, validation.ErrorTypeInvalid, "[0].name", "must be a DNS label (at most 63 characters, matching regex [a-z0-9]([-a-z0-9]*[a-z0-9])?): e.g. \"my-name\""},
"name not unique": {[]api.Volume{{Name: "abc", VolumeSource: emptyVS}, {Name: "abc", VolumeSource: emptyVS}}, validation.ErrorTypeDuplicate, "[1].name", ""},
"empty portal": {[]api.Volume{{Name: "badportal", VolumeSource: emptyPortal}}, validation.ErrorTypeRequired, "[0].source.iscsi.targetPortal", ""},
"empty iqn": {[]api.Volume{{Name: "badiqn", VolumeSource: emptyIQN}}, validation.ErrorTypeRequired, "[0].source.iscsi.iqn", ""},
"empty hosts": {[]api.Volume{{Name: "badhost", VolumeSource: emptyHosts}}, validation.ErrorTypeRequired, "[0].source.glusterfs.endpoints", ""},
"empty path": {[]api.Volume{{Name: "badpath", VolumeSource: emptyPath}}, validation.ErrorTypeRequired, "[0].source.glusterfs.path", ""},
"empty datasetName": {[]api.Volume{{Name: "badname", VolumeSource: emptyName}}, validation.ErrorTypeRequired, "[0].source.flocker.datasetName", ""},
"empty mon": {[]api.Volume{{Name: "badmon", VolumeSource: emptyMon}}, validation.ErrorTypeRequired, "[0].source.rbd.monitors", ""},
"empty image": {[]api.Volume{{Name: "badimage", VolumeSource: emptyImage}}, validation.ErrorTypeRequired, "[0].source.rbd.image", ""},
"empty cephfs mon": {[]api.Volume{{Name: "badmon", VolumeSource: emptyCephFSMon}}, validation.ErrorTypeRequired, "[0].source.cephfs.monitors", ""},
"empty metatada path": {[]api.Volume{{Name: "emptyname", VolumeSource: emptyPathName}}, validation.ErrorTypeRequired, "[0].source.downwardApi.path", ""},
"absolute path": {[]api.Volume{{Name: "absolutepath", VolumeSource: absolutePathName}}, validation.ErrorTypeForbidden, "[0].source.downwardApi.path", ""},
"dot dot path": {[]api.Volume{{Name: "dotdotpath", VolumeSource: dotDotInPath}}, validation.ErrorTypeInvalid, "[0].source.downwardApi.path", "must not contain \"..\""},
"dot dot file name": {[]api.Volume{{Name: "dotdotfilename", VolumeSource: dotDotPathName}}, validation.ErrorTypeInvalid, "[0].source.downwardApi.path", "must not start with \"..\""},
"dot dot first level dirent": {[]api.Volume{{Name: "dotdotdirfilename", VolumeSource: dotDotFirstLevelDirent}}, validation.ErrorTypeInvalid, "[0].source.downwardApi.path", "must not start with \"..\""},
"empty wwn": {[]api.Volume{{Name: "badimage", VolumeSource: zeroWWN}}, validation.ErrorTypeRequired, "[0].source.fc.targetWWNs", ""},
"empty lun": {[]api.Volume{{Name: "badimage", VolumeSource: emptyLun}}, validation.ErrorTypeRequired, "[0].source.fc.lun", ""},
"slash in datasetName": {[]api.Volume{{Name: "slashinname", VolumeSource: slashInName}}, validation.ErrorTypeInvalid, "[0].source.flocker.datasetName", "must not contain '/'"},
"starts with '..'": {[]api.Volume{{Name: "badprefix", VolumeSource: startsWithDots}}, validation.ErrorTypeInvalid, "[0].source.gitRepo.directory", "must not start with \"..\""},
"contains '..'": {[]api.Volume{{Name: "containsdots", VolumeSource: containsDots}}, validation.ErrorTypeInvalid, "[0].source.gitRepo.directory", "must not contain \"..\""},
"absolute target": {[]api.Volume{{Name: "absolutetarget", VolumeSource: absPath}}, validation.ErrorTypeForbidden, "[0].source.gitRepo.directory", ""},
"zero-length name": {
[]api.Volume{{Name: "", VolumeSource: emptyVS}},
validation.ErrorTypeRequired,
"name", "",
},
"name > 63 characters": {
[]api.Volume{{Name: strings.Repeat("a", 64), VolumeSource: emptyVS}},
validation.ErrorTypeInvalid,
"name", "must be a DNS label",
},
"name not a DNS label": {
[]api.Volume{{Name: "a.b.c", VolumeSource: emptyVS}},
validation.ErrorTypeInvalid,
"name", "must be a DNS label",
},
"name not unique": {
[]api.Volume{{Name: "abc", VolumeSource: emptyVS}, {Name: "abc", VolumeSource: emptyVS}},
validation.ErrorTypeDuplicate,
"[1].name", "",
},
"empty portal": {
[]api.Volume{{Name: "badportal", VolumeSource: emptyPortal}},
validation.ErrorTypeRequired,
"iscsi.targetPortal", "",
},
"empty iqn": {
[]api.Volume{{Name: "badiqn", VolumeSource: emptyIQN}},
validation.ErrorTypeRequired,
"iscsi.iqn", "",
},
"empty hosts": {
[]api.Volume{{Name: "badhost", VolumeSource: emptyHosts}},
validation.ErrorTypeRequired,
"glusterfs.endpoints", "",
},
"empty path": {
[]api.Volume{{Name: "badpath", VolumeSource: emptyPath}},
validation.ErrorTypeRequired,
"glusterfs.path", "",
},
"empty datasetName": {
[]api.Volume{{Name: "badname", VolumeSource: emptyName}},
validation.ErrorTypeRequired,
"flocker.datasetName", "",
},
"empty mon": {
[]api.Volume{{Name: "badmon", VolumeSource: emptyMon}},
validation.ErrorTypeRequired,
"rbd.monitors", "",
},
"empty image": {
[]api.Volume{{Name: "badimage", VolumeSource: emptyImage}},
validation.ErrorTypeRequired,
"rbd.image", "",
},
"empty cephfs mon": {
[]api.Volume{{Name: "badmon", VolumeSource: emptyCephFSMon}},
validation.ErrorTypeRequired,
"cephfs.monitors", "",
},
"empty metatada path": {
[]api.Volume{{Name: "emptyname", VolumeSource: emptyPathName}},
validation.ErrorTypeRequired,
"downwardAPI.path", "",
},
"absolute path": {
[]api.Volume{{Name: "absolutepath", VolumeSource: absolutePathName}},
validation.ErrorTypeForbidden,
"downwardAPI.path", "",
},
"dot dot path": {
[]api.Volume{{Name: "dotdotpath", VolumeSource: dotDotInPath}},
validation.ErrorTypeInvalid,
"downwardAPI.path", `must not contain ".."`,
},
"dot dot file name": {
[]api.Volume{{Name: "dotdotfilename", VolumeSource: dotDotPathName}},
validation.ErrorTypeInvalid,
"downwardAPI.path", `must not start with ".."`,
},
"dot dot first level dirent": {
[]api.Volume{{Name: "dotdotdirfilename", VolumeSource: dotDotFirstLevelDirent}},
validation.ErrorTypeInvalid,
"downwardAPI.path", `must not start with ".."`,
},
"empty wwn": {
[]api.Volume{{Name: "badimage", VolumeSource: zeroWWN}},
validation.ErrorTypeRequired,
"fc.targetWWNs", "",
},
"empty lun": {
[]api.Volume{{Name: "badimage", VolumeSource: emptyLun}},
validation.ErrorTypeRequired,
"fc.lun", "",
},
"slash in datasetName": {
[]api.Volume{{Name: "slashinname", VolumeSource: slashInName}},
validation.ErrorTypeInvalid,
"flocker.datasetName", "must not contain '/'",
},
"starts with '..'": {
[]api.Volume{{Name: "badprefix", VolumeSource: startsWithDots}},
validation.ErrorTypeInvalid,
"gitRepo.directory", `must not start with ".."`,
},
"contains '..'": {
[]api.Volume{{Name: "containsdots", VolumeSource: containsDots}},
validation.ErrorTypeInvalid,
"gitRepo.directory", `must not contain ".."`,
},
"absolute target": {
[]api.Volume{{Name: "absolutetarget", VolumeSource: absPath}},
validation.ErrorTypeForbidden,
"gitRepo.directory", "",
},
}
for k, v := range errorCases {
_, errs := validateVolumes(v.V)
_, errs := validateVolumes(v.V, validation.NewFieldPath("field"))
if len(errs) == 0 {
t.Errorf("expected failure %s for %v", k, v.V)
continue
}
for i := range errs {
if errs[i].Type != v.T {
t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
t.Errorf("%s: expected error to have type %q: %q", k, v.T, errs[i].Type)
}
if errs[i].Field != v.F {
t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
if !strings.Contains(errs[i].Field, v.F) {
t.Errorf("%s: expected error field %q: %q", k, v.F, errs[i].Field)
}
detail := errs[i].Detail
if detail != v.D {
t.Errorf("%s: expected error detail \"%s\", got \"%s\"", k, v.D, detail)
if !strings.Contains(errs[i].Detail, v.D) {
t.Errorf("%s: expected error detail %q, got %q", k, v.D, errs[i].Detail)
}
}
}
@@ -596,14 +706,14 @@ func TestValidatePorts(t *testing.T) {
{Name: "do-re-me", ContainerPort: 84, Protocol: "UDP"},
{ContainerPort: 85, Protocol: "TCP"},
}
if errs := validatePorts(successCase); len(errs) != 0 {
if errs := validateContainerPorts(successCase, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
nonCanonicalCase := []api.ContainerPort{
{ContainerPort: 80, Protocol: "TCP"},
}
if errs := validatePorts(nonCanonicalCase); len(errs) != 0 {
if errs := validateContainerPorts(nonCanonicalCase, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
@@ -613,35 +723,74 @@ func TestValidatePorts(t *testing.T) {
F string
D string
}{
"name > 15 characters": {[]api.ContainerPort{{Name: strings.Repeat("a", 16), ContainerPort: 80, Protocol: "TCP"}}, validation.ErrorTypeInvalid, "[0].name", PortNameErrorMsg},
"name not a IANA svc name ": {[]api.ContainerPort{{Name: "a.b.c", ContainerPort: 80, Protocol: "TCP"}}, validation.ErrorTypeInvalid, "[0].name", PortNameErrorMsg},
"name not a IANA svc name (i.e. a number)": {[]api.ContainerPort{{Name: "80", ContainerPort: 80, Protocol: "TCP"}}, validation.ErrorTypeInvalid, "[0].name", PortNameErrorMsg},
"name not unique": {[]api.ContainerPort{
{Name: "abc", ContainerPort: 80, Protocol: "TCP"},
{Name: "abc", ContainerPort: 81, Protocol: "TCP"},
}, validation.ErrorTypeDuplicate, "[1].name", ""},
"zero container port": {[]api.ContainerPort{{ContainerPort: 0, Protocol: "TCP"}}, validation.ErrorTypeInvalid, "[0].containerPort", PortRangeErrorMsg},
"invalid container port": {[]api.ContainerPort{{ContainerPort: 65536, Protocol: "TCP"}}, validation.ErrorTypeInvalid, "[0].containerPort", PortRangeErrorMsg},
"invalid host port": {[]api.ContainerPort{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}}, validation.ErrorTypeInvalid, "[0].hostPort", PortRangeErrorMsg},
"invalid protocol case": {[]api.ContainerPort{{ContainerPort: 80, Protocol: "tcp"}}, validation.ErrorTypeNotSupported, "[0].protocol", "supported values: TCP, UDP"},
"invalid protocol": {[]api.ContainerPort{{ContainerPort: 80, Protocol: "ICMP"}}, validation.ErrorTypeNotSupported, "[0].protocol", "supported values: TCP, UDP"},
"protocol required": {[]api.ContainerPort{{Name: "abc", ContainerPort: 80}}, validation.ErrorTypeRequired, "[0].protocol", ""},
"name > 15 characters": {
[]api.ContainerPort{{Name: strings.Repeat("a", 16), ContainerPort: 80, Protocol: "TCP"}},
validation.ErrorTypeInvalid,
"name", PortNameErrorMsg,
},
"name not a IANA svc name ": {
[]api.ContainerPort{{Name: "a.b.c", ContainerPort: 80, Protocol: "TCP"}},
validation.ErrorTypeInvalid,
"name", PortNameErrorMsg,
},
"name not a IANA svc name (i.e. a number)": {
[]api.ContainerPort{{Name: "80", ContainerPort: 80, Protocol: "TCP"}},
validation.ErrorTypeInvalid,
"name", PortNameErrorMsg,
},
"name not unique": {
[]api.ContainerPort{
{Name: "abc", ContainerPort: 80, Protocol: "TCP"},
{Name: "abc", ContainerPort: 81, Protocol: "TCP"},
},
validation.ErrorTypeDuplicate,
"[1].name", "",
},
"zero container port": {
[]api.ContainerPort{{ContainerPort: 0, Protocol: "TCP"}},
validation.ErrorTypeInvalid,
"containerPort", PortRangeErrorMsg,
},
"invalid container port": {
[]api.ContainerPort{{ContainerPort: 65536, Protocol: "TCP"}},
validation.ErrorTypeInvalid,
"containerPort", PortRangeErrorMsg,
},
"invalid host port": {
[]api.ContainerPort{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}},
validation.ErrorTypeInvalid,
"hostPort", PortRangeErrorMsg,
},
"invalid protocol case": {
[]api.ContainerPort{{ContainerPort: 80, Protocol: "tcp"}},
validation.ErrorTypeNotSupported,
"protocol", "supported values: TCP, UDP",
},
"invalid protocol": {
[]api.ContainerPort{{ContainerPort: 80, Protocol: "ICMP"}},
validation.ErrorTypeNotSupported,
"protocol", "supported values: TCP, UDP",
},
"protocol required": {
[]api.ContainerPort{{Name: "abc", ContainerPort: 80}},
validation.ErrorTypeRequired,
"protocol", "",
},
}
for k, v := range errorCases {
errs := validatePorts(v.P)
errs := validateContainerPorts(v.P, validation.NewFieldPath("field"))
if len(errs) == 0 {
t.Errorf("expected failure for %s", k)
}
for i := range errs {
if errs[i].Type != v.T {
t.Errorf("%s: expected errors to have type %s: %v", k, v.T, errs[i])
t.Errorf("%s: expected error to have type %q: %q", k, v.T, errs[i].Type)
}
if errs[i].Field != v.F {
t.Errorf("%s: expected errors to have field %s: %v", k, v.F, errs[i])
if !strings.Contains(errs[i].Field, v.F) {
t.Errorf("%s: expected error field %q: %q", k, v.F, errs[i].Field)
}
detail := errs[i].Detail
if detail != v.D {
t.Errorf("%s: expected error detail either empty or %s, got %s", k, v.D, detail)
if !strings.Contains(errs[i].Detail, v.D) {
t.Errorf("%s: expected error detail %q, got %q", k, v.D, errs[i].Detail)
}
}
}
@@ -663,7 +812,7 @@ func TestValidateEnv(t *testing.T) {
},
},
}
if errs := validateEnv(successCase); len(errs) != 0 {
if errs := validateEnv(successCase, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
@@ -770,17 +919,17 @@ func TestValidateEnv(t *testing.T) {
},
},
}},
expectedError: "[0].valueFrom.fieldRef.fieldPath: unsupported value 'status.phase', Details: supported values: metadata.name, metadata.namespace, status.podIP",
expectedError: "valueFrom.fieldRef.fieldPath: unsupported value 'status.phase', Details: supported values: metadata.name, metadata.namespace, status.podIP",
},
}
for _, tc := range errorCases {
if errs := validateEnv(tc.envs); len(errs) == 0 {
if errs := validateEnv(tc.envs, validation.NewFieldPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %s", tc.name)
} else {
for i := range errs {
str := errs[i].Error()
if str != "" && str != tc.expectedError {
t.Errorf("%s: expected error detail either empty or %s, got %s", tc.name, tc.expectedError, str)
if str != "" && !strings.Contains(str, tc.expectedError) {
t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str)
}
}
}
@@ -795,7 +944,7 @@ func TestValidateVolumeMounts(t *testing.T) {
{Name: "123", MountPath: "/foo"},
{Name: "abc-123", MountPath: "/bar"},
}
if errs := validateVolumeMounts(successCase, volumes); len(errs) != 0 {
if errs := validateVolumeMounts(successCase, volumes, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
@@ -805,7 +954,7 @@ func TestValidateVolumeMounts(t *testing.T) {
"empty mountpath": {{Name: "abc", MountPath: ""}},
}
for k, v := range errorCases {
if errs := validateVolumeMounts(v, volumes); len(errs) == 0 {
if errs := validateVolumeMounts(v, volumes, validation.NewFieldPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %s", k)
}
}
@@ -823,7 +972,7 @@ func TestValidateProbe(t *testing.T) {
}
for _, p := range successCases {
if errs := validateProbe(p); len(errs) != 0 {
if errs := validateProbe(p, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
@@ -835,7 +984,7 @@ func TestValidateProbe(t *testing.T) {
errorCases = append(errorCases, probe)
}
for _, p := range errorCases {
if errs := validateProbe(p); len(errs) == 0 {
if errs := validateProbe(p, validation.NewFieldPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %v", p)
}
}
@@ -849,7 +998,7 @@ func TestValidateHandler(t *testing.T) {
{HTTPGet: &api.HTTPGetAction{Path: "/", Port: intstr.FromString("port"), Host: "", Scheme: "HTTP"}},
}
for _, h := range successCases {
if errs := validateHandler(&h); len(errs) != 0 {
if errs := validateHandler(&h, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
@@ -862,7 +1011,7 @@ func TestValidateHandler(t *testing.T) {
{HTTPGet: &api.HTTPGetAction{Path: "", Port: intstr.FromString(""), Host: ""}},
}
for _, h := range errorCases {
if errs := validateHandler(&h); len(errs) == 0 {
if errs := validateHandler(&h, validation.NewFieldPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %#v", h)
}
}
@@ -901,7 +1050,7 @@ func TestValidatePullPolicy(t *testing.T) {
}
for k, v := range testCases {
ctr := &v.Container
errs := validatePullPolicy(ctr)
errs := validatePullPolicy(ctr.ImagePullPolicy, validation.NewFieldPath("field"))
if len(errs) != 0 {
t.Errorf("case[%s] expected success, got %#v", k, errs)
}
@@ -1017,7 +1166,7 @@ func TestValidateContainers(t *testing.T) {
},
{Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", SecurityContext: fakeValidSecurityContext(true)},
}
if errs := validateContainers(successCase, volumes); len(errs) != 0 {
if errs := validateContainers(successCase, volumes, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
@@ -1197,7 +1346,7 @@ func TestValidateContainers(t *testing.T) {
},
}
for k, v := range errorCases {
if errs := validateContainers(v, volumes); len(errs) == 0 {
if errs := validateContainers(v, volumes, validation.NewFieldPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %s", k)
}
}
@@ -1210,7 +1359,7 @@ func TestValidateRestartPolicy(t *testing.T) {
api.RestartPolicyNever,
}
for _, policy := range successCases {
if errs := validateRestartPolicy(&policy); len(errs) != 0 {
if errs := validateRestartPolicy(&policy, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
@@ -1218,7 +1367,7 @@ func TestValidateRestartPolicy(t *testing.T) {
errorCases := []api.RestartPolicy{"", "newpolicy"}
for k, policy := range errorCases {
if errs := validateRestartPolicy(&policy); len(errs) == 0 {
if errs := validateRestartPolicy(&policy, validation.NewFieldPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %d", k)
}
}
@@ -1227,14 +1376,14 @@ func TestValidateRestartPolicy(t *testing.T) {
func TestValidateDNSPolicy(t *testing.T) {
successCases := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault, api.DNSPolicy(api.DNSClusterFirst)}
for _, policy := range successCases {
if errs := validateDNSPolicy(&policy); len(errs) != 0 {
if errs := validateDNSPolicy(&policy, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
errorCases := []api.DNSPolicy{api.DNSPolicy("invalid")}
for _, policy := range errorCases {
if errs := validateDNSPolicy(&policy); len(errs) == 0 {
if errs := validateDNSPolicy(&policy, validation.NewFieldPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %v", policy)
}
}
@@ -1295,7 +1444,7 @@ func TestValidatePodSpec(t *testing.T) {
},
}
for i := range successCases {
if errs := ValidatePodSpec(&successCases[i]); len(errs) != 0 {
if errs := ValidatePodSpec(&successCases[i], validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
@@ -1360,7 +1509,7 @@ func TestValidatePodSpec(t *testing.T) {
},
}
for k, v := range failureCases {
if errs := ValidatePodSpec(&v); len(errs) == 0 {
if errs := ValidatePodSpec(&v, validation.NewFieldPath("field")); len(errs) == 0 {
t.Errorf("expected failure for %q", k)
}
}
@@ -2715,7 +2864,7 @@ func TestValidateNode(t *testing.T) {
"metadata.labels": true,
"metadata.annotations": true,
"metadata.namespace": true,
"spec.ExternalID": true,
"spec.externalID": true,
}
if expectedFields[field] == false {
t.Errorf("%s: missing prefix for: %v", k, errs[i])
@@ -3035,7 +3184,7 @@ func TestValidateResourceNames(t *testing.T) {
{"kubernetes.io/will/not/work/", false},
}
for k, item := range table {
err := validateResourceName(item.input, "sth")
err := validateResourceName(item.input, validation.NewFieldPath("field"))
if len(err) != 0 && item.success {
t.Errorf("expected no failure for input %q", item.input)
} else if len(err) == 0 && !item.success {
@@ -3158,7 +3307,7 @@ func TestValidateLimitRange(t *testing.T) {
},
},
}},
"Default is not supported when limit type is Pod",
"not supported when limit type is Pod",
},
"default-request-limit-type-pod": {
api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{
@@ -3171,7 +3320,7 @@ func TestValidateLimitRange(t *testing.T) {
},
},
}},
"DefaultRequest is not supported when limit type is Pod",
"not supported when limit type is Pod",
},
"min value 100m is greater than max value 10m": {
api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{
@@ -3234,7 +3383,7 @@ func TestValidateLimitRange(t *testing.T) {
},
},
}},
"maxLimitRequestRatio 800m is less than 1",
"ratio 800m is less than 1",
},
"invalid spec maxLimitRequestRatio greater than max/min": {
api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: api.LimitRangeSpec{
@@ -3247,7 +3396,7 @@ func TestValidateLimitRange(t *testing.T) {
},
},
}},
"maxLimitRequestRatio 10 is greater than max/min = 4.000000",
"ratio 10 is greater than max/min = 4.000000",
},
}
@@ -3259,7 +3408,7 @@ func TestValidateLimitRange(t *testing.T) {
for i := range errs {
detail := errs[i].Detail
if detail != v.D {
t.Errorf("%s: expected error detail either empty or %s, got %s", k, v.D, detail)
t.Errorf("[%s]: expected error detail either empty or %q, got %q", k, v.D, detail)
}
}
}
@@ -3362,13 +3511,8 @@ func TestValidateResourceQuota(t *testing.T) {
t.Errorf("expected failure for %s", k)
}
for i := range errs {
field := errs[i].Field
detail := errs[i].Detail
if field != "metadata.name" && field != "metadata.namespace" && !api.IsStandardResourceName(field) {
t.Errorf("%s: missing prefix for: %v", k, field)
}
if detail != v.D {
t.Errorf("%s: expected error detail either empty or %s, got %s", k, v.D, detail)
if errs[i].Detail != v.D {
t.Errorf("[%s]: expected error detail either empty or %s, got %s", k, v.D, errs[i].Detail)
}
}
}
@@ -4020,7 +4164,7 @@ func TestValidateSecurityContext(t *testing.T) {
"no run as user": {noRunAsUser},
}
for k, v := range successCases {
if errs := ValidateSecurityContext(v.sc); len(errs) != 0 {
if errs := ValidateSecurityContext(v.sc, validation.NewFieldPath("field")); len(errs) != 0 {
t.Errorf("Expected success for %s, got %v", k, errs)
}
}
@@ -4050,7 +4194,7 @@ func TestValidateSecurityContext(t *testing.T) {
},
}
for k, v := range errorCases {
if errs := ValidateSecurityContext(v.sc); len(errs) == 0 || errs[0].Type != v.errorType || errs[0].Detail != v.errorDetail {
if errs := ValidateSecurityContext(v.sc, validation.NewFieldPath("field")); len(errs) == 0 || errs[0].Type != v.errorType || errs[0].Detail != v.errorDetail {
t.Errorf("Expected error type %s with detail %s for %s, got %v", v.errorType, v.errorDetail, k, errs)
}
}