From 94a810751539afeb1590ccc1a9745f1d5767fda2 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 19 Dec 2017 09:07:49 -0500 Subject: Add support for adding devices to container Also add --quiet option to kpod create/run since this will help with writing tests. Signed-off-by: Daniel J Walsh Closes: #140 Approved by: TomSweeneyRedHat --- cmd/podman/common.go | 4 + cmd/podman/create.go | 14 ++- cmd/podman/run.go | 8 +- cmd/podman/spec.go | 29 +++++- completions/bash/podman | 1 + docs/podman-create.1.md | 4 + docs/podman-run.1.md | 4 + libpod/runtime_img.go | 4 +- test/podman_run_device.bats | 27 ++++++ vendor.conf | 9 +- .../runc/libcontainer/devices/devices_linux.go | 100 +++++++++++++++++++++ .../libcontainer/devices/devices_unsupported.go | 3 + .../runc/libcontainer/devices/number.go | 24 +++++ 13 files changed, 219 insertions(+), 12 deletions(-) create mode 100644 test/podman_run_device.bats create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/devices/devices_linux.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/devices/devices_unsupported.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/devices/number.go diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 99685107b..57e2ff717 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -362,6 +362,10 @@ var createFlags = []cli.Flag{ Name: "publish-all, P", Usage: "Publish all exposed ports to random ports on the host interface", }, + cli.BoolFlag{ + Name: "quiet, q", + Usage: "Suppress output information when pulling images", + }, cli.BoolFlag{ Name: "read-only", Usage: "Make containers root filesystem read-only", diff --git a/cmd/podman/create.go b/cmd/podman/create.go index f65bc49c6..79f08220d 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "fmt" + "io" "os" "strconv" "strings" @@ -14,7 +15,6 @@ import ( "github.com/projectatomic/libpod/libpod" "github.com/sirupsen/logrus" "github.com/urfave/cli" - pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" ) type mountType string @@ -72,7 +72,7 @@ type createConfig struct { CgroupParent string // cgroup-parent Command []string Detach bool // detach - Devices []*pb.Device // device + Devices []string // device DNSOpt []string //dns-opt DNSSearch []string //dns-search DNSServers []string //dns @@ -101,6 +101,7 @@ type createConfig struct { Privileged bool //privileged Publish []string //publish PublishAll bool //publish-all + Quiet bool //quiet ReadOnlyRootfs bool //read-only Resources createResourceConfig Rm bool //rm @@ -167,8 +168,11 @@ func createCmd(c *cli.Context) error { if createImage.LocalName == "" { // The image wasnt found by the user input'd name or its fqname // Pull the image - fmt.Printf("Trying to pull %s...", createImage.PullName) - createImage.Pull() + var writer io.Writer + if !createConfig.Quiet { + writer = os.Stdout + } + createImage.Pull(writer) } runtimeSpec, err := createConfigToOCISpec(createConfig) @@ -419,6 +423,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er CgroupParent: c.String("cgroup-parent"), Command: command, Detach: c.Bool("detach"), + Devices: c.StringSlice("device"), DNSOpt: c.StringSlice("dns-opt"), DNSSearch: c.StringSlice("dns-search"), DNSServers: c.StringSlice("dns"), @@ -447,6 +452,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er Privileged: c.Bool("privileged"), Publish: c.StringSlice("publish"), PublishAll: c.Bool("publish-all"), + Quiet: c.Bool("quiet"), ReadOnlyRootfs: c.Bool("read-only"), Resources: createResourceConfig{ BlkioWeight: blkioWeight, diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 6ba501c76..bc93459ad 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -2,6 +2,8 @@ package main import ( "fmt" + "io" + "os" "sync" "github.com/pkg/errors" @@ -44,7 +46,11 @@ func runCmd(c *cli.Context) error { if createImage.LocalName == "" { // The image wasnt found by the user input'd name or its fqname // Pull the image - createImage.Pull() + var writer io.Writer + if !createConfig.Quiet { + writer = os.Stdout + } + createImage.Pull(writer) } runtimeSpec, err := createConfigToOCISpec(createConfig) diff --git a/cmd/podman/spec.go b/cmd/podman/spec.go index b13556d93..550f74218 100644 --- a/cmd/podman/spec.go +++ b/cmd/podman/spec.go @@ -10,6 +10,7 @@ import ( "github.com/docker/docker/daemon/caps" "github.com/docker/docker/pkg/mount" "github.com/docker/go-units" + "github.com/opencontainers/runc/libcontainer/devices" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" @@ -163,6 +164,25 @@ func setupCapabilities(config *createConfig, configSpec *spec.Spec) error { return nil } +func addDevice(g *generate.Generator, device string) error { + dev, err := devices.DeviceFromPath(device, "rwm") + if err != nil { + return errors.Wrapf(err, "%s is not a valid device", device) + } + linuxdev := spec.LinuxDevice{ + Path: dev.Path, + Type: string(dev.Type), + Major: dev.Major, + Minor: dev.Minor, + FileMode: &dev.FileMode, + UID: &dev.Uid, + GID: &dev.Gid, + } + g.AddDevice(linuxdev) + g.AddLinuxResourcesDevice(true, string(dev.Type), &dev.Major, &dev.Minor, dev.Permissions) + return nil +} + // Parses information needed to create a container into an OCI runtime spec func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) { g := generate.New() @@ -233,6 +253,13 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) { g.SetLinuxResourcesCPUMems(config.Resources.CPUsetMems) } + // Devices + for _, device := range config.Devices { + if err := addDevice(&g, device); err != nil { + return nil, err + } + } + // SECURITY OPTS g.SetProcessNoNewPrivileges(config.NoNewPrivileges) g.SetProcessApparmorProfile(config.ApparmorProfile) @@ -321,7 +348,6 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) { Hooks: &configSpec.Hooks{}, //Annotations Resources: &configSpec.LinuxResources{ - Devices: config.GetDefaultDevices(), BlockIO: &blkio, //HugepageLimits: Network: &configSpec.LinuxNetwork{ @@ -331,7 +357,6 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) { }, //CgroupsPath: //Namespaces: []LinuxNamespace - //Devices // DefaultAction: // Architectures // Syscalls: diff --git a/completions/bash/podman b/completions/bash/podman index 9a5351ade..ecf752de9 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1018,6 +1018,7 @@ _podman_container_run() { --oom-kill-disable --privileged --publish-all -P + --quiet --read-only --tty -t " diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 9deeb8149..117a076a6 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -373,6 +373,10 @@ port to a random port on the host within an *ephemeral port range* defined by `/proc/sys/net/ipv4/ip_local_port_range`. To find the mapping between the host ports and the exposed ports, use `podman port`. +**--quiet, -q** + +Suppress output information when pulling images + **--read-only**=*true*|*false* Mount the container's root filesystem as read only. diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index 36efc2a2e..19e73aea2 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -379,6 +379,10 @@ port to a random port on the host within an *ephemeral port range* defined by `/proc/sys/net/ipv4/ip_local_port_range`. To find the mapping between the host ports and the exposed ports, use `podman port`. +**--quiet, -q** + +Suppress output information when pulling images + **--read-only**=*true*|*false* Mount the container's root filesystem as read only. diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 6107c2fdb..53211ff54 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -397,7 +397,7 @@ func (k *Image) HasLatest() (bool, error) { } // Pull is a wrapper function to pull and image -func (k *Image) Pull() error { +func (k *Image) Pull(writer io.Writer) error { // If the image hasn't been decomposed yet if !k.beenDecomposed { err := k.Decompose() @@ -405,7 +405,7 @@ func (k *Image) Pull() error { return err } } - k.runtime.PullImage(k.PullName, CopyOptions{Writer: os.Stdout, SignaturePolicyPath: k.runtime.config.SignaturePolicyPath}) + k.runtime.PullImage(k.PullName, CopyOptions{Writer: writer, SignaturePolicyPath: k.runtime.config.SignaturePolicyPath}) return nil } diff --git a/test/podman_run_device.bats b/test/podman_run_device.bats new file mode 100644 index 000000000..98d6833eb --- /dev/null +++ b/test/podman_run_device.bats @@ -0,0 +1,27 @@ +#!/usr/bin/env bats + +load helpers + +function teardown() { + cleanup_test +} + +function setup() { + prepare_network_conf + copy_images +} + +@test "run baddevice test" { + run ${PODMAN_BINARY} ${PODMAN_OPTIONS} run -q --device /dev/baddevice ${ALPINE} ls /dev/kmsg + echo $output + [ "$status" -ne 0 ] +} + +@test "run device test" { + run ${PODMAN_BINARY} ${PODMAN_OPTIONS} run -q --device /dev/kmsg ${ALPINE} ls --color=never /dev/kmsg + echo "$output" + [ "$status" -eq 0 ] + device=$(echo $output | tr -d '\r') + echo "<$device>" + [ "$device" = "/dev/kmsg" ] +} diff --git a/vendor.conf b/vendor.conf index cc335f7ea..8450dfbc1 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,14 +1,14 @@ # github.com/sirupsen/logrus v1.0.0 -github.com/containers/image c8bcd6aa11c62637c5a7da1420f43dd6a15f0e8d +github.com/containers/image 9b4510f6d1627c8e53c3303a8fe48ca7842c2ace github.com/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1 github.com/ostreedev/ostree-go master -github.com/containers/storage 9e0c323a4b425557f8310ee8d125634acd39d8f5 +github.com/containers/storage 1824cf917a6b42d8c41179e807bb20a5fd6c0f0a github.com/containernetworking/cni v0.4.0 google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd github.com/opencontainers/go-digest v1.0.0-rc0 -github.com/opencontainers/runtime-tools d3f7e9e9e631c7e87552d67dc7c86de33c3fb68a +github.com/opencontainers/runtime-tools v0.3.0 github.com/opencontainers/runc 45bde006ca8c90e089894508708bcf0e2cdf9e13 github.com/mrunalp/fileutils master github.com/vishvananda/netlink master @@ -97,3 +97,6 @@ github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987 github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2 github.com/containerd/continuity master +github.com/xeipuuv/gojsonschema master +github.com/xeipuuv/gojsonreference master +github.com/xeipuuv/gojsonpointer master diff --git a/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_linux.go new file mode 100644 index 000000000..461dc097c --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_linux.go @@ -0,0 +1,100 @@ +package devices + +import ( + "errors" + "io/ioutil" + "os" + "path/filepath" + + "github.com/opencontainers/runc/libcontainer/configs" + + "golang.org/x/sys/unix" +) + +var ( + ErrNotADevice = errors.New("not a device node") +) + +// Testing dependencies +var ( + unixLstat = unix.Lstat + ioutilReadDir = ioutil.ReadDir +) + +// Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct. +func DeviceFromPath(path, permissions string) (*configs.Device, error) { + var stat unix.Stat_t + err := unixLstat(path, &stat) + if err != nil { + return nil, err + } + var ( + devType rune + mode = stat.Mode + ) + switch { + case mode&unix.S_IFBLK == unix.S_IFBLK: + devType = 'b' + case mode&unix.S_IFCHR == unix.S_IFCHR: + devType = 'c' + default: + return nil, ErrNotADevice + } + devNumber := int(stat.Rdev) + uid := stat.Uid + gid := stat.Gid + return &configs.Device{ + Type: devType, + Path: path, + Major: Major(devNumber), + Minor: Minor(devNumber), + Permissions: permissions, + FileMode: os.FileMode(mode), + Uid: uid, + Gid: gid, + }, nil +} + +func HostDevices() ([]*configs.Device, error) { + return getDevices("/dev") +} + +func getDevices(path string) ([]*configs.Device, error) { + files, err := ioutilReadDir(path) + if err != nil { + return nil, err + } + out := []*configs.Device{} + for _, f := range files { + switch { + case f.IsDir(): + switch f.Name() { + // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825 + case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts": + continue + default: + sub, err := getDevices(filepath.Join(path, f.Name())) + if err != nil { + return nil, err + } + + out = append(out, sub...) + continue + } + case f.Name() == "console": + continue + } + device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm") + if err != nil { + if err == ErrNotADevice { + continue + } + if os.IsNotExist(err) { + continue + } + return nil, err + } + out = append(out, device) + } + return out, nil +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_unsupported.go new file mode 100644 index 000000000..6649b9f2d --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/devices/devices_unsupported.go @@ -0,0 +1,3 @@ +// +build !linux + +package devices diff --git a/vendor/github.com/opencontainers/runc/libcontainer/devices/number.go b/vendor/github.com/opencontainers/runc/libcontainer/devices/number.go new file mode 100644 index 000000000..885b6e5dd --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/devices/number.go @@ -0,0 +1,24 @@ +// +build linux freebsd + +package devices + +/* + +This code provides support for manipulating linux device numbers. It should be replaced by normal syscall functions once http://code.google.com/p/go/issues/detail?id=8106 is solved. + +You can read what they are here: + + - http://www.makelinux.net/ldd3/chp-3-sect-2 + - http://www.linux-tutorial.info/modules.php?name=MContent&pageid=94 + +Note! These are NOT the same as the MAJOR(dev_t device);, MINOR(dev_t device); and MKDEV(int major, int minor); functions as defined in as the representation of device numbers used by go is different than the one used internally to the kernel! - https://github.com/torvalds/linux/blob/master/include/linux/kdev_t.h#L9 + +*/ + +func Major(devNumber int) int64 { + return int64((devNumber >> 8) & 0xfff) +} + +func Minor(devNumber int) int64 { + return int64((devNumber & 0xff) | ((devNumber >> 12) & 0xfff00)) +} -- cgit v1.2.3-54-g00ecf