diff --git a/.gitignore b/.gitignore index 0bf5a0d..c67d230 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ main /ctr/ctr /hack/benchmark /output +/example/example diff --git a/container.go b/container.go index 25cf3c3..f8f8854 100644 --- a/container.go +++ b/container.go @@ -10,20 +10,34 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" ) -func NewContainer(root, id string, m Mount, s *specs.Spec, driver ExecutionDriver) (*Container, error) { - path := filepath.Join(root, id) +type ContainerConfig interface { + ID() string + Root() string + Spec(*Mount) (*specs.Spec, error) +} + +type GraphDriver interface { + Mount(id string) (*Mount, error) +} + +func NewContainer(config ContainerConfig, graph GraphDriver, exec ExecutionDriver) (*Container, error) { + var ( + id = config.ID() + root = config.Root() + path = filepath.Join(root, id) + ) + mount, err := graph.Mount(id) + if err != nil { + return nil, err + } + s, err := config.Spec(mount) + if err != nil { + return nil, err + } + // HACK: for runc to allow to use this path without a premounted rootfs if err := os.MkdirAll(filepath.Join(path, s.Root.Path), 0711); err != nil { return nil, err } - // FIXME: find a better UI for this - s.Mounts = append([]specs.Mount{ - { - Type: m.Type, - Source: m.Source, - Destination: "/", - Options: m.Options, - }, - }, s.Mounts...) f, err := os.Create(filepath.Join(path, "config.json")) if err != nil { return nil, err @@ -38,17 +52,21 @@ func NewContainer(root, id string, m Mount, s *specs.Spec, driver ExecutionDrive id: id, path: path, s: s, - driver: driver, + driver: exec, }, nil } -func LoadContainer(root, id string, driver ExecutionDriver) (*Container, error) { - path := filepath.Join(root, id) +func LoadContainer(config ContainerConfig, exec ExecutionDriver) (*Container, error) { + var ( + id = config.ID() + root = config.Root() + path = filepath.Join(root, id) + ) spec, err := loadSpec(path) if err != nil { return nil, err } - process, err := driver.Load(id) + process, err := exec.Load(id) if err != nil { return nil, err } @@ -57,10 +75,10 @@ func LoadContainer(root, id string, driver ExecutionDriver) (*Container, error) id: id, path: path, s: spec, - driver: driver, + driver: exec, init: &Process{ d: process, - driver: driver, + driver: exec, }, }, nil } diff --git a/example/main.go b/example/main.go index 8206c00..67dda66 100644 --- a/example/main.go +++ b/example/main.go @@ -3,8 +3,6 @@ package main import ( "fmt" "os" - "path/filepath" - "runtime" "github.com/Sirupsen/logrus" "github.com/docker/containerkit" @@ -13,37 +11,19 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" ) -// this demos how the graph/layer subsystem will create the rootfs and -// provide it to the container, the Mount type ties the execution and -// filesystem layers together -func getContainerRootfs() containerkit.Mount { - return containerkit.Mount{ - Type: "bind", - Source: "/containers/redis/rootfs", - Options: []string{ - "rbind", - "rw", - }, - } -} - func runContainer() error { // create a new runc runtime that implements the ExecutionDriver interface - driver, err := oci.New(oci.Opts{ + runc, err := oci.New(oci.Opts{ Root: "/run/runc", Name: "runc", }) if err != nil { return err } + dockerContainer := &testConfig{} + // create a new container - container, err := containerkit.NewContainer( - "/var/lib/containerkit", /* container root */ - "test", /* container id */ - getContainerRootfs(), /* mount from the graph subsystem for the container */ - spec("test"), /* the spec for the container */ - driver, /* the exec driver to use for the container */ - ) + container, err := containerkit.NewContainer(dockerContainer, NewBindDriver(), runc) if err != nil { return err } @@ -92,11 +72,7 @@ func runContainer() error { logrus.Infof("process %d returned with %d", i, procStatus) } - container, err = containerkit.LoadContainer( - "/var/lib/containerkit", /* container root */ - "test", /* container id */ - driver, /* the exec driver to use for the container */ - ) + container, err = containerkit.LoadContainer(dockerContainer, runc) if err != nil { return err } @@ -126,136 +102,3 @@ func main() { logrus.Fatal(err) } } - -var ( - RWM = "rwm" - caps = []string{ - "CAP_AUDIT_WRITE", - "CAP_KILL", - "CAP_FOWNER", - "CAP_CHOWN", - "CAP_MKNOD", - "CAP_FSETID", - "CAP_DAC_OVERRIDE", - "CAP_SETFCAP", - "CAP_SETPCAP", - "CAP_SETGID", - "CAP_SETUID", - "CAP_NET_BIND_SERVICE", - } - env = []string{ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - } -) - -// bla bla bla spec stuff -func spec(id string) *specs.Spec { - cgpath := filepath.Join("/containerkit", id) - return &specs.Spec{ - Version: specs.Version, - Platform: specs.Platform{ - OS: runtime.GOOS, - Arch: runtime.GOARCH, - }, - Root: specs.Root{ - Path: "rootfs", - Readonly: false, - }, - Process: specs.Process{ - Env: env, - Args: []string{"sleep", "30"}, - Terminal: false, - Cwd: "/", - NoNewPrivileges: true, - Capabilities: caps, - }, - Hostname: "containerkit", - Mounts: []specs.Mount{ - { - Destination: "/proc", - Type: "proc", - Source: "proc", - }, - { - 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"}, - }, - { - Destination: "/run", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - { - Destination: "/etc/resolv.conf", - Type: "bind", - Source: "/etc/resolv.conf", - Options: []string{"rbind", "ro"}, - }, - { - Destination: "/etc/hosts", - Type: "bind", - Source: "/etc/hosts", - Options: []string{"rbind", "ro"}, - }, - { - Destination: "/etc/localtime", - Type: "bind", - Source: "/etc/localtime", - Options: []string{"rbind", "ro"}, - }, - }, - Linux: &specs.Linux{ - CgroupsPath: &cgpath, - Resources: &specs.Resources{ - Devices: []specs.DeviceCgroup{ - { - Allow: false, - Access: &RWM, - }, - }, - }, - Namespaces: []specs.Namespace{ - { - Type: "pid", - }, - { - Type: "ipc", - }, - { - Type: "uts", - }, - { - Type: "mount", - }, - }, - }, - } - -} diff --git a/example/utils.go b/example/utils.go new file mode 100644 index 0000000..daba6a5 --- /dev/null +++ b/example/utils.go @@ -0,0 +1,179 @@ +package main + +import ( + "path/filepath" + "runtime" + + "github.com/docker/containerkit" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func NewBindDriver() containerkit.GraphDriver { + return &bindDriver{} +} + +// this demos how the graph/layer subsystem will create the rootfs and +// provide it to the container, the Mount type ties the execution and +// filesystem layers together +type bindDriver struct { +} + +func (b *bindDriver) Mount(id string) (*containerkit.Mount, error) { + return &containerkit.Mount{ + Target: "/", + Type: "bind", + Source: "/containers/redis/rootfs", + Options: []string{ + "rbind", + "rw", + }, + }, nil +} + +var ( + RWM = "rwm" + caps = []string{ + "CAP_AUDIT_WRITE", + "CAP_KILL", + "CAP_FOWNER", + "CAP_CHOWN", + "CAP_MKNOD", + "CAP_FSETID", + "CAP_DAC_OVERRIDE", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_SETGID", + "CAP_SETUID", + "CAP_NET_BIND_SERVICE", + } + env = []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + } +) + +type testConfig struct { +} + +func (t *testConfig) ID() string { + return "test" +} + +func (t *testConfig) Root() string { + return "/var/lib/containerkit" +} + +func (t *testConfig) Spec(m *containerkit.Mount) (*specs.Spec, error) { + cgpath := filepath.Join("/containerkit", t.ID()) + return &specs.Spec{ + Version: specs.Version, + Platform: specs.Platform{ + OS: runtime.GOOS, + Arch: runtime.GOARCH, + }, + Root: specs.Root{ + Path: "rootfs", + Readonly: false, + }, + Process: specs.Process{ + Env: env, + Args: []string{"sleep", "30"}, + Terminal: false, + Cwd: "/", + NoNewPrivileges: true, + Capabilities: caps, + }, + Hostname: "containerkit", + Mounts: []specs.Mount{ + { + Destination: m.Target, + Type: m.Type, + Source: m.Source, + Options: m.Options, + }, + { + Destination: "/proc", + Type: "proc", + Source: "proc", + }, + { + 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"}, + }, + { + Destination: "/run", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + { + Destination: "/etc/resolv.conf", + Type: "bind", + Source: "/etc/resolv.conf", + Options: []string{"rbind", "ro"}, + }, + { + Destination: "/etc/hosts", + Type: "bind", + Source: "/etc/hosts", + Options: []string{"rbind", "ro"}, + }, + { + Destination: "/etc/localtime", + Type: "bind", + Source: "/etc/localtime", + Options: []string{"rbind", "ro"}, + }, + }, + Linux: &specs.Linux{ + CgroupsPath: &cgpath, + Resources: &specs.Resources{ + Devices: []specs.DeviceCgroup{ + { + Allow: false, + Access: &RWM, + }, + }, + }, + Namespaces: []specs.Namespace{ + { + Type: "pid", + }, + { + Type: "ipc", + }, + { + Type: "uts", + }, + { + Type: "mount", + }, + }, + }, + }, nil +}