Commit 5bf84db7 authored by Serguei Bezverkhi's avatar Serguei Bezverkhi

VolumeDevices validation and tests

parent c20aca07
...@@ -264,7 +264,7 @@ func DropDisabledFields(podSpec, oldPodSpec *api.PodSpec) { ...@@ -264,7 +264,7 @@ func DropDisabledFields(podSpec, oldPodSpec *api.PodSpec) {
} }
} }
dropDisabledVolumeDevicesAlphaFields(podSpec, oldPodSpec) dropDisabledVolumeDevicesFields(podSpec, oldPodSpec)
dropDisabledRunAsGroupField(podSpec, oldPodSpec) dropDisabledRunAsGroupField(podSpec, oldPodSpec)
...@@ -330,25 +330,16 @@ func dropDisabledProcMountField(podSpec, oldPodSpec *api.PodSpec) { ...@@ -330,25 +330,16 @@ func dropDisabledProcMountField(podSpec, oldPodSpec *api.PodSpec) {
} }
} }
// dropDisabledVolumeDevicesAlphaFields removes disabled fields from []VolumeDevice. // dropDisabledVolumeDevicesFields removes disabled fields from []VolumeDevice if it has not been already populated.
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a VolumeDevice // This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a VolumeDevice
func dropDisabledVolumeDevicesAlphaFields(podSpec, oldPodSpec *api.PodSpec) { func dropDisabledVolumeDevicesFields(podSpec, oldPodSpec *api.PodSpec) {
if !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) { if !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) && !volumeDevicesInUse(oldPodSpec) {
for i := range podSpec.Containers { for i := range podSpec.Containers {
podSpec.Containers[i].VolumeDevices = nil podSpec.Containers[i].VolumeDevices = nil
} }
for i := range podSpec.InitContainers { for i := range podSpec.InitContainers {
podSpec.InitContainers[i].VolumeDevices = nil podSpec.InitContainers[i].VolumeDevices = nil
} }
if oldPodSpec != nil {
for i := range oldPodSpec.Containers {
oldPodSpec.Containers[i].VolumeDevices = nil
}
for i := range oldPodSpec.InitContainers {
oldPodSpec.InitContainers[i].VolumeDevices = nil
}
}
} }
} }
...@@ -436,3 +427,21 @@ func emptyDirSizeLimitInUse(podSpec *api.PodSpec) bool { ...@@ -436,3 +427,21 @@ func emptyDirSizeLimitInUse(podSpec *api.PodSpec) bool {
} }
return false return false
} }
// volumeDevicesInUse returns true if the pod spec is non-nil and has VolumeDevices set.
func volumeDevicesInUse(podSpec *api.PodSpec) bool {
if podSpec == nil {
return false
}
for i := range podSpec.Containers {
if podSpec.Containers[i].VolumeDevices != nil {
return true
}
}
for i := range podSpec.InitContainers {
if podSpec.InitContainers[i].VolumeDevices != nil {
return true
}
}
return false
}
...@@ -266,74 +266,146 @@ func TestPodConfigmaps(t *testing.T) { ...@@ -266,74 +266,146 @@ func TestPodConfigmaps(t *testing.T) {
} }
func TestDropAlphaVolumeDevices(t *testing.T) { func TestDropAlphaVolumeDevices(t *testing.T) {
testPod := api.Pod{ podWithVolumeDevices := func() *api.Pod {
Spec: api.PodSpec{ return &api.Pod{
RestartPolicy: api.RestartPolicyNever, Spec: api.PodSpec{
Containers: []api.Container{ RestartPolicy: api.RestartPolicyNever,
{ Containers: []api.Container{
Name: "container1", {
Image: "testimage", Name: "container1",
VolumeDevices: []api.VolumeDevice{ Image: "testimage",
{ VolumeDevices: []api.VolumeDevice{
Name: "myvolume", {
DevicePath: "/usr/test", Name: "myvolume",
DevicePath: "/usr/test",
},
}, },
}, },
}, },
}, InitContainers: []api.Container{
InitContainers: []api.Container{ {
{ Name: "container1",
Name: "container1", Image: "testimage",
Image: "testimage", VolumeDevices: []api.VolumeDevice{
VolumeDevices: []api.VolumeDevice{ {
{ Name: "myvolume",
Name: "myvolume", DevicePath: "/usr/test",
DevicePath: "/usr/test", },
},
},
},
Volumes: []api.Volume{
{
Name: "myvolume",
VolumeSource: api.VolumeSource{
HostPath: &api.HostPathVolumeSource{
Path: "/dev/xvdc",
},
}, },
}, },
}, },
}, },
Volumes: []api.Volume{ }
{ }
Name: "myvolume", podWithoutVolumeDevices := func() *api.Pod {
VolumeSource: api.VolumeSource{ return &api.Pod{
HostPath: &api.HostPathVolumeSource{ Spec: api.PodSpec{
Path: "/dev/xvdc", RestartPolicy: api.RestartPolicyNever,
Containers: []api.Container{
{
Name: "container1",
Image: "testimage",
},
},
InitContainers: []api.Container{
{
Name: "container1",
Image: "testimage",
},
},
Volumes: []api.Volume{
{
Name: "myvolume",
VolumeSource: api.VolumeSource{
HostPath: &api.HostPathVolumeSource{
Path: "/dev/xvdc",
},
}, },
}, },
}, },
}, },
}, }
} }
// Enable alpha feature BlockVolume podInfo := []struct {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)() description string
hasVolumeDevices bool
pod func() *api.Pod
}{
{
description: "has VolumeDevices",
hasVolumeDevices: true,
pod: podWithVolumeDevices,
},
{
description: "does not have VolumeDevices",
hasVolumeDevices: false,
pod: podWithoutVolumeDevices,
},
{
description: "is nil",
hasVolumeDevices: false,
pod: func() *api.Pod { return nil },
},
}
// now test dropping the fields - should not be dropped for _, enabled := range []bool{true, false} {
DropDisabledFields(&testPod.Spec, nil) for _, oldPodInfo := range podInfo {
for _, newPodInfo := range podInfo {
oldPodHasVolumeDevices, oldPod := oldPodInfo.hasVolumeDevices, oldPodInfo.pod()
newPodHasVolumeDevices, newPod := newPodInfo.hasVolumeDevices, newPodInfo.pod()
if newPod == nil {
continue
}
// check to make sure VolumeDevices is still present t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
// if featureset is set to true defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, enabled)()
if testPod.Spec.Containers[0].VolumeDevices == nil {
t.Error("VolumeDevices in Container should not have been dropped based on feature-gate")
}
if testPod.Spec.InitContainers[0].VolumeDevices == nil {
t.Error("VolumeDevices in InitContainers should not have been dropped based on feature-gate")
}
// Disable alpha feature BlockVolume var oldPodSpec *api.PodSpec
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, false)() if oldPod != nil {
oldPodSpec = &oldPod.Spec
}
DropDisabledFields(&newPod.Spec, oldPodSpec)
// now test dropping the fields // old pod should never be changed
DropDisabledFields(&testPod.Spec, nil) if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
}
// check to make sure VolumeDevices is nil switch {
// if featureset is set to false case enabled || oldPodHasVolumeDevices:
if testPod.Spec.Containers[0].VolumeDevices != nil { // new pod should not be changed if the feature is enabled, or if the old pod had VolumeDevices
t.Error("DropDisabledFields for Containers failed") if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
} t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
if testPod.Spec.InitContainers[0].VolumeDevices != nil { }
t.Error("DropDisabledFields for InitContainers failed") case newPodHasVolumeDevices:
// new pod should be changed
if reflect.DeepEqual(newPod, newPodInfo.pod()) {
t.Errorf("new pod was not changed")
}
// new pod should not have VolumeDevices
if !reflect.DeepEqual(newPod, podWithoutVolumeDevices()) {
t.Errorf("new pod had VolumeDevices: %v", diff.ObjectReflectDiff(newPod, podWithoutVolumeDevices()))
}
default:
// new pod should not need to be changed
if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
}
}
})
}
}
} }
} }
......
...@@ -2298,10 +2298,6 @@ func ValidateVolumeDevices(devices []core.VolumeDevice, volmounts map[string]str ...@@ -2298,10 +2298,6 @@ func ValidateVolumeDevices(devices []core.VolumeDevice, volmounts map[string]str
devicepath := sets.NewString() devicepath := sets.NewString()
devicename := sets.NewString() devicename := sets.NewString()
if devices != nil && !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("volumeDevices"), "Container volumeDevices is disabled by feature-gate"))
return allErrs
}
if devices != nil { if devices != nil {
for i, dev := range devices { for i, dev := range devices {
idxPath := fldPath.Index(i) idxPath := fldPath.Index(i)
......
...@@ -4974,10 +4974,6 @@ func TestAlphaValidateVolumeDevices(t *testing.T) { ...@@ -4974,10 +4974,6 @@ func TestAlphaValidateVolumeDevices(t *testing.T) {
return return
} }
disabledAlphaVolDevice := []core.VolumeDevice{
{Name: "abc", DevicePath: "/foo"},
}
successCase := []core.VolumeDevice{ successCase := []core.VolumeDevice{
{Name: "abc", DevicePath: "/foo"}, {Name: "abc", DevicePath: "/foo"},
{Name: "abc-123", DevicePath: "/usr/share/test"}, {Name: "abc-123", DevicePath: "/usr/share/test"},
...@@ -5005,8 +5001,6 @@ func TestAlphaValidateVolumeDevices(t *testing.T) { ...@@ -5005,8 +5001,6 @@ func TestAlphaValidateVolumeDevices(t *testing.T) {
{Name: "abc-123", MountPath: "/this/path/exists"}, {Name: "abc-123", MountPath: "/this/path/exists"},
} }
// enable BlockVolume
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
// Success Cases: // Success Cases:
// Validate normal success cases - only PVC volumeSource // Validate normal success cases - only PVC volumeSource
if errs := ValidateVolumeDevices(successCase, GetVolumeMountMap(goodVolumeMounts), vols, field.NewPath("field")); len(errs) != 0 { if errs := ValidateVolumeDevices(successCase, GetVolumeMountMap(goodVolumeMounts), vols, field.NewPath("field")); len(errs) != 0 {
...@@ -5020,12 +5014,6 @@ func TestAlphaValidateVolumeDevices(t *testing.T) { ...@@ -5020,12 +5014,6 @@ func TestAlphaValidateVolumeDevices(t *testing.T) {
t.Errorf("expected failure for %s", k) t.Errorf("expected failure for %s", k)
} }
} }
// disable BlockVolume
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, false)()
if errs := ValidateVolumeDevices(disabledAlphaVolDevice, GetVolumeMountMap(goodVolumeMounts), vols, field.NewPath("field")); len(errs) == 0 {
t.Errorf("expected failure: %v", errs)
}
} }
func TestValidateProbe(t *testing.T) { func TestValidateProbe(t *testing.T) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment