@@ -20,16 +20,21 @@ limitations under the License.
20
20
package kubelet
21
21
22
22
import (
23
+ "fmt"
23
24
"testing"
24
25
25
26
"github.com/stretchr/testify/assert"
26
27
v1 "k8s.io/api/core/v1"
27
28
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
30
+ featuregatetesting "k8s.io/component-base/featuregate/testing"
28
31
"k8s.io/utils/ptr"
29
32
30
33
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
31
34
_ "k8s.io/kubernetes/pkg/apis/core/install"
35
+ "k8s.io/kubernetes/pkg/features"
32
36
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
37
+ "k8s.io/kubernetes/pkg/volume"
33
38
volumetest "k8s.io/kubernetes/pkg/volume/testing"
34
39
"k8s.io/kubernetes/pkg/volume/util/hostutil"
35
40
"k8s.io/kubernetes/pkg/volume/util/subpath"
@@ -40,16 +45,21 @@ func TestMakeMounts(t *testing.T) {
40
45
propagationHostToContainer := v1 .MountPropagationHostToContainer
41
46
propagationBidirectional := v1 .MountPropagationBidirectional
42
47
propagationNone := v1 .MountPropagationNone
48
+ image1 := & runtimeapi.ImageSpec {Image : "image1" }
49
+ image2 := & runtimeapi.ImageSpec {Image : "image2" }
43
50
44
51
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
51
60
}{
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 },
53
63
podVolumes : kubecontainer.VolumeMap {
54
64
"disk" : kubecontainer.VolumeInfo {Mounter : & stubVolume {path : "/mnt/disk" }},
55
65
"disk4" : kubecontainer.VolumeInfo {Mounter : & stubVolume {path : "/mnt/host" }},
@@ -118,7 +128,8 @@ func TestMakeMounts(t *testing.T) {
118
128
},
119
129
expectErr : false ,
120
130
},
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 },
122
133
podVolumes : kubecontainer.VolumeMap {
123
134
"disk" : kubecontainer.VolumeInfo {Mounter : & stubVolume {path : "/mnt/disk" }},
124
135
"disk4" : kubecontainer.VolumeInfo {Mounter : & stubVolume {path : "/mnt/host" }},
@@ -177,7 +188,198 @@ func TestMakeMounts(t *testing.T) {
177
188
},
178
189
expectErr : false ,
179
190
},
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
+ },
180
381
"invalid absolute SubPath" : {
382
+ imageVolumeFeatureEnabled : []bool {true , false },
181
383
podVolumes : kubecontainer.VolumeMap {
182
384
"disk" : kubecontainer.VolumeInfo {Mounter : & stubVolume {path : "/mnt/disk" }},
183
385
},
@@ -195,6 +397,7 @@ func TestMakeMounts(t *testing.T) {
195
397
expectedErrMsg : "error SubPath `/must/not/be/absolute` must not be an absolute path" ,
196
398
},
197
399
"invalid SubPath with backsteps" : {
400
+ imageVolumeFeatureEnabled : []bool {true , false },
198
401
podVolumes : kubecontainer.VolumeMap {
199
402
"disk" : kubecontainer.VolumeInfo {Mounter : & stubVolume {path : "/mnt/disk" }},
200
403
},
@@ -212,7 +415,7 @@ func TestMakeMounts(t *testing.T) {
212
415
expectedErrMsg : "unable to provision SubPath `no/backsteps/../allowed`: must not contain '..'" ,
213
416
},
214
417
"volume doesn't exist" : {
215
- podVolumes : kubecontainer. VolumeMap { },
418
+ imageVolumeFeatureEnabled : [] bool { true , false },
216
419
container : v1.Container {
217
420
VolumeMounts : []v1.VolumeMount {
218
421
{
@@ -226,6 +429,7 @@ func TestMakeMounts(t *testing.T) {
226
429
expectedErrMsg : "cannot find volume \" disk\" to mount into container \" \" " ,
227
430
},
228
431
"volume mounter is nil" : {
432
+ imageVolumeFeatureEnabled : []bool {true , false },
229
433
podVolumes : kubecontainer.VolumeMap {
230
434
"disk" : kubecontainer.VolumeInfo {},
231
435
},
@@ -244,32 +448,36 @@ func TestMakeMounts(t *testing.T) {
244
448
}
245
449
246
450
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
+ }
255
462
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 )
257
464
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
262
471
}
263
- return
264
- }
265
472
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
+ }
270
477
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
+ }
273
481
}
274
482
}
275
483
0 commit comments