@@ -11,6 +11,7 @@ import (
1111 "os"
1212 "os/exec"
1313 "path/filepath"
14+ "strconv"
1415 "strings"
1516 "time"
1617
@@ -26,6 +27,7 @@ import (
2627
2728 "github.com/runfinch/finch-daemon/api/types"
2829 "github.com/runfinch/finch-daemon/e2e/client"
30+ "github.com/runfinch/finch-daemon/e2e/util"
2931)
3032
3133type containerCreateResponse struct {
@@ -34,7 +36,7 @@ type containerCreateResponse struct {
3436}
3537
3638// ContainerCreate tests the `POST containers/create` API.
37- func ContainerCreate (opt * option.Option ) {
39+ func ContainerCreate (opt * option.Option , pOpt util. NewOpt ) {
3840 Describe ("create container" , func () {
3941 var (
4042 uClient * http.Client
@@ -1412,6 +1414,113 @@ func ContainerCreate(opt *option.Option) {
14121414 Expect (ok ).Should (BeTrue ())
14131415 Expect (annotations ["com.example.key" ]).Should (Equal ("test-value" ))
14141416 })
1417+
1418+ It ("should create a container with device mappings" , func () {
1419+ // Create a temporary file to use as backing store
1420+ tmpFileOpt , _ := pOpt ([]string {"touch" , "/tmp/loopdev" })
1421+ command .Run (tmpFileOpt )
1422+ defer func () {
1423+ rmOpt , _ := pOpt ([]string {"rm" , "-f" , "/tmp/loopdev" })
1424+ command .Run (rmOpt )
1425+ }()
1426+
1427+ // Write 4KB of data to the file
1428+ ddOpt , _ := pOpt ([]string {"dd" , "if=/dev/zero" , "of=/tmp/loopdev" , "bs=4096" , "count=1" })
1429+ command .Run (ddOpt )
1430+
1431+ // Set up loop device
1432+ loopDevOpt , _ := pOpt ([]string {"losetup" , "-f" , "--show" , "/tmp/loopdev" })
1433+ loopDev := command .StdoutStr (loopDevOpt )
1434+ Expect (loopDev ).ShouldNot (BeEmpty ())
1435+ defer func () {
1436+ detachOpt , _ := pOpt ([]string {"losetup" , "-d" , loopDev })
1437+ command .Run (detachOpt )
1438+ }()
1439+
1440+ // Write test content to the device
1441+ writeOpt , _ := pOpt ([]string {"sh" , "-c" , "echo -n test-content > " + loopDev })
1442+ command .Run (writeOpt )
1443+
1444+ // Get device info to verify major/minor numbers
1445+ statOpt , _ := pOpt ([]string {"stat" , "-c" , "%t,%T" , loopDev })
1446+ devNums := command .StdoutStr (statOpt )
1447+ parts := strings .Split (devNums , "," )
1448+ major , _ := strconv .ParseUint (parts [0 ], 16 , 64 )
1449+ minor , _ := strconv .ParseUint (parts [1 ], 16 , 64 )
1450+
1451+ options .Cmd = []string {"sleep" , "Infinity" }
1452+ options .HostConfig .Devices = []types.DeviceMapping {
1453+ {
1454+ PathOnHost : loopDev ,
1455+ PathInContainer : loopDev ,
1456+ CgroupPermissions : "rwm" ,
1457+ },
1458+ }
1459+
1460+ // Create container
1461+ statusCode , ctr := createContainer (uClient , url , testContainerName , options )
1462+ Expect (statusCode ).Should (Equal (http .StatusCreated ))
1463+ Expect (ctr .ID ).ShouldNot (BeEmpty ())
1464+
1465+ // Start container
1466+ command .Run (opt , "start" , testContainerName )
1467+
1468+ // Inspect using native format
1469+ nativeResp := command .Stdout (opt , "inspect" , "--mode=native" , testContainerName )
1470+ var nativeInspect []map [string ]interface {}
1471+ err := json .Unmarshal (nativeResp , & nativeInspect )
1472+ Expect (err ).Should (BeNil ())
1473+ Expect (nativeInspect ).Should (HaveLen (1 ))
1474+
1475+ // Navigate to the linux section
1476+ spec , ok := nativeInspect [0 ]["Spec" ].(map [string ]interface {})
1477+ Expect (ok ).Should (BeTrue ())
1478+ linux , ok := spec ["linux" ].(map [string ]interface {})
1479+ Expect (ok ).Should (BeTrue ())
1480+
1481+ // Verify device in linux.devices
1482+ devices , ok := linux ["devices" ].([]interface {})
1483+ Expect (ok ).Should (BeTrue ())
1484+
1485+ foundDevice := false
1486+ for _ , device := range devices {
1487+ d := device .(map [string ]interface {})
1488+ if d ["path" ] == loopDev {
1489+ foundDevice = true
1490+ Expect (d ["type" ]).Should (Equal ("b" )) // block device
1491+ Expect (d ["major" ].(float64 )).Should (Equal (float64 (major )))
1492+ Expect (d ["minor" ].(float64 )).Should (Equal (float64 (minor )))
1493+ break
1494+ }
1495+ }
1496+ Expect (foundDevice ).Should (BeTrue ())
1497+
1498+ // Verify device permissions in linux.resources.devices
1499+ resources , ok := linux ["resources" ].(map [string ]interface {})
1500+ Expect (ok ).Should (BeTrue ())
1501+ resourceDevices , ok := resources ["devices" ].([]interface {})
1502+ Expect (ok ).Should (BeTrue ())
1503+
1504+ // First rule should be deny all
1505+ denyAll := resourceDevices [0 ].(map [string ]interface {})
1506+ Expect (denyAll ["allow" ]).Should (BeFalse ())
1507+ Expect (denyAll ["access" ]).Should (Equal ("rwm" ))
1508+
1509+ // Should find an allow rule for our device
1510+ foundAllowRule := false
1511+ for _ , rule := range resourceDevices {
1512+ r := rule .(map [string ]interface {})
1513+ if r ["allow" ] == true &&
1514+ r ["type" ] == "b" &&
1515+ r ["major" ].(float64 ) == float64 (major ) &&
1516+ r ["minor" ].(float64 ) == float64 (minor ) {
1517+ foundAllowRule = true
1518+ Expect (r ["access" ]).Should (Equal ("rwm" ))
1519+ break
1520+ }
1521+ }
1522+ Expect (foundAllowRule ).Should (BeTrue ())
1523+ })
14151524 })
14161525}
14171526
0 commit comments