Skip to content

Commit 2529d7d

Browse files
committed
TestMakeMounts: add new cases for the image volume feature
1 parent bc79d3b commit 2529d7d

File tree

2 files changed

+241
-32
lines changed

2 files changed

+241
-32
lines changed

pkg/kubelet/kubelet_pods_linux_test.go

Lines changed: 238 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,21 @@ limitations under the License.
2020
package kubelet
2121

2222
import (
23+
"fmt"
2324
"testing"
2425

2526
"github.com/stretchr/testify/assert"
2627
v1 "k8s.io/api/core/v1"
2728
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
utilfeature "k8s.io/apiserver/pkg/util/feature"
30+
featuregatetesting "k8s.io/component-base/featuregate/testing"
2831
"k8s.io/utils/ptr"
2932

3033
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
3134
_ "k8s.io/kubernetes/pkg/apis/core/install"
35+
"k8s.io/kubernetes/pkg/features"
3236
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
37+
"k8s.io/kubernetes/pkg/volume"
3338
volumetest "k8s.io/kubernetes/pkg/volume/testing"
3439
"k8s.io/kubernetes/pkg/volume/util/hostutil"
3540
"k8s.io/kubernetes/pkg/volume/util/subpath"
@@ -40,16 +45,21 @@ func TestMakeMounts(t *testing.T) {
4045
propagationHostToContainer := v1.MountPropagationHostToContainer
4146
propagationBidirectional := v1.MountPropagationBidirectional
4247
propagationNone := v1.MountPropagationNone
48+
image1 := &runtimeapi.ImageSpec{Image: "image1"}
49+
image2 := &runtimeapi.ImageSpec{Image: "image2"}
4350

4451
testCases := map[string]struct {
45-
container v1.Container
46-
podVolumes kubecontainer.VolumeMap
47-
supportsRRO bool
48-
expectErr bool
49-
expectedErrMsg string
50-
expectedMounts []kubecontainer.Mount
52+
container v1.Container
53+
podVolumes kubecontainer.VolumeMap
54+
imageVolumes kubecontainer.ImageVolumes
55+
imageVolumeFeatureEnabled []bool
56+
supportsRRO bool
57+
expectErr bool
58+
expectedErrMsg string
59+
expectedMounts []kubecontainer.Mount
5160
}{
52-
"valid mounts in unprivileged container": {
61+
"valid mounts in unprivileged container": { // TODO: remove it once image volume feature is GA
62+
imageVolumeFeatureEnabled: []bool{true, false},
5363
podVolumes: kubecontainer.VolumeMap{
5464
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
5565
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
@@ -118,7 +128,8 @@ func TestMakeMounts(t *testing.T) {
118128
},
119129
expectErr: false,
120130
},
121-
"valid mounts in privileged container": {
131+
"valid mounts in privileged container": { // TODO: remove it once image volume feature is GA
132+
imageVolumeFeatureEnabled: []bool{true, false},
122133
podVolumes: kubecontainer.VolumeMap{
123134
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
124135
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
@@ -177,7 +188,198 @@ func TestMakeMounts(t *testing.T) {
177188
},
178189
expectErr: false,
179190
},
191+
"valid mounts in unprivileged container with image volumes": {
192+
imageVolumeFeatureEnabled: []bool{true},
193+
podVolumes: kubecontainer.VolumeMap{
194+
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
195+
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
196+
"disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/var/lib/kubelet/podID/volumes/empty/disk5"}},
197+
"image1": kubecontainer.VolumeInfo{Mounter: &stubVolume{attributes: volume.Attributes{ReadOnly: true}}},
198+
"image2": kubecontainer.VolumeInfo{Mounter: &stubVolume{attributes: volume.Attributes{ReadOnly: true}}},
199+
},
200+
imageVolumes: kubecontainer.ImageVolumes{
201+
"image1": image1,
202+
"image2": image2,
203+
},
204+
container: v1.Container{
205+
Name: "container1",
206+
VolumeMounts: []v1.VolumeMount{
207+
{
208+
MountPath: "/etc/hosts",
209+
Name: "disk",
210+
ReadOnly: false,
211+
MountPropagation: &propagationHostToContainer,
212+
},
213+
{
214+
MountPath: "/mnt/path3",
215+
Name: "disk",
216+
ReadOnly: true,
217+
MountPropagation: &propagationNone,
218+
},
219+
{
220+
MountPath: "/mnt/path4",
221+
Name: "disk4",
222+
ReadOnly: false,
223+
},
224+
{
225+
MountPath: "/mnt/path5",
226+
Name: "disk5",
227+
ReadOnly: false,
228+
},
229+
{
230+
MountPath: "/mnt/image1",
231+
Name: "image1",
232+
ReadOnly: false,
233+
},
234+
{
235+
MountPath: "/mnt/image2",
236+
Name: "image2",
237+
ReadOnly: true,
238+
},
239+
},
240+
},
241+
expectedMounts: []kubecontainer.Mount{
242+
{
243+
Name: "disk",
244+
ContainerPath: "/etc/hosts",
245+
HostPath: "/mnt/disk",
246+
ReadOnly: false,
247+
SELinuxRelabel: false,
248+
Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
249+
},
250+
{
251+
Name: "disk",
252+
ContainerPath: "/mnt/path3",
253+
HostPath: "/mnt/disk",
254+
ReadOnly: true,
255+
SELinuxRelabel: false,
256+
Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE,
257+
},
258+
{
259+
Name: "disk4",
260+
ContainerPath: "/mnt/path4",
261+
HostPath: "/mnt/host",
262+
ReadOnly: false,
263+
SELinuxRelabel: false,
264+
Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE,
265+
},
266+
{
267+
Name: "disk5",
268+
ContainerPath: "/mnt/path5",
269+
HostPath: "/var/lib/kubelet/podID/volumes/empty/disk5",
270+
ReadOnly: false,
271+
SELinuxRelabel: false,
272+
Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE,
273+
},
274+
{
275+
Name: "image1",
276+
ContainerPath: "/mnt/image1",
277+
Image: image1,
278+
ReadOnly: true,
279+
SELinuxRelabel: false,
280+
},
281+
{
282+
Name: "image2",
283+
ContainerPath: "/mnt/image2",
284+
Image: image2,
285+
ReadOnly: true,
286+
SELinuxRelabel: false,
287+
},
288+
},
289+
expectErr: false,
290+
},
291+
"valid mounts in privileged container with image volumes": {
292+
imageVolumeFeatureEnabled: []bool{true},
293+
podVolumes: kubecontainer.VolumeMap{
294+
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
295+
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}},
296+
"disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/var/lib/kubelet/podID/volumes/empty/disk5"}},
297+
"image1": kubecontainer.VolumeInfo{Mounter: &stubVolume{attributes: volume.Attributes{ReadOnly: true}}},
298+
"image2": kubecontainer.VolumeInfo{Mounter: &stubVolume{attributes: volume.Attributes{ReadOnly: true}}},
299+
},
300+
imageVolumes: kubecontainer.ImageVolumes{
301+
"image1": image1,
302+
"image2": image2,
303+
},
304+
container: v1.Container{
305+
Name: "container1",
306+
VolumeMounts: []v1.VolumeMount{
307+
{
308+
MountPath: "/etc/hosts",
309+
Name: "disk",
310+
ReadOnly: false,
311+
MountPropagation: &propagationBidirectional,
312+
},
313+
{
314+
MountPath: "/mnt/path3",
315+
Name: "disk",
316+
ReadOnly: true,
317+
MountPropagation: &propagationHostToContainer,
318+
},
319+
{
320+
MountPath: "/mnt/path4",
321+
Name: "disk4",
322+
ReadOnly: false,
323+
},
324+
{
325+
MountPath: "/mnt/image1",
326+
Name: "image1",
327+
ReadOnly: false,
328+
},
329+
{
330+
MountPath: "/mnt/image2",
331+
Name: "image2",
332+
ReadOnly: true,
333+
},
334+
},
335+
SecurityContext: &v1.SecurityContext{
336+
Privileged: &bTrue,
337+
},
338+
},
339+
expectedMounts: []kubecontainer.Mount{
340+
{
341+
Name: "disk",
342+
ContainerPath: "/etc/hosts",
343+
HostPath: "/mnt/disk",
344+
ReadOnly: false,
345+
SELinuxRelabel: false,
346+
Propagation: runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL,
347+
},
348+
{
349+
Name: "disk",
350+
ContainerPath: "/mnt/path3",
351+
HostPath: "/mnt/disk",
352+
ReadOnly: true,
353+
SELinuxRelabel: false,
354+
Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER,
355+
},
356+
{
357+
Name: "disk4",
358+
ContainerPath: "/mnt/path4",
359+
HostPath: "/mnt/host",
360+
ReadOnly: false,
361+
SELinuxRelabel: false,
362+
Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE,
363+
},
364+
{
365+
Name: "image1",
366+
ContainerPath: "/mnt/image1",
367+
Image: image1,
368+
ReadOnly: true,
369+
SELinuxRelabel: false,
370+
},
371+
{
372+
Name: "image2",
373+
ContainerPath: "/mnt/image2",
374+
Image: image2,
375+
ReadOnly: true,
376+
SELinuxRelabel: false,
377+
},
378+
},
379+
expectErr: false,
380+
},
180381
"invalid absolute SubPath": {
382+
imageVolumeFeatureEnabled: []bool{true, false},
181383
podVolumes: kubecontainer.VolumeMap{
182384
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
183385
},
@@ -195,6 +397,7 @@ func TestMakeMounts(t *testing.T) {
195397
expectedErrMsg: "error SubPath `/must/not/be/absolute` must not be an absolute path",
196398
},
197399
"invalid SubPath with backsteps": {
400+
imageVolumeFeatureEnabled: []bool{true, false},
198401
podVolumes: kubecontainer.VolumeMap{
199402
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}},
200403
},
@@ -212,7 +415,7 @@ func TestMakeMounts(t *testing.T) {
212415
expectedErrMsg: "unable to provision SubPath `no/backsteps/../allowed`: must not contain '..'",
213416
},
214417
"volume doesn't exist": {
215-
podVolumes: kubecontainer.VolumeMap{},
418+
imageVolumeFeatureEnabled: []bool{true, false},
216419
container: v1.Container{
217420
VolumeMounts: []v1.VolumeMount{
218421
{
@@ -226,6 +429,7 @@ func TestMakeMounts(t *testing.T) {
226429
expectedErrMsg: "cannot find volume \"disk\" to mount into container \"\"",
227430
},
228431
"volume mounter is nil": {
432+
imageVolumeFeatureEnabled: []bool{true, false},
229433
podVolumes: kubecontainer.VolumeMap{
230434
"disk": kubecontainer.VolumeInfo{},
231435
},
@@ -244,32 +448,36 @@ func TestMakeMounts(t *testing.T) {
244448
}
245449

246450
for name, tc := range testCases {
247-
t.Run(name, func(t *testing.T) {
248-
fhu := hostutil.NewFakeHostUtil(nil)
249-
fsp := &subpath.FakeSubpath{}
250-
pod := v1.Pod{
251-
Spec: v1.PodSpec{
252-
HostNetwork: true,
253-
},
254-
}
451+
for _, featureEnabled := range tc.imageVolumeFeatureEnabled {
452+
name := fmt.Sprintf("features.ImageVolume is %v, %s", featureEnabled, name)
453+
t.Run(name, func(t *testing.T) {
454+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ImageVolume, featureEnabled)
455+
fhu := hostutil.NewFakeHostUtil(nil)
456+
fsp := &subpath.FakeSubpath{}
457+
pod := v1.Pod{
458+
Spec: v1.PodSpec{
459+
HostNetwork: true,
460+
},
461+
}
255462

256-
mounts, _, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", []string{""}, tc.podVolumes, fhu, fsp, nil, tc.supportsRRO, nil)
463+
mounts, _, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", []string{""}, tc.podVolumes, fhu, fsp, nil, tc.supportsRRO, tc.imageVolumes)
257464

258-
// validate only the error if we expect an error
259-
if tc.expectErr {
260-
if err == nil || err.Error() != tc.expectedErrMsg {
261-
t.Fatalf("expected error message `%s` but got `%v`", tc.expectedErrMsg, err)
465+
// validate only the error if we expect an error
466+
if tc.expectErr {
467+
if err == nil || err.Error() != tc.expectedErrMsg {
468+
t.Fatalf("expected error message `%s` but got `%v`", tc.expectedErrMsg, err)
469+
}
470+
return
262471
}
263-
return
264-
}
265472

266-
// otherwise validate the mounts
267-
if err != nil {
268-
t.Fatal(err)
269-
}
473+
// otherwise validate the mounts
474+
if err != nil {
475+
t.Fatal(err)
476+
}
270477

271-
assert.Equal(t, tc.expectedMounts, mounts, "mounts of container %+v", tc.container)
272-
})
478+
assert.Equal(t, tc.expectedMounts, mounts, "mounts of container %+v", tc.container)
479+
})
480+
}
273481
}
274482
}
275483

pkg/kubelet/kubelet_volumes_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,8 @@ func TestVolumeUnmountAndDetachControllerEnabled(t *testing.T) {
689689
}
690690

691691
type stubVolume struct {
692-
path string
692+
path string
693+
attributes volume.Attributes
693694
volume.MetricsNil
694695
}
695696

@@ -698,7 +699,7 @@ func (f *stubVolume) GetPath() string {
698699
}
699700

700701
func (f *stubVolume) GetAttributes() volume.Attributes {
701-
return volume.Attributes{}
702+
return f.attributes
702703
}
703704

704705
func (f *stubVolume) SetUp(mounterArgs volume.MounterArgs) error {

0 commit comments

Comments
 (0)