2015-07-03 00:59:30 +08:00
|
|
|
// +build linux
|
|
|
|
|
2015-06-22 10:31:12 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2015-09-02 00:32:29 +08:00
|
|
|
"io/ioutil"
|
2015-07-03 00:59:30 +08:00
|
|
|
"os"
|
2015-06-22 10:31:12 +08:00
|
|
|
"runtime"
|
|
|
|
|
2015-07-03 00:59:30 +08:00
|
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
2016-04-13 04:35:51 +08:00
|
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
2016-06-07 02:45:46 +08:00
|
|
|
"github.com/urfave/cli"
|
2015-06-22 10:31:12 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var specCommand = cli.Command{
|
2016-03-19 02:54:06 +08:00
|
|
|
Name: "spec",
|
|
|
|
Usage: "create a new specification file",
|
|
|
|
ArgsUsage: "",
|
|
|
|
Description: `The spec command creates the new specification file named "` + specConfig + `" for
|
|
|
|
the bundle.
|
|
|
|
|
|
|
|
The spec generated is just a starter file. Editing of the spec is required to
|
|
|
|
achieve desired results. For example, the newly generated spec includes an args
|
|
|
|
parameter that is initially set to call the "sh" command when the container is
|
|
|
|
started. Calling "sh" may work for an ubuntu container or busybox, but will not
|
|
|
|
work for containers that do not include the "sh" program.
|
|
|
|
|
|
|
|
EXAMPLE:
|
|
|
|
To run docker's hello-world container one needs to set the args parameter
|
|
|
|
in the spec to call hello. This can be done using the sed command or a text
|
|
|
|
editor. The following commands create a bundle for hello-world, change the
|
|
|
|
default args parameter in the spec from "sh" to "/hello", then run the hello
|
|
|
|
command in a new hello-world container named container1:
|
|
|
|
|
|
|
|
mkdir hello
|
|
|
|
cd hello
|
|
|
|
docker pull hello-world
|
|
|
|
docker export $(docker create hello-world) > hello-world.tar
|
|
|
|
mkdir rootfs
|
|
|
|
tar -C rootfs -xf hello-world.tar
|
|
|
|
runc spec
|
|
|
|
sed -i 's;"sh";"/hello";' ` + specConfig + `
|
2016-06-03 13:53:59 +08:00
|
|
|
runc run container1
|
2016-03-19 02:54:06 +08:00
|
|
|
|
2016-06-03 13:53:59 +08:00
|
|
|
In the run command above, "container1" is the name for the instance of the
|
2016-03-19 02:54:06 +08:00
|
|
|
container that you are starting. The name you provide for the container instance
|
|
|
|
must be unique on your host.
|
|
|
|
|
2016-12-16 14:04:50 +08:00
|
|
|
An alternative for generating a customized spec config is to use "oci-runtime-tool", the
|
|
|
|
sub-command "oci-runtime-tool generate" has lots of options that can be used to do any
|
|
|
|
customizations as you want, see [runtime-tools](https://github.com/opencontainers/runtime-tools)
|
2016-05-20 11:46:35 +08:00
|
|
|
to get more information.
|
2016-04-21 12:57:26 +08:00
|
|
|
|
2016-03-19 02:54:06 +08:00
|
|
|
When starting a container through runc, runc needs root privilege. If not
|
|
|
|
already running as root, you can use sudo to give runc root privilege. For
|
|
|
|
example: "sudo runc start container1" will give runc root privilege to start the
|
|
|
|
container on your host.`,
|
2015-09-02 00:32:29 +08:00
|
|
|
Flags: []cli.Flag{
|
2015-09-16 10:06:59 +08:00
|
|
|
cli.StringFlag{
|
2015-10-28 03:23:44 +08:00
|
|
|
Name: "bundle, b",
|
|
|
|
Value: "",
|
|
|
|
Usage: "path to the root of the bundle directory",
|
2015-09-16 10:06:59 +08:00
|
|
|
},
|
2015-09-02 00:32:29 +08:00
|
|
|
},
|
2016-05-10 13:58:09 +08:00
|
|
|
Action: func(context *cli.Context) error {
|
2016-10-28 23:43:10 +08:00
|
|
|
if err := checkArgs(context, 0, exactArgs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-03-11 06:18:39 +08:00
|
|
|
spec := specs.Spec{
|
|
|
|
Version: specs.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",
|
2015-06-30 02:21:05 +08:00
|
|
|
},
|
2016-03-11 06:18:39 +08:00
|
|
|
Env: []string{
|
|
|
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
|
|
"TERM=xterm",
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
2016-03-11 06:18:39 +08:00
|
|
|
Cwd: "/",
|
|
|
|
NoNewPrivileges: true,
|
|
|
|
Capabilities: []string{
|
|
|
|
"CAP_AUDIT_WRITE",
|
|
|
|
"CAP_KILL",
|
|
|
|
"CAP_NET_BIND_SERVICE",
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
2016-12-17 13:01:53 +08:00
|
|
|
Rlimits: []specs.LinuxRlimit{
|
2015-07-15 09:31:39 +08:00
|
|
|
{
|
2016-03-11 06:18:39 +08:00
|
|
|
Type: "RLIMIT_NOFILE",
|
|
|
|
Hard: uint64(1024),
|
|
|
|
Soft: uint64(1024),
|
2015-07-15 09:31:39 +08:00
|
|
|
},
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
2015-07-03 00:59:30 +08:00
|
|
|
},
|
2016-03-11 06:18:39 +08:00
|
|
|
Hostname: "runc",
|
|
|
|
Mounts: []specs.Mount{
|
|
|
|
{
|
|
|
|
Destination: "/proc",
|
|
|
|
Type: "proc",
|
|
|
|
Source: "proc",
|
|
|
|
Options: nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Destination: "/dev",
|
|
|
|
Type: "tmpfs",
|
|
|
|
Source: "tmpfs",
|
|
|
|
Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Destination: "/dev/pts",
|
|
|
|
Type: "devpts",
|
|
|
|
Source: "devpts",
|
|
|
|
Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Destination: "/dev/shm",
|
|
|
|
Type: "tmpfs",
|
|
|
|
Source: "shm",
|
|
|
|
Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Destination: "/dev/mqueue",
|
|
|
|
Type: "mqueue",
|
|
|
|
Source: "mqueue",
|
|
|
|
Options: []string{"nosuid", "noexec", "nodev"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Destination: "/sys",
|
|
|
|
Type: "sysfs",
|
|
|
|
Source: "sysfs",
|
|
|
|
Options: []string{"nosuid", "noexec", "nodev", "ro"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Destination: "/sys/fs/cgroup",
|
|
|
|
Type: "cgroup",
|
|
|
|
Source: "cgroup",
|
|
|
|
Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
|
|
|
|
},
|
|
|
|
},
|
2016-09-12 06:00:14 +08:00
|
|
|
Linux: &specs.Linux{
|
2016-04-02 04:04:47 +08:00
|
|
|
MaskedPaths: []string{
|
|
|
|
"/proc/kcore",
|
|
|
|
"/proc/latency_stats",
|
Adding /proc/timer_list to the masked paths list
/proc/timer_list seems to leak information about the host. Here is
an example from a busybox container running on docker+kubernetes.
# cat /proc/timer_list | grep -i -e kube
<ffff8800b8cc3db0>, hrtimer_wakeup, S:01, futex_wait_queue_me, kubelet/2497
<ffff880129ac3db0>, hrtimer_wakeup, S:01, futex_wait_queue_me, kube-proxy/3478
<ffff8800b1b77db0>, hrtimer_wakeup, S:01, futex_wait_queue_me, kube-proxy/3470
<ffff8800bb6abdb0>, hrtimer_wakeup, S:01, futex_wait_queue_me, kubelet/2499
Signed-off-by: Davanum Srinivas <davanum@gmail.com>
2016-08-12 03:03:47 +08:00
|
|
|
"/proc/timer_list",
|
2016-04-02 04:04:47 +08:00
|
|
|
"/proc/timer_stats",
|
|
|
|
"/proc/sched_debug",
|
2016-09-23 15:02:10 +08:00
|
|
|
"/sys/firmware",
|
2016-04-02 04:04:47 +08:00
|
|
|
},
|
|
|
|
ReadonlyPaths: []string{
|
|
|
|
"/proc/asound",
|
|
|
|
"/proc/bus",
|
|
|
|
"/proc/fs",
|
|
|
|
"/proc/irq",
|
|
|
|
"/proc/sys",
|
|
|
|
"/proc/sysrq-trigger",
|
|
|
|
},
|
2016-12-17 13:01:53 +08:00
|
|
|
Resources: &specs.LinuxResources{
|
|
|
|
Devices: []specs.LinuxDeviceCgroup{
|
2016-02-06 07:15:25 +08:00
|
|
|
{
|
|
|
|
Allow: false,
|
|
|
|
Access: sPtr("rwm"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-12-17 13:01:53 +08:00
|
|
|
Namespaces: []specs.LinuxNamespace{
|
2015-07-03 00:59:30 +08:00
|
|
|
{
|
2015-08-05 05:12:18 +08:00
|
|
|
Type: "pid",
|
2015-07-03 00:59:30 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: "network",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: "ipc",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: "uts",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: "mount",
|
|
|
|
},
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2016-02-06 02:46:12 +08:00
|
|
|
|
2015-09-02 00:32:29 +08:00
|
|
|
checkNoFile := func(name string) error {
|
|
|
|
_, err := os.Stat(name)
|
|
|
|
if err == nil {
|
|
|
|
return fmt.Errorf("File %s exists. Remove it first", name)
|
|
|
|
}
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2015-10-28 03:23:44 +08:00
|
|
|
bundle := context.String("bundle")
|
|
|
|
if bundle != "" {
|
|
|
|
if err := os.Chdir(bundle); err != nil {
|
2016-05-10 13:58:09 +08:00
|
|
|
return err
|
2015-10-28 03:23:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := checkNoFile(specConfig); err != nil {
|
2016-05-10 13:58:09 +08:00
|
|
|
return err
|
2015-09-02 00:32:29 +08:00
|
|
|
}
|
2015-06-22 10:31:12 +08:00
|
|
|
data, err := json.MarshalIndent(&spec, "", "\t")
|
|
|
|
if err != nil {
|
2016-05-10 13:58:09 +08:00
|
|
|
return err
|
2015-06-22 10:31:12 +08:00
|
|
|
}
|
2015-10-28 03:23:44 +08:00
|
|
|
if err := ioutil.WriteFile(specConfig, data, 0666); err != nil {
|
2016-05-10 13:58:09 +08:00
|
|
|
return err
|
2015-09-02 00:32:29 +08:00
|
|
|
}
|
2016-05-10 13:58:09 +08:00
|
|
|
return nil
|
2015-06-22 10:31:12 +08:00
|
|
|
},
|
|
|
|
}
|
2015-07-03 00:59:30 +08:00
|
|
|
|
2017-01-07 08:10:13 +08:00
|
|
|
func sPtr(s string) *string { return &s }
|
2016-02-06 02:46:12 +08:00
|
|
|
|
2015-07-03 00:59:30 +08:00
|
|
|
// loadSpec loads the specification from the provided path.
|
2016-03-11 06:18:39 +08:00
|
|
|
func loadSpec(cPath string) (spec *specs.Spec, err error) {
|
2015-09-02 00:32:29 +08:00
|
|
|
cf, err := os.Open(cPath)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
2016-02-06 02:46:12 +08:00
|
|
|
return nil, fmt.Errorf("JSON specification file %s not found", cPath)
|
2015-09-02 00:32:29 +08:00
|
|
|
}
|
2016-03-06 11:52:26 +08:00
|
|
|
return nil, err
|
2015-07-03 00:59:30 +08:00
|
|
|
}
|
2015-09-16 10:54:53 +08:00
|
|
|
defer cf.Close()
|
|
|
|
|
2015-09-02 00:32:29 +08:00
|
|
|
if err = json.NewDecoder(cf).Decode(&spec); err != nil {
|
2016-03-06 11:52:26 +08:00
|
|
|
return nil, err
|
2015-09-02 00:32:29 +08:00
|
|
|
}
|
Valide platform on loading config.json
run an arm64 image on an amd64 platform, it will failed with
````
panic: standard_init_linux.go:175: exec user process caused "exec format error" [recovered]
panic: standard_init_linux.go:175: exec user process caused "exec format error"
goroutine 1 [running, locked to thread]:
panic(0x7e7e40, 0xc820124380)
/usr/local/go/src/runtime/panic.go:481 +0x3e6
github.com/urfave/cli.HandleAction.func1(0xc8200c52f8)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:478 +0x38e
panic(0x7e7e40, 0xc820124380)
/usr/local/go/src/runtime/panic.go:443 +0x4e9
github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization.func1(0xc8200c4c08, 0xc8200220a0, 0xc8200c4d18)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:259 +0x136
github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization(0xc82006c780, 0x7fb9124733f8, 0xc820124380)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:277 +0x5b1
main.glob.func8(0xc820090780, 0x0, 0x0)
/home/lei/opencontainers/runc/main_unix.go:26 +0x68
reflect.Value.call(0x74ca00, 0x8fda40, 0x13, 0x844470, 0x4, 0xc8200c5278, 0x1, 0x1, 0x0, 0x0, ...)
/usr/local/go/src/reflect/value.go:435 +0x120d
reflect.Value.Call(0x74ca00, 0x8fda40, 0x13, 0xc8200c5278, 0x1, 0x1, 0x0, 0x0, 0x0)
/usr/local/go/src/reflect/value.go:303 +0xb1
github.com/urfave/cli.HandleAction(0x74ca00, 0x8fda40, 0xc820090780, 0x0, 0x0)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:487 +0x2ee
github.com/urfave/cli.Command.Run(0x847330, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8dcec0, 0x51, 0x0, ...)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/command.go:191 +0xfec
github.com/urfave/cli.(*App).Run(0xc820001980, 0xc82000a100, 0x2, 0x2, 0x0, 0x0)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:240 +0xaa4
main.main()
/home/lei/opencontainers/runc/main.go:137 +0xe24
````
instead of throwing panic on execing the container process, we should
throw the platform mismatch at the very beginning, it's much more
clear and can tell user what's wrong.
Signed-off-by: Lei Jitang <leijitang@huawei.com>
2016-10-14 14:14:36 +08:00
|
|
|
if err = validatePlatform(&spec.Platform); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-03-08 01:36:14 +08:00
|
|
|
return spec, validateProcessSpec(&spec.Process)
|
2015-07-03 00:59:30 +08:00
|
|
|
}
|
|
|
|
|
2016-12-17 13:01:53 +08:00
|
|
|
func createLibContainerRlimit(rlimit specs.LinuxRlimit) (configs.Rlimit, error) {
|
2015-09-02 00:32:29 +08:00
|
|
|
rl, err := strToRlimit(rlimit.Type)
|
|
|
|
if err != nil {
|
|
|
|
return configs.Rlimit{}, err
|
|
|
|
}
|
2015-08-23 19:17:31 +08:00
|
|
|
return configs.Rlimit{
|
2015-09-02 00:32:29 +08:00
|
|
|
Type: rl,
|
2017-01-07 08:10:13 +08:00
|
|
|
Hard: rlimit.Hard,
|
|
|
|
Soft: rlimit.Soft,
|
2015-09-02 00:32:29 +08:00
|
|
|
}, nil
|
2015-08-23 19:17:31 +08:00
|
|
|
}
|
Valide platform on loading config.json
run an arm64 image on an amd64 platform, it will failed with
````
panic: standard_init_linux.go:175: exec user process caused "exec format error" [recovered]
panic: standard_init_linux.go:175: exec user process caused "exec format error"
goroutine 1 [running, locked to thread]:
panic(0x7e7e40, 0xc820124380)
/usr/local/go/src/runtime/panic.go:481 +0x3e6
github.com/urfave/cli.HandleAction.func1(0xc8200c52f8)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:478 +0x38e
panic(0x7e7e40, 0xc820124380)
/usr/local/go/src/runtime/panic.go:443 +0x4e9
github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization.func1(0xc8200c4c08, 0xc8200220a0, 0xc8200c4d18)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:259 +0x136
github.com/opencontainers/runc/libcontainer.(*LinuxFactory).StartInitialization(0xc82006c780, 0x7fb9124733f8, 0xc820124380)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/opencontainers/runc/libcontainer/factory_linux.go:277 +0x5b1
main.glob.func8(0xc820090780, 0x0, 0x0)
/home/lei/opencontainers/runc/main_unix.go:26 +0x68
reflect.Value.call(0x74ca00, 0x8fda40, 0x13, 0x844470, 0x4, 0xc8200c5278, 0x1, 0x1, 0x0, 0x0, ...)
/usr/local/go/src/reflect/value.go:435 +0x120d
reflect.Value.Call(0x74ca00, 0x8fda40, 0x13, 0xc8200c5278, 0x1, 0x1, 0x0, 0x0, 0x0)
/usr/local/go/src/reflect/value.go:303 +0xb1
github.com/urfave/cli.HandleAction(0x74ca00, 0x8fda40, 0xc820090780, 0x0, 0x0)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:487 +0x2ee
github.com/urfave/cli.Command.Run(0x847330, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8dcec0, 0x51, 0x0, ...)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/command.go:191 +0xfec
github.com/urfave/cli.(*App).Run(0xc820001980, 0xc82000a100, 0x2, 0x2, 0x0, 0x0)
/home/lei/opencontainers/runc/Godeps/_workspace/src/github.com/urfave/cli/app.go:240 +0xaa4
main.main()
/home/lei/opencontainers/runc/main.go:137 +0xe24
````
instead of throwing panic on execing the container process, we should
throw the platform mismatch at the very beginning, it's much more
clear and can tell user what's wrong.
Signed-off-by: Lei Jitang <leijitang@huawei.com>
2016-10-14 14:14:36 +08:00
|
|
|
|
|
|
|
func validatePlatform(platform *specs.Platform) error {
|
|
|
|
if platform.OS != runtime.GOOS {
|
|
|
|
return fmt.Errorf("target os %s mismatch with current os %s", platform.OS, runtime.GOOS)
|
|
|
|
}
|
|
|
|
if platform.Arch != runtime.GOARCH {
|
|
|
|
return fmt.Errorf("target arch %s mismatch with current arch %s", platform.Arch, runtime.GOARCH)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|