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 <justin.cormack@docker.com>
This commit is contained in:
Justin Cormack
2017-08-16 16:40:58 +01:00
parent df16f6fb9e
commit 0c81ce19e8
14 changed files with 420 additions and 280 deletions

View File

@@ -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/

View File

@@ -1,5 +1,4 @@
IMAGE=runc
NETWORK=1
DEPS=$(wildcard cmd/onboot/*.go)
include ../package.mk

View File

@@ -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)
}

View File

@@ -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)
}