From 845fc65e548b9685a435576c2d6c61a4ae3f2808 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 2 Jul 2015 09:59:30 -0700 Subject: [PATCH] Create linux spec for runc spec command Signed-off-by: Michael Crosby --- README.md | 59 ++++++- main.go | 2 +- main_unsupported.go | 5 +- spec.go | 410 ++++++++++++++++++++++++++++++++++++++------ spec_linux.go | 283 ------------------------------ utils.go | 5 +- 6 files changed, 416 insertions(+), 348 deletions(-) delete mode 100644 spec_linux.go diff --git a/README.md b/README.md index 14da3766471..94f31079f28 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,13 @@ to have a v1 of the spec out within a quick timeframe of a few weeks, ~July 2015 so the `runc` config format will be constantly changing until the spec is finalized. However, we encourage you to try out the tool and give feedback. +### OCF + +How does `runc` integrate with the Open Container Format? `runc` depends on the types +specified in the [specs](https://github.com/opencontainers/specs) repository. Whenever +the specification is updated and ready to be versioned `runc` will update it's dependency +on the specs repository and support the update spec. + ### Building: ```bash @@ -42,7 +49,7 @@ user named `daemon` defined within that file-system. ```json { - "version": "0.1.1", + "version": "pre-draft", "platform": { "os": "linux", "arch": "amd64" @@ -107,21 +114,61 @@ user named `daemon` defined within that file-system. } ], "linux": { + "uidMapping": null, + "gidMapping": null, + "rlimits": null, + "systemProperties": null, + "resources": { + "disableOOMKiller": false, + "memory": { + "limit": 0, + "reservation": 0, + "swap": 0, + "kernel": 0 + }, + "cpu": { + "shares": 0, + "quota": 0, + "period": 0, + "realtimeRuntime": 0, + "realtimePeriod": 0, + "cpus": "", + "mems": "" + }, + "blockIO": { + "blkioWeight": 0, + "blkioWeightDevice": "", + "blkioThrottleReadBpsDevice": "", + "blkioThrottleWriteBpsDevice": "", + "blkioThrottleReadIopsDevice": "", + "blkioThrottleWriteIopsDevice": "" + }, + "hugepageLimits": null, + "network": { + "classId": "", + "priorities": null + } + }, "namespaces": [ { - "type": "process" + "type": "process", + "path": "" }, { - "type": "network" + "type": "network", + "path": "" }, { - "type": "mount" + "type": "ipc", + "path": "" }, { - "type": "ipc" + "type": "uts", + "path": "" }, { - "type": "uts" + "type": "mount", + "path": "" } ], "capabilities": [ diff --git a/main.go b/main.go index 72958a76985..baf7a0dce1a 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ import ( ) const ( - version = "0.1.1" + version = "0.2" usage = `Open Container Project runtime runc is a command line client for running applications packaged according to the Open Container Format (OCF) and is diff --git a/main_unsupported.go b/main_unsupported.go index be33a65877e..837324c6b8c 100644 --- a/main_unsupported.go +++ b/main_unsupported.go @@ -7,10 +7,6 @@ import ( "github.com/codegangsta/cli" ) -type User struct { - NOTSUPPORTED string -} - func getDefaultID() string { return "" } @@ -19,6 +15,7 @@ var ( checkpointCommand cli.Command eventsCommand cli.Command restoreCommand cli.Command + specCommand cli.Command ) func runAction(*cli.Context) { diff --git a/spec.go b/spec.go index 60ed0f77469..4a31f02d1c9 100644 --- a/spec.go +++ b/spec.go @@ -1,12 +1,21 @@ +// +build linux + package main import ( "encoding/json" "fmt" + "os" + "path/filepath" "runtime" + "strings" + "syscall" "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/devices" "github.com/opencontainers/specs" ) @@ -14,64 +23,98 @@ var specCommand = cli.Command{ Name: "spec", Usage: "create a new specification file", Action: func(context *cli.Context) { - spec := specs.Spec{ - Version: version, - Platform: specs.Platform{ - OS: runtime.GOOS, - Arch: runtime.GOARCH, - }, - Root: specs.Root{ - Path: "rootfs", - Readonly: true, - }, - Process: specs.Process{ - Terminal: true, - User: specs.User{}, - Args: []string{ - "sh", + spec := specs.LinuxSpec{ + Spec: specs.Spec{ + Version: specs.Version, + Platform: specs.Platform{ + OS: runtime.GOOS, + Arch: runtime.GOARCH, }, - Env: []string{ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "TERM=xterm", - }, - }, - Hostname: "shell", - Mounts: []specs.Mount{ - { - Type: "proc", - Source: "proc", - Destination: "/proc", - Options: "", + Root: specs.Root{ + Path: "rootfs", + Readonly: true, }, - { - Type: "tmpfs", - Source: "tmpfs", - Destination: "/dev", - Options: "nosuid,strictatime,mode=755,size=65536k", + Process: specs.Process{ + Terminal: true, + User: specs.User{}, + Args: []string{ + "sh", + }, + Env: []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + }, }, - { - Type: "devpts", - Source: "devpts", - Destination: "/dev/pts", - Options: "nosuid,noexec,newinstance,ptmxmode=0666,mode=0620,gid=5", + Hostname: "shell", + Mounts: []specs.Mount{ + { + Type: "proc", + Source: "proc", + Destination: "/proc", + Options: "", + }, + { + Type: "tmpfs", + Source: "tmpfs", + Destination: "/dev", + Options: "nosuid,strictatime,mode=755,size=65536k", + }, + { + Type: "devpts", + Source: "devpts", + Destination: "/dev/pts", + Options: "nosuid,noexec,newinstance,ptmxmode=0666,mode=0620,gid=5", + }, + { + Type: "tmpfs", + Source: "shm", + Destination: "/dev/shm", + Options: "nosuid,noexec,nodev,mode=1777,size=65536k", + }, + { + Type: "mqueue", + Source: "mqueue", + Destination: "/dev/mqueue", + Options: "nosuid,noexec,nodev", + }, + { + Type: "sysfs", + Source: "sysfs", + Destination: "/sys", + Options: "nosuid,noexec,nodev", + }, }, - { - Type: "tmpfs", - Source: "shm", - Destination: "/dev/shm", - Options: "nosuid,noexec,nodev,mode=1777,size=65536k", + }, + Linux: specs.Linux{ + Namespaces: []specs.Namespace{ + { + Type: "process", + }, + { + Type: "network", + }, + { + Type: "ipc", + }, + { + Type: "uts", + }, + { + Type: "mount", + }, }, - { - Type: "mqueue", - Source: "mqueue", - Destination: "/dev/mqueue", - Options: "nosuid,noexec,nodev", + Capabilities: []string{ + "AUDIT_WRITE", + "KILL", + "NET_BIND_SERVICE", }, - { - Type: "sysfs", - Source: "sysfs", - Destination: "/sys", - Options: "nosuid,noexec,nodev", + Devices: []string{ + "null", + "random", + "full", + "tty", + "zero", + "urandom", }, }, } @@ -82,3 +125,266 @@ var specCommand = cli.Command{ fmt.Printf("%s", data) }, } + +var namespaceMapping = map[string]configs.NamespaceType{ + "process": configs.NEWPID, + "network": configs.NEWNET, + "mount": configs.NEWNS, + "user": configs.NEWUSER, + "ipc": configs.NEWIPC, + "uts": configs.NEWUTS, +} + +// loadSpec loads the specification from the provided path. +// If the path is empty then the default path will be "config.json" +func loadSpec(path string) (*specs.LinuxSpec, error) { + if path == "" { + path = "config.json" + } + f, err := os.Open(path) + if err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("JSON specification file for %s not found", path) + } + return nil, err + } + defer f.Close() + var s *specs.LinuxSpec + if err := json.NewDecoder(f).Decode(&s); err != nil { + return nil, err + } + return s, checkSpecVersion(s) +} + +// checkSpecVersion makes sure that the spec version matches runc's while we are in the initial +// development period. It is better to hard fail than have missing fields or options in the spec. +func checkSpecVersion(s *specs.LinuxSpec) error { + if s.Version != specs.Version { + return fmt.Errorf("spec version is not compatible with implemented version %q: spec %q", specs.Version, s.Version) + } + return nil +} + +func createLibcontainerConfig(spec *specs.LinuxSpec) (*configs.Config, error) { + cwd, err := os.Getwd() + if err != nil { + return nil, err + } + rootfsPath := spec.Root.Path + if !filepath.IsAbs(rootfsPath) { + rootfsPath = filepath.Join(cwd, rootfsPath) + } + config := &configs.Config{ + Rootfs: rootfsPath, + Capabilities: spec.Linux.Capabilities, + Readonlyfs: spec.Root.Readonly, + Hostname: spec.Hostname, + Privatefs: true, + } + for _, ns := range spec.Linux.Namespaces { + t, exists := namespaceMapping[ns.Type] + if !exists { + return nil, fmt.Errorf("namespace %q does not exist", ns) + } + config.Namespaces.Add(t, ns.Path) + } + for _, m := range spec.Mounts { + config.Mounts = append(config.Mounts, createLibcontainerMount(cwd, m)) + } + if err := createDevices(spec, config); err != nil { + return nil, err + } + if err := setupUserNamespace(spec, config); err != nil { + return nil, err + } + c, err := createCgroupConfig(spec, config.Devices) + if err != nil { + return nil, err + } + config.Cgroups = c + if config.Readonlyfs { + setReadonly(config) + config.MaskPaths = []string{ + "/proc/kcore", + } + config.ReadonlyPaths = []string{ + "/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus", + } + } + return config, nil +} + +func createLibcontainerMount(cwd string, m specs.Mount) *configs.Mount { + flags, data := parseMountOptions(m.Options) + source := m.Source + if m.Type == "bind" { + if !filepath.IsAbs(source) { + source = filepath.Join(cwd, m.Source) + } + } + return &configs.Mount{ + Device: m.Type, + Source: source, + Destination: m.Destination, + Data: data, + Flags: flags, + } +} + +func createCgroupConfig(spec *specs.LinuxSpec, devices []*configs.Device) (*configs.Cgroup, error) { + myCgroupPath, err := cgroups.GetThisCgroupDir("devices") + if err != nil { + return nil, err + } + c := &configs.Cgroup{ + Name: getDefaultID(), + Parent: myCgroupPath, + AllowedDevices: append(devices, allowedDevices...), + } + r := spec.Linux.Resources + c.MemoryReservation = r.Memory.Reservation + c.MemorySwap = r.Memory.Swap + c.KernelMemory = r.Memory.Kernel + c.CpuShares = r.CPU.Shares + c.CpuQuota = r.CPU.Quota + c.CpuPeriod = r.CPU.Period + c.CpuRtRuntime = r.CPU.RealtimeRuntime + c.CpuRtPeriod = r.CPU.RealtimePeriod + c.CpusetCpus = r.CPU.Cpus + c.CpusetMems = r.CPU.Mems + c.BlkioThrottleReadBpsDevice = r.BlockIO.ThrottleReadBpsDevice + c.BlkioThrottleWriteBpsDevice = r.BlockIO.ThrottleWriteBpsDevice + c.BlkioThrottleReadIOpsDevice = r.BlockIO.ThrottleReadIOpsDevice + c.BlkioThrottleWriteIOpsDevice = r.BlockIO.ThrottleWriteIOpsDevice + c.BlkioWeight = r.BlockIO.Weight + c.BlkioWeightDevice = r.BlockIO.WeightDevice + for _, l := range r.HugepageLimits { + c.HugetlbLimit = append(c.HugetlbLimit, &configs.HugepageLimit{ + Pagesize: l.Pagesize, + Limit: l.Limit, + }) + } + c.OomKillDisable = r.DisableOOMKiller + c.NetClsClassid = r.Network.ClassID + for _, m := range r.Network.Priorities { + c.NetPrioIfpriomap = append(c.NetPrioIfpriomap, &configs.IfPrioMap{ + Interface: m.Name, + Priority: m.Priority, + }) + } + return c, nil +} + +func createDevices(spec *specs.LinuxSpec, config *configs.Config) error { + for _, name := range spec.Linux.Devices { + d, err := devices.DeviceFromPath(filepath.Join("/dev", name), "rwm") + if err != nil { + return err + } + config.Devices = append(config.Devices, d) + } + return nil +} + +func setReadonly(config *configs.Config) { + for _, m := range config.Mounts { + if m.Device == "sysfs" { + m.Flags |= syscall.MS_RDONLY + } + } +} + +func setupUserNamespace(spec *specs.LinuxSpec, config *configs.Config) error { + if len(spec.Linux.UidMappings) == 0 { + return nil + } + config.Namespaces.Add(configs.NEWUSER, "") + create := func(m specs.IDMapping) configs.IDMap { + return configs.IDMap{ + ContainerID: int(m.From), + HostID: int(m.To), + Size: int(m.Count), + } + } + for _, m := range spec.Linux.UidMappings { + config.UidMappings = append(config.UidMappings, create(m)) + } + for _, m := range spec.Linux.GidMappings { + config.GidMappings = append(config.GidMappings, create(m)) + } + rootUid, err := config.HostUID() + if err != nil { + return err + } + rootGid, err := config.HostGID() + if err != nil { + return err + } + for _, node := range config.Devices { + node.Uid = uint32(rootUid) + node.Gid = uint32(rootGid) + } + return nil +} + +// parseMountOptions parses the string and returns the flags and any mount data that +// it contains. +func parseMountOptions(options string) (int, string) { + var ( + flag int + data []string + ) + flags := map[string]struct { + clear bool + flag int + }{ + "defaults": {false, 0}, + "ro": {false, syscall.MS_RDONLY}, + "rw": {true, syscall.MS_RDONLY}, + "suid": {true, syscall.MS_NOSUID}, + "nosuid": {false, syscall.MS_NOSUID}, + "dev": {true, syscall.MS_NODEV}, + "nodev": {false, syscall.MS_NODEV}, + "exec": {true, syscall.MS_NOEXEC}, + "noexec": {false, syscall.MS_NOEXEC}, + "sync": {false, syscall.MS_SYNCHRONOUS}, + "async": {true, syscall.MS_SYNCHRONOUS}, + "dirsync": {false, syscall.MS_DIRSYNC}, + "remount": {false, syscall.MS_REMOUNT}, + "mand": {false, syscall.MS_MANDLOCK}, + "nomand": {true, syscall.MS_MANDLOCK}, + "atime": {true, syscall.MS_NOATIME}, + "noatime": {false, syscall.MS_NOATIME}, + "diratime": {true, syscall.MS_NODIRATIME}, + "nodiratime": {false, syscall.MS_NODIRATIME}, + "bind": {false, syscall.MS_BIND}, + "rbind": {false, syscall.MS_BIND | syscall.MS_REC}, + "unbindable": {false, syscall.MS_UNBINDABLE}, + "runbindable": {false, syscall.MS_UNBINDABLE | syscall.MS_REC}, + "private": {false, syscall.MS_PRIVATE}, + "rprivate": {false, syscall.MS_PRIVATE | syscall.MS_REC}, + "shared": {false, syscall.MS_SHARED}, + "rshared": {false, syscall.MS_SHARED | syscall.MS_REC}, + "slave": {false, syscall.MS_SLAVE}, + "rslave": {false, syscall.MS_SLAVE | syscall.MS_REC}, + "relatime": {false, syscall.MS_RELATIME}, + "norelatime": {true, syscall.MS_RELATIME}, + "strictatime": {false, syscall.MS_STRICTATIME}, + "nostrictatime": {true, syscall.MS_STRICTATIME}, + } + for _, o := range strings.Split(options, ",") { + // If the option does not exist in the flags table or the flag + // is not supported on the platform, + // then it is a data value for a specific fs type + if f, exists := flags[o]; exists && f.flag != 0 { + if f.clear { + flag &= ^f.flag + } else { + flag |= f.flag + } + } else { + data = append(data, o) + } + } + return flag, strings.Join(data, ",") +} diff --git a/spec_linux.go b/spec_linux.go deleted file mode 100644 index ff3b9dd9387..00000000000 --- a/spec_linux.go +++ /dev/null @@ -1,283 +0,0 @@ -// +build linux - -package main - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - "syscall" - - "github.com/opencontainers/runc/libcontainer/cgroups" - "github.com/opencontainers/runc/libcontainer/configs" - "github.com/opencontainers/runc/libcontainer/devices" - "github.com/opencontainers/specs" -) - -var namespaceMapping = map[string]configs.NamespaceType{ - "process": configs.NEWPID, - "network": configs.NEWNET, - "mount": configs.NEWNS, - "user": configs.NEWUSER, - "ipc": configs.NEWIPC, - "uts": configs.NEWUTS, -} - -// loadSpec loads the specification from the provided path. -// If the path is empty then the default path will be "config.json" -func loadSpec(path string) (*specs.LinuxSpec, error) { - if path == "" { - path = "config.json" - } - f, err := os.Open(path) - if err != nil { - if os.IsNotExist(err) { - return nil, fmt.Errorf("JSON specification file for %s not found", path) - } - return nil, err - } - defer f.Close() - var s *specs.LinuxSpec - if err := json.NewDecoder(f).Decode(&s); err != nil { - return nil, err - } - return s, checkSpecVersion(s) -} - -// checkSpecVersion makes sure that the spec version matches runc's while we are in the initial -// development period. It is better to hard fail than have missing fields or options in the spec. -func checkSpecVersion(s *specs.LinuxSpec) error { - if s.Version != version { - return fmt.Errorf("spec version is not compatible with runc version %q: spec %q", version, s.Version) - } - return nil -} - -func createLibcontainerConfig(spec *specs.LinuxSpec) (*configs.Config, error) { - cwd, err := os.Getwd() - if err != nil { - return nil, err - } - rootfsPath := spec.Root.Path - if !filepath.IsAbs(rootfsPath) { - rootfsPath = filepath.Join(cwd, rootfsPath) - } - config := &configs.Config{ - Rootfs: rootfsPath, - Capabilities: spec.Linux.Capabilities, - Readonlyfs: spec.Root.Readonly, - Hostname: spec.Hostname, - Privatefs: true, - } - for _, ns := range spec.Linux.Namespaces { - t, exists := namespaceMapping[ns.Type] - if !exists { - return nil, fmt.Errorf("namespace %q does not exist", ns) - } - config.Namespaces.Add(t, ns.Path) - } - for _, m := range spec.Mounts { - config.Mounts = append(config.Mounts, createLibcontainerMount(cwd, m)) - } - if err := createDevices(spec, config); err != nil { - return nil, err - } - if err := setupUserNamespace(spec, config); err != nil { - return nil, err - } - c, err := createCgroupConfig(spec, config.Devices) - if err != nil { - return nil, err - } - config.Cgroups = c - if config.Readonlyfs { - setReadonly(config) - config.MaskPaths = []string{ - "/proc/kcore", - } - config.ReadonlyPaths = []string{ - "/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus", - } - } - return config, nil -} - -func createLibcontainerMount(cwd string, m specs.Mount) *configs.Mount { - flags, data := parseMountOptions(m.Options) - source := m.Source - if m.Type == "bind" { - if !filepath.IsAbs(source) { - source = filepath.Join(cwd, m.Source) - } - } - return &configs.Mount{ - Device: m.Type, - Source: source, - Destination: m.Destination, - Data: data, - Flags: flags, - } -} - -func createCgroupConfig(spec *specs.LinuxSpec, devices []*configs.Device) (*configs.Cgroup, error) { - myCgroupPath, err := cgroups.GetThisCgroupDir("devices") - if err != nil { - return nil, err - } - c := &configs.Cgroup{ - Name: getDefaultID(), - Parent: myCgroupPath, - AllowedDevices: append(devices, allowedDevices...), - MemorySwap: -1, - MemorySwappiness: -1, - } - if r := spec.Linux.Resources; r != nil { - c.MemoryReservation = r.Memory.Reservation - c.MemorySwap = r.Memory.Swap - c.KernelMemory = r.Memory.Kernel - c.CpuShares = r.CPU.Shares - c.CpuQuota = r.CPU.Quota - c.CpuPeriod = r.CPU.Period - c.CpuRtRuntime = r.CPU.RealtimeRuntime - c.CpuRtPeriod = r.CPU.RealtimePeriod - c.CpusetCpus = r.CPU.Cpus - c.CpusetMems = r.CPU.Mems - c.BlkioThrottleReadBpsDevice = r.BlockIO.ThrottleReadBpsDevice - c.BlkioThrottleWriteBpsDevice = r.BlockIO.ThrottleWriteBpsDevice - c.BlkioThrottleReadIOpsDevice = r.BlockIO.ThrottleReadIOpsDevice - c.BlkioThrottleWriteIOpsDevice = r.BlockIO.ThrottleWriteIOpsDevice - c.BlkioWeight = r.BlockIO.Weight - c.BlkioWeightDevice = r.BlockIO.WeightDevice - for _, l := range r.HugepageLimits { - c.HugetlbLimit = append(c.HugetlbLimit, &configs.HugepageLimit{ - Pagesize: l.Pagesize, - Limit: l.Limit, - }) - } - c.OomKillDisable = r.DisableOOMKiller - c.NetClsClassid = r.Network.ClassID - for _, m := range r.Network.Priorities { - c.NetPrioIfpriomap = append(c.NetPrioIfpriomap, &configs.IfPrioMap{ - Interface: m.Name, - Priority: m.Priority, - }) - } - } - return c, nil -} - -func createDevices(spec *specs.LinuxSpec, config *configs.Config) error { - for _, name := range spec.Linux.Devices { - d, err := devices.DeviceFromPath(filepath.Join("/dev", name), "rwm") - if err != nil { - return err - } - config.Devices = append(config.Devices, d) - } - return nil -} - -func setReadonly(config *configs.Config) { - for _, m := range config.Mounts { - if m.Device == "sysfs" { - m.Flags |= syscall.MS_RDONLY - } - } -} - -func setupUserNamespace(spec *specs.LinuxSpec, config *configs.Config) error { - if len(spec.Linux.UidMapping) == 0 { - return nil - } - config.Namespaces.Add(configs.NEWUSER, "") - create := func(m specs.IDMapping) configs.IDMap { - return configs.IDMap{ - ContainerID: int(m.From), - HostID: int(m.To), - Size: int(m.Count), - } - } - for _, m := range spec.Linux.UidMapping { - config.UidMappings = append(config.UidMappings, create(m)) - } - for _, m := range spec.Linux.GidMapping { - config.GidMappings = append(config.GidMappings, create(m)) - } - rootUid, err := config.HostUID() - if err != nil { - return err - } - rootGid, err := config.HostGID() - if err != nil { - return err - } - for _, node := range config.Devices { - node.Uid = uint32(rootUid) - node.Gid = uint32(rootGid) - } - return nil -} - -// parseMountOptions parses the string and returns the flags and any mount data that -// it contains. -func parseMountOptions(options string) (int, string) { - var ( - flag int - data []string - ) - flags := map[string]struct { - clear bool - flag int - }{ - "defaults": {false, 0}, - "ro": {false, syscall.MS_RDONLY}, - "rw": {true, syscall.MS_RDONLY}, - "suid": {true, syscall.MS_NOSUID}, - "nosuid": {false, syscall.MS_NOSUID}, - "dev": {true, syscall.MS_NODEV}, - "nodev": {false, syscall.MS_NODEV}, - "exec": {true, syscall.MS_NOEXEC}, - "noexec": {false, syscall.MS_NOEXEC}, - "sync": {false, syscall.MS_SYNCHRONOUS}, - "async": {true, syscall.MS_SYNCHRONOUS}, - "dirsync": {false, syscall.MS_DIRSYNC}, - "remount": {false, syscall.MS_REMOUNT}, - "mand": {false, syscall.MS_MANDLOCK}, - "nomand": {true, syscall.MS_MANDLOCK}, - "atime": {true, syscall.MS_NOATIME}, - "noatime": {false, syscall.MS_NOATIME}, - "diratime": {true, syscall.MS_NODIRATIME}, - "nodiratime": {false, syscall.MS_NODIRATIME}, - "bind": {false, syscall.MS_BIND}, - "rbind": {false, syscall.MS_BIND | syscall.MS_REC}, - "unbindable": {false, syscall.MS_UNBINDABLE}, - "runbindable": {false, syscall.MS_UNBINDABLE | syscall.MS_REC}, - "private": {false, syscall.MS_PRIVATE}, - "rprivate": {false, syscall.MS_PRIVATE | syscall.MS_REC}, - "shared": {false, syscall.MS_SHARED}, - "rshared": {false, syscall.MS_SHARED | syscall.MS_REC}, - "slave": {false, syscall.MS_SLAVE}, - "rslave": {false, syscall.MS_SLAVE | syscall.MS_REC}, - "relatime": {false, syscall.MS_RELATIME}, - "norelatime": {true, syscall.MS_RELATIME}, - "strictatime": {false, syscall.MS_STRICTATIME}, - "nostrictatime": {true, syscall.MS_STRICTATIME}, - } - for _, o := range strings.Split(options, ",") { - // If the option does not exist in the flags table or the flag - // is not supported on the platform, - // then it is a data value for a specific fs type - if f, exists := flags[o]; exists && f.flag != 0 { - if f.clear { - flag &= ^f.flag - } else { - flag |= f.flag - } - } else { - data = append(data, o) - } - } - return flag, strings.Join(data, ",") -} diff --git a/utils.go b/utils.go index cc4cc429fee..5c35edc6b2c 100644 --- a/utils.go +++ b/utils.go @@ -165,8 +165,9 @@ func getDefaultImagePath(context *cli.Context) string { // spec and stdio from the current process. func newProcess(p specs.Process) *libcontainer.Process { return &libcontainer.Process{ - Args: p.Args, - Env: p.Env, + Args: p.Args, + Env: p.Env, + // TODO: fix libcontainer's API to better support uid/gid in a typesafe way. User: fmt.Sprintf("%d:%d", p.User.Uid, p.User.Gid), Cwd: p.Cwd, Stdin: os.Stdin,