@@ -35,6 +35,11 @@ import (
35
35
"github.com/firecracker-microvm/firecracker-containerd/runtime/firecrackeroci"
36
36
)
37
37
38
+ const (
39
+ jailerUID = 300001
40
+ jailerGID = 300001
41
+ )
42
+
38
43
func TestJailer_Isolated (t * testing.T ) {
39
44
prepareIntegTest (t )
40
45
t .Run ("Without Jailer" , func (t * testing.T ) {
@@ -44,15 +49,35 @@ func TestJailer_Isolated(t *testing.T) {
44
49
t .Run ("With Jailer" , func (t * testing.T ) {
45
50
t .Parallel ()
46
51
testJailer (t , & proto.JailerConfig {
47
- UID : 300001 ,
48
- GID : 300001 ,
52
+ UID : jailerUID ,
53
+ GID : jailerGID ,
49
54
})
50
55
})
51
56
t .Run ("With Jailer and bind-mount" , func (t * testing.T ) {
52
57
t .Parallel ()
53
58
testJailer (t , & proto.JailerConfig {
54
- UID : 300001 ,
55
- GID : 300001 ,
59
+ UID : jailerUID ,
60
+ GID : jailerGID ,
61
+ DriveExposePolicy : proto .DriveExposePolicy_BIND ,
62
+ })
63
+ })
64
+ }
65
+
66
+ func TestAttachBlockDevice_Isolated (t * testing.T ) {
67
+ prepareIntegTest (t )
68
+ t .Run ("Without Jailer" , func (t * testing.T ) {
69
+ testAttachBlockDevice (t , nil )
70
+ })
71
+ t .Run ("With Jailer" , func (t * testing.T ) {
72
+ testAttachBlockDevice (t , & proto.JailerConfig {
73
+ UID : jailerUID ,
74
+ GID : jailerGID ,
75
+ })
76
+ })
77
+ t .Run ("With Jailer and bind-mount" , func (t * testing.T ) {
78
+ testAttachBlockDevice (t , & proto.JailerConfig {
79
+ UID : jailerUID ,
80
+ GID : jailerGID ,
56
81
DriveExposePolicy : proto .DriveExposePolicy_BIND ,
57
82
})
58
83
})
@@ -170,3 +195,110 @@ func TestJailerCPUSet_Isolated(t *testing.T) {
170
195
}
171
196
testJailer (t , config )
172
197
}
198
+
199
+ func testAttachBlockDevice (t * testing.T , jailerConfig * proto.JailerConfig ) {
200
+ require := require .New (t )
201
+ client , err := containerd .New (containerdSockPath , containerd .WithDefaultRuntime (firecrackerRuntime ))
202
+ require .NoError (err , "unable to create client to containerd service at %s, is containerd running?" , containerdSockPath )
203
+ defer client .Close ()
204
+
205
+ ctx := namespaces .WithNamespace (context .Background (), "default" )
206
+
207
+ image , err := alpineImage (ctx , client , defaultSnapshotterName )
208
+ require .NoError (err , "failed to get alpine image" )
209
+
210
+ fcClient , err := newFCControlClient (containerdSockPath )
211
+ require .NoError (err )
212
+
213
+ vmID := testNameToVMID (t .Name ())
214
+
215
+ // default BlockDeviceFile setup, will change UID & GID if a jailerConfig is passed
216
+ deviceFile := internal.BlockDeviceFile {
217
+ Subpath : "dir/block1" ,
218
+ UID : 0 ,
219
+ GID : 0 ,
220
+ Dev : 259 , // 259 is the major number for blkext which is one type of block devices
221
+ }
222
+
223
+ if jailerConfig != nil {
224
+ // cover "With Jailer" & "With Jailer and bind-mount" test cases
225
+ deviceFile .UID = int (jailerConfig .UID )
226
+ deviceFile .GID = int (jailerConfig .GID )
227
+ }
228
+ additionalBlockDevice := internal .CreateBlockDevice (ctx , t , "ext4" , deviceFile )
229
+ blockExampleDir := filepath .Dir (additionalBlockDevice )
230
+ defer os .RemoveAll (filepath .Dir (blockExampleDir )) // clean up
231
+
232
+ request := proto.CreateVMRequest {
233
+ VMID : vmID ,
234
+ JailerConfig : jailerConfig ,
235
+ DriveMounts : []* proto.FirecrackerDriveMount {
236
+ {HostPath : additionalBlockDevice , VMPath : "/home/driveMount" , FilesystemType : "ext4" },
237
+ },
238
+ }
239
+
240
+ // If the drive files are bind-mounted, the files must be readable from the jailer's user.
241
+ if jailerConfig != nil && jailerConfig .DriveExposePolicy == proto .DriveExposePolicy_BIND {
242
+ f , err := ioutil .TempFile ("" , fsSafeTestName (t )+ "_rootfs" )
243
+ require .NoError (err )
244
+ defer f .Close ()
245
+
246
+ dst := f .Name ()
247
+
248
+ // Copy the root drive before chown, since the file is used by other tests.
249
+ err = copyFile (defaultRuntimeConfig .RootDrive , dst , 0400 )
250
+ require .NoErrorf (err , "failed to copy a rootfs as %q" , dst )
251
+
252
+ err = os .Chown (dst , int (jailerConfig .UID ), int (jailerConfig .GID ))
253
+ require .NoError (err , "failed to chown %q" , dst )
254
+
255
+ request .RootDrive = & proto.FirecrackerRootDrive {HostPath : dst }
256
+
257
+ // The additional drive file is only used by this test.
258
+ err = os .Chown (additionalBlockDevice , int (jailerConfig .UID ), int (jailerConfig .GID ))
259
+ require .NoError (err , "failed to chown %q" , additionalBlockDevice )
260
+ }
261
+
262
+ _ , err = fcClient .CreateVM (ctx , & request )
263
+ require .NoError (err )
264
+
265
+ // create a container to test bind mount block device into the container
266
+ c , err := client .NewContainer (ctx ,
267
+ vmID + "-container" ,
268
+ containerd .WithSnapshotter (defaultSnapshotterName ),
269
+ containerd .WithNewSnapshot (vmID + "-snapshot" , image ),
270
+ containerd .WithNewSpec (
271
+ oci .WithProcessArgs (
272
+ "/bin/sh" , "-c" , "echo heyhey && cd /mnt/blockDeviceTest" ,
273
+ ),
274
+ firecrackeroci .WithVMID (vmID ),
275
+ oci .WithMounts ([]specs.Mount {{
276
+ Source : "/home/driveMount" ,
277
+ Destination : "/mnt/blockDeviceTest" ,
278
+ Options : []string {"bind" },
279
+ }}),
280
+ ),
281
+ )
282
+ require .NoError (err )
283
+
284
+ stdout := startAndWaitTask (ctx , t , c )
285
+ require .Equal ("heyhey\n " , stdout )
286
+
287
+ stat , err := os .Stat (filepath .Join (shimBaseDir (), "default#" + vmID ))
288
+ require .NoError (err )
289
+ assert .True (t , stat .IsDir ())
290
+
291
+ err = c .Delete (ctx , containerd .WithSnapshotCleanup )
292
+ require .NoError (err , "failed to delete a container-block-device" )
293
+
294
+ _ , err = fcClient .StopVM (ctx , & proto.StopVMRequest {VMID : vmID })
295
+ require .NoError (err )
296
+
297
+ _ , err = os .Stat (filepath .Join (shimBaseDir (), "default#" + vmID ))
298
+ assert .Error (t , err )
299
+ assert .True (t , os .IsNotExist (err ))
300
+
301
+ shimContents , err := ioutil .ReadDir (shimBaseDir ())
302
+ require .NoError (err )
303
+ assert .Len (t , shimContents , 0 )
304
+ }
0 commit comments