Merge pull request #66 from crosbymichael/config-spec-update
Update config based on spec changes
This commit is contained in:
commit
85e264d0b9
2
Makefile
2
Makefile
|
@ -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
|
||||||
|
|
||||||
|
|
66
README.md
66
README.md
|
@ -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",
|
||||||
|
|
8
main.go
8
main.go
|
@ -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)
|
||||||
|
|
|
@ -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
9
run.go
|
@ -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
82
spec.go
|
@ -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",
|
||||||
|
|
|
@ -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,
|
||||||
|
|
2
utils.go
2
utils.go
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue