From 0c81ce19e8e5386e6590e64d8a74b6a687cfcda5 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Wed, 16 Aug 2017 16:40:58 +0100 Subject: [PATCH] Add a runtime config This adds support for a runtime configuration file that can do: - `mkdir` to make a directory at runtime, eg in `/var` or `/tmp`, to avoid workarounds - `interface` that can create network interfaces in a container or move them - `bindNS` that can bind mount namespaces of an `onboot` container to a file so a service can be started in that namespace. It merges the `service` and `onboot` tools (in `init`) to avoid duplication. This also saves some size for eg LCOW which did not use the `onboot` code in `runc`. Signed-off-by: Justin Cormack --- pkg/containerd/Dockerfile | 5 +- pkg/containerd/cmd/service/prepare.go | 87 ------ pkg/containerd/cmd/service/skanky-vendor.sh | 14 - pkg/init/Dockerfile | 28 +- pkg/init/Makefile | 3 +- pkg/{containerd => init}/cmd/service/main.go | 17 +- pkg/init/cmd/service/prepare.go | 255 ++++++++++++++++++ pkg/init/cmd/service/runc.go | 111 ++++++++ pkg/{containerd => init}/cmd/service/start.go | 18 +- .../cmd/service/system_init.go | 0 pkg/runc/Dockerfile | 8 +- pkg/runc/Makefile | 1 - pkg/runc/cmd/onboot/main.go | 66 ----- pkg/runc/cmd/onboot/prepare.go | 87 ------ 14 files changed, 420 insertions(+), 280 deletions(-) delete mode 100644 pkg/containerd/cmd/service/prepare.go delete mode 100755 pkg/containerd/cmd/service/skanky-vendor.sh rename pkg/{containerd => init}/cmd/service/main.go (80%) create mode 100644 pkg/init/cmd/service/prepare.go create mode 100644 pkg/init/cmd/service/runc.go rename pkg/{containerd => init}/cmd/service/start.go (85%) rename pkg/{containerd => init}/cmd/service/system_init.go (100%) delete mode 100644 pkg/runc/cmd/onboot/main.go delete mode 100644 pkg/runc/cmd/onboot/prepare.go diff --git a/pkg/containerd/Dockerfile b/pkg/containerd/Dockerfile index fcb03c29d..27090c04d 100644 --- a/pkg/containerd/Dockerfile +++ b/pkg/containerd/Dockerfile @@ -20,9 +20,6 @@ RUN git checkout $CONTAINERD_COMMIT RUN make binaries EXTRA_FLAGS="-buildmode pie" EXTRA_LDFLAGS="-extldflags \\\"-fno-PIC -static\\\"" RUN cp bin/containerd bin/ctr bin/containerd-shim /usr/bin/ -ADD cmd /go/src/cmd -RUN cd /go/src/cmd/service && ./skanky-vendor.sh $GOPATH/src/github.com/containerd/containerd -RUN go-compile.sh /go/src/cmd/service RUN mkdir -p /etc/init.d && ln -s /usr/bin/service /etc/init.d/020-containerd WORKDIR / @@ -31,7 +28,7 @@ COPY . . FROM scratch ENTRYPOINT [] WORKDIR / -COPY --from=alpine /usr/bin/containerd /usr/bin/ctr /usr/bin/containerd-shim /go/bin/service /usr/bin/ +COPY --from=alpine /usr/bin/containerd /usr/bin/ctr /usr/bin/containerd-shim /usr/bin/ COPY --from=alpine /etc/containerd/config.toml /etc/containerd/ COPY --from=alpine /usr/share/zoneinfo/UTC /etc/localtime COPY --from=alpine /etc/init.d/ /etc/init.d/ diff --git a/pkg/containerd/cmd/service/prepare.go b/pkg/containerd/cmd/service/prepare.go deleted file mode 100644 index 1cf9f7731..000000000 --- a/pkg/containerd/cmd/service/prepare.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -// Please note this file is shared between pkg/runc and pkg/containerd -// Update it in both places if you make changes - -import ( - "fmt" - "os" - "path/filepath" - "syscall" -) - -func prepare(path string) error { - // see if we are dealing with a read only or read write container - if _, err := os.Stat(filepath.Join(path, "lower")); err != nil { - if os.IsNotExist(err) { - return prepareRO(path) - } - return err - } - return prepareRW(path) -} - -func prepareRO(path string) error { - // make rootfs a mount point, as runc doesn't like it much otherwise - rootfs := filepath.Join(path, "rootfs") - if err := syscall.Mount(rootfs, rootfs, "", syscall.MS_BIND, ""); err != nil { - return err - } - return nil -} - -func prepareRW(path string) error { - // mount a tmpfs on tmp for upper and workdirs - // make it private as nothing else should be using this - tmp := filepath.Join(path, "tmp") - if err := syscall.Mount("tmpfs", tmp, "tmpfs", 0, "size=10%"); err != nil { - return err - } - // make it private as nothing else should be using this - if err := syscall.Mount("", tmp, "", syscall.MS_REMOUNT|syscall.MS_PRIVATE, ""); err != nil { - return err - } - upper := filepath.Join(tmp, "upper") - // make the mount points - if err := os.Mkdir(upper, 0755); err != nil { - return err - } - work := filepath.Join(tmp, "work") - if err := os.Mkdir(work, 0755); err != nil { - return err - } - lower := filepath.Join(path, "lower") - rootfs := filepath.Join(path, "rootfs") - opt := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lower, upper, work) - if err := syscall.Mount("overlay", rootfs, "overlay", 0, opt); err != nil { - return err - } - return nil -} - -// cleanup functions are best efforts only, mainly for rw onboot containers -func cleanup(path string) { - // see if we are dealing with a read only or read write container - if _, err := os.Stat(filepath.Join(path, "lower")); err != nil { - cleanupRO(path) - } else { - cleanupRW(path) - } -} - -func cleanupRO(path string) { - // remove the bind mount - rootfs := filepath.Join(path, "rootfs") - _ = syscall.Unmount(rootfs, 0) -} - -func cleanupRW(path string) { - // remove the overlay mount - rootfs := filepath.Join(path, "rootfs") - _ = os.RemoveAll(rootfs) - _ = syscall.Unmount(rootfs, 0) - // remove the tmpfs - tmp := filepath.Join(path, "tmp") - _ = os.RemoveAll(tmp) - _ = syscall.Unmount(tmp, 0) -} diff --git a/pkg/containerd/cmd/service/skanky-vendor.sh b/pkg/containerd/cmd/service/skanky-vendor.sh deleted file mode 100755 index 483002264..000000000 --- a/pkg/containerd/cmd/service/skanky-vendor.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -# -# We only need the containerd client and its transitive dependencies -# and we conveniently have a checkout already. We actually prefer to -# reuse containerd's vendoring for consistency anyway. - -set -eu -ctrd=$1 -cp -r $ctrd/vendor/ vendor/ -# We need containerd itself of course -mkdir -p vendor/github.com/containerd -cp -r $ctrd vendor/github.com/containerd/containerd -# Stop go finding nested vendorings -rm -rf vendor/github.com/containerd/containerd/vendor diff --git a/pkg/init/Dockerfile b/pkg/init/Dockerfile index da7363bf4..3696b43cf 100644 --- a/pkg/init/Dockerfile +++ b/pkg/init/Dockerfile @@ -1,17 +1,28 @@ -FROM linuxkit/alpine:87a0cd10449d72f374f950004467737dbf440630 AS build -RUN apk add --no-cache --initdb alpine-baselayout make gcc musl-dev +FROM linuxkit/alpine:0fd732eb9e99c4db0953ae8de23d95de340ab847 AS build +RUN apk add --no-cache --initdb alpine-baselayout make gcc musl-dev git linux-headers -ADD usermode-helper.c . -RUN make usermode-helper +ADD usermode-helper.c ./ +RUN LDFLAGS=-static CFLAGS=-Werror make usermode-helper RUN apk add --no-cache go musl-dev ENV GOPATH=/go PATH=$PATH:/go/bin -COPY init.go /go/src/init/ -COPY vendor /go/src/init/vendor/ -RUN go-compile.sh /go/src/init/ +COPY cmd /go/src/cmd +RUN go-compile.sh /go/src/cmd/init -FROM linuxkit/alpine:87a0cd10449d72f374f950004467737dbf440630 AS mirror +# checkout containerd for vendoring +ENV GOPATH=/go PATH=$PATH:/go/bin +# CONTAINERD_REPO and CONTAINERD_COMMIT are defined in linuxkit/alpine +RUN mkdir -p $GOPATH/src/github.com/containerd && \ + cd $GOPATH/src/github.com/containerd && \ + git clone $CONTAINERD_REPO +WORKDIR $GOPATH/src/github.com/containerd/containerd +RUN git checkout $CONTAINERD_COMMIT + +RUN cd /go/src/cmd/service && ./skanky-vendor.sh $GOPATH/src/github.com/containerd/containerd +RUN go-compile.sh /go/src/cmd/service + +FROM linuxkit/alpine:6ed3b299f5243acb6459b4993549c5045e4ad7f4 AS mirror RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ RUN apk add --no-cache --initdb -p /out alpine-baselayout busybox musl @@ -23,6 +34,7 @@ ENTRYPOINT [] CMD [] WORKDIR / COPY --from=build /go/bin/init / +COPY --from=build /go/bin/service /usr/bin/ COPY --from=build usermode-helper /sbin/ COPY --from=mirror /out/ / COPY etc etc/ diff --git a/pkg/init/Makefile b/pkg/init/Makefile index b42d2ca91..2c207b40e 100644 --- a/pkg/init/Makefile +++ b/pkg/init/Makefile @@ -1,4 +1,5 @@ IMAGE=init -DEPS=init.go vendor.conf usermode-helper.c $(wildcard etc/*) $(wildcard etc/init.d/*) +NETWORK=1 +DEPS=usermode-helper.c $(wildcard etc/*) $(wildcard etc/init.d/*) $(shell find cmd -type f) include ../package.mk diff --git a/pkg/containerd/cmd/service/main.go b/pkg/init/cmd/service/main.go similarity index 80% rename from pkg/containerd/cmd/service/main.go rename to pkg/init/cmd/service/main.go index d2477d999..c34201ce5 100644 --- a/pkg/containerd/cmd/service/main.go +++ b/pkg/init/cmd/service/main.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "strings" log "github.com/sirupsen/logrus" ) @@ -13,6 +14,9 @@ const ( defaultSocket = "/run/containerd/containerd.sock" defaultPath = "/containers/services" defaultContainerd = "/usr/bin/containerd" + installPath = "/usr/bin/service" + onbootPath = "/containers/onboot" + shutdownPath = "/containers/onshutdown" ) var ( @@ -66,8 +70,17 @@ func main() { args := flag.Args() if len(args) < 1 { - systemInitCmd(args) - os.Exit(0) + // check if called form startup scripts + command := os.Args[0] + switch { + case strings.Contains(command, "onboot"): + os.Exit(runcInit(onbootPath)) + case strings.Contains(command, "onshutdown"): + os.Exit(runcInit(shutdownPath)) + case strings.Contains(command, "containerd"): + systemInitCmd([]string{}) + os.Exit(0) + } } switch args[0] { diff --git a/pkg/init/cmd/service/prepare.go b/pkg/init/cmd/service/prepare.go new file mode 100644 index 000000000..eedd8bdab --- /dev/null +++ b/pkg/init/cmd/service/prepare.go @@ -0,0 +1,255 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "syscall" + + "github.com/vishvananda/netlink" +) + +const ( + wgPath = "/usr/bin/wg" + nsenterPath = "/usr/bin/nsenter-net" +) + +// Note these definitions are from moby/tool/src/moby/config.go and should be kept in sync + +// Runtime is the type of config processed at runtime, not used to build the OCI spec +type Runtime struct { + Mkdir []string `yaml:"mkdir" json:"mkdir,omitempty"` + Interfaces []Interface `yaml:"interfaces" json:"interfaces,omitempty"` + BindNS *Namespaces `yaml:"bindNS" json:"bindNS,omitempty"` +} + +// Namespaces is the type for configuring paths to bind namespaces +type Namespaces struct { + Cgroup string `yaml:"cgroup" json:"cgroup,omitempty"` + Ipc string `yaml:"ipc" json:"ipc,omitempty"` + Mnt string `yaml:"mnt" json:"mnt,omitempty"` + Net string `yaml:"net" json:"net,omitempty"` + Pid string `yaml:"pid" json:"pid,omitempty"` + User string `yaml:"user" json:"user,omitempty"` + Uts string `yaml:"uts" json:"uts,omitempty"` +} + +// Interface is the runtime config for network interfaces +type Interface struct { + Name string `yaml:"name" json:"name,omitempty"` + Add string `yaml:"add" json:"add,omitempty"` + Peer string `yaml:"peer" json:"peer,omitempty"` + CreateInRoot bool `yaml:"createInRoot" json:"createInRoot"` +} + +func getRuntimeConfig(path string) Runtime { + var runtime Runtime + conf, err := ioutil.ReadFile(filepath.Join(path, "runtime.json")) + if err != nil { + // if it does not exist it is fine to return an empty runtime, to not do anything + if os.IsNotExist(err) { + return runtime + } + log.Fatalf("Cannot read runtime config: %v", err) + } + if err := json.Unmarshal(conf, &runtime); err != nil { + log.Fatalf("Cannot parse runtime config: %v", err) + } + return runtime +} + +// prepareFilesystem sets up the mounts, before the container is created +func prepareFilesystem(path string, runtime Runtime) error { + // execute the runtime config that should be done up front + for _, dir := range runtime.Mkdir { + // in future we may need to change the structure to set mode, ownership + var mode os.FileMode = 0755 + err := os.MkdirAll(dir, mode) + if err != nil { + return fmt.Errorf("Cannot create directory %s: %v", dir, err) + } + } + + // see if we are dealing with a read only or read write container + if _, err := os.Stat(filepath.Join(path, "lower")); err != nil { + if os.IsNotExist(err) { + return prepareRO(path) + } + return err + } + return prepareRW(path) +} + +func prepareRO(path string) error { + // make rootfs a mount point, as runc doesn't like it much otherwise + rootfs := filepath.Join(path, "rootfs") + if err := syscall.Mount(rootfs, rootfs, "", syscall.MS_BIND, ""); err != nil { + return err + } + return nil +} + +func prepareRW(path string) error { + // mount a tmpfs on tmp for upper and workdirs + // make it private as nothing else should be using this + tmp := filepath.Join(path, "tmp") + if err := syscall.Mount("tmpfs", tmp, "tmpfs", 0, "size=10%"); err != nil { + return err + } + // make it private as nothing else should be using this + if err := syscall.Mount("", tmp, "", syscall.MS_REMOUNT|syscall.MS_PRIVATE, ""); err != nil { + return err + } + upper := filepath.Join(tmp, "upper") + // make the mount points + if err := os.Mkdir(upper, 0755); err != nil { + return err + } + work := filepath.Join(tmp, "work") + if err := os.Mkdir(work, 0755); err != nil { + return err + } + lower := filepath.Join(path, "lower") + rootfs := filepath.Join(path, "rootfs") + opt := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lower, upper, work) + if err := syscall.Mount("overlay", rootfs, "overlay", 0, opt); err != nil { + return err + } + return nil +} + +// bind mount a namespace file +func bindNS(ns string, path string, pid int) error { + if path == "" { + return nil + } + // the path and file need to exist for the bind to succeed, so try to create + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("Cannot create leading directories %s for bind mount destination: %v", dir, err) + } + fi, err := os.Create(path) + if err != nil { + return fmt.Errorf("Cannot create a mount point for namespace bind at %s: %v", path, err) + } + if err := fi.Close(); err != nil { + return err + } + if err := syscall.Mount(fmt.Sprintf("/proc/%d/ns/%s", pid, ns), path, "", syscall.MS_BIND, ""); err != nil { + return fmt.Errorf("Failed to bind %s namespace at %s: %v", ns, path, err) + } + return nil +} + +// prepareProcess sets up anything that needs to be done after the container process is created, but before it runs +// for example networking +func prepareProcess(pid int, runtime Runtime) error { + for _, iface := range runtime.Interfaces { + if iface.Name == "" { + return fmt.Errorf("Interface requires a name") + } + + var link netlink.Link + var ns interface{} = netlink.NsPid(pid) + var move bool + var err error + + if iface.Peer != "" && iface.Add == "" { + // must be a veth if specify peer + iface.Add = "veth" + } + + // if create in root is set, create in root namespace first, then move + // also do the same for a veth pair + if iface.CreateInRoot || iface.Add == "veth" { + ns = nil + move = true + } + + if iface.Add != "" { + switch iface.Add { + case "veth": + if iface.Peer == "" { + return fmt.Errorf("Creating a veth pair %s requires a peer to be set", iface.Name) + } + la := netlink.LinkAttrs{Name: iface.Name, Namespace: ns} + link = &netlink.Veth{LinkAttrs: la, PeerName: iface.Peer} + default: + // no special creation options needed + la := netlink.LinkAttrs{Name: iface.Name, Namespace: ns} + link = &netlink.GenericLink{la, iface.Add} + } + if err := netlink.LinkAdd(link); err != nil { + return fmt.Errorf("Link add %s of type %s failed: %v", iface.Name, iface.Add, err) + } + fmt.Fprintf(os.Stderr, "Created interface %s type %s\n", iface.Name, iface.Add) + } else { + // find existing interface + link, err = netlink.LinkByName(iface.Name) + if err != nil { + return fmt.Errorf("Cannot find interface %s: %v", iface.Name, err) + } + // then move into namespace + move = true + } + if move { + if err := netlink.LinkSetNsPid(link, int(pid)); err != nil { + return fmt.Errorf("Cannot move interface %s into namespace: %v", iface.Name, err) + } + fmt.Fprintf(os.Stderr, "Moved interface %s to pid %d\n", iface.Name, pid) + } + } + + if runtime.BindNS != nil { + binds := []struct { + ns string + path string + }{ + {"cgroup", runtime.BindNS.Cgroup}, + {"ipc", runtime.BindNS.Ipc}, + {"mnt", runtime.BindNS.Mnt}, + {"net", runtime.BindNS.Net}, + {"pid", runtime.BindNS.Pid}, + {"user", runtime.BindNS.User}, + {"uts", runtime.BindNS.Uts}, + } + + for _, b := range binds { + if err := bindNS(b.ns, b.path, pid); err != nil { + return err + } + } + } + + return nil +} + +// cleanup functions are best efforts only, mainly for rw onboot containers +func cleanup(path string) { + // see if we are dealing with a read only or read write container + if _, err := os.Stat(filepath.Join(path, "lower")); err != nil { + cleanupRO(path) + } else { + cleanupRW(path) + } +} + +func cleanupRO(path string) { + // remove the bind mount + rootfs := filepath.Join(path, "rootfs") + _ = syscall.Unmount(rootfs, 0) +} + +func cleanupRW(path string) { + // remove the overlay mount + rootfs := filepath.Join(path, "rootfs") + _ = os.RemoveAll(rootfs) + _ = syscall.Unmount(rootfs, 0) + // remove the tmpfs + tmp := filepath.Join(path, "tmp") + _ = os.RemoveAll(tmp) + _ = syscall.Unmount(tmp, 0) +} diff --git a/pkg/init/cmd/service/runc.go b/pkg/init/cmd/service/runc.go new file mode 100644 index 000000000..8e26ea240 --- /dev/null +++ b/pkg/init/cmd/service/runc.go @@ -0,0 +1,111 @@ +package main + +import ( + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strconv" + + "github.com/containerd/containerd/sys" +) + +const ( + runcBinary = "/usr/bin/runc" +) + +func runcInit(rootPath string) int { + // do nothing if the path does not exist + if _, err := os.Stat(rootPath); err != nil && os.IsNotExist(err) { + return 0 + } + + // get files; note ReadDir already sorts them + files, err := ioutil.ReadDir(rootPath) + if err != nil { + log.Fatalf("Cannot read files in %s: %v", rootPath, err) + } + + tmpdir, err := ioutil.TempDir("", filepath.Base(rootPath)) + if err != nil { + log.Fatalf("Cannot create temporary directory: %v", err) + } + + // need to set ourselves as a child subreaper or we cannot wait for runc as reparents to init + if err := sys.SetSubreaper(1); err != nil { + log.Fatalf("Cannot set as subreaper: %v", err) + } + + status := 0 + + for _, file := range files { + name := file.Name() + path := filepath.Join(rootPath, name) + + runtimeConfig := getRuntimeConfig(path) + + if err := prepareFilesystem(path, runtimeConfig); err != nil { + log.Printf("Error preparing %s: %v", name, err) + status = 1 + continue + } + pidfile := filepath.Join(tmpdir, name) + cmd := exec.Command(runcBinary, "create", "--bundle", path, "--pid-file", pidfile, name) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + log.Printf("Error creating %s: %v", name, err) + status = 1 + // skip cleanup on error for debug + continue + } + pf, err := ioutil.ReadFile(pidfile) + if err != nil { + log.Printf("Cannot read pidfile: %v", err) + status = 1 + continue + } + pid, err := strconv.Atoi(string(pf)) + if err != nil { + log.Printf("Cannot parse pid from pidfile: %v", err) + status = 1 + continue + } + + if err := prepareProcess(pid, runtimeConfig); err != nil { + log.Printf("Cannot prepare process: %v", err) + status = 1 + continue + } + + waitFor := make(chan *os.ProcessState) + go func() { + // never errors in Unix + p, _ := os.FindProcess(pid) + state, err := p.Wait() + if err != nil { + log.Printf("Process wait error: %v", err) + } + waitFor <- state + }() + + cmd = exec.Command(runcBinary, "start", name) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + log.Printf("Error starting %s: %v", name, err) + status = 1 + continue + } + + _ = <-waitFor + + cleanup(path) + _ = os.Remove(pidfile) + } + + _ = os.RemoveAll(tmpdir) + + return status +} diff --git a/pkg/containerd/cmd/service/start.go b/pkg/init/cmd/service/start.go similarity index 85% rename from pkg/containerd/cmd/service/start.go rename to pkg/init/cmd/service/start.go index 8924c953b..a85945f14 100644 --- a/pkg/containerd/cmd/service/start.go +++ b/pkg/init/cmd/service/start.go @@ -55,11 +55,15 @@ func startCmd(args []string) { log.Debugf("Started %s pid %d", id, pid) } -func start(service, sock, path, dumpSpec string) (string, uint32, string, error) { - rootfs := filepath.Join(path, service, "rootfs") +func start(service, sock, basePath, dumpSpec string) (string, uint32, string, error) { + path := filepath.Join(basePath, service) - if err := prepare(filepath.Join(path, service)); err != nil { - return "", 0, "preparing rootfs", err + runtimeConfig := getRuntimeConfig(path) + + rootfs := filepath.Join(path, "rootfs") + + if err := prepareFilesystem(path, runtimeConfig); err != nil { + return "", 0, "preparing filesystem", err } client, err := containerd.New(sock) @@ -70,7 +74,7 @@ func start(service, sock, path, dumpSpec string) (string, uint32, string, error) ctx := namespaces.WithNamespace(context.Background(), "default") var spec *specs.Spec - specf, err := os.Open(filepath.Join(path, service, "config.json")) + specf, err := os.Open(filepath.Join(path, "config.json")) if err != nil { return "", 0, "failed to read service spec", err } @@ -119,6 +123,10 @@ func start(service, sock, path, dumpSpec string) (string, uint32, string, error) return "", 0, "failed to create task", err } + if err := prepareProcess(int(task.Pid()), runtimeConfig); err != nil { + return "", 0, "preparing process", err + } + if err := task.Start(ctx); err != nil { // Don't destroy the container here so it can be inspected for debugging. return "", 0, "failed to start task", err diff --git a/pkg/containerd/cmd/service/system_init.go b/pkg/init/cmd/service/system_init.go similarity index 100% rename from pkg/containerd/cmd/service/system_init.go rename to pkg/init/cmd/service/system_init.go diff --git a/pkg/runc/Dockerfile b/pkg/runc/Dockerfile index 316cf5492..e9a71a3ea 100644 --- a/pkg/runc/Dockerfile +++ b/pkg/runc/Dockerfile @@ -20,14 +20,12 @@ RUN git checkout $RUNC_COMMIT RUN make static BUILDTAGS="seccomp" EXTRA_FLAGS="-buildmode pie" EXTRA_LDFLAGS="-extldflags \\\"-fno-PIC -static\\\"" RUN cp runc /usr/bin/ -ADD cmd /go/src/cmd -RUN go-compile.sh /go/src/cmd/onboot -RUN mkdir -p /etc/init.d && ln -s /usr/bin/onboot /etc/init.d/010-onboot -RUN mkdir -p /etc/shutdown.d && ln -s /usr/bin/onboot /etc/shutdown.d/010-onshutdown +RUN mkdir -p /etc/init.d && ln -s /usr/bin/service /etc/init.d/010-onboot +RUN mkdir -p /etc/shutdown.d && ln -s /usr/bin/service /etc/shutdown.d/010-onshutdown FROM scratch WORKDIR / ENTRYPOINT [] -COPY --from=alpine /usr/bin/runc /go/bin/onboot /usr/bin/ +COPY --from=alpine /usr/bin/runc /usr/bin/ COPY --from=alpine /etc/init.d/ /etc/init.d/ COPY --from=alpine /etc/shutdown.d/ /etc/shutdown.d/ diff --git a/pkg/runc/Makefile b/pkg/runc/Makefile index 5691a025d..7f676177c 100644 --- a/pkg/runc/Makefile +++ b/pkg/runc/Makefile @@ -1,5 +1,4 @@ IMAGE=runc NETWORK=1 -DEPS=$(wildcard cmd/onboot/*.go) include ../package.mk diff --git a/pkg/runc/cmd/onboot/main.go b/pkg/runc/cmd/onboot/main.go deleted file mode 100644 index 29430be3f..000000000 --- a/pkg/runc/cmd/onboot/main.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -import ( - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "strings" -) - -const ( - runcBinary = "/usr/bin/runc" - onbootPath = "/containers/onboot" - shutdownPath = "/containers/onshutdown" -) - -func main() { - // try to work out how we are being called - command := os.Args[0] - if len(os.Args) > 1 { - command = os.Args[1] - } - var path = onbootPath - switch { - case strings.Contains(command, "boot"): - path = onbootPath - case strings.Contains(command, "shutdown"): - path = shutdownPath - } - - // do nothing if the path does not exist - if _, err := os.Stat(path); err != nil && os.IsNotExist(err) { - os.Exit(0) - } - - // get files; note ReadDir already sorts them - files, err := ioutil.ReadDir(path) - if err != nil { - log.Fatalf("Cannot read files in %s: %v", path, err) - } - - status := 0 - - for _, file := range files { - name := file.Name() - fullPath := filepath.Join(path, name) - if err := prepare(fullPath); err != nil { - log.Printf("Error preparing %s: %v", name, err) - status = 1 - continue - } - cmd := exec.Command(runcBinary, "run", "--bundle", fullPath, name) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - log.Printf("Error running %s: %v", name, err) - status = 1 - } else { - // do not clean up on error to make debug easier - cleanup(fullPath) - } - } - - os.Exit(status) -} diff --git a/pkg/runc/cmd/onboot/prepare.go b/pkg/runc/cmd/onboot/prepare.go deleted file mode 100644 index 1cf9f7731..000000000 --- a/pkg/runc/cmd/onboot/prepare.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -// Please note this file is shared between pkg/runc and pkg/containerd -// Update it in both places if you make changes - -import ( - "fmt" - "os" - "path/filepath" - "syscall" -) - -func prepare(path string) error { - // see if we are dealing with a read only or read write container - if _, err := os.Stat(filepath.Join(path, "lower")); err != nil { - if os.IsNotExist(err) { - return prepareRO(path) - } - return err - } - return prepareRW(path) -} - -func prepareRO(path string) error { - // make rootfs a mount point, as runc doesn't like it much otherwise - rootfs := filepath.Join(path, "rootfs") - if err := syscall.Mount(rootfs, rootfs, "", syscall.MS_BIND, ""); err != nil { - return err - } - return nil -} - -func prepareRW(path string) error { - // mount a tmpfs on tmp for upper and workdirs - // make it private as nothing else should be using this - tmp := filepath.Join(path, "tmp") - if err := syscall.Mount("tmpfs", tmp, "tmpfs", 0, "size=10%"); err != nil { - return err - } - // make it private as nothing else should be using this - if err := syscall.Mount("", tmp, "", syscall.MS_REMOUNT|syscall.MS_PRIVATE, ""); err != nil { - return err - } - upper := filepath.Join(tmp, "upper") - // make the mount points - if err := os.Mkdir(upper, 0755); err != nil { - return err - } - work := filepath.Join(tmp, "work") - if err := os.Mkdir(work, 0755); err != nil { - return err - } - lower := filepath.Join(path, "lower") - rootfs := filepath.Join(path, "rootfs") - opt := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lower, upper, work) - if err := syscall.Mount("overlay", rootfs, "overlay", 0, opt); err != nil { - return err - } - return nil -} - -// cleanup functions are best efforts only, mainly for rw onboot containers -func cleanup(path string) { - // see if we are dealing with a read only or read write container - if _, err := os.Stat(filepath.Join(path, "lower")); err != nil { - cleanupRO(path) - } else { - cleanupRW(path) - } -} - -func cleanupRO(path string) { - // remove the bind mount - rootfs := filepath.Join(path, "rootfs") - _ = syscall.Unmount(rootfs, 0) -} - -func cleanupRW(path string) { - // remove the overlay mount - rootfs := filepath.Join(path, "rootfs") - _ = os.RemoveAll(rootfs) - _ = syscall.Unmount(rootfs, 0) - // remove the tmpfs - tmp := filepath.Join(path, "tmp") - _ = os.RemoveAll(tmp) - _ = syscall.Unmount(tmp, 0) -}