diff --git a/docker/Dockerfile.builder.amd64 b/docker/Dockerfile.builder.amd64 new file mode 100644 index 000000000..edfb77094 --- /dev/null +++ b/docker/Dockerfile.builder.amd64 @@ -0,0 +1,21 @@ +# Image for compiling mantle. + +FROM ubuntu:17.10 + +ARG GO_VERSION=1.10 + +RUN apt-get update && apt-get install -y \ + apt-utils \ + gcc \ + gcc-aarch64-linux-gnu \ + git \ + wget \ + && rm -rf /var/lib/apt/lists/* + +RUN wget --no-verbose https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz && \ + tar -C /usr/local -xf go${GO_VERSION}.linux-amd64.tar.gz && \ + rm go${GO_VERSION}.linux-amd64.tar.gz + +ENV PATH /usr/local/go/bin:${PATH} + +CMD /bin/bash diff --git a/docker/Dockerfile.builder.arm64 b/docker/Dockerfile.builder.arm64 new file mode 100644 index 000000000..165999c27 --- /dev/null +++ b/docker/Dockerfile.builder.arm64 @@ -0,0 +1,20 @@ +# Image for compiling mantle. + +FROM arm64v8/ubuntu:17.10 + +ARG GO_VERSION=1.10 + +RUN apt-get update && apt-get install -y \ + apt-utils \ + gcc \ + git \ + wget \ + && rm -rf /var/lib/apt/lists/* + +RUN wget --no-verbose https://storage.googleapis.com/golang/go${GO_VERSION}.linux-arm64.tar.gz && \ + tar -C /usr/local -xf go${GO_VERSION}.linux-arm64.tar.gz && \ + rm go${GO_VERSION}.linux-arm64.tar.gz + +ENV PATH /usr/local/go/bin:${PATH} + +CMD /bin/bash diff --git a/docker/Dockerfile.runner.amd64 b/docker/Dockerfile.runner.amd64 new file mode 100644 index 000000000..a6b673527 --- /dev/null +++ b/docker/Dockerfile.runner.amd64 @@ -0,0 +1,16 @@ +# Image for running mantle programs. + +FROM ubuntu:17.10 + +RUN apt-get update && apt-get install -y \ + apt-utils \ + qemu-system-arm \ + qemu-system-x86-64 \ + dnsmasq \ + && rm -rf /var/lib/apt/lists/* + +COPY ./bin /usr/lib/kola/ + +ENV PATH /usr/lib/kola:${PATH} + +CMD /bin/bash diff --git a/docker/Dockerfile.runner.arm64 b/docker/Dockerfile.runner.arm64 new file mode 100644 index 000000000..b6f358214 --- /dev/null +++ b/docker/Dockerfile.runner.arm64 @@ -0,0 +1,15 @@ +# Image for running mantle programs. + +FROM arm64v8/ubuntu:17.10 + +RUN apt-get update && apt-get install -y \ + apt-utils \ + qemu-system-arm \ + dnsmasq \ + && rm -rf /var/lib/apt/lists/* + +COPY ./bin /usr/lib/kola/ + +ENV PATH /usr/lib/kola:${PATH} + +CMD /bin/bash diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..8b87197ad --- /dev/null +++ b/docker/README.md @@ -0,0 +1,37 @@ +# Mantle Container Support + +## Command Summary + + - `build-builder` Builds a docker image that contains tools for building mantle. Default docker image tag is `mantle-builder:${VERSION}${ARCH_TAG}`. + - `build-mantle` Builds mantle programs using a mantle-builder container. + - `build-runner` Builds a docker image that containes the mantle programs. Default docker image tag is `mantle-runner:${VERSION}${ARCH_TAG}`. + - `run-mantle` Runs mantle programs in a mantle-runner container. + +## Examples + +### Build the mantle-runner Container Image + + ./build-builder -v + ./build-mantle -vt + ./build-runner -v + +### Run Kola Tests in a mantle-runner Container + +See https://coreos.com/releases for release info. + + CHANNEL=alpha + BOARD=amd64-usr + #BOARD=arm64-usr + RELEASE=current + SERVER=https://${CHANNEL}.release.core-os.net/${BOARD}/${RELEASE} + RELEASES=/tmp/coreos-releses + + mkdir -p ${RELEASES}/${CHANNEL}/${BOARD}/${RELEASE} + pushd ${RELEASES}/${CHANNEL}/${BOARD}/${RELEASE} + wget --no-verbose ${SERVER}/coreos_production_qemu_uefi{.DIGESTS,.sh,_efi_code.fd,_efi_vars.fd} + wget --no-verbose ${SERVER}/coreos_production_image.bin.bz2{.DIGESTS,} + bunzip2 coreos_production_image.bin.bz2 + popd + + ls ${RELEASES}/${CHANNEL}/${BOARD}/${RELEASE} + DOCKER_ARGS="-v ${RELEASES}:/releases:ro" ./run-mantle -kv -- kola run coreos.basic --board="${BOARD}" --parallel=2 --platform=qemu --qemu-bios=/releases/${CHANNEL}/${BOARD}/${RELEASE}/coreos_production_qemu_uefi_efi_code.fd --qemu-image=/releases/${CHANNEL}/${BOARD}/${RELEASE}/coreos_production_image.bin diff --git a/docker/build-builder b/docker/build-builder new file mode 100755 index 000000000..583f6e808 --- /dev/null +++ b/docker/build-builder @@ -0,0 +1,101 @@ +#!/usr/bin/env bash + +set -e + +name="$(basename $0)" + +: ${TOP_DIR:="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"} + +source ${TOP_DIR}/docker/builder-include + +: ${DOCKER_FILE:="${TOP_DIR}/docker/Dockerfile.builder.$(goarch)"} +: ${GO_VERSION:="1.10"} + +usage () { + echo "${name} - Builds a docker image that contains tools for building mantle." >&2 + echo "Usage: ${name} [flags]" >&2 + echo "Option flags:" >&2 + echo " -d --dry-run - Do not run docker commands." >&2 + echo " -h --help - Show this help and exit." >&2 + echo " -p --purge - Remove existing docker image and rebuild." >&2 + echo " -r --rebuild - Rebuild existing docker image." >&2 + echo " -t --tag - Print Docker tag to stdout and exit." >&2 + echo " -v --verbose - Verbose execution." >&2 + echo "Environment:" >&2 + echo " DOCKER_FILE - Default: '${DOCKER_FILE}'" >&2 + echo " DOCKER_TAG - Default: '${DOCKER_TAG}'" >&2 + echo " GO_VERSION - Default: '${GO_VERSION}'" >&2 +} + +short_opts="dhprtv" +long_opts="dry-run,help,purge,rebuild,tag,verbose" + +opts=$(getopt --options ${short_opts} --long ${long_opts} -n "${name}" -- "$@") + +if [ $? != 0 ]; then + echo "${name}: Terminating..." >&2 + exit 1 +fi + +eval set -- "${opts}" + +while true ; do + case "${1}" in + -d | --dry-run) + dry_run="--dry-run" + shift + ;; + -h | --help) + usage + exit 0 + ;; + -p | --purge) + purge=1 + shift + ;; + -r | --rebuild) + rebuild=1 + shift + ;; + -t | --tag) + show_tag + exit 0 + ;; + -v | --verbose) + set -x + verbose=1 + shift + ;; + --) + shift + break + ;; + *) + echo "Error: Unknown option '${1}'." >&2 + usage + exit 1 + ;; + esac +done + +if [[ -n "${purge}" ]] && docker inspect --type image ${DOCKER_TAG} >/dev/null 2>/dev/null; then + echo "Removing docker image: ${DOCKER_TAG}" >&2 + [[ -n ${dry_run} ]] || docker rmi --force ${DOCKER_TAG} +elif [[ -z "${rebuild}" ]] && docker inspect --type image ${DOCKER_TAG} >/dev/null 2>/dev/null; then + echo "Docker image exists: ${DOCKER_TAG}" >&2 + show_tag + exit 0 +fi + +echo "Building docker image: ${DOCKER_TAG}" >&2 + +if [[ -z ${dry_run} ]]; then + cd ${TOP_DIR} + docker build \ + --file ${DOCKER_FILE} \ + --build-arg GO_VERSION=${GO_VERSION} \ + --tag ${DOCKER_TAG} \ + . +fi + +show_tag diff --git a/docker/build-mantle b/docker/build-mantle new file mode 100755 index 000000000..c45f4dc7d --- /dev/null +++ b/docker/build-mantle @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +set -e + +: ${TOP_DIR:="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"} + +source ${TOP_DIR}/docker/builder-include + +: ${CC:="gcc"} +: ${GOARCH:="$(goarch)"} +: ${NO_CROSS:=""} + +usage () { + echo "$(basename $0) - Builds mantle programs using a ${DOCKER_TAG} container." >&2 + echo "Usage: $(basename $0) [flags] [--] [build_args]" >&2 + echo "Option flags:" >&2 + echo " -a --arm64 - Build with common arm64 cross compile args." >&2 + echo " -d --dry-run - Do not run docker commands." >&2 + echo " -h --help - Show this help and exit." >&2 + echo " -r --verify - Run golang tests after build." >&2 + echo " -t --test - Run golang tests." >&2 + echo " -v --verbose - Verbose execution." >&2 + echo "Environment:" >&2 + echo " CC - Default: '${CC}'" >&2 + echo " GOARCH - Default: '${GOARCH}'" >&2 + echo " NO_CROSS - Default: '${NO_CROSS}'" >&2 + echo " DOCKER_TAG - Default: '${DOCKER_TAG}'" >&2 + echo "Examples:" >&2 + echo " $(basename $0) -vt" + echo " $(basename $0) -v -- cork" + echo " CC=aarch64-linux-gnu-gcc GOARCH=arm64 NO_CROSS=1 $(basename $0)" +} + +short_opts="adhrtv" +long_opts="arm64,dry-run,help,verify,test,verbose" + +opts=$(getopt --options ${short_opts} --long ${long_opts} -n "${name}" -- "$@") + +if [ $? != 0 ]; then + echo "${name}: Terminating..." >&2 + exit 1 +fi + +eval set -- "${opts}" + +while true ; do + case "${1}" in + -a | --arm64) + CC="aarch64-linux-gnu-gcc" + GOARCH="arm64" + NO_CROSS="1" + shift + ;; + -d | --dry-run) + dry_run="--dry-run" + shift + ;; + -h | --help) + usage + exit 0 + ;; + -r | --verify) + verify=1 + shift + ;; + -t | --test) + test=1 + shift + ;; + -v | --verbose) + set -x + verbose=1 + bash_debug="bash -x" + test_extra="-v" + shift + ;; + --) + shift + build_args=${*} + break + ;; + *) + build_args="${build_args} ${1}" + shift + ;; + esac +done + +docker_args="--rm \ + -e CC=${CC} + -e GOARCH=${GOARCH} \ + -e GOCACHE=/tmp/gocache \ + -e NO_CROSS=${NO_CROSS} \ + -u $(id -u):$(id -g) \ + -v /etc/group:/etc/group:ro \ + -v /etc/passwd:/etc/passwd:ro \ + -v ${TOP_DIR}:/opt/mantle \ + -w /opt/mantle \ +" + +# Need this for linkat to work in container. +docker_test_args="-v /tmp:/tmp:rw" + +cd ${TOP_DIR} + +[[ -z ${dry_run} ]] || exit 0 + +if [[ -z "${test}" ]]; then + docker run ${docker_args} ${DOCKER_TAG} ${bash_debug} ./build ${build_args} +fi + +if [[ -n "${test}" || -n "${verify}" ]]; then + docker run ${docker_args} ${docker_test_args} ${DOCKER_TAG} ${bash_debug} ./test ${test_extra} +fi diff --git a/docker/build-runner b/docker/build-runner new file mode 100755 index 000000000..0aaf7a172 --- /dev/null +++ b/docker/build-runner @@ -0,0 +1,147 @@ +#!/usr/bin/env bash + +set -e + +get_version () { + local version=$(git describe --dirty) + version="${version#v}" + echo "${version}" +} + +goarch() { + local m="$(uname -m)" + + case "${m}" in + aarch64) echo "arm64" ;; + x86_64) echo "amd64" ;; + *) echo "${m}" ;; + esac +} + +arch_tag() { + local a="$(goarch)" + + case "${a}" in + amd64) echo "" ;; + *) echo "-${a}" ;; + esac +} + +: ${TOP_DIR:="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"} +: ${VERSION:="$(get_version)"} +: ${ARCH_TAG:="$(arch_tag)"} +: ${DOCKER_NAME:="mantle-runner"} +: ${DOCKER_TAG:="${DOCKER_NAME}:${VERSION}${ARCH_TAG}"} +: ${DOCKER_FILE:="${TOP_DIR}/docker/Dockerfile.runner.$(goarch)"} + +show_tag () { + echo "${DOCKER_TAG}" +} + + +usage () { + echo "$(basename $0) - Builds a docker image that containes the mantle programs." >&2 + echo "Usage: $(basename $0) [flags]" >&2 + echo "Option flags:" >&2 + echo " -d --dry-run - Do not run docker commands." >&2 + echo " -h --help - Show this help and exit." >&2 + echo " -p --purge - Remove existing docker image and rebuild." >&2 + echo " -r --rebuild - Rebuild existing docker image." >&2 + echo " -s --script - Only generate run-mantle helper script." >&2 + echo " -t --tag - Print docker tag to stdout and exit." >&2 + echo " -v --verbose - Verbose execution." >&2 + echo "Environment:" >&2 + echo " DOCKER_FILE - Default: '${DOCKER_FILE}'" >&2 + echo " DOCKER_TAG - Default: '${DOCKER_TAG}'" >&2 +} + +short_opts="dhprstv" +long_opts="dry-run,help,purge,rebuild,script,tag,verbose" + +opts=$(getopt --options ${short_opts} --long ${long_opts} -n "${name}" -- "$@") + +if [ $? != 0 ]; then + echo "${name}: Terminating..." >&2 + exit 1 +fi + +eval set -- "${opts}" + +while true ; do + case "${1}" in + -d | --dry-run) + dry_run="--dry-run" + shift + ;; + -h | --help) + usage + exit 0 + ;; + -p | --purge) + purge=1 + shift + ;; + -r | --rebuild) + rebuild=1 + shift + ;; + -s | --script) + script=1 + shift + ;; + -t | --tag) + show_tag + exit 0 + ;; + -v | --verbose) + set -x + shift + ;; + --) + shift + break + ;; + *) + echo "Error: Unknown option '${1}'." >&2 + usage + exit 1 + ;; + esac +done + +if [[ -n "${purge}" ]] && docker inspect --type image ${DOCKER_TAG} >/dev/null 2>/dev/null; then + echo "Removing docker image: ${DOCKER_TAG}" >&2 + [[ -n ${dry_run} ]] || docker rmi --force ${DOCKER_TAG} +elif [[ -z "${rebuild}" ]] && docker inspect --type image ${DOCKER_TAG} >/dev/null 2>/dev/null; then + echo "Docker image exists: ${DOCKER_TAG}" >&2 + show_tag + exit 0 +fi + +echo "Building docker image: ${DOCKER_TAG}" >&2 + +# Stand-alone helper script. +helper_in="${TOP_DIR}/docker/run-mantle.in" +helper_out="$(pwd)/run-mantle" + +gen_script () { + cat ${helper_in} | sed -e "s/@DOCKER_TAG@/${DOCKER_TAG}/g" > ${helper_out} + chmod +x ${helper_out} + echo "Wrote helper: ${helper_out}" +} + +if [[ -z ${dry_run} ]]; then + rm -f ${helper_out} + + if [[ -z ${script} ]]; then + cd ${TOP_DIR} + docker build \ + --file ${DOCKER_FILE} \ + --tag ${DOCKER_TAG} \ + . + fi + + gen_script +fi + +show_tag diff --git a/docker/builder-include b/docker/builder-include new file mode 100644 index 000000000..30f17079f --- /dev/null +++ b/docker/builder-include @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +goarch() { + local m="$(uname -m)" + + case "${m}" in + aarch64) echo "arm64" ;; + x86_64) echo "amd64" ;; + *) echo "${m}" ;; + esac +} + +arch_tag() { + local a="$(goarch)" + + case "${a}" in + amd64) echo "" ;; + *) echo "-${a}" ;; + esac +} + +: ${VERSION:="3"} +: ${ARCH_TAG:="$(arch_tag)"} +: ${DOCKER_NAME:="mantle-builder"} +: ${DOCKER_TAG:="${DOCKER_NAME}:${VERSION}${ARCH_TAG}"} + +show_tag () { + echo "${DOCKER_TAG}" +} diff --git a/docker/run-mantle.in b/docker/run-mantle.in new file mode 100755 index 000000000..fdb4a01d0 --- /dev/null +++ b/docker/run-mantle.in @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +set -e + +: ${DOCKER_TAG:="@DOCKER_TAG@"} + +usage () { + echo "$(basename $0) - Runs mantle programs in a ${DOCKER_TAG} container." >&2 + echo "Usage: $(basename $0) [flags] {--} " >&2 + echo "Option flags:" >&2 + echo " -d --dry-run - Do not run docker commands." >&2 + echo " -h --help - Show this help and exit." >&2 + echo " -k --kvm - Run container with kvm privileges." >&2 + echo " -p --privileged - Run container as privileged" >&2 + echo " -r --root - Run container as root." >&2 + echo " -v --verbose - Verbose execution." >&2 + echo "Environment:" >&2 + echo " DOCKER_ARGS - Extra docker args - Default: '${DOCKER_ARGS}'" >&2 + echo " DOCKER_TAG - Default: '${DOCKER_TAG}'" >&2 +} + +short_opts="+dhkprv" +long_opts="dry-run,help,kvm,privileged,root,verbose" + +opts=$(getopt --options ${short_opts} --long ${long_opts} -n "${name}" -- "${@}") + +if [ $? != 0 ]; then + echo "${name}: Terminating..." >&2 + exit 1 +fi + +eval set -- "${opts}" + +while true ; do + case "${1}" in + -d | --dry-run) + dry_run="--dry-run" + shift + ;; + -h | --help) + usage + exit 0 + ;; + -k | --kvm) + kvm=1 + shift + ;; + -p | --privileged) + privileged=1 + shift + ;; + -r | --root) + root=1 + shift + ;; + -v | --verbose) + set -x + verbose=1 + shift + ;; + --) + shift + break + ;; + *) + echo "Error: Unknown option '${1}'." >&2 + usage + exit 1 + ;; + esac +done + +docker_common_args=" \ + -v $(pwd):/opt/mantle-runner \ + -w /opt/mantle-runner +" + +docker_user_args=" \ + --user $(id -u):$(id -g) \ + -v /etc/group:/etc/group:ro \ + -v /etc/passwd:/etc/passwd:ro \ +" + +docker_kvm_args=" \ + --cap-add=SYS_ADMIN \ + --cap-add=NET_ADMIN \ + --device=/dev/kvm:/dev/kvm \ + --device=/dev/net:/dev/net \ + --net=host \ +" + +docker_privileged_args=" \ + --privileged \ + --net=host \ +" + +if [[ -n ${privileged} ]]; then + docker_args="${docker_common_args} ${docker_privileged_args}" +elif [[ -n ${kvm} ]]; then + docker_args="${docker_common_args} ${docker_kvm_args}" +elif [[ -n ${root} ]]; then + docker_args="${docker_common_args}" +else + docker_args="${docker_common_args} ${docker_user_args}" +fi + +if [[ -z ${dry_run} ]]; then + docker run --rm ${docker_args} ${DOCKER_ARGS} ${DOCKER_TAG} ${@} +fi