Merge pull request #66 from crosbymichael/config-spec-update

Update config based on spec changes
This commit is contained in:
Alexander Morozov 2015-06-29 13:55:58 -07:00
commit 85e264d0b9
8 changed files with 101 additions and 143 deletions

View File

@ -2,7 +2,7 @@ all:
go get github.com/tools/godep go get github.com/tools/godep
godep go build -o runc . godep go build -o runc .
install: all install:
cp runc /usr/local/bin/runc cp runc /usr/local/bin/runc
rm runc rm runc

View File

@ -42,60 +42,28 @@ user named `daemon` defined within that file-system.
```json ```json
{ {
"version": "0.1", "version": "0.1.1",
"os": "linux", "platform": {
"arch": "amd64", "os": "linux",
"processes": [ "arch": "amd64"
{ },
"tty": true, "process": {
"user": "daemon", "terminal": true,
"args": [ "user": "daemon",
"sh" "args": [
], "sh"
"env": [ ],
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "env": [
"TERM=xterm" "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
], "TERM=xterm"
"cwd": "" ],
} "cwd": ""
], },
"root": { "root": {
"path": "rootfs", "path": "rootfs",
"readonly": true "readonly": true
}, },
"cpus": 1.1,
"memory": 1024,
"hostname": "shell", "hostname": "shell",
"namespaces": [
{
"type": "process"
},
{
"type": "network"
},
{
"type": "mount"
},
{
"type": "ipc"
},
{
"type": "uts"
}
],
"capabilities": [
"AUDIT_WRITE",
"KILL",
"NET_BIND_SERVICE"
],
"devices": [
"null",
"random",
"full",
"tty",
"zero",
"urandom"
],
"mounts": [ "mounts": [
{ {
"type": "proc", "type": "proc",

View File

@ -10,7 +10,7 @@ import (
) )
const ( const (
version = "0.1" version = "0.1.1"
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
@ -79,13 +79,13 @@ func main() {
} }
// default action is to execute a container // default action is to execute a container
app.Action = func(context *cli.Context) { app.Action = func(context *cli.Context) {
if os.Geteuid() != 0 {
logrus.Fatal("runc should be run as root")
}
spec, err := loadSpec(context.Args().First()) spec, err := loadSpec(context.Args().First())
if err != nil { if err != nil {
fatal(err) fatal(err)
} }
if os.Geteuid() != 0 {
logrus.Fatal("runc should be run as root")
}
status, err := execContainer(context, spec) status, err := execContainer(context, spec)
if err != nil { if err != nil {
logrus.Fatalf("Container start failed: %v", err) logrus.Fatalf("Container start failed: %v", err)

View File

@ -44,7 +44,7 @@ var restoreCommand = cli.Command{
}, },
} }
func restoreContainer(context *cli.Context, spec *LinuxSpec, config *configs.Config, imagePath string) (code int, err error) { func restoreContainer(context *cli.Context, spec *Spec, config *configs.Config, imagePath string) (code int, err error) {
rootuid := 0 rootuid := 0
factory, err := loadFactory(context) factory, err := loadFactory(context)
if err != nil { if err != nil {
@ -82,7 +82,7 @@ func restoreContainer(context *cli.Context, spec *LinuxSpec, config *configs.Con
Stdout: os.Stdout, Stdout: os.Stdout,
Stderr: os.Stderr, Stderr: os.Stderr,
} }
tty, err := newTty(spec.Processes[0].TTY, process, rootuid) tty, err := newTty(spec.Process.Terminal, process, rootuid)
if err != nil { if err != nil {
return -1, err return -1, err
} }

9
run.go
View File

@ -9,10 +9,7 @@ import (
"github.com/opencontainers/runc/libcontainer" "github.com/opencontainers/runc/libcontainer"
) )
func execContainer(context *cli.Context, spec *LinuxSpec) (int, error) { func execContainer(context *cli.Context, spec *Spec) (int, error) {
if len(spec.Processes) != 1 {
return -1, fmt.Errorf("runc only supports one(1) process for the container")
}
config, err := createLibcontainerConfig(spec) config, err := createLibcontainerConfig(spec)
if err != nil { if err != nil {
return -1, err return -1, err
@ -38,8 +35,8 @@ func execContainer(context *cli.Context, spec *LinuxSpec) (int, error) {
// ensure that the container is always removed if we were the process // ensure that the container is always removed if we were the process
// that created it. // that created it.
defer destroy(container) defer destroy(container)
process := newProcess(spec.Processes[0]) process := newProcess(spec.Process)
tty, err := newTty(spec.Processes[0].TTY, process, rootuid) tty, err := newTty(spec.Process.Terminal, process, rootuid)
if err != nil { if err != nil {
return -1, err return -1, err
} }

82
spec.go
View File

@ -18,11 +18,11 @@ type Mount struct {
} }
type Process struct { type Process struct {
TTY bool `json:"tty"` Terminal bool `json:"terminal"`
User string `json:"user"` User string `json:"user"`
Args []string `json:"args"` Args []string `json:"args"`
Env []string `json:"env"` Env []string `json:"env"`
Cwd string `json:"cwd"` Cwd string `json:"cwd"`
} }
type Root struct { type Root struct {
@ -30,24 +30,18 @@ type Root struct {
Readonly bool `json:"readonly"` Readonly bool `json:"readonly"`
} }
type Namespace struct { type Platform struct {
Type string `json:"type"` OS string `json:"os"`
Path string `json:"path,omitempty"` Arch string `json:"arch"`
} }
type PortableSpec struct { type PortableSpec struct {
Version string `json:"version"` Version string `json:"version"`
OS string `json:"os"` Platform Platform `json:"platform"`
Arch string `json:"arch"` Process Process `json:"process"`
Processes []*Process `json:"processes"` Root Root `json:"root"`
Root Root `json:"root"` Hostname string `json:"hostname"`
Cpus float64 `json:"cpus"` // in 1.1 for 110% cpus Mounts []Mount `json:"mounts"`
Memory int64 `json:"memory"` // in mb; 1024m
Hostname string `json:"hostname"`
Namespaces []Namespace `json:"namespaces"`
Capabilities []string `json:"capabilities"`
Devices []string `json:"devices"`
Mounts []Mount `json:"mounts"`
} }
var specCommand = cli.Command{ var specCommand = cli.Command{
@ -56,48 +50,26 @@ var specCommand = cli.Command{
Action: func(context *cli.Context) { Action: func(context *cli.Context) {
spec := PortableSpec{ spec := PortableSpec{
Version: version, Version: version,
OS: runtime.GOOS, Platform: Platform{
Arch: runtime.GOARCH, OS: runtime.GOOS,
Arch: runtime.GOARCH,
},
Root: Root{ Root: Root{
Path: "rootfs", Path: "rootfs",
Readonly: true, Readonly: true,
}, },
Processes: []*Process{ Process: Process{
{ Terminal: true,
TTY: true, User: "daemon",
User: "daemon", Args: []string{
Args: []string{ "sh",
"sh", },
}, Env: []string{
Env: []string{ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM=xterm",
"TERM=xterm",
},
}, },
}, },
Cpus: 1.1,
Memory: 1024,
Hostname: "shell", Hostname: "shell",
Capabilities: []string{
"AUDIT_WRITE",
"KILL",
"NET_BIND_SERVICE",
},
Devices: []string{
"null",
"random",
"full",
"tty",
"zero",
"urandom",
},
Namespaces: []Namespace{
{Type: "process"},
{Type: "network"},
{Type: "mount"},
{Type: "ipc"},
{Type: "uts"},
},
Mounts: []Mount{ Mounts: []Mount{
{ {
Type: "proc", Type: "proc",

View File

@ -15,6 +15,26 @@ import (
"github.com/opencontainers/runc/libcontainer/devices" "github.com/opencontainers/runc/libcontainer/devices"
) )
type Spec struct {
PortableSpec
Linux Linux `json:"linux"`
}
type Linux struct {
UserMapping map[string]UserMapping `json:"userMapping"`
Rlimits []Rlimit `json:"rlimits"`
SystemProperties map[string]string `json:"systemProperties"`
Resources *Resources `json:"resources"`
Namespaces []Namespace `json:"namespaces"`
Capabilities []string `json:"capabilities"`
Devices []string `json:"devices"`
}
type Namespace struct {
Type string `json:"type"`
Path string `json:"path"`
}
type UserMapping struct { type UserMapping struct {
From int `json:"from"` From int `json:"from"`
To int `json:"to"` To int `json:"to"`
@ -38,6 +58,8 @@ type IfPrioMap struct {
} }
type Resources struct { type Resources struct {
// Memory limit (in bytes)
MemoryLimit int64 `json:"memoryLimit"`
// Memory reservation or soft_limit (in bytes) // Memory reservation or soft_limit (in bytes)
MemoryReservation int64 `json:"memoryReservation"` MemoryReservation int64 `json:"memoryReservation"`
// Total memory usage (memory + swap); set `-1' to disable swap // Total memory usage (memory + swap); set `-1' to disable swap
@ -80,14 +102,6 @@ type Resources struct {
NetClsClassid string `json:"netClsClassid"` NetClsClassid string `json:"netClsClassid"`
} }
type LinuxSpec struct {
PortableSpec
UserMapping map[string]UserMapping `json:"userMapping"`
Rlimits []Rlimit `json:"rlimits"`
SystemProperties map[string]string `json:"systemProperties"`
Resources *Resources `json:"resources"`
}
var namespaceMapping = map[string]configs.NamespaceType{ var namespaceMapping = map[string]configs.NamespaceType{
"process": configs.NEWPID, "process": configs.NEWPID,
"network": configs.NEWNET, "network": configs.NEWNET,
@ -98,10 +112,10 @@ var namespaceMapping = map[string]configs.NamespaceType{
} }
// loadSpec loads the specification from the provided path. // loadSpec loads the specification from the provided path.
// If the path is empty then the default path will be "container.json" // If the path is empty then the default path will be "config.json"
func loadSpec(path string) (*LinuxSpec, error) { func loadSpec(path string) (*Spec, error) {
if path == "" { if path == "" {
path = "container.json" path = "config.json"
} }
f, err := os.Open(path) f, err := os.Open(path)
if err != nil { if err != nil {
@ -111,14 +125,23 @@ func loadSpec(path string) (*LinuxSpec, error) {
return nil, err return nil, err
} }
defer f.Close() defer f.Close()
var s *LinuxSpec var s *Spec
if err := json.NewDecoder(f).Decode(&s); err != nil { if err := json.NewDecoder(f).Decode(&s); err != nil {
return nil, err return nil, err
} }
return s, nil return s, checkSpecVersion(s)
} }
func createLibcontainerConfig(spec *LinuxSpec) (*configs.Config, error) { // 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 *Spec) 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 *Spec) (*configs.Config, error) {
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
return nil, err return nil, err
@ -128,13 +151,13 @@ func createLibcontainerConfig(spec *LinuxSpec) (*configs.Config, error) {
rootfsPath = filepath.Join(cwd, rootfsPath) rootfsPath = filepath.Join(cwd, rootfsPath)
} }
config := &configs.Config{ config := &configs.Config{
Capabilities: spec.Capabilities,
Rootfs: rootfsPath, Rootfs: rootfsPath,
Capabilities: spec.Linux.Capabilities,
Readonlyfs: spec.Root.Readonly, Readonlyfs: spec.Root.Readonly,
Hostname: spec.Hostname, Hostname: spec.Hostname,
Privatefs: true, Privatefs: true,
} }
for _, ns := range spec.Namespaces { for _, ns := range spec.Linux.Namespaces {
t, exists := namespaceMapping[ns.Type] t, exists := namespaceMapping[ns.Type]
if !exists { if !exists {
return nil, fmt.Errorf("namespace %q does not exist", ns) return nil, fmt.Errorf("namespace %q does not exist", ns)
@ -184,7 +207,7 @@ func createLibcontainerMount(cwd string, m Mount) *configs.Mount {
} }
} }
func createCgroupConfig(spec *LinuxSpec, devices []*configs.Device) (*configs.Cgroup, error) { func createCgroupConfig(spec *Spec, devices []*configs.Device) (*configs.Cgroup, error) {
myCgroupPath, err := cgroups.GetThisCgroupDir("devices") myCgroupPath, err := cgroups.GetThisCgroupDir("devices")
if err != nil { if err != nil {
return nil, err return nil, err
@ -193,12 +216,10 @@ func createCgroupConfig(spec *LinuxSpec, devices []*configs.Device) (*configs.Cg
Name: getDefaultID(), Name: getDefaultID(),
Parent: myCgroupPath, Parent: myCgroupPath,
AllowedDevices: append(devices, allowedDevices...), AllowedDevices: append(devices, allowedDevices...),
CpuQuota: getCPUQuota(spec.Cpus),
Memory: spec.Memory * 1024 * 1024,
MemorySwap: -1, MemorySwap: -1,
MemorySwappiness: -1, MemorySwappiness: -1,
} }
if r := spec.Resources; r != nil { if r := spec.Linux.Resources; r != nil {
c.MemoryReservation = r.MemoryReservation c.MemoryReservation = r.MemoryReservation
c.MemorySwap = r.MemorySwap c.MemorySwap = r.MemorySwap
c.KernelMemory = r.KernelMemory c.KernelMemory = r.KernelMemory
@ -233,8 +254,8 @@ func createCgroupConfig(spec *LinuxSpec, devices []*configs.Device) (*configs.Cg
return c, nil return c, nil
} }
func createDevices(spec *LinuxSpec, config *configs.Config) error { func createDevices(spec *Spec, config *configs.Config) error {
for _, name := range spec.Devices { for _, name := range spec.Linux.Devices {
d, err := devices.DeviceFromPath(filepath.Join("/dev", name), "rwm") d, err := devices.DeviceFromPath(filepath.Join("/dev", name), "rwm")
if err != nil { if err != nil {
return err return err
@ -256,13 +277,13 @@ func getCPUQuota(cpus float64) int64 {
return int64(cpus * cpuQuotaMultiplyer) return int64(cpus * cpuQuotaMultiplyer)
} }
func setupUserNamespace(spec *LinuxSpec, config *configs.Config) error { func setupUserNamespace(spec *Spec, config *configs.Config) error {
if len(spec.UserMapping) == 0 { if len(spec.Linux.UserMapping) == 0 {
return nil return nil
} }
config.Namespaces.Add(configs.NEWUSER, "") config.Namespaces.Add(configs.NEWUSER, "")
mappings := make(map[string][]configs.IDMap) mappings := make(map[string][]configs.IDMap)
for k, v := range spec.UserMapping { for k, v := range spec.Linux.UserMapping {
mappings[k] = append(mappings[k], configs.IDMap{ mappings[k] = append(mappings[k], configs.IDMap{
ContainerID: v.From, ContainerID: v.From,
HostID: v.To, HostID: v.To,

View File

@ -160,7 +160,7 @@ func getDefaultImagePath(context *cli.Context) string {
// newProcess returns a new libcontainer Process with the arguments from the // newProcess returns a new libcontainer Process with the arguments from the
// spec and stdio from the current process. // spec and stdio from the current process.
func newProcess(p *Process) *libcontainer.Process { func newProcess(p Process) *libcontainer.Process {
return &libcontainer.Process{ return &libcontainer.Process{
Args: p.Args, Args: p.Args,
Env: p.Env, Env: p.Env,