Create linux spec for runc spec command

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby
2015-07-02 09:59:30 -07:00
parent f4c35e70d1
commit 845fc65e54
6 changed files with 417 additions and 349 deletions
+53 -6
View File
@@ -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 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. 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: ### Building:
```bash ```bash
@@ -42,7 +49,7 @@ user named `daemon` defined within that file-system.
```json ```json
{ {
"version": "0.1.1", "version": "pre-draft",
"platform": { "platform": {
"os": "linux", "os": "linux",
"arch": "amd64" "arch": "amd64"
@@ -107,21 +114,61 @@ user named `daemon` defined within that file-system.
} }
], ],
"linux": { "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": [ "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": [ "capabilities": [
+1 -1
View File
@@ -8,7 +8,7 @@ import (
) )
const ( const (
version = "0.1.1" version = "0.2"
usage = `Open Container Project runtime usage = `Open Container Project runtime
runc is a command line client for running applications packaged according to the Open Container Format (OCF) and is runc is a command line client for running applications packaged according to the Open Container Format (OCF) and is
+1 -4
View File
@@ -7,10 +7,6 @@ import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )
type User struct {
NOTSUPPORTED string
}
func getDefaultID() string { func getDefaultID() string {
return "" return ""
} }
@@ -19,6 +15,7 @@ var (
checkpointCommand cli.Command checkpointCommand cli.Command
eventsCommand cli.Command eventsCommand cli.Command
restoreCommand cli.Command restoreCommand cli.Command
specCommand cli.Command
) )
func runAction(*cli.Context) { func runAction(*cli.Context) {
+359 -53
View File
@@ -1,12 +1,21 @@
// +build linux
package main package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"path/filepath"
"runtime" "runtime"
"strings"
"syscall"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/codegangsta/cli" "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" "github.com/opencontainers/specs"
) )
@@ -14,64 +23,98 @@ var specCommand = cli.Command{
Name: "spec", Name: "spec",
Usage: "create a new specification file", Usage: "create a new specification file",
Action: func(context *cli.Context) { Action: func(context *cli.Context) {
spec := specs.Spec{ spec := specs.LinuxSpec{
Version: version, Spec: specs.Spec{
Platform: specs.Platform{ Version: specs.Version,
OS: runtime.GOOS, Platform: specs.Platform{
Arch: runtime.GOARCH, OS: runtime.GOOS,
}, Arch: runtime.GOARCH,
Root: specs.Root{
Path: "rootfs",
Readonly: true,
},
Process: specs.Process{
Terminal: true,
User: specs.User{},
Args: []string{
"sh",
}, },
Env: []string{ Root: specs.Root{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", Path: "rootfs",
"TERM=xterm", Readonly: true,
},
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",
},
},
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",
},
}, },
}, },
Hostname: "shell", Linux: specs.Linux{
Mounts: []specs.Mount{ Namespaces: []specs.Namespace{
{ {
Type: "proc", Type: "process",
Source: "proc", },
Destination: "/proc", {
Options: "", Type: "network",
},
{
Type: "ipc",
},
{
Type: "uts",
},
{
Type: "mount",
},
}, },
{ Capabilities: []string{
Type: "tmpfs", "AUDIT_WRITE",
Source: "tmpfs", "KILL",
Destination: "/dev", "NET_BIND_SERVICE",
Options: "nosuid,strictatime,mode=755,size=65536k",
}, },
{ Devices: []string{
Type: "devpts", "null",
Source: "devpts", "random",
Destination: "/dev/pts", "full",
Options: "nosuid,noexec,newinstance,ptmxmode=0666,mode=0620,gid=5", "tty",
}, "zero",
{ "urandom",
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",
}, },
}, },
} }
@@ -82,3 +125,266 @@ var specCommand = cli.Command{
fmt.Printf("%s", data) 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, ",")
}
-283
View File
@@ -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, ",")
}
+3 -2
View File
@@ -165,8 +165,9 @@ func getDefaultImagePath(context *cli.Context) string {
// spec and stdio from the current process. // spec and stdio from the current process.
func newProcess(p specs.Process) *libcontainer.Process { func newProcess(p specs.Process) *libcontainer.Process {
return &libcontainer.Process{ return &libcontainer.Process{
Args: p.Args, Args: p.Args,
Env: p.Env, 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), User: fmt.Sprintf("%d:%d", p.User.Uid, p.User.Gid),
Cwd: p.Cwd, Cwd: p.Cwd,
Stdin: os.Stdin, Stdin: os.Stdin,