GenericEphemeralVolume: feature gate, API, documentation
As explained in https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/1698-generic-ephemeral-volumes, CSI inline volumes are not suitable for more "normal" kinds of storage systems. For those a new approach is needed: "generic ephemeral inline volumes".
This commit is contained in:
@@ -932,42 +932,195 @@ func testVolumeClaimStorageClassInAnnotationAndSpec(name, namespace, scNameInAnn
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
func testValidatePVC(t *testing.T, ephemeral bool) {
|
||||
invalidClassName := "-invalid-"
|
||||
validClassName := "valid"
|
||||
invalidMode := core.PersistentVolumeMode("fakeVolumeMode")
|
||||
validMode := core.PersistentVolumeFilesystem
|
||||
goodName := "foo"
|
||||
goodNS := "ns"
|
||||
if ephemeral {
|
||||
// Must be empty for ephemeral inline volumes.
|
||||
goodName = ""
|
||||
goodNS = ""
|
||||
}
|
||||
goodClaimSpec := core.PersistentVolumeClaimSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "key2",
|
||||
Operator: "Exists",
|
||||
},
|
||||
},
|
||||
},
|
||||
AccessModes: []core.PersistentVolumeAccessMode{
|
||||
core.ReadWriteOnce,
|
||||
core.ReadOnlyMany,
|
||||
},
|
||||
Resources: core.ResourceRequirements{
|
||||
Requests: core.ResourceList{
|
||||
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||
},
|
||||
},
|
||||
StorageClassName: &validClassName,
|
||||
VolumeMode: &validMode,
|
||||
}
|
||||
now := metav1.Now()
|
||||
ten := int64(10)
|
||||
|
||||
scenarios := map[string]struct {
|
||||
isExpectedFailure bool
|
||||
claim *core.PersistentVolumeClaim
|
||||
}{
|
||||
"good-claim": {
|
||||
isExpectedFailure: false,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "key2",
|
||||
Operator: "Exists",
|
||||
},
|
||||
claim: testVolumeClaim(goodName, goodNS, goodClaimSpec),
|
||||
},
|
||||
"missing-name": {
|
||||
isExpectedFailure: !ephemeral,
|
||||
claim: testVolumeClaim("", goodNS, goodClaimSpec),
|
||||
},
|
||||
"missing-namespace": {
|
||||
isExpectedFailure: !ephemeral,
|
||||
claim: testVolumeClaim(goodName, "", goodClaimSpec),
|
||||
},
|
||||
"with-generate-name": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.GenerateName = "pvc-"
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-uid": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.UID = "ac051fac-2ead-46d9-b8b4-4e0fbeb7455d"
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-resource-version": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.ResourceVersion = "1"
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-generation": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.Generation = 100
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-creation-timestamp": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.CreationTimestamp = now
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-deletion-grace-period-seconds": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.DeletionGracePeriodSeconds = &ten
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-owner-references": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.OwnerReferences = []metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: "v1",
|
||||
Kind: "pod",
|
||||
Name: "foo",
|
||||
UID: "ac051fac-2ead-46d9-b8b4-4e0fbeb7455d",
|
||||
},
|
||||
},
|
||||
AccessModes: []core.PersistentVolumeAccessMode{
|
||||
core.ReadWriteOnce,
|
||||
core.ReadOnlyMany,
|
||||
},
|
||||
Resources: core.ResourceRequirements{
|
||||
Requests: core.ResourceList{
|
||||
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||
}
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-finalizers": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.Finalizers = []string{
|
||||
"example.com/foo",
|
||||
}
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-cluster-name": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.ClusterName = "foo"
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-managed-fields": {
|
||||
isExpectedFailure: ephemeral,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.ManagedFields = []metav1.ManagedFieldsEntry{
|
||||
{
|
||||
FieldsType: "FieldsV1",
|
||||
Operation: "Apply",
|
||||
APIVersion: "apps/v1",
|
||||
Manager: "foo",
|
||||
},
|
||||
},
|
||||
StorageClassName: &validClassName,
|
||||
VolumeMode: &validMode,
|
||||
}),
|
||||
}
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-good-labels": {
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.Labels = map[string]string{
|
||||
"apps.kubernetes.io/name": "test",
|
||||
}
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-bad-labels": {
|
||||
isExpectedFailure: true,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.Labels = map[string]string{
|
||||
"hello-world": "hyphen not allowed",
|
||||
}
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-good-annotations": {
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.Labels = map[string]string{
|
||||
"foo": "bar",
|
||||
}
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"with-bad-annotations": {
|
||||
isExpectedFailure: true,
|
||||
claim: func() *core.PersistentVolumeClaim {
|
||||
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
|
||||
claim.Labels = map[string]string{
|
||||
"hello-world": "hyphen not allowed",
|
||||
}
|
||||
return claim
|
||||
}(),
|
||||
},
|
||||
"invalid-claim-zero-capacity": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
@@ -990,7 +1143,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
},
|
||||
"invalid-label-selector": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
@@ -1013,7 +1166,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
},
|
||||
"invalid-accessmode": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
AccessModes: []core.PersistentVolumeAccessMode{"fakemode"},
|
||||
Resources: core.ResourceRequirements{
|
||||
Requests: core.ResourceList{
|
||||
@@ -1022,23 +1175,9 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
},
|
||||
"missing-namespace": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "", core.PersistentVolumeClaimSpec{
|
||||
AccessModes: []core.PersistentVolumeAccessMode{
|
||||
core.ReadWriteOnce,
|
||||
core.ReadOnlyMany,
|
||||
},
|
||||
Resources: core.ResourceRequirements{
|
||||
Requests: core.ResourceList{
|
||||
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
"no-access-modes": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
Resources: core.ResourceRequirements{
|
||||
Requests: core.ResourceList{
|
||||
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||
@@ -1048,7 +1187,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
},
|
||||
"no-resource-requests": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
AccessModes: []core.PersistentVolumeAccessMode{
|
||||
core.ReadWriteOnce,
|
||||
},
|
||||
@@ -1056,7 +1195,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
},
|
||||
"invalid-resource-requests": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
AccessModes: []core.PersistentVolumeAccessMode{
|
||||
core.ReadWriteOnce,
|
||||
},
|
||||
@@ -1069,7 +1208,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
},
|
||||
"negative-storage-request": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
@@ -1091,7 +1230,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
},
|
||||
"zero-storage-request": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
@@ -1113,7 +1252,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
},
|
||||
"invalid-storage-class-name": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
@@ -1136,7 +1275,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
},
|
||||
"invalid-volume-mode": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
AccessModes: []core.PersistentVolumeAccessMode{
|
||||
core.ReadWriteOnce,
|
||||
core.ReadOnlyMany,
|
||||
@@ -1153,17 +1292,43 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
|
||||
for name, scenario := range scenarios {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
errs := ValidatePersistentVolumeClaim(scenario.claim)
|
||||
var errs field.ErrorList
|
||||
if ephemeral {
|
||||
volumes := []core.Volume{
|
||||
{
|
||||
Name: "foo",
|
||||
VolumeSource: core.VolumeSource{
|
||||
Ephemeral: &core.EphemeralVolumeSource{
|
||||
VolumeClaimTemplate: &core.PersistentVolumeClaimTemplate{
|
||||
ObjectMeta: scenario.claim.ObjectMeta,
|
||||
Spec: scenario.claim.Spec,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, errs = ValidateVolumes(volumes, nil, field.NewPath(""))
|
||||
} else {
|
||||
errs = ValidatePersistentVolumeClaim(scenario.claim)
|
||||
}
|
||||
if len(errs) == 0 && scenario.isExpectedFailure {
|
||||
t.Errorf("Unexpected success for scenario: %s", name)
|
||||
t.Error("Unexpected success for scenario")
|
||||
}
|
||||
if len(errs) > 0 && !scenario.isExpectedFailure {
|
||||
t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
|
||||
t.Errorf("Unexpected failure: %+v", errs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePersistentVolumeClaim(t *testing.T) {
|
||||
testValidatePVC(t, false)
|
||||
}
|
||||
|
||||
func TestValidateEphemeralVolume(t *testing.T) {
|
||||
testValidatePVC(t, true)
|
||||
}
|
||||
|
||||
func TestAlphaPVVolumeModeUpdate(t *testing.T) {
|
||||
block := core.PersistentVolumeBlock
|
||||
file := core.PersistentVolumeFilesystem
|
||||
@@ -3825,7 +3990,7 @@ func TestValidateVolumes(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
names, errs := ValidateVolumes([]core.Volume{tc.vol}, field.NewPath("field"))
|
||||
names, errs := ValidateVolumes([]core.Volume{tc.vol}, nil, field.NewPath("field"))
|
||||
if len(errs) != len(tc.errs) {
|
||||
t.Fatalf("unexpected error(s): got %d, want %d: %v", len(tc.errs), len(errs), errs)
|
||||
}
|
||||
@@ -3851,7 +4016,7 @@ func TestValidateVolumes(t *testing.T) {
|
||||
{Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
|
||||
{Name: "abc", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
|
||||
}
|
||||
_, errs := ValidateVolumes(dupsCase, field.NewPath("field"))
|
||||
_, errs := ValidateVolumes(dupsCase, nil, field.NewPath("field"))
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected error")
|
||||
} else if len(errs) != 1 {
|
||||
@@ -3864,7 +4029,7 @@ func TestValidateVolumes(t *testing.T) {
|
||||
hugePagesCase := core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{Medium: core.StorageMediumHugePages}}
|
||||
|
||||
// Enable HugePages
|
||||
if errs := validateVolumeSource(&hugePagesCase, field.NewPath("field").Index(0), "working"); len(errs) != 0 {
|
||||
if errs := validateVolumeSource(&hugePagesCase, field.NewPath("field").Index(0), "working", nil); len(errs) != 0 {
|
||||
t.Errorf("Unexpected error when HugePages feature is enabled.")
|
||||
}
|
||||
|
||||
@@ -4194,7 +4359,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if errs := validateVolumeSource(&tc, field.NewPath("spec"), "tmpvol"); len(errs) != 0 {
|
||||
if errs := validateVolumeSource(&tc, field.NewPath("spec"), "tmpvol", nil); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
@@ -4937,7 +5102,7 @@ func TestValidateVolumeMounts(t *testing.T) {
|
||||
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
||||
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||
}
|
||||
vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
|
||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
||||
if len(v1err) > 0 {
|
||||
t.Errorf("Invalid test volume - expected success %v", v1err)
|
||||
return
|
||||
@@ -5000,7 +5165,7 @@ func TestValidateDisabledSubpath(t *testing.T) {
|
||||
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
||||
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||
}
|
||||
vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
|
||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
||||
if len(v1err) > 0 {
|
||||
t.Errorf("Invalid test volume - expected success %v", v1err)
|
||||
return
|
||||
@@ -5062,7 +5227,7 @@ func TestValidateSubpathMutuallyExclusive(t *testing.T) {
|
||||
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
||||
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||
}
|
||||
vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
|
||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
||||
if len(v1err) > 0 {
|
||||
t.Errorf("Invalid test volume - expected success %v", v1err)
|
||||
return
|
||||
@@ -5143,7 +5308,7 @@ func TestValidateDisabledSubpathExpr(t *testing.T) {
|
||||
{Name: "abc-123", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "testclaim2"}}},
|
||||
{Name: "123", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||
}
|
||||
vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
|
||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
||||
if len(v1err) > 0 {
|
||||
t.Errorf("Invalid test volume - expected success %v", v1err)
|
||||
return
|
||||
@@ -5337,7 +5502,7 @@ func TestValidateMountPropagation(t *testing.T) {
|
||||
volumes := []core.Volume{
|
||||
{Name: "foo", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||
}
|
||||
vols2, v2err := ValidateVolumes(volumes, field.NewPath("field"))
|
||||
vols2, v2err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
||||
if len(v2err) > 0 {
|
||||
t.Errorf("Invalid test volume - expected success %v", v2err)
|
||||
return
|
||||
@@ -5360,7 +5525,7 @@ func TestAlphaValidateVolumeDevices(t *testing.T) {
|
||||
{Name: "def", VolumeSource: core.VolumeSource{HostPath: &core.HostPathVolumeSource{Path: "/foo/baz", Type: newHostPathType(string(core.HostPathUnset))}}},
|
||||
}
|
||||
|
||||
vols, v1err := ValidateVolumes(volumes, field.NewPath("field"))
|
||||
vols, v1err := ValidateVolumes(volumes, nil, field.NewPath("field"))
|
||||
if len(v1err) > 0 {
|
||||
t.Errorf("Invalid test volumes - expected success %v", v1err)
|
||||
return
|
||||
@@ -6560,14 +6725,14 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
badfsGroupChangePolicy1 := core.PodFSGroupChangePolicy("invalid")
|
||||
badfsGroupChangePolicy2 := core.PodFSGroupChangePolicy("")
|
||||
|
||||
successCases := []core.PodSpec{
|
||||
{ // Populate basic fields, leave defaults for most.
|
||||
successCases := map[string]core.PodSpec{
|
||||
"populate basic fields, leave defaults for most": {
|
||||
Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate all fields.
|
||||
"populate all fields": {
|
||||
Volumes: []core.Volume{
|
||||
{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
|
||||
},
|
||||
@@ -6582,7 +6747,7 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
ActiveDeadlineSeconds: &activeDeadlineSeconds,
|
||||
ServiceAccountName: "acct",
|
||||
},
|
||||
{ // Populate all fields with larger active deadline.
|
||||
"populate all fields with larger active deadline": {
|
||||
Volumes: []core.Volume{
|
||||
{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}},
|
||||
},
|
||||
@@ -6597,7 +6762,7 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
ActiveDeadlineSeconds: &activeDeadlineSecondsMax,
|
||||
ServiceAccountName: "acct",
|
||||
},
|
||||
{ // Populate HostNetwork.
|
||||
"populate HostNetwork": {
|
||||
Containers: []core.Container{
|
||||
{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
|
||||
Ports: []core.ContainerPort{
|
||||
@@ -6610,7 +6775,7 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate RunAsUser SupplementalGroups FSGroup with minID 0
|
||||
"populate RunAsUser SupplementalGroups FSGroup with minID 0": {
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
SecurityContext: &core.PodSecurityContext{
|
||||
SupplementalGroups: []int64{minGroupID},
|
||||
@@ -6620,7 +6785,7 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate RunAsUser SupplementalGroups FSGroup with maxID 2147483647
|
||||
"populate RunAsUser SupplementalGroups FSGroup with maxID 2147483647": {
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
SecurityContext: &core.PodSecurityContext{
|
||||
SupplementalGroups: []int64{maxGroupID},
|
||||
@@ -6630,7 +6795,7 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate HostIPC.
|
||||
"populate HostIPC": {
|
||||
SecurityContext: &core.PodSecurityContext{
|
||||
HostIPC: true,
|
||||
},
|
||||
@@ -6639,7 +6804,7 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate HostPID.
|
||||
"populate HostPID": {
|
||||
SecurityContext: &core.PodSecurityContext{
|
||||
HostPID: true,
|
||||
},
|
||||
@@ -6648,27 +6813,27 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate Affinity.
|
||||
"populate Affinity": {
|
||||
Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate HostAliases.
|
||||
"populate HostAliases": {
|
||||
HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"host1", "host2"}}},
|
||||
Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate HostAliases with `foo.bar` hostnames.
|
||||
"populate HostAliases with `foo.bar` hostnames": {
|
||||
HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"host1.foo", "host2.bar"}}},
|
||||
Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate HostAliases with HostNetwork.
|
||||
"populate HostAliases with HostNetwork": {
|
||||
HostAliases: []core.HostAlias{{IP: "12.34.56.78", Hostnames: []string{"host1.foo", "host2.bar"}}},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
SecurityContext: &core.PodSecurityContext{
|
||||
@@ -6677,14 +6842,14 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate PriorityClassName.
|
||||
"populate PriorityClassName": {
|
||||
Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
PriorityClassName: "valid-name",
|
||||
},
|
||||
{ // Populate ShareProcessNamespace
|
||||
"populate ShareProcessNamespace": {
|
||||
Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
@@ -6693,20 +6858,20 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
ShareProcessNamespace: &[]bool{true}[0],
|
||||
},
|
||||
},
|
||||
{ // Populate RuntimeClassName
|
||||
"populate RuntimeClassName": {
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
RuntimeClassName: utilpointer.StringPtr("valid-sandbox"),
|
||||
},
|
||||
{ // Populate Overhead
|
||||
"populate Overhead": {
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
RuntimeClassName: utilpointer.StringPtr("valid-sandbox"),
|
||||
Overhead: core.ResourceList{},
|
||||
},
|
||||
{
|
||||
"populate DNSPolicy": {
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
SecurityContext: &core.PodSecurityContext{
|
||||
FSGroupChangePolicy: &goodfsGroupChangePolicy,
|
||||
@@ -6715,10 +6880,12 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
for i := range successCases {
|
||||
if errs := ValidatePodSpec(&successCases[i], field.NewPath("field")); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
for k, v := range successCases {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
if errs := ValidatePodSpec(&v, nil, field.NewPath("field")); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
activeDeadlineSeconds = int64(0)
|
||||
@@ -6919,7 +7086,7 @@ func TestValidatePodSpec(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for k, v := range failureCases {
|
||||
if errs := ValidatePodSpec(&v, field.NewPath("field")); len(errs) == 0 {
|
||||
if errs := ValidatePodSpec(&v, nil, field.NewPath("field")); len(errs) == 0 {
|
||||
t.Errorf("expected failure for %q", k)
|
||||
}
|
||||
}
|
||||
@@ -6946,9 +7113,24 @@ func TestValidatePod(t *testing.T) {
|
||||
}
|
||||
return spec
|
||||
}
|
||||
validPVCSpec := core.PersistentVolumeClaimSpec{
|
||||
AccessModes: []core.PersistentVolumeAccessMode{
|
||||
core.ReadWriteOnce,
|
||||
},
|
||||
Resources: core.ResourceRequirements{
|
||||
Requests: core.ResourceList{
|
||||
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||
},
|
||||
},
|
||||
}
|
||||
validPVCTemplate := core.PersistentVolumeClaimTemplate{
|
||||
Spec: validPVCSpec,
|
||||
}
|
||||
longPodName := strings.Repeat("a", 200)
|
||||
longVolName := strings.Repeat("b", 60)
|
||||
|
||||
successCases := []core.Pod{
|
||||
{ // Basic fields.
|
||||
successCases := map[string]core.Pod{
|
||||
"basic fields": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
|
||||
Spec: core.PodSpec{
|
||||
Volumes: []core.Volume{{Name: "vol", VolumeSource: core.VolumeSource{EmptyDir: &core.EmptyDirVolumeSource{}}}},
|
||||
@@ -6957,7 +7139,7 @@ func TestValidatePod(t *testing.T) {
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // Just about everything.
|
||||
"just about everything": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "abc.123.do-re-mi", Namespace: "ns"},
|
||||
Spec: core.PodSpec{
|
||||
Volumes: []core.Volume{
|
||||
@@ -6972,7 +7154,7 @@ func TestValidatePod(t *testing.T) {
|
||||
NodeName: "foobar",
|
||||
},
|
||||
},
|
||||
{ // Serialized node affinity requirements.
|
||||
"serialized node affinity requirements": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7032,7 +7214,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
),
|
||||
},
|
||||
{ // Serialized node affinity requirements.
|
||||
"serialized node affinity requirements, II": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7073,7 +7255,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
),
|
||||
},
|
||||
{ // Serialized pod affinity in affinity requirements in annotations.
|
||||
"serialized pod affinity in affinity requirements in annotations": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7129,7 +7311,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
},
|
||||
{ // Serialized pod anti affinity with different Label Operators in affinity requirements in annotations.
|
||||
"serialized pod anti affinity with different Label Operators in affinity requirements in annotations": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7183,63 +7365,63 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
},
|
||||
{ // populate forgiveness tolerations with exists operator in annotations.
|
||||
"populate forgiveness tolerations with exists operator in annotations.": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Exists", Value: "", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]}}),
|
||||
},
|
||||
{ // populate forgiveness tolerations with equal operator in annotations.
|
||||
"populate forgiveness tolerations with equal operator in annotations.": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]}}),
|
||||
},
|
||||
{ // populate tolerations equal operator in annotations.
|
||||
"populate tolerations equal operator in annotations.": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"}}),
|
||||
},
|
||||
{ // populate tolerations exists operator in annotations.
|
||||
"populate tolerations exists operator in annotations.": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: validPodSpec(nil),
|
||||
},
|
||||
{ // empty key with Exists operator is OK for toleration, empty toleration key means match all taint keys.
|
||||
"empty key with Exists operator is OK for toleration, empty toleration key means match all taint keys.": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Operator: "Exists", Effect: "NoSchedule"}}),
|
||||
},
|
||||
{ // empty operator is OK for toleration, defaults to Equal.
|
||||
"empty operator is OK for toleration, defaults to Equal.": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Value: "bar", Effect: "NoSchedule"}}),
|
||||
},
|
||||
{ // empty effect is OK for toleration, empty toleration effect means match all taint effects.
|
||||
"empty effect is OK for toleration, empty toleration effect means match all taint effects.": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "foo", Operator: "Equal", Value: "bar"}}),
|
||||
},
|
||||
{ // negative tolerationSeconds is OK for toleration.
|
||||
"negative tolerationSeconds is OK for toleration.": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod-forgiveness-invalid",
|
||||
Namespace: "ns",
|
||||
},
|
||||
Spec: extendPodSpecwithTolerations(validPodSpec(nil), []core.Toleration{{Key: "node.kubernetes.io/not-ready", Operator: "Exists", Effect: "NoExecute", TolerationSeconds: &[]int64{-2}[0]}}),
|
||||
},
|
||||
{ // runtime default seccomp profile
|
||||
"runtime default seccomp profile": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7249,7 +7431,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
Spec: validPodSpec(nil),
|
||||
},
|
||||
{ // docker default seccomp profile
|
||||
"docker default seccomp profile": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7259,7 +7441,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
Spec: validPodSpec(nil),
|
||||
},
|
||||
{ // unconfined seccomp profile
|
||||
"unconfined seccomp profile": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7269,7 +7451,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
Spec: validPodSpec(nil),
|
||||
},
|
||||
{ // localhost seccomp profile
|
||||
"localhost seccomp profile": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7279,7 +7461,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
Spec: validPodSpec(nil),
|
||||
},
|
||||
{ // localhost seccomp profile for a container
|
||||
"localhost seccomp profile for a container": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7289,7 +7471,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
Spec: validPodSpec(nil),
|
||||
},
|
||||
{ // runtime default seccomp profile for a pod
|
||||
"runtime default seccomp profile for a pod": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7305,7 +7487,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // runtime default seccomp profile for a container
|
||||
"runtime default seccomp profile for a container": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7322,7 +7504,7 @@ func TestValidatePod(t *testing.T) {
|
||||
DNSPolicy: core.DNSDefault,
|
||||
},
|
||||
},
|
||||
{ // unconfined seccomp profile for a pod
|
||||
"unconfined seccomp profile for a pod": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7338,7 +7520,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // unconfined seccomp profile for a container
|
||||
"unconfined seccomp profile for a container": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7355,7 +7537,7 @@ func TestValidatePod(t *testing.T) {
|
||||
DNSPolicy: core.DNSDefault,
|
||||
},
|
||||
},
|
||||
{ // localhost seccomp profile for a pod
|
||||
"localhost seccomp profile for a pod": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7372,7 +7554,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // localhost seccomp profile for a container
|
||||
"localhost seccomp profile for a container, II": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7390,7 +7572,7 @@ func TestValidatePod(t *testing.T) {
|
||||
DNSPolicy: core.DNSDefault,
|
||||
},
|
||||
},
|
||||
{ // default AppArmor profile for a container
|
||||
"default AppArmor profile for a container": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7400,7 +7582,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
Spec: validPodSpec(nil),
|
||||
},
|
||||
{ // default AppArmor profile for an init container
|
||||
"default AppArmor profile for an init container": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7415,7 +7597,7 @@ func TestValidatePod(t *testing.T) {
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // localhost AppArmor profile for a container
|
||||
"localhost AppArmor profile for a container": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7425,7 +7607,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
Spec: validPodSpec(nil),
|
||||
},
|
||||
{ // syntactically valid sysctls
|
||||
"syntactically valid sysctls": {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "123",
|
||||
Namespace: "ns",
|
||||
@@ -7452,7 +7634,7 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // valid extended resources for init container
|
||||
"valid extended resources for init container": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
|
||||
Spec: core.PodSpec{
|
||||
InitContainers: []core.Container{
|
||||
@@ -7476,7 +7658,7 @@ func TestValidatePod(t *testing.T) {
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // valid extended resources for regular container
|
||||
"valid extended resources for regular container": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
|
||||
Spec: core.PodSpec{
|
||||
InitContainers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
@@ -7500,7 +7682,7 @@ func TestValidatePod(t *testing.T) {
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // valid serviceaccount token projected volume with serviceaccount name specified
|
||||
"valid serviceaccount token projected volume with serviceaccount name specified": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
|
||||
Spec: core.PodSpec{
|
||||
ServiceAccountName: "some-service-account",
|
||||
@@ -7527,11 +7709,25 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
"ephemeral volume + PVC, no conflict between them": {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
|
||||
Spec: core.PodSpec{
|
||||
Volumes: []core.Volume{
|
||||
{Name: "pvc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "my-pvc"}}},
|
||||
{Name: "ephemeral", VolumeSource: core.VolumeSource{Ephemeral: &core.EphemeralVolumeSource{VolumeClaimTemplate: &validPVCTemplate}}},
|
||||
},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, pod := range successCases {
|
||||
if errs := ValidatePodCreate(&pod, PodValidationOptions{}); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
for k, v := range successCases {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
if errs := ValidatePodCreate(&v, PodValidationOptions{}); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
errorCases := map[string]struct {
|
||||
@@ -8421,15 +8617,47 @@ func TestValidatePod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
"final PVC name for ephemeral volume must be valid": {
|
||||
expectedError: "spec.volumes[1].name: Invalid value: \"" + longVolName + "\": PVC name \"" + longPodName + "-" + longVolName + "\": must be no more than 253 characters",
|
||||
spec: core.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: longPodName, Namespace: "ns"},
|
||||
Spec: core.PodSpec{
|
||||
Volumes: []core.Volume{
|
||||
{Name: "pvc", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "my-pvc"}}},
|
||||
{Name: longVolName, VolumeSource: core.VolumeSource{Ephemeral: &core.EphemeralVolumeSource{VolumeClaimTemplate: &validPVCTemplate}}},
|
||||
},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
"PersistentVolumeClaimVolumeSource must not reference a generated PVC": {
|
||||
expectedError: "spec.volumes[0].persistentVolumeClaim.claimName: Invalid value: \"123-ephemeral-volume\": must not reference a PVC that gets created for an ephemeral volume",
|
||||
spec: core.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
|
||||
Spec: core.PodSpec{
|
||||
Volumes: []core.Volume{
|
||||
{Name: "pvc-volume", VolumeSource: core.VolumeSource{PersistentVolumeClaim: &core.PersistentVolumeClaimVolumeSource{ClaimName: "123-ephemeral-volume"}}},
|
||||
{Name: "ephemeral-volume", VolumeSource: core.VolumeSource{Ephemeral: &core.EphemeralVolumeSource{VolumeClaimTemplate: &validPVCTemplate}}},
|
||||
},
|
||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||
RestartPolicy: core.RestartPolicyAlways,
|
||||
DNSPolicy: core.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
if errs := ValidatePodCreate(&v.spec, PodValidationOptions{}); len(errs) == 0 {
|
||||
t.Errorf("expected failure for %q", k)
|
||||
} else if v.expectedError == "" {
|
||||
t.Errorf("missing expectedError for %q, got %q", k, errs.ToAggregate().Error())
|
||||
} else if actualError := errs.ToAggregate().Error(); !strings.Contains(actualError, v.expectedError) {
|
||||
t.Errorf("expected error for %q to contain %q, got %q", k, v.expectedError, actualError)
|
||||
}
|
||||
t.Run(k, func(t *testing.T) {
|
||||
if errs := ValidatePodCreate(&v.spec, PodValidationOptions{}); len(errs) == 0 {
|
||||
t.Errorf("expected failure")
|
||||
} else if v.expectedError == "" {
|
||||
t.Errorf("missing expectedError, got %q", errs.ToAggregate().Error())
|
||||
} else if actualError := errs.ToAggregate().Error(); !strings.Contains(actualError, v.expectedError) {
|
||||
t.Errorf("expected error to contain %q, got %q", v.expectedError, actualError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user