Skip to content

Instantly share code, notes, and snippets.

@crosbymichael
Created May 16, 2015 01:00

Revisions

  1. crosbymichael created this gist May 16, 2015.
    232 changes: 232 additions & 0 deletions pod.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,232 @@
    package main

    import (
    "encoding/json"
    "fmt"
    "log"
    "os"
    "path/filepath"
    "syscall"

    "github.com/docker/libcontainer"
    "github.com/docker/libcontainer/cgroups"
    "github.com/docker/libcontainer/configs"
    "github.com/docker/libcontainer/utils"
    )

    func main() {
    var spec PodSpec
    if err := json.NewDecoder(os.Stdin).Decode(&spec); err != nil {
    log.Fatal(err)
    }
    pod, err := New("/var/run/pods", &spec)
    if err != nil {
    log.Fatal(err)
    }
    if err := pod.Start(); err != nil {
    log.Fatal(err)
    }
    pod.Wait()
    }

    type PodSpec struct {
    Kind string
    ApiVersion string `json:"apiVersion"`
    Metadata struct {
    Name string
    }
    Spec struct {
    Containers []struct {
    Name string
    Image string // path
    Command []string
    Args []string

    Env []struct {
    Name string
    Value string
    }
    Ports []struct {
    HostIP string `json:"hostIP"`
    HostPort string `json:"hostPort"`
    ContainerPort string `json:"containerPort"`
    }
    VolumeMounts []struct {
    Name string
    MountPath string `json:"mountPath"`
    } `json:"volumeMounts"`
    }
    Volumes []struct {
    Name string
    HostPath struct {
    Path string
    } `json:"hostPath"`
    }
    }
    Crap struct {
    IP string
    }
    }

    func New(root string, spec *PodSpec) (*Pod, error) {
    factory, err := libcontainer.New(root)
    if err != nil {
    return nil, err
    }
    var pod Pod
    for _, c := range spec.Spec.Containers {
    config := getTemplate()
    config.Rootfs = c.Image
    config.Cgroups.Name = c.Name
    cont, err := factory.Create(c.Name, config)
    if err != nil {
    return nil, fmt.Errorf("failed to create container %q: %v", c.Name, err)
    }
    var env []string
    for _, e := range c.Env {
    env = append(env, e.Name+"="+e.Value)
    }
    pod.containers = append(pod.containers, containerProcess{
    name: c.Name,
    process: &libcontainer.Process{
    Args: append(c.Command, c.Args...),
    Env: env,
    Stdout: os.Stdout,
    Stderr: os.Stderr,
    },
    container: cont,
    })
    }
    return &Pod{
    spec: spec,
    factory: factory,
    }, nil
    }

    type containerProcess struct {
    name string
    process *libcontainer.Process
    container libcontainer.Container
    }

    type Pod struct {
    spec *PodSpec
    factory libcontainer.Factory
    // namespaces shares all the namespaces except the mount and pid namespace with the pod.
    cgroups *configs.Cgroup
    containers []containerProcess
    }

    func (p *Pod) Start() error {
    if err := syscall.Unshare(syscall.CLONE_NEWNET | syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC); err != nil {
    return fmt.Errorf("failed to unshare namespaces: %v", err)
    }
    for _, cp := range p.containers {
    if err := cp.container.Start(cp.process); err != nil {
    return fmt.Errorf("failed to start container %q: %v", cp.name, err)
    }
    }
    return nil
    }

    func (p *Pod) Wait() {
    for _, cp := range p.containers {
    state, err := cp.process.Wait()
    if err != nil {
    log.Println(err)
    continue
    }
    log.Printf("container exited with status %d\n", utils.ExitStatus(state))
    }
    }

    func getTemplate() *configs.Config {
    cgroupRoot, err := cgroups.GetThisCgroupDir("devices")
    if err != nil {
    panic(err)
    }
    return &configs.Config{
    ParentDeathSignal: int(syscall.SIGKILL),
    Capabilities: []string{
    "CHOWN",
    "DAC_OVERRIDE",
    "FSETID",
    "FOWNER",
    "MKNOD",
    "NET_RAW",
    "SETGID",
    "SETUID",
    "SETFCAP",
    "SETPCAP",
    "NET_BIND_SERVICE",
    "SYS_CHROOT",
    "KILL",
    "AUDIT_WRITE",
    },
    Namespaces: configs.Namespaces([]configs.Namespace{
    {Type: configs.NEWNS},
    {Type: configs.NEWPID},
    }),
    Cgroups: &configs.Cgroup{
    Name: filepath.Base(cwd),
    Parent: cgroupRoot,
    AllowAllDevices: false,
    AllowedDevices: configs.DefaultAllowedDevices,
    },
    Devices: configs.DefaultAutoCreatedDevices,
    MaskPaths: []string{
    "/proc/kcore",
    },
    ReadonlyPaths: []string{
    "/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
    },
    Mounts: []*configs.Mount{
    {
    Source: "proc",
    Destination: "/proc",
    Device: "proc",
    Flags: defaultMountFlags,
    },
    {
    Source: "tmpfs",
    Destination: "/dev",
    Device: "tmpfs",
    Flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME,
    Data: "mode=755",
    },
    {
    Source: "devpts",
    Destination: "/dev/pts",
    Device: "devpts",
    Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC,
    Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
    },
    {
    Device: "tmpfs",
    Source: "shm",
    Destination: "/dev/shm",
    Data: "mode=1777,size=65536k",
    Flags: defaultMountFlags,
    },
    {
    Source: "mqueue",
    Destination: "/dev/mqueue",
    Device: "mqueue",
    Flags: defaultMountFlags,
    },
    {
    Source: "sysfs",
    Destination: "/sys",
    Device: "sysfs",
    Flags: defaultMountFlags | syscall.MS_RDONLY,
    },
    },
    Rlimits: []configs.Rlimit{
    {
    Type: syscall.RLIMIT_NOFILE,
    Hard: 1024,
    Soft: 1024,
    },
    },
    }
    }