diff --git a/cmd/buildah/bud.go b/cmd/buildah/bud.go index 728e202dcfd..9d9e8c7d80f 100644 --- a/cmd/buildah/bud.go +++ b/cmd/buildah/bud.go @@ -273,7 +273,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budResults) error { if err != nil { return err } - devices = append(devices, dev) + devices = append(devices, dev...) } options := imagebuildah.BuildOptions{ diff --git a/cmd/buildah/from.go b/cmd/buildah/from.go index 5826311d23a..55d048a1623 100644 --- a/cmd/buildah/from.go +++ b/cmd/buildah/from.go @@ -218,7 +218,7 @@ func fromCmd(c *cobra.Command, args []string, iopts fromReply) error { if err != nil { return err } - devices = append(devices, dev) + devices = append(devices, dev...) } options := buildah.BuilderOptions{ diff --git a/docs/buildah-bud.md b/docs/buildah-bud.md index a8fbfe46f50..c477adcfc06 100644 --- a/docs/buildah-bud.md +++ b/docs/buildah-bud.md @@ -176,7 +176,7 @@ value can be entered. The password is entered without echo. **--device**=*device* -Add a host device to the container. The format is `[:][:]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm) +Add a host device or devices under a directory to the container. The format is `[:][:]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm) **--disable-compression, -D** Don't compress filesystem layers when building the image unless it is required diff --git a/docs/buildah-from.md b/docs/buildah-from.md index f3ddb40c77d..67170632534 100644 --- a/docs/buildah-from.md +++ b/docs/buildah-from.md @@ -173,7 +173,7 @@ value can be entered. The password is entered without echo. **--device**=*device* -Add a host device to the container. The format is `[:][:]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm) +Add a host device or devices under a directory to the container. The format is `[:][:]` (e.g. --device=/dev/sdc:/dev/xvdc:rwm) **--dns**=[] diff --git a/pkg/parse/parse_test.go b/pkg/parse/parse_test.go index c7afdaca018..302961f8a3b 100644 --- a/pkg/parse/parse_test.go +++ b/pkg/parse/parse_test.go @@ -74,17 +74,22 @@ func TestDeviceFromPath(t *testing.T) { // Path is valid dev, err := DeviceFromPath("/dev/null") assert.NoError(t, err) - assert.Equal(t, dev.Major, int64(1)) - assert.Equal(t, dev.Minor, int64(3)) - assert.Equal(t, dev.Permissions, "rwm") - assert.Equal(t, dev.Uid, uint32(0)) - assert.Equal(t, dev.Gid, uint32(0)) + assert.Equal(t, len(dev), 1) + assert.Equal(t, dev[0].Major, int64(1)) + assert.Equal(t, dev[0].Minor, int64(3)) + assert.Equal(t, dev[0].Permissions, "rwm") + assert.Equal(t, dev[0].Uid, uint32(0)) + assert.Equal(t, dev[0].Gid, uint32(0)) // Path does not exists _, err = DeviceFromPath("/dev/BOGUS") assert.Error(t, err) - // Path exists but is not a device + // Path is a directory of devices _, err = DeviceFromPath("/dev/pts") + assert.NoError(t, err) + + // path of directory has no device + _, err = DeviceFromPath("/etc/passwd") assert.Error(t, err) } diff --git a/pkg/parse/parse_unix.go b/pkg/parse/parse_unix.go index 238293894f3..1aaeca27863 100644 --- a/pkg/parse/parse_unix.go +++ b/pkg/parse/parse_unix.go @@ -4,6 +4,8 @@ package parse import ( "fmt" + "os" + "path/filepath" "github.com/containers/buildah/pkg/unshare" "github.com/opencontainers/runc/libcontainer/configs" @@ -24,18 +26,40 @@ func getDefaultProcessLimits() []string { return defaultLimits } -func DeviceFromPath(device string) (configs.Device, error) { +func DeviceFromPath(device string) ([]configs.Device, error) { + var devs []configs.Device src, dst, permissions, err := Device(device) if err != nil { - return configs.Device{}, err + return nil, err } if unshare.IsRootless() { - return configs.Device{}, errors.Errorf("Renaming device %s to %s is not a supported in rootless containers", src, dst) + return nil, errors.Errorf("Renaming device %s to %s is not a supported in rootless containers", src, dst) } - dev, err := devices.DeviceFromPath(src, permissions) + srcInfo, err := os.Stat(src) if err != nil { - return configs.Device{}, errors.Wrapf(err, "%s is not a valid device", src) + return nil, errors.Wrapf(err, "error getting info of source device %s", src) } - dev.Path = dst - return *dev, nil + + if !srcInfo.IsDir() { + + dev, err := devices.DeviceFromPath(src, permissions) + if err != nil { + return nil, errors.Wrapf(err, "%s is not a valid device", src) + } + dev.Path = dst + devs = append(devs, *dev) + return devs, nil + } + + // If source device is a directory + srcDevices, err := devices.GetDevices(src) + if err != nil { + return nil, errors.Wrapf(err, "error getting source devices from directory %s", src) + } + for _, d := range srcDevices { + d.Path = filepath.Join(dst, filepath.Base(d.Path)) + d.Permissions = permissions + devs = append(devs, *d) + } + return devs, nil } diff --git a/tests/bud.bats b/tests/bud.bats index 6a6829146c2..8313701fa4f 100644 --- a/tests/bud.bats +++ b/tests/bud.bats @@ -1690,6 +1690,18 @@ load helpers run_buildah bud --signature-policy ${TESTSDIR}/policy.json --squash ${TESTSDIR}/bud/layers-squash } +@test "bud with additional directory of devices" { + target=alpine-image + mkdir ${TESTSDIR}/foo + mknod ${TESTSDIR}/foo/null c 1 3 + run_buildah bud --signature-policy ${TESTSDIR}/policy.json --device ${TESTSDIR}/foo:/dev/foo -t ${target} -f ${TESTSDIR}/bud/device/Dockerfile.dirdevice ${TESTSDIR}/bud/device + [ "${status}" -eq 0 ] + expect_output --substring "null" + + buildah rmi ${target} + rm -rf ${TESTSDIR}/foo +} + @test "bud with additional device" { target=alpine-image run_buildah bud --signature-policy ${TESTSDIR}/policy.json --device /dev/fuse -t ${target} -f ${TESTSDIR}/bud/device/Dockerfile ${TESTSDIR}/bud/device @@ -1793,7 +1805,7 @@ load helpers echo "$output" expect_output --substring "abc.txt" - rm ${TESTSDIR}/bud/use-args/abc.txt + rm ${TESTSDIR}/bud/use-args/abc.txt buildah rm --all buildah rmi --all --force }