|
|
|
@@ -50,10 +50,11 @@ import (
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
dnsLabelErrMsg = "a lowercase RFC 1123 label must consist of"
|
|
|
|
|
dnsSubdomainLabelErrMsg = "a lowercase RFC 1123 subdomain"
|
|
|
|
|
envVarNameErrMsg = "a valid environment variable name must consist of"
|
|
|
|
|
defaultGracePeriod = int64(30)
|
|
|
|
|
dnsLabelErrMsg = "a lowercase RFC 1123 label must consist of"
|
|
|
|
|
dnsSubdomainLabelErrMsg = "a lowercase RFC 1123 subdomain"
|
|
|
|
|
envVarNameErrMsg = "a valid environment variable name must consist of"
|
|
|
|
|
relaxedEnvVarNameFmtErrMsg string = "a valid environment variable names must be printable ASCII characters other than '=' character"
|
|
|
|
|
defaultGracePeriod = int64(30)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
@@ -5981,6 +5982,361 @@ func TestHugePagesEnv(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRelaxedValidateEnv(t *testing.T) {
|
|
|
|
|
successCase := []core.EnvVar{
|
|
|
|
|
{Name: "!\"#$%&'()", Value: "value"},
|
|
|
|
|
{Name: "* +,-./0123456789", Value: "value"},
|
|
|
|
|
{Name: ":;<>?@", Value: "value"},
|
|
|
|
|
{Name: "ABCDEFG", Value: "value"},
|
|
|
|
|
{Name: "abcdefghijklmn", Value: "value"},
|
|
|
|
|
{Name: "[\\]^_`{}|~", Value: "value"},
|
|
|
|
|
{
|
|
|
|
|
Name: "!\"#$%&'()",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "metadata.annotations['key']",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: "!\"#$%&'()",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "metadata.labels['key']",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: "* +,-./0123456789",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "metadata.name",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: "* +,-./0123456789",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "metadata.namespace",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: "* +,-./0123456789",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "metadata.uid",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: ":;<>?@",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "spec.nodeName",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: ":;<>?@",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "spec.serviceAccountName",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: ":;<>?@",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "status.hostIP",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: ":;<>?@",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "status.podIP",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: "abcdefghijklmn",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "status.podIPs",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Name: "abcdefghijklmn",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
SecretKeyRef: &core.SecretKeySelector{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{
|
|
|
|
|
Name: "some-secret",
|
|
|
|
|
},
|
|
|
|
|
Key: "secret-key",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Name: "!\"#$%&'()",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
ConfigMapKeyRef: &core.ConfigMapKeySelector{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{
|
|
|
|
|
Name: "some-config-map",
|
|
|
|
|
},
|
|
|
|
|
Key: "some-key",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
if errs := ValidateEnv(successCase, field.NewPath("field"), PodValidationOptions{AllowRelaxedEnvironmentVariableValidation: true}); len(errs) != 0 {
|
|
|
|
|
t.Errorf("expected success, got: %v", errs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errorCases := []struct {
|
|
|
|
|
name string
|
|
|
|
|
envs []core.EnvVar
|
|
|
|
|
expectedError string
|
|
|
|
|
}{{
|
|
|
|
|
name: "illegal character",
|
|
|
|
|
envs: []core.EnvVar{{Name: "=abc"}},
|
|
|
|
|
expectedError: `[0].name: Invalid value: "=abc": ` + relaxedEnvVarNameFmtErrMsg,
|
|
|
|
|
}, {
|
|
|
|
|
name: "zero-length name",
|
|
|
|
|
envs: []core.EnvVar{{Name: ""}},
|
|
|
|
|
expectedError: "[0].name: Required value",
|
|
|
|
|
}, {
|
|
|
|
|
name: "value and valueFrom specified",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
Value: "foo",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "metadata.name",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "[0].valueFrom: Invalid value: \"\": may not be specified when `value` is not empty",
|
|
|
|
|
}, {
|
|
|
|
|
name: "valueFrom without a source",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "[0].valueFrom: Invalid value: \"\": must specify one of: `fieldRef`, `resourceFieldRef`, `configMapKeyRef` or `secretKeyRef`",
|
|
|
|
|
}, {
|
|
|
|
|
name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "metadata.name",
|
|
|
|
|
},
|
|
|
|
|
SecretKeyRef: &core.SecretKeySelector{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{
|
|
|
|
|
Name: "a-secret",
|
|
|
|
|
},
|
|
|
|
|
Key: "a-key",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "[0].valueFrom: Invalid value: \"\": may not have more than one field specified at a time",
|
|
|
|
|
}, {
|
|
|
|
|
name: "valueFrom.fieldRef and valueFrom.configMapKeyRef set",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "some_var_name",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "metadata.name",
|
|
|
|
|
},
|
|
|
|
|
ConfigMapKeyRef: &core.ConfigMapKeySelector{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{
|
|
|
|
|
Name: "some-config-map",
|
|
|
|
|
},
|
|
|
|
|
Key: "some-key",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
FieldPath: "metadata.name",
|
|
|
|
|
},
|
|
|
|
|
SecretKeyRef: &core.SecretKeySelector{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{
|
|
|
|
|
Name: "a-secret",
|
|
|
|
|
},
|
|
|
|
|
Key: "a-key",
|
|
|
|
|
},
|
|
|
|
|
ConfigMapKeyRef: &core.ConfigMapKeySelector{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{
|
|
|
|
|
Name: "some-config-map",
|
|
|
|
|
},
|
|
|
|
|
Key: "some-key",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "valueFrom.secretKeyRef.name invalid",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
SecretKeyRef: &core.SecretKeySelector{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{
|
|
|
|
|
Name: "$%^&*#",
|
|
|
|
|
},
|
|
|
|
|
Key: "a-key",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
}, {
|
|
|
|
|
name: "valueFrom.configMapKeyRef.name invalid",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
ConfigMapKeyRef: &core.ConfigMapKeySelector{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{
|
|
|
|
|
Name: "$%^&*#",
|
|
|
|
|
},
|
|
|
|
|
Key: "some-key",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
}, {
|
|
|
|
|
name: "missing FieldPath on ObjectFieldSelector",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `[0].valueFrom.fieldRef.fieldPath: Required value`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "missing APIVersion on ObjectFieldSelector",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
FieldPath: "metadata.name",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `[0].valueFrom.fieldRef.apiVersion: Required value`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "invalid fieldPath",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
FieldPath: "metadata.whoops",
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.whoops": error converting fieldPath`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "metadata.name with subscript",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "labels",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
FieldPath: "metadata.name['key']",
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.name['key']": error converting fieldPath: field label does not support subscript`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "metadata.labels without subscript",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "labels",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
FieldPath: "metadata.labels",
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.labels": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.hostIPs", "status.podIP", "status.podIPs"`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "metadata.annotations without subscript",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
FieldPath: "metadata.annotations",
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.annotations": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.hostIPs", "status.podIP", "status.podIPs"`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "metadata.annotations with invalid key",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
FieldPath: "metadata.annotations['invalid~key']",
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `field[0].valueFrom.fieldRef: Invalid value: "invalid~key"`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "metadata.labels with invalid key",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
FieldPath: "metadata.labels['Www.k8s.io/test']",
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `field[0].valueFrom.fieldRef: Invalid value: "Www.k8s.io/test"`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "unsupported fieldPath",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
|
Name: "abc",
|
|
|
|
|
ValueFrom: &core.EnvVarSource{
|
|
|
|
|
FieldRef: &core.ObjectFieldSelector{
|
|
|
|
|
FieldPath: "status.phase",
|
|
|
|
|
APIVersion: "v1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.hostIPs", "status.podIP", "status.podIPs"`,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
for _, tc := range errorCases {
|
|
|
|
|
if errs := ValidateEnv(tc.envs, field.NewPath("field"), PodValidationOptions{AllowRelaxedEnvironmentVariableValidation: true}); len(errs) == 0 {
|
|
|
|
|
t.Errorf("expected failure for %s", tc.name)
|
|
|
|
|
} else {
|
|
|
|
|
for i := range errs {
|
|
|
|
|
str := errs[i].Error()
|
|
|
|
|
if str != "" && !strings.Contains(str, tc.expectedError) {
|
|
|
|
|
t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestValidateEnv(t *testing.T) {
|
|
|
|
|
successCase := []core.EnvVar{
|
|
|
|
|
{Name: "abc", Value: "value"},
|
|
|
|
@@ -6094,6 +6450,67 @@ func TestValidateEnv(t *testing.T) {
|
|
|
|
|
t.Errorf("expected success, got: %v", errs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateSuccessCase := []core.EnvVar{
|
|
|
|
|
{Name: "!\"#$%&'()", Value: "value"},
|
|
|
|
|
{Name: "* +,-./0123456789", Value: "value"},
|
|
|
|
|
{Name: ":;<>?@", Value: "value"},
|
|
|
|
|
{Name: "ABCDEFG", Value: "value"},
|
|
|
|
|
{Name: "abcdefghijklmn", Value: "value"},
|
|
|
|
|
{Name: "[\\]^_`{}|~", Value: "value"},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if errs := ValidateEnv(updateSuccessCase, field.NewPath("field"), PodValidationOptions{AllowRelaxedEnvironmentVariableValidation: true}); len(errs) != 0 {
|
|
|
|
|
t.Errorf("expected success, got: %v", errs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateErrorCase := []struct {
|
|
|
|
|
name string
|
|
|
|
|
envs []core.EnvVar
|
|
|
|
|
expectedError string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "invalid name a",
|
|
|
|
|
envs: []core.EnvVar{
|
|
|
|
|
{Name: "!\"#$%&'()", Value: "value"},
|
|
|
|
|
},
|
|
|
|
|
expectedError: `field[0].name: Invalid value: ` + "\"!\\\"#$%&'()\": " + envVarNameErrMsg,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid name b",
|
|
|
|
|
envs: []core.EnvVar{
|
|
|
|
|
{Name: "* +,-./0123456789", Value: "value"},
|
|
|
|
|
},
|
|
|
|
|
expectedError: `field[0].name: Invalid value: ` + "\"* +,-./0123456789\": " + envVarNameErrMsg,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid name c",
|
|
|
|
|
envs: []core.EnvVar{
|
|
|
|
|
{Name: ":;<>?@", Value: "value"},
|
|
|
|
|
},
|
|
|
|
|
expectedError: `field[0].name: Invalid value: ` + "\":;<>?@\": " + envVarNameErrMsg,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid name d",
|
|
|
|
|
envs: []core.EnvVar{
|
|
|
|
|
{Name: "[\\]^_{}|~", Value: "value"},
|
|
|
|
|
},
|
|
|
|
|
expectedError: `field[0].name: Invalid value: ` + "\"[\\\\]^_{}|~\": " + envVarNameErrMsg,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tc := range updateErrorCase {
|
|
|
|
|
if errs := ValidateEnv(tc.envs, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
|
|
|
|
|
t.Errorf("expected failure for %s", tc.name)
|
|
|
|
|
} else {
|
|
|
|
|
for i := range errs {
|
|
|
|
|
str := errs[i].Error()
|
|
|
|
|
if str != "" && !strings.Contains(str, tc.expectedError) {
|
|
|
|
|
t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errorCases := []struct {
|
|
|
|
|
name string
|
|
|
|
|
envs []core.EnvVar
|
|
|
|
@@ -6102,22 +6519,6 @@ func TestValidateEnv(t *testing.T) {
|
|
|
|
|
name: "zero-length name",
|
|
|
|
|
envs: []core.EnvVar{{Name: ""}},
|
|
|
|
|
expectedError: "[0].name: Required value",
|
|
|
|
|
}, {
|
|
|
|
|
name: "illegal character",
|
|
|
|
|
envs: []core.EnvVar{{Name: "a!b"}},
|
|
|
|
|
expectedError: `[0].name: Invalid value: "a!b": ` + envVarNameErrMsg,
|
|
|
|
|
}, {
|
|
|
|
|
name: "dot only",
|
|
|
|
|
envs: []core.EnvVar{{Name: "."}},
|
|
|
|
|
expectedError: `[0].name: Invalid value: ".": must not be`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "double dots only",
|
|
|
|
|
envs: []core.EnvVar{{Name: ".."}},
|
|
|
|
|
expectedError: `[0].name: Invalid value: "..": must not be`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "leading double dots",
|
|
|
|
|
envs: []core.EnvVar{{Name: "..abc"}},
|
|
|
|
|
expectedError: `[0].name: Invalid value: "..abc": must not start with`,
|
|
|
|
|
}, {
|
|
|
|
|
name: "value and valueFrom specified",
|
|
|
|
|
envs: []core.EnvVar{{
|
|
|
|
@@ -6377,10 +6778,112 @@ func TestValidateEnvFrom(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
if errs := ValidateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 {
|
|
|
|
|
if errs := ValidateEnvFrom(successCase, nil, PodValidationOptions{}); len(errs) != 0 {
|
|
|
|
|
t.Errorf("expected success: %v", errs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateSuccessCase := []core.EnvFromSource{{
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Prefix: "* +,-./0123456789",
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Prefix: ":;<>?@",
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Prefix: "abcdefghijklmn",
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Prefix: "[\\]^_`{}|~",
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}}
|
|
|
|
|
|
|
|
|
|
if errs := ValidateEnvFrom(updateSuccessCase, field.NewPath("field"), PodValidationOptions{AllowRelaxedEnvironmentVariableValidation: true}); len(errs) != 0 {
|
|
|
|
|
t.Errorf("expected success, got: %v", errs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateErrorCase := []struct {
|
|
|
|
|
name string
|
|
|
|
|
envs []core.EnvFromSource
|
|
|
|
|
expectedError string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "invalid name a",
|
|
|
|
|
envs: []core.EnvFromSource{
|
|
|
|
|
{
|
|
|
|
|
Prefix: "!\"#$%&'()",
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expectedError: `field[0].prefix: Invalid value: ` + "\"!\\\"#$%&'()\": " + envVarNameErrMsg,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid name b",
|
|
|
|
|
envs: []core.EnvFromSource{
|
|
|
|
|
{
|
|
|
|
|
Prefix: "* +,-./0123456789",
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expectedError: `field[0].prefix: Invalid value: ` + "\"* +,-./0123456789\": " + envVarNameErrMsg,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid name c",
|
|
|
|
|
envs: []core.EnvFromSource{
|
|
|
|
|
{
|
|
|
|
|
Prefix: ":;<>?@",
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expectedError: `field[0].prefix: Invalid value: ` + "\":;<>?@\": " + envVarNameErrMsg,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid name d",
|
|
|
|
|
envs: []core.EnvFromSource{
|
|
|
|
|
{
|
|
|
|
|
Prefix: "[\\]^_{}|~",
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expectedError: `field[0].prefix: Invalid value: ` + "\"[\\\\]^_{}|~\": " + envVarNameErrMsg,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tc := range updateErrorCase {
|
|
|
|
|
if errs := ValidateEnvFrom(tc.envs, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
|
|
|
|
|
t.Errorf("expected failure for %s", tc.name)
|
|
|
|
|
} else {
|
|
|
|
|
for i := range errs {
|
|
|
|
|
str := errs[i].Error()
|
|
|
|
|
if str != "" && !strings.Contains(str, tc.expectedError) {
|
|
|
|
|
t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errorCases := []struct {
|
|
|
|
|
name string
|
|
|
|
|
envs []core.EnvFromSource
|
|
|
|
@@ -6399,14 +6902,6 @@ func TestValidateEnvFrom(t *testing.T) {
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "$"}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "field[0].configMapRef.name: Invalid value",
|
|
|
|
|
}, {
|
|
|
|
|
name: "invalid prefix",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
|
Prefix: "a!b",
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
|
|
|
|
|
}, {
|
|
|
|
|
name: "zero-length name",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
@@ -6421,14 +6916,6 @@ func TestValidateEnvFrom(t *testing.T) {
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "&"}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "field[0].secretRef.name: Invalid value",
|
|
|
|
|
}, {
|
|
|
|
|
name: "invalid prefix",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
|
Prefix: "a!b",
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
|
|
|
|
|
}, {
|
|
|
|
|
name: "no refs",
|
|
|
|
|
envs: []core.EnvFromSource{
|
|
|
|
@@ -6461,7 +6948,123 @@ func TestValidateEnvFrom(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
for _, tc := range errorCases {
|
|
|
|
|
if errs := ValidateEnvFrom(tc.envs, field.NewPath("field")); len(errs) == 0 {
|
|
|
|
|
if errs := ValidateEnvFrom(tc.envs, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
|
|
|
|
|
t.Errorf("expected failure for %s", tc.name)
|
|
|
|
|
} else {
|
|
|
|
|
for i := range errs {
|
|
|
|
|
str := errs[i].Error()
|
|
|
|
|
if str != "" && !strings.Contains(str, tc.expectedError) {
|
|
|
|
|
t.Errorf("%s: expected error detail either empty or %q, got %q", tc.name, tc.expectedError, str)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRelaxedValidateEnvFrom(t *testing.T) {
|
|
|
|
|
successCase := []core.EnvFromSource{{
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Prefix: "!\"#$%&'()",
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Prefix: "* +,-./0123456789",
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Prefix: ":;<>?@",
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
Prefix: "[\\]^_`{}|~",
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
if errs := ValidateEnvFrom(successCase, field.NewPath("field"), PodValidationOptions{AllowRelaxedEnvironmentVariableValidation: true}); len(errs) != 0 {
|
|
|
|
|
t.Errorf("expected success: %v", errs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errorCases := []struct {
|
|
|
|
|
name string
|
|
|
|
|
envs []core.EnvFromSource
|
|
|
|
|
expectedError string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "zero-length name",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: ""}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "field[0].configMapRef.name: Required value",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid prefix",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
|
Prefix: "=abc",
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: `field[0].prefix: Invalid value: "=abc": ` + relaxedEnvVarNameFmtErrMsg,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "zero-length name",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: ""}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "field[0].secretRef.name: Required value",
|
|
|
|
|
}, {
|
|
|
|
|
name: "invalid name",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "&"}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "field[0].secretRef.name: Invalid value",
|
|
|
|
|
}, {
|
|
|
|
|
name: "no refs",
|
|
|
|
|
envs: []core.EnvFromSource{
|
|
|
|
|
{},
|
|
|
|
|
},
|
|
|
|
|
expectedError: "field: Invalid value: \"\": must specify one of: `configMapRef` or `secretRef`",
|
|
|
|
|
}, {
|
|
|
|
|
name: "multiple refs",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "field: Invalid value: \"\": may not have more than one field specified at a time",
|
|
|
|
|
}, {
|
|
|
|
|
name: "invalid secret ref name",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
|
SecretRef: &core.SecretEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "field[0].secretRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
|
|
|
|
|
}, {
|
|
|
|
|
name: "invalid config ref name",
|
|
|
|
|
envs: []core.EnvFromSource{{
|
|
|
|
|
ConfigMapRef: &core.ConfigMapEnvSource{
|
|
|
|
|
LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
|
|
|
|
|
}},
|
|
|
|
|
expectedError: "field[0].configMapRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
for _, tc := range errorCases {
|
|
|
|
|
if errs := ValidateEnvFrom(tc.envs, field.NewPath("field"), PodValidationOptions{AllowRelaxedEnvironmentVariableValidation: true}); len(errs) == 0 {
|
|
|
|
|
t.Errorf("expected failure for %s", tc.name)
|
|
|
|
|
} else {
|
|
|
|
|
for i := range errs {
|
|
|
|
|