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 <dwalsh@redhat.com>

Closes: #140
Approved by: TomSweeneyRedHat
This commit is contained in:
Daniel J Walsh
2017-12-19 09:07:49 -05:00
committed by Atomic Bot
parent c0432eb0e8
commit 94a8107515
13 changed files with 219 additions and 12 deletions

View File

@ -362,6 +362,10 @@ var createFlags = []cli.Flag{
Name: "publish-all, P", Name: "publish-all, P",
Usage: "Publish all exposed ports to random ports on the host interface", 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{ cli.BoolFlag{
Name: "read-only", Name: "read-only",
Usage: "Make containers root filesystem read-only", Usage: "Make containers root filesystem read-only",

View File

@ -3,6 +3,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"os" "os"
"strconv" "strconv"
"strings" "strings"
@ -14,7 +15,6 @@ import (
"github.com/projectatomic/libpod/libpod" "github.com/projectatomic/libpod/libpod"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
) )
type mountType string type mountType string
@ -72,7 +72,7 @@ type createConfig struct {
CgroupParent string // cgroup-parent CgroupParent string // cgroup-parent
Command []string Command []string
Detach bool // detach Detach bool // detach
Devices []*pb.Device // device Devices []string // device
DNSOpt []string //dns-opt DNSOpt []string //dns-opt
DNSSearch []string //dns-search DNSSearch []string //dns-search
DNSServers []string //dns DNSServers []string //dns
@ -101,6 +101,7 @@ type createConfig struct {
Privileged bool //privileged Privileged bool //privileged
Publish []string //publish Publish []string //publish
PublishAll bool //publish-all PublishAll bool //publish-all
Quiet bool //quiet
ReadOnlyRootfs bool //read-only ReadOnlyRootfs bool //read-only
Resources createResourceConfig Resources createResourceConfig
Rm bool //rm Rm bool //rm
@ -167,8 +168,11 @@ func createCmd(c *cli.Context) error {
if createImage.LocalName == "" { if createImage.LocalName == "" {
// The image wasnt found by the user input'd name or its fqname // The image wasnt found by the user input'd name or its fqname
// Pull the image // Pull the image
fmt.Printf("Trying to pull %s...", createImage.PullName) var writer io.Writer
createImage.Pull() if !createConfig.Quiet {
writer = os.Stdout
}
createImage.Pull(writer)
} }
runtimeSpec, err := createConfigToOCISpec(createConfig) runtimeSpec, err := createConfigToOCISpec(createConfig)
@ -419,6 +423,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
CgroupParent: c.String("cgroup-parent"), CgroupParent: c.String("cgroup-parent"),
Command: command, Command: command,
Detach: c.Bool("detach"), Detach: c.Bool("detach"),
Devices: c.StringSlice("device"),
DNSOpt: c.StringSlice("dns-opt"), DNSOpt: c.StringSlice("dns-opt"),
DNSSearch: c.StringSlice("dns-search"), DNSSearch: c.StringSlice("dns-search"),
DNSServers: c.StringSlice("dns"), DNSServers: c.StringSlice("dns"),
@ -447,6 +452,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, er
Privileged: c.Bool("privileged"), Privileged: c.Bool("privileged"),
Publish: c.StringSlice("publish"), Publish: c.StringSlice("publish"),
PublishAll: c.Bool("publish-all"), PublishAll: c.Bool("publish-all"),
Quiet: c.Bool("quiet"),
ReadOnlyRootfs: c.Bool("read-only"), ReadOnlyRootfs: c.Bool("read-only"),
Resources: createResourceConfig{ Resources: createResourceConfig{
BlkioWeight: blkioWeight, BlkioWeight: blkioWeight,

View File

@ -2,6 +2,8 @@ package main
import ( import (
"fmt" "fmt"
"io"
"os"
"sync" "sync"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -44,7 +46,11 @@ func runCmd(c *cli.Context) error {
if createImage.LocalName == "" { if createImage.LocalName == "" {
// The image wasnt found by the user input'd name or its fqname // The image wasnt found by the user input'd name or its fqname
// Pull the image // Pull the image
createImage.Pull() var writer io.Writer
if !createConfig.Quiet {
writer = os.Stdout
}
createImage.Pull(writer)
} }
runtimeSpec, err := createConfigToOCISpec(createConfig) runtimeSpec, err := createConfigToOCISpec(createConfig)

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/docker/daemon/caps" "github.com/docker/docker/daemon/caps"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/devices"
spec "github.com/opencontainers/runtime-spec/specs-go" spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label" "github.com/opencontainers/selinux/go-selinux/label"
@ -163,6 +164,25 @@ func setupCapabilities(config *createConfig, configSpec *spec.Spec) error {
return nil 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 // Parses information needed to create a container into an OCI runtime spec
func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) { func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
g := generate.New() g := generate.New()
@ -233,6 +253,13 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
g.SetLinuxResourcesCPUMems(config.Resources.CPUsetMems) g.SetLinuxResourcesCPUMems(config.Resources.CPUsetMems)
} }
// Devices
for _, device := range config.Devices {
if err := addDevice(&g, device); err != nil {
return nil, err
}
}
// SECURITY OPTS // SECURITY OPTS
g.SetProcessNoNewPrivileges(config.NoNewPrivileges) g.SetProcessNoNewPrivileges(config.NoNewPrivileges)
g.SetProcessApparmorProfile(config.ApparmorProfile) g.SetProcessApparmorProfile(config.ApparmorProfile)
@ -321,7 +348,6 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
Hooks: &configSpec.Hooks{}, Hooks: &configSpec.Hooks{},
//Annotations //Annotations
Resources: &configSpec.LinuxResources{ Resources: &configSpec.LinuxResources{
Devices: config.GetDefaultDevices(),
BlockIO: &blkio, BlockIO: &blkio,
//HugepageLimits: //HugepageLimits:
Network: &configSpec.LinuxNetwork{ Network: &configSpec.LinuxNetwork{
@ -331,7 +357,6 @@ func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
}, },
//CgroupsPath: //CgroupsPath:
//Namespaces: []LinuxNamespace //Namespaces: []LinuxNamespace
//Devices
// DefaultAction: // DefaultAction:
// Architectures // Architectures
// Syscalls: // Syscalls:

View File

@ -1018,6 +1018,7 @@ _podman_container_run() {
--oom-kill-disable --oom-kill-disable
--privileged --privileged
--publish-all -P --publish-all -P
--quiet
--read-only --read-only
--tty -t --tty -t
" "

View File

@ -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 `/proc/sys/net/ipv4/ip_local_port_range`. To find the mapping between the host
ports and the exposed ports, use `podman port`. ports and the exposed ports, use `podman port`.
**--quiet, -q**
Suppress output information when pulling images
**--read-only**=*true*|*false* **--read-only**=*true*|*false*
Mount the container's root filesystem as read only. Mount the container's root filesystem as read only.

View File

@ -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 `/proc/sys/net/ipv4/ip_local_port_range`. To find the mapping between the host
ports and the exposed ports, use `podman port`. ports and the exposed ports, use `podman port`.
**--quiet, -q**
Suppress output information when pulling images
**--read-only**=*true*|*false* **--read-only**=*true*|*false*
Mount the container's root filesystem as read only. Mount the container's root filesystem as read only.

View File

@ -397,7 +397,7 @@ func (k *Image) HasLatest() (bool, error) {
} }
// Pull is a wrapper function to pull and image // 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 the image hasn't been decomposed yet
if !k.beenDecomposed { if !k.beenDecomposed {
err := k.Decompose() err := k.Decompose()
@ -405,7 +405,7 @@ func (k *Image) Pull() error {
return err 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 return nil
} }

View File

@ -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" ]
}

View File

@ -1,14 +1,14 @@
# #
github.com/sirupsen/logrus v1.0.0 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/docker/docker-credential-helpers d68f9aeca33f5fd3f08eeae5e9d175edf4e731d1
github.com/ostreedev/ostree-go master github.com/ostreedev/ostree-go master
github.com/containers/storage 9e0c323a4b425557f8310ee8d125634acd39d8f5 github.com/containers/storage 1824cf917a6b42d8c41179e807bb20a5fd6c0f0a
github.com/containernetworking/cni v0.4.0 github.com/containernetworking/cni v0.4.0
google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go
github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
github.com/opencontainers/go-digest v1.0.0-rc0 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/opencontainers/runc 45bde006ca8c90e089894508708bcf0e2cdf9e13
github.com/mrunalp/fileutils master github.com/mrunalp/fileutils master
github.com/vishvananda/netlink master github.com/vishvananda/netlink master
@ -97,3 +97,6 @@ github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987 github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2 github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
github.com/containerd/continuity master github.com/containerd/continuity master
github.com/xeipuuv/gojsonschema master
github.com/xeipuuv/gojsonreference master
github.com/xeipuuv/gojsonpointer master

View File

@ -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
}

View File

@ -0,0 +1,3 @@
// +build !linux
package devices

View File

@ -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 <linux/kdev_t.h> 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))
}