From df4a126175a00b50984be2a1959f9bf32e780cd5 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Sun, 8 Feb 2015 21:38:37 -0700 Subject: [PATCH] Initial commit --- .dockerignore | 3 + .gitignore | 7 + .package | 1 + .wrap-docker-args | 1 + .../_workspace/src/github.com/Sirupsen/logrus | 1 + .../_workspace/src/github.com/docker/docker | 1 + .../src/github.com/fsouza/go-dockerclient | 1 + Godeps/_workspace/src/github.com/kless/term | 1 + assets/empty-hd.img.gz | Bin 0 -> 113532 bytes build.sh | 10 + config/config.go | 237 ++++++++++++++ config/config_test.go | 29 ++ docker/client.go | 35 +++ init/init.go | 289 ++++++++++++++++++ main.go | 24 ++ scripts/bootstrap | 15 + scripts/build | 29 ++ scripts/build-common | 125 ++++++++ scripts/build-images | 53 ++++ scripts/ci | 48 +++ scripts/clean | 5 + scripts/dockerimages/00-base | 3 + scripts/dockerimages/01-state | 5 + scripts/dockerimages/02-udev | 3 + scripts/dockerimages/03-network | 2 + scripts/dockerimages/04-userdocker | 4 + scripts/dockerimages/05-console | 2 + scripts/dockerimages/06-rescue | 2 + scripts/dockerimages/scripts/docker.sh | 19 ++ scripts/dockerimages/scripts/os-dockerfile | 4 + scripts/dockerimages/scripts/udev.sh | 6 + scripts/download | 8 + scripts/install | 11 + scripts/package | 42 +++ scripts/run | 45 +++ sysinit/sysinit.go | 159 ++++++++++ user/system-docker.go | 25 ++ util/util.go | 96 ++++++ 38 files changed, 1351 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 .package create mode 100644 .wrap-docker-args create mode 160000 Godeps/_workspace/src/github.com/Sirupsen/logrus create mode 160000 Godeps/_workspace/src/github.com/docker/docker create mode 160000 Godeps/_workspace/src/github.com/fsouza/go-dockerclient create mode 160000 Godeps/_workspace/src/github.com/kless/term create mode 100644 assets/empty-hd.img.gz create mode 100755 build.sh create mode 100644 config/config.go create mode 100644 config/config_test.go create mode 100644 docker/client.go create mode 100644 init/init.go create mode 100644 main.go create mode 100755 scripts/bootstrap create mode 100755 scripts/build create mode 100644 scripts/build-common create mode 100755 scripts/build-images create mode 100755 scripts/ci create mode 100755 scripts/clean create mode 100644 scripts/dockerimages/00-base create mode 100644 scripts/dockerimages/01-state create mode 100644 scripts/dockerimages/02-udev create mode 100644 scripts/dockerimages/03-network create mode 100644 scripts/dockerimages/04-userdocker create mode 100644 scripts/dockerimages/05-console create mode 100644 scripts/dockerimages/06-rescue create mode 100755 scripts/dockerimages/scripts/docker.sh create mode 100644 scripts/dockerimages/scripts/os-dockerfile create mode 100755 scripts/dockerimages/scripts/udev.sh create mode 100755 scripts/download create mode 100755 scripts/install create mode 100755 scripts/package create mode 100755 scripts/run create mode 100644 sysinit/sysinit.go create mode 100644 user/system-docker.go create mode 100644 util/util.go diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..4d23e280 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +build +dist diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..45131c34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/bin +/build +/dist +/gopath +.dockerfile +*.swp +Dockerfile diff --git a/.package b/.package new file mode 100644 index 00000000..f6668c73 --- /dev/null +++ b/.package @@ -0,0 +1 @@ +github.com/rancherio/os diff --git a/.wrap-docker-args b/.wrap-docker-args new file mode 100644 index 00000000..3c1a8ef1 --- /dev/null +++ b/.wrap-docker-args @@ -0,0 +1 @@ +--privileged diff --git a/Godeps/_workspace/src/github.com/Sirupsen/logrus b/Godeps/_workspace/src/github.com/Sirupsen/logrus new file mode 160000 index 00000000..0b189e01 --- /dev/null +++ b/Godeps/_workspace/src/github.com/Sirupsen/logrus @@ -0,0 +1 @@ +Subproject commit 0b189e019aabcec0af8e433b10b3073ad9382b44 diff --git a/Godeps/_workspace/src/github.com/docker/docker b/Godeps/_workspace/src/github.com/docker/docker new file mode 160000 index 00000000..c61e97c7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/docker/docker @@ -0,0 +1 @@ +Subproject commit c61e97c728e551efbdad6f81eb0118da4269fa88 diff --git a/Godeps/_workspace/src/github.com/fsouza/go-dockerclient b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient new file mode 160000 index 00000000..57da4a6d --- /dev/null +++ b/Godeps/_workspace/src/github.com/fsouza/go-dockerclient @@ -0,0 +1 @@ +Subproject commit 57da4a6dd45ab6d734c466e28253182d8467e335 diff --git a/Godeps/_workspace/src/github.com/kless/term b/Godeps/_workspace/src/github.com/kless/term new file mode 160000 index 00000000..d57d9d2d --- /dev/null +++ b/Godeps/_workspace/src/github.com/kless/term @@ -0,0 +1 @@ +Subproject commit d57d9d2d5be197e12d9dee142d855470d83ce62f diff --git a/assets/empty-hd.img.gz b/assets/empty-hd.img.gz new file mode 100644 index 0000000000000000000000000000000000000000..7be0214283ce75eac9f2255376e1d1fdf9192319 GIT binary patch literal 113532 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 %s", src, dest) + if err = os.Symlink(src, dest); err != nil { + return err + } + } + } + + return nil +} + +func createDirs(dirs ...string) error { + for _, dir := range dirs { + if _, err := os.Stat(dir); os.IsNotExist(err) { + log.Debugf("Creating %s", dir) + err = os.MkdirAll(dir, 0755) + if err != nil { + return err + } + } + } + + return nil +} + +func createMounts(mounts ...[]string) error { + for _, mount := range mounts { + log.Debugf("Mounting %s %s %s %s", mount[0], mount[1], mount[2], mount[3]) + err := util.Mount(mount[0], mount[1], mount[2], mount[3]) + if err != nil { + return err + } + } + + return nil +} + +func remountRo(cfg *config.Config) error { + return util.Remount("/", "ro") +} + +func mountCgroups(cfg *config.Config) error { + for _, cgroup := range cgroups { + err := createDirs("/sys/fs/cgroup/" + cgroup) + if err != nil { + return err + } + + err = createMounts([][]string{ + []string{"none", "sys/fs/cgroup/" + cgroup, "cgroup", cgroup}, + }...) + if err != nil { + return err + } + } + + log.Debug("Done mouting cgroupfs") + + return nil +} + +func extractModules(cfg *config.Config) error { + if _, err := os.Stat(cfg.ModulesArchive); os.IsNotExist(err) { + log.Debug("Modules do not exist") + return nil + } + + log.Debug("Extracting modules") + return util.ExtractTar(cfg.ModulesArchive, "/") +} + +func setResolvConf(cfg *config.Config) error { + log.Debug("Creating /etc/resolv.conf") + //f, err := os.OpenFile("/etc/resolv.conf", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + f, err := os.Create("/etc/resolv.conf") + if err != nil { + return err + } + + defer f.Close() + + for _, dns := range cfg.Dns { + content := fmt.Sprintf("nameserver %s\n", dns) + if _, err = f.Write([]byte(content)); err != nil { + return err + } + } + + return nil +} + +func loadModules(cfg *config.Config) error { + filesystems, err := ioutil.ReadFile("/proc/filesystems") + if err != nil { + return err + } + + if !strings.Contains(string(filesystems), "nodev\toverlay\n") { + log.Debug("Loading overlay module") + err = exec.Command("/sbin/modprobe", "overlay").Run() + if err != nil { + return err + } + } + + for _, module := range cfg.Modules { + log.Debugf("Loading module %s", module) + err = exec.Command("/sbin/modprobe", module).Run() + if err != nil { + return err + } + } + + return nil +} + +func sysInit(cfg *config.Config) error { + cmd := exec.Command("openvt", "-s", "/sbin/init-sys") + //cmd := exec.Command("/sbin/init-sys") + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + if err := cmd.Start(); err != nil { + return err + } + + return nil + + //log.Debug("Launching host console") + //return exec.Command("openvt", "/bin/sh").Run() + + //log.Debug("Launching console") + //return exec.Command("/bin/openvt", "-s", "/bin/console-container.sh").Start() +} + +func execDocker(cfg *config.Config) error { + log.Info("Launching Docker") + return syscall.Exec(cfg.DockerBin, cfg.SystemDockerArgs, os.Environ()) +} + +func MainInit() { + if err := RunInit(); err != nil { + log.Fatal(err) + } +} + +func mountState(cfg *config.Config) error { + var err error + if len(cfg.StateDev) == 0 { + log.Debugf("State will not be persisted") + err = util.Mount("none", STATE, "tmpfs", "") + } else { + log.Debugf("Mounting state device %s", cfg.StateDev) + err = util.Mount(cfg.StateDev, STATE, cfg.StateDevFSType, "") + } + + if err != nil { + return err + } + + for _, i := range []string{"docker", "images"} { + dir := path.Join(STATE, i) + err = os.MkdirAll(dir, 0755) + if err != nil { + return err + } + } + + log.Debugf("Bind mounting %s to %s", path.Join(STATE, "docker"), DOCKER) + err = util.Mount(path.Join(STATE, "docker"), DOCKER, "", "bind") + if err != nil && cfg.StateRequired { + return err + } + + return nil +} + +func pause(cfg *config.Config) error { + time.Sleep(5 + time.Minute) + return nil +} + +func RunInit() error { + var cfg config.Config + + os.Setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") + os.Setenv("DOCKER_RAMDISK", "true") + + initFuncs := []config.InitFunc{ + func(cfg *config.Config) error { + return createDirs(dirs...) + }, + func(cfg *config.Config) error { + return createMounts(mounts...) + }, + func(cfg *config.Config) error { + newCfg, err := config.LoadConfig() + if err == nil { + *cfg = *newCfg + } + return err + }, + createSymlinks, + setResolvConf, + extractModules, + remountRo, + mountCgroups, + loadModules, + mountState, + sysInit, + } + + if err := config.RunInitFuncs(&cfg, initFuncs); err != nil { + return err + } + + return execDocker(&cfg) +} diff --git a/main.go b/main.go new file mode 100644 index 00000000..ac1cfd6d --- /dev/null +++ b/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "os" + + log "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/reexec" + osInit "github.com/rancherio/os/init" + "github.com/rancherio/os/sysinit" + "github.com/rancherio/os/user" +) + +func main() { + reexec.Register("init", osInit.MainInit) + reexec.Register("/init", osInit.MainInit) + reexec.Register("./init", osInit.MainInit) + reexec.Register("/sbin/init-sys", sysinit.SysInit) + reexec.Register("/usr/bin/system-docker", user.SystemDocker) + reexec.Register("system-docker", user.SystemDocker) + + if !reexec.Init() { + log.Fatalf("Failed to find an entry point for %s", os.Args[0]) + } +} diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 00000000..2fffdc15 --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,15 @@ +#!/bin/bash +set -e +set -x + +cd $(dirname $0)/.. + +apt-get update +apt-get install -y curl rsync build-essential syslinux xorriso + + +curl -sL https://test.docker.com/builds/Linux/x86_64/docker-1.5.0-rc4 > /usr/bin/docker + +chmod +x /usr/bin/docker + +curl -sL https://storage.googleapis.com/golang/go1.4.1.linux-amd64.tar.gz | tar xvzf - -C /usr/local diff --git a/scripts/build b/scripts/build new file mode 100755 index 00000000..8694bd87 --- /dev/null +++ b/scripts/build @@ -0,0 +1,29 @@ +#!/bin/bash -e + +if [[ ! -x "$(which go)" && -x /usr/local/go/bin/go ]]; then + PATH=/usr/local/go/bin:${PATH} +fi + +cd $(dirname $0)/.. + +if [ -f ./build/bootstrap.envs ];then + . ./build/bootstrap.envs +fi + +export GOPATH=$(pwd)/Godeps/_workspace:$(pwd)/gopath + +PACKAGE=./gopath/src/$(<.package) + +if [ -L ${PACKAGE} ]; then + rm ${PACKAGE} +fi + +if [ ! -e ${PACKAGE} ]; then + mkdir -p $(dirname $PACKAGE) + ln -s $(pwd) $PACKAGE +fi + +echo export GOPATH=$GOPATH + +mkdir -p bin +go build -ldflags "-linkmode external -extldflags -static" -o bin/rancheros diff --git a/scripts/build-common b/scripts/build-common new file mode 100644 index 00000000..06fba0eb --- /dev/null +++ b/scripts/build-common @@ -0,0 +1,125 @@ +#!/bin/bash +set -e + +: ${ARTIFACTS:=$(pwd)/assets} +: ${BUILD:=$(pwd)/build} +: ${CONFIG:=$(pwd)/config} +: ${DIST:=$(pwd)/dist} + +BUILDROOT=buildroot-2014.11.tar.bz2 +DOCKER_FILE=${CONFIG}/.dockerfile + +mkdir -p ${BUILD} ${DIST} + +busybox_install() +{ + apt-get update + apt-get install -y build-essential wget libncurses5-dev unzip bc + + cd ${BUILD} + + rm -rf ${BUILDROOT/.tar.bz2//} + tar xvjf ${ARTIFACTS}/${BUILDROOT} + cd ${BUILDROOT/.tar.bz2//} + + cp $1 .config + make oldconfig + make + + cp output/images/rootfs.tar $2 +} + +write_base() +{ + if [ "${BASE_WRITTEN}" = "true" ]; then + return + fi + + mkdir -p $(dirname ${DOCKER_FILE}) + + cat > ${DOCKER_FILE} << EOF +FROM ${DOCKER_BASE:=ubuntu:14.04.1} +ENV TERM xterm +WORKDIR /source +CMD ["/source/scripts/install"] +EOF + + BASE_WRITTEN=true +} + +run() +{ + local content + + while [ $# -gt 1 ]; do + case $1 in + --assets) + shift 1 + if [ -e "$1" ]; then + content="$content\nCOPY $1 /source/$1" + else + content="$content\nCOPY $1 /source/" + fi + ;; + esac + + shift 1 + done + + write_base + if [ -n "$content" ]; then + echo -e "$content" >> ${DOCKER_FILE} + fi + if [ -n "$1" ]; then + echo -e "\nCOPY $1 /source/$1" >> ${DOCKER_FILE} + echo -e "RUN /source/$1" >> ${DOCKER_FILE} + fi +} + +finish() +{ + local cmd="docker build -t ${DOCKER_IMAGE} -f ${DOCKER_FILE} ." + echo Running $cmd + echo Pwd $(pwd) + + cat ${DOCKER_FILE} + + $cmd +} + +reset_docker_build() +{ + BASE_WRITTEN=false +} + +check() +{ + local hash=$1 + local file=$2 + + if [ ! -e "$file" ]; then + return 1 + fi + + CURRENT=$(sha1sum $file | awk '{print $1}') + + [ "$hash" = "$CURRENT" ] +} + +download() +{ + mkdir -p ${ARTIFACTS} + + local url=$2 + local file=${ARTIFACTS}/$(basename $2) + local hash=$1 + + if ! check $hash $file; then + curl -sL $url > $file + fi + + if ! check $hash $file; then + echo "ERROR: $file does not match checksum $hash, got $CURRENT" 1>&2 + return 1 + fi +} diff --git a/scripts/build-images b/scripts/build-images new file mode 100755 index 00000000..ee8084a8 --- /dev/null +++ b/scripts/build-images @@ -0,0 +1,53 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +source scripts/build-common + +mkdir -p ${BUILD}/initrd ${DIST}/artifacts + +echo Extracting ${ARTIFACTS}/os-base.tar.xz +tar xJf ${ARTIFACTS}/os-base.tar.xz -C ${BUILD} + +cp /etc/ssl/certs/ca-certificates.crt ${BUILD}/ca.crt + +rm -rf ${BUILD}/initrd/lib +cp -rf ${BUILD}/dist/kernel/lib ${BUILD}/initrd +( + cd ${BUILD}/initrd/lib/modules + # Taken from boot2docker + # Remove useless kernel modules, based on unclejack/debian2docker + rm -rf ./*/kernel/sound/* + rm -rf ./*/kernel/drivers/gpu/* + rm -rf ./*/kernel/drivers/infiniband/* + rm -rf ./*/kernel/drivers/isdn/* + rm -rf ./*/kernel/drivers/media/* + rm -rf ./*/kernel/drivers/staging/lustre/* + rm -rf ./*/kernel/drivers/staging/comedi/* + rm -rf ./*/kernel/fs/ocfs2/* + rm -rf ./*/kernel/net/bluetooth/* + rm -rf ./*/kernel/net/mac80211/* + rm -rf ./*/kernel/net/wireless/* +) +cp -f ${ARTIFACTS}/docker* ${BUILD}/initrd/docker +chmod +x ${BUILD}/initrd/docker + +cp ${BUILD}/dist/kernel/bzImage ${DIST}/artifacts/vmlinuz + +tar xf ${BUILD}/dist/rootfs-static.tar -C ${BUILD}/initrd --strip-components=2 ./bin/busybox + +if ! docker info >/dev/null 2>&1 && [ -x "$(which wrapdocker)" ]; then + wrapdocker +fi + +>${BUILD}/tags +for i in scripts/dockerimages/[0-9]*; do + tag=$(echo $i | cut -f2 -d-) + echo Building $tag + docker build -t $tag -f $i . + echo $tag >> ${BUILD}/tags +done + +echo Creating images.tar +docker save $(<${BUILD}/tags) > ${BUILD}/initrd/images.tar diff --git a/scripts/ci b/scripts/ci new file mode 100755 index 00000000..8a7873bf --- /dev/null +++ b/scripts/ci @@ -0,0 +1,48 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +export DOCKER_IMAGE=${DOCKER_IMAGE:=rancher-os-build} +export DOCKER_BASE=rancher/docker-dind-base:latest + +source scripts/build-common + +DOCKER_FILE=$(pwd)/.dockerfile + +generate_images() +{ + IMAGE_ID=$(docker images --no-trunc -q ${DOCKER_IMAGE}) + + if [ -e ${BUILD}/${IMAGE_ID} ]; then + DOCKER_BASE=$(<${BUILD}/${IMAGE_ID}) + else + CID=$(docker run -d --privileged ${DOCKER_IMAGE} /source/scripts/build-images) + docker logs -f ${CID} & + trap "docker rm -f ${CID}" exit + docker wait $CID + DOCKER_BASE=$(docker commit $CID) + + echo ${DOCKER_BASE} > ${BUILD}/${IMAGE_ID} + fi +} + +run ./scripts/bootstrap +run --assets ./scripts/build-common ./scripts/download +run --assets ./scripts/dockerimages --assets ./scripts/build-images +finish + +generate_images + +reset_docker_build +ARGS= +for i in $(ls -d * .* | sort -u | grep -Ev '(\.|\.\.|\.dockerfile|build|dist|.git|scripts|bin)$'); do + if [ -d $i ]; then + run --assets $i + else + ARGS="${ARGS} $i" + fi +done +run --assets "${ARGS}" ./scripts/build +run --assets ./scripts/install ./scripts/package +finish diff --git a/scripts/clean b/scripts/clean new file mode 100755 index 00000000..e4882661 --- /dev/null +++ b/scripts/clean @@ -0,0 +1,5 @@ +#!/bin/bash + +cd $(dirname $0)/.. + +rm -rf build dist diff --git a/scripts/dockerimages/00-base b/scripts/dockerimages/00-base new file mode 100644 index 00000000..0e6620d7 --- /dev/null +++ b/scripts/dockerimages/00-base @@ -0,0 +1,3 @@ +FROM scratch +ADD build/dist/rootfs.tar / +CMD ["/bin/sh"] diff --git a/scripts/dockerimages/01-state b/scripts/dockerimages/01-state new file mode 100644 index 00000000..168a7d7e --- /dev/null +++ b/scripts/dockerimages/01-state @@ -0,0 +1,5 @@ +FROM base +VOLUME /home +VOLUME /var/lib/docker +VOLUME /var/run +CMD ["echo"] diff --git a/scripts/dockerimages/02-udev b/scripts/dockerimages/02-udev new file mode 100644 index 00000000..478e0472 --- /dev/null +++ b/scripts/dockerimages/02-udev @@ -0,0 +1,3 @@ +FROM base +COPY scripts/dockerimages/scripts/udev.sh / +CMD ["/udev.sh"] diff --git a/scripts/dockerimages/03-network b/scripts/dockerimages/03-network new file mode 100644 index 00000000..24ae2a92 --- /dev/null +++ b/scripts/dockerimages/03-network @@ -0,0 +1,2 @@ +FROM base +CMD ["udhcpc", "-i", "eth0"] diff --git a/scripts/dockerimages/04-userdocker b/scripts/dockerimages/04-userdocker new file mode 100644 index 00000000..7621054c --- /dev/null +++ b/scripts/dockerimages/04-userdocker @@ -0,0 +1,4 @@ +FROM base +COPY scripts/dockerimages/scripts/docker.sh / +COPY build/ca.crt /etc/ssl/certs/ca-certificates.crt +CMD ["/docker.sh"] diff --git a/scripts/dockerimages/05-console b/scripts/dockerimages/05-console new file mode 100644 index 00000000..bd790f7a --- /dev/null +++ b/scripts/dockerimages/05-console @@ -0,0 +1,2 @@ +FROM base +CMD ["/bin/sh"] diff --git a/scripts/dockerimages/06-rescue b/scripts/dockerimages/06-rescue new file mode 100644 index 00000000..9232f12d --- /dev/null +++ b/scripts/dockerimages/06-rescue @@ -0,0 +1,2 @@ +FROM console +CMD ["/bin/sh"] diff --git a/scripts/dockerimages/scripts/docker.sh b/scripts/dockerimages/scripts/docker.sh new file mode 100755 index 00000000..ac0a4f4c --- /dev/null +++ b/scripts/dockerimages/scripts/docker.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -x -e + +CGROUPS="perf_event net_cls freezer devices blkio memory cpuacct cpu cpuset" + +mkdir -p /sys/fs/cgroup +mount -t tmpfs none /sys/fs/cgroup + +for i in $CGROUPS; do + mkdir -p /sys/fs/cgroup/$i + mount -t cgroup -o $i none /sys/fs/cgroup/$i +done + +if ! lsmod | grep -q br_netfilter; then + modprobe br_netfilter +fi + +rm -f /var/run/docker.pid +exec docker -d -s overlay diff --git a/scripts/dockerimages/scripts/os-dockerfile b/scripts/dockerimages/scripts/os-dockerfile new file mode 100644 index 00000000..25cd82d8 --- /dev/null +++ b/scripts/dockerimages/scripts/os-dockerfile @@ -0,0 +1,4 @@ +FROM scratch +COPY init busybox docker images.tar / +COPY lib /lib +CMD ["/init"] diff --git a/scripts/dockerimages/scripts/udev.sh b/scripts/dockerimages/scripts/udev.sh new file mode 100755 index 00000000..335c1980 --- /dev/null +++ b/scripts/dockerimages/scripts/udev.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +mount --bind /host/dev /dev +udevd --daemon +udevadm trigger --action=add +udevadm settle diff --git a/scripts/download b/scripts/download new file mode 100755 index 00000000..940a2a7e --- /dev/null +++ b/scripts/download @@ -0,0 +1,8 @@ +#!/bin/bash + +cd $(dirname $0)/.. + +source scripts/build-common + +download a8e5925eab10a472af4b9326dc3ff1068fd56886 https://github.com/rancherio/os-base/releases/download/v0.0.1/os-base.tar.xz +download c7d977c2bdc48c022fcf3a51a6e26229247cfb15 https://test.docker.com/builds/Linux/x86_64/docker-1.5.0-rc4 diff --git a/scripts/install b/scripts/install new file mode 100755 index 00000000..1c74fd2a --- /dev/null +++ b/scripts/install @@ -0,0 +1,11 @@ +#!/bin/bash +set -x + +cd $(dirname $0)/.. + +. scripts/build-common + +mkdir -p target +rm -rf target/artifacts + +cp -rf ${DIST}/artifacts target diff --git a/scripts/package b/scripts/package new file mode 100755 index 00000000..b2da8d67 --- /dev/null +++ b/scripts/package @@ -0,0 +1,42 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. + +source scripts/build-common + +cp bin/rancheros ${BUILD}/initrd/init +cp scripts/dockerimages/scripts/os-dockerfile ${BUILD}/initrd/Dockerfile + +cd ${BUILD}/initrd + +find | cpio -H newc -o | lzma -c > ${DIST}/artifacts/initrd + +CD=${BUILD}/cd + +mkdir -p ${CD}/boot/isolinux +cp ${DIST}/artifacts/vmlinuz ${CD}/boot +cp ${DIST}/artifacts/initrd ${CD}/boot +cp /usr/lib/syslinux/isolinux.bin ${CD}/boot/isolinux +cp /usr/lib/syslinux/linux.c32 ${CD}/boot/isolinux/ldlinux.c32 +cat > ${CD}/boot/isolinux/isolinux.cfg << EOF +default rancheros +label rancheros + kernel /boot/vmlinuz + initrd /boot/initrd + append quiet + +prompt 1 +timeout 1 +EOF + +# Copied from boot2docker, thanks. +cd ${CD} + xorriso \ + -publisher "Rancher Labs, Inc." \ + -as mkisofs \ + -l -J -R -V "RancherOS" \ + -no-emul-boot -boot-load-size 4 -boot-info-table \ + -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat \ + -isohybrid-mbr /usr/lib/syslinux/isohdpfx.bin \ + -o ${DIST}/artifacts/rancheros.iso $(pwd) diff --git a/scripts/run b/scripts/run new file mode 100755 index 00000000..bf1c735a --- /dev/null +++ b/scripts/run @@ -0,0 +1,45 @@ +#!/bin/bash +set -e +set -x + +cd $(dirname $0)/.. + +source scripts/build-common + +BASE=$(pwd) + +KERNEL=${BASE}/dist/artifacts/vmlinuz +INITRD=${BASE}/dist/artifacts/initrd +HD=${BASE}/build/empty-hd.img +HD_GZ=${ARTIFACTS}/empty-hd.img.gz +INITRD_TMP=${BUILD}/$(sha1sum ${INITRD} | awk '{print $1}') +INITRD_TEST=${BUILD}/initrd.test + +if [[ ! -e ${KERNEL} || ! -e ${INITRD} ]]; then + echo "Failed to find ${KERNEL} or ${INITRD}" 1>&2 + exit 1 +fi + +if [ ! -d ${INITRD_TMP} ]; then + mkdir -p ${INITRD_TMP} + pushd ${INITRD_TMP} + lzma -dc ${INITRD} | sudo cpio -idmv + rm -f init + popd +fi + +cp bin/rancheros ${INITRD_TMP}/init +cd ${INITRD_TMP} + +if [ "$1" == "--docker" ]; then + docker build -t rancheros-run . + docker run --privileged -it rancheros-run +else + find | cpio -H newc -o > ${INITRD_TEST} + + if [ ! -e ${HD} ]; then + zcat ${HD_GZ} > ${HD} + fi + + kvm -m 1024 -kernel ${KERNEL} -initrd ${INITRD_TEST} -append "$1" -hda ${HD} -serial stdio -netdev user,id=hostnet0 -device virtio-net-pci,romfile=,netdev=hostnet0 +fi diff --git a/sysinit/sysinit.go b/sysinit/sysinit.go new file mode 100644 index 00000000..8df8d13e --- /dev/null +++ b/sysinit/sysinit.go @@ -0,0 +1,159 @@ +package sysinit + +import ( + "os" + "os/exec" + "path" + "strings" + + log "github.com/Sirupsen/logrus" + dockerClient "github.com/fsouza/go-dockerclient" + "github.com/rancherio/os/config" + "github.com/rancherio/os/docker" + initPkg "github.com/rancherio/os/init" +) + +func SysInit() { + if err := sysInit(); err != nil { + log.Fatal(err) + } +} + +func importImage(client *dockerClient.Client, name, fileName string) error { + file, err := os.Open(fileName) + if err != nil { + return err + } + + defer file.Close() + + log.Debugf("Importing image for %s", fileName) + repo, tag := dockerClient.ParseRepositoryTag(name) + return client.ImportImage(dockerClient.ImportImageOptions{ + Source: "-", + Repository: repo, + Tag: tag, + InputStream: file, + }) +} + +func hasImage(name string) bool { + stamp := path.Join(initPkg.STATE, name) + if _, err := os.Stat(stamp); os.IsNotExist(err) { + return false + } + return true +} + +func findImages(cfg *config.Config) ([]string, error) { + log.Debugf("Looking for images at %s", cfg.ImagesPath) + + result := []string{} + + dir, err := os.Open(cfg.ImagesPath) + if os.IsNotExist(err) { + log.Debugf("Not loading images, %s does not exist") + return result, nil + } + if err != nil { + return nil, err + } + + defer dir.Close() + + files, err := dir.Readdirnames(0) + if err != nil { + return nil, err + } + + for _, fileName := range files { + log.Debugf("Checking %s", fileName) + if ok, _ := path.Match(cfg.ImagesPattern, fileName); ok { + log.Debugf("Found %s", fileName) + result = append(result, fileName) + } + } + + return result, nil +} + +func loadImages(cfg *config.Config) error { + images, err := findImages(cfg) + if err != nil || len(images) == 0 { + return err + } + + client, err := docker.NewClient(cfg) + if err != nil { + return err + } + + for _, image := range images { + if hasImage(image) { + continue + } + + inputFileName := path.Join(cfg.ImagesPath, image) + input, err := os.Open(inputFileName) + if err != nil { + return err + } + + defer input.Close() + + log.Debugf("Loading images from %s", inputFileName) + err = client.LoadImage(dockerClient.LoadImageOptions{ + InputStream: input, + }) + + if err != nil { + return err + } + } + + return nil +} + +func runContainers(cfg *config.Config) error { + containers := cfg.SystemContainers + if cfg.Rescue { + log.Debug("Running rescue container") + containers = []config.ContainerConfig{cfg.RescueContainer} + } + + for _, container := range containers { + args := append([]string{"run"}, container.Options...) + args = append(args, container.Image) + args = append(args, container.Args...) + + cmd := exec.Command(cfg.DockerBin, args...) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + + //log.Infof("Is a tty : %v", term.IsTerminal(0)) + //log.Infof("Is a tty : %v", term.IsTerminal(1)) + //log.Infof("Is a tty : %v", term.IsTerminal(2)) + log.Debugf("Running %s", strings.Join(args, " ")) + err := cmd.Run() + if err != nil { + log.Errorf("Failed to run %v: %v", args, err) + } + } + + return nil +} + +func sysInit() error { + cfg, err := config.LoadConfig() + if err != nil { + return err + } + + initFuncs := []config.InitFunc{ + loadImages, + runContainers, + } + + return config.RunInitFuncs(cfg, initFuncs) +} diff --git a/user/system-docker.go b/user/system-docker.go new file mode 100644 index 00000000..ac28466a --- /dev/null +++ b/user/system-docker.go @@ -0,0 +1,25 @@ +package user + +import ( + "os" + "strings" + "syscall" + + log "github.com/Sirupsen/logrus" +) + +func SystemDocker() { + var newEnv []string + for _, env := range os.Environ() { + if !strings.HasPrefix(env, "DOCKER_HOST=") { + newEnv = append(newEnv, env) + } + } + + newEnv = append(newEnv, "DOCKER_HOST=unix://var/run/system-docker.sock") + + os.Args[0] = "/usr/bin/docker" + if err := syscall.Exec(os.Args[0], os.Args, newEnv); err != nil { + log.Fatal(err) + } +} diff --git a/util/util.go b/util/util.go new file mode 100644 index 00000000..b525512f --- /dev/null +++ b/util/util.go @@ -0,0 +1,96 @@ +package util + +import ( + "archive/tar" + "fmt" + "io" + "os" + "path" + "syscall" + + "github.com/docker/docker/pkg/mount" +) + +func mountProc() error { + if _, err := os.Stat("/proc/self/mountinfo"); os.IsNotExist(err) { + if _, err := os.Stat("/proc"); os.IsNotExist(err) { + if err = os.Mkdir("/proc", 0755); err != nil { + return err + } + } + + if err := syscall.Mount("none", "/proc", "proc", 0, ""); err != nil { + return err + } + } + + return nil +} + +func Mount(device, directory, fsType, options string) error { + if err := mountProc(); err != nil { + return nil + } + + if _, err := os.Stat(directory); os.IsNotExist(err) { + err = os.MkdirAll(directory, 0755) + if err != nil { + return err + } + } + + return mount.Mount(device, directory, fsType, options) +} + +func Remount(directory, options string) error { + return mount.Mount("", directory, "", fmt.Sprintf("remount,%s", options)) +} + +func ExtractTar(archive string, dest string) error { + f, err := os.Open(archive) + if err != nil { + return err + } + defer f.Close() + + input := tar.NewReader(f) + + for { + header, err := input.Next() + if err == io.EOF { + break + } else if err != nil { + return err + } + + if header == nil { + break + } + + fileInfo := header.FileInfo() + fileName := path.Join(dest, header.Name) + if fileInfo.IsDir() { + //log.Debugf("DIR : %s", fileName) + err = os.MkdirAll(fileName, fileInfo.Mode()) + if err != nil { + return err + } + } else { + //log.Debugf("FILE: %s", fileName) + destFile, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileInfo.Mode()) + if err != nil { + return err + } + + _, err = io.Copy(destFile, input) + // Not deferring, concerned about holding open too many files + destFile.Close() + + if err != nil { + return err + } + } + } + + return nil +}