diff --git a/devstack/plugin.sh b/devstack/plugin.sh index 8889c77e..50c652e7 100644 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -1,9 +1,18 @@ # install tempest plugin +function build_test_container { + pushd "${DEST}/kuryr-tempest-plugin/test_container" + + docker build -t kuryr/demo . -f Dockerfile + popd +} + function install_kuryr_tempest_plugin { setup_dev_lib "kuryr-tempest-plugin" } if [[ "$1" == "stack" && "$2" == "install" ]]; then + echo_summary "Building kuryr/demo test container" + build_test_container echo_summary "Installing Kuryr Tempest Plugin" install_kuryr_tempest_plugin fi diff --git a/kuryr_tempest_plugin/tests/scenario/base.py b/kuryr_tempest_plugin/tests/scenario/base.py index 8825a78c..afa166cb 100644 --- a/kuryr_tempest_plugin/tests/scenario/base.py +++ b/kuryr_tempest_plugin/tests/scenario/base.py @@ -52,7 +52,7 @@ class BaseKuryrScenarioTest(manager.NetworkScenarioTest): cls.os_admin.floating_ips_client.delete_floatingip( fip['floatingip']['id']) - def create_pod(self, name=None, image='celebdor/kuryr-demo', + def create_pod(self, name=None, image='kuryr/demo', namespace="default"): name = data_utils.rand_name(prefix='kuryr-pod') pod = self.k8s_client.V1Pod() diff --git a/test_container/Dockerfile b/test_container/Dockerfile new file mode 100644 index 00000000..8a941a3d --- /dev/null +++ b/test_container/Dockerfile @@ -0,0 +1,9 @@ +FROM scratch +ADD rootfs.tar.xz / +RUN adduser -S kuryr + +USER kuryr +WORKDIR /home/kuryr + +EXPOSE 8080 +CMD ["/usr/bin/helloserver"] diff --git a/test_container/Dockerfile.builder b/test_container/Dockerfile.builder new file mode 100644 index 00000000..7e0ae0b5 --- /dev/null +++ b/test_container/Dockerfile.builder @@ -0,0 +1,172 @@ +FROM alpine:3.7 + +RUN apk add --no-cache \ + bash \ + bzip2 \ + coreutils \ + curl \ + gcc \ + go \ + linux-headers \ + make \ + musl-dev \ + perl \ + tzdata \ + \ +# explicitly using gnupg1 instead of gnupg (which is 2.x) due to an arm32v6 bug (likely related to our arm64v8 hardware) +# SIGILL {si_signo=SIGILL, si_code=ILL_ILLOPC, si_addr=0xf7b79394} --- +# (in dirmngr) + gnupg1 + +# pub 1024D/ACC9965B 2006-12-12 +# Key fingerprint = C9E9 416F 76E6 10DB D09D 040F 47B7 0C55 ACC9 965B +# uid Denis Vlasenko +# sub 1024g/2C766641 2006-12-12 +RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys C9E9416F76E610DBD09D040F47B70C55ACC9965B + +ENV BUSYBOX_VERSION 1.28.0 + +RUN set -ex; \ + tarball="busybox-${BUSYBOX_VERSION}.tar.bz2"; \ + curl -fL -o busybox.tar.bz2 "https://busybox.net/downloads/$tarball"; \ + curl -fL -o busybox.tar.bz2.sign "https://busybox.net/downloads/$tarball.sign"; \ + gpg --batch --decrypt --output busybox.tar.bz2.txt busybox.tar.bz2.sign; \ + awk '$1 == "SHA1:" && $2 ~ /^[0-9a-f]+$/ && $3 == "'"$tarball"'" { print $2, "*busybox.tar.bz2" }' busybox.tar.bz2.txt > busybox.tar.bz2.sha1; \ + test -s busybox.tar.bz2.sha1; \ + sha1sum -c busybox.tar.bz2.sha1; \ + mkdir -p /usr/src/busybox; \ + tar -xf busybox.tar.bz2 -C /usr/src/busybox --strip-components 1; \ + rm busybox.tar.bz2* + +WORKDIR /usr/src/busybox + +# https://www.mail-archive.com/toybox@lists.landley.net/msg02528.html +# https://www.mail-archive.com/toybox@lists.landley.net/msg02526.html +RUN sed -i 's/^struct kconf_id \*$/static &/g' scripts/kconfig/zconf.hash.c_shipped + +# CONFIG_LAST_SUPPORTED_WCHAR: see https://github.com/docker-library/busybox/issues/13 (UTF-8 input) +# see http://wiki.musl-libc.org/wiki/Building_Busybox +RUN set -ex; \ + \ + setConfs=' \ + CONFIG_FEATURE_SUID=y \ + CONFIG_AR=y \ + CONFIG_FEATURE_AR_CREATE=y \ + CONFIG_FEATURE_AR_LONG_FILENAMES=y \ + CONFIG_LAST_SUPPORTED_WCHAR=0 \ + CONFIG_STATIC=y \ + '; \ + \ + unsetConfs=' \ + CONFIG_FEATURE_SYNC_FANCY \ + \ + CONFIG_FEATURE_HAVE_RPC \ + CONFIG_FEATURE_INETD_RPC \ + CONFIG_FEATURE_UTMP \ + CONFIG_FEATURE_WTMP \ + '; \ + \ + make defconfig; \ + \ + for conf in $unsetConfs; do \ + sed -i \ + -e "s!^$conf=.*\$!# $conf is not set!" \ + .config; \ + done; \ + \ + for confV in $setConfs; do \ + conf="${confV%=*}"; \ + sed -i \ + -e "s!^$conf=.*\$!$confV!" \ + -e "s!^# $conf is not set\$!$confV!" \ + .config; \ + if ! grep -q "^$confV\$" .config; then \ + echo "$confV" >> .config; \ + fi; \ + done; \ + \ + make oldconfig; \ + \ +# trust, but verify + for conf in $unsetConfs; do \ + ! grep -q "^$conf=" .config; \ + done; \ + for confV in $setConfs; do \ + grep -q "^$confV\$" .config; \ + done; + +RUN set -ex \ + && make -j "$(nproc)" \ + busybox \ + && ./busybox --help \ + && mkdir -p rootfs/bin \ + && ln -vL busybox rootfs/bin/ \ + && chroot rootfs /bin/busybox --install /bin + +# grab a simplified getconf port from Alpine we can statically compile +RUN set -x \ + && aportsVersion="v$(cat /etc/alpine-release)" \ + && curl -fsSL \ + "http://git.alpinelinux.org/cgit/aports/plain/main/musl/getconf.c?h=${aportsVersion}" \ + -o /usr/src/getconf.c \ + && gcc -o rootfs/bin/getconf -static -Os /usr/src/getconf.c \ + && chroot rootfs /bin/getconf _NPROCESSORS_ONLN + +# download a few extra files from buildroot (/etc/passwd, etc) +RUN set -ex; \ + buildrootVersion='2017.11.1'; \ + mkdir -p rootfs/etc; \ + for f in passwd shadow group; do \ + curl -fL -o "rootfs/etc/$f" "https://git.busybox.net/buildroot/plain/system/skeleton/etc/$f?id=$buildrootVersion"; \ + done; \ +# set expected permissions, etc too (https://git.busybox.net/buildroot/tree/system/device_table.txt) + curl -fL -o buildroot-device-table.txt "https://git.busybox.net/buildroot/plain/system/device_table.txt?id=$buildrootVersion"; \ + awk ' \ + !/^#/ { \ + if ($2 != "d" && $2 != "f") { \ + printf "error: unknown type \"%s\" encountered in line %d: %s\n", $2, NR, $0 > "/dev/stderr"; \ + exit 1; \ + } \ + sub(/^\/?/, "rootfs/", $1); \ + if ($2 == "d") { \ + printf "mkdir -p %s\n", $1; \ + } \ + printf "chmod %s %s\n", $3, $1; \ + } \ + ' buildroot-device-table.txt | sh -eux; \ + rm buildroot-device-table.txt + +# create missing home directories +RUN set -ex \ + && cd rootfs \ + && for userHome in $(awk -F ':' '{ print $3 ":" $4 "=" $6 }' etc/passwd); do \ + user="${userHome%%=*}"; \ + home="${userHome#*=}"; \ + home="./${home#/}"; \ + if [ ! -d "$home" ]; then \ + mkdir -p "$home"; \ + chown "$user" "$home"; \ + chmod 755 "$home"; \ + fi; \ + done + +# test and make sure it works +RUN chroot rootfs /bin/sh -xec 'true' + +# ensure correct timezone (UTC) +RUN set -ex; \ + ln -vL /usr/share/zoneinfo/UTC rootfs/etc/localtime; \ + [ "$(chroot rootfs date +%Z)" = 'UTC' ] + +# test and make sure DNS works too +RUN cp -L /etc/resolv.conf rootfs/etc/ \ + && chroot rootfs /bin/sh -xec 'nslookup google.com' \ +&& rm rootfs/etc/resolv.conf + +ADD ./curl_builder.sh . +RUN mkdir -p rootfs/usr/bin; \ + ./curl_builder.sh; \ + cp /usr/local/bin/curl rootfs/usr/bin/curl + +ADD ./server.go . +RUN go build -ldflags "-linkmode external -extldflags -static" -o rootfs/usr/bin/helloserver server.go diff --git a/test_container/README.rst b/test_container/README.rst new file mode 100644 index 00000000..82e88b06 --- /dev/null +++ b/test_container/README.rst @@ -0,0 +1,66 @@ +====================================== +Kuryr Testing container infrastructure +====================================== + +This directory is the official source for building Docker hub's kuryr/demo +images. + +The build consists on two parts: + +Builder container +----------------- + +The builder container is based on the musl compiled Alpine distribution. In the +process of building the image, it downloads and compiles: + +* busybox +* musl +* curl and its dependencies + +It also includes golang so that we can use it in our test web server: + +* server.go + +Everything that is to be included in the kuryr/demo image is put in:: + + /usr/src/busybox/rootfs + +The reason for this is that this build is based on Docker's busybox build +system and the rootfs won't have any library, so all you want to add must be +statically compiled there. + +kuryr/demo container +-------------------- + +This is the actual container used in the tests. It includes: + +* Busybox: It gives us a very lightweight userspace that provides things like + the ip command, vi, etc. +* curl: Useful for testing HTTP/HTTPS connectivity to the API and other + services. +* helloserver: An HTTP server that binds to 8080 and prints out a message + that includes the hostname, so it can be used to see which pod replies to a + service request. + +When and how to build +--------------------- + +builder container + kuryr/demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You should only need to build the whole set if you want to change the library +app version of something in kuryr/demo or add another tool like bind9 dig. + +The way to do this is:: + + sudo ./mkrootfs.sh + + +kuryr/demo +~~~~~~~~~~ + +Everytime you want to run the tests, you should build the kuryr/demo container +locally to avoid pulls from dockerhub to make sure you run the latest +authoritative version. + +Note that the kuryr-tempest-plugin devstack will build it for you. diff --git a/test_container/curl_builder.sh b/test_container/curl_builder.sh new file mode 100755 index 00000000..b910b819 --- /dev/null +++ b/test_container/curl_builder.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +# Names of latest versions of each package +export VERSION_MUSL=musl-1.1.18 +export VERSION_ZLIB=zlib-1.2.11 +export VERSION_LIBRESSL=libressl-2.6.3 +export VERSION_CURL=curl-7.58.0 + +# URLs to the source directories +export SOURCE_MUSL=http://www.musl-libc.org/releases/ +export SOURCE_LIBRESSL=http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/ +export SOURCE_CURL=https://curl.haxx.se/download/ +export SOURCE_ZLIB=http://zlib.net/ + +# Path to local build +export BUILD_DIR=/tmp/curl-static-libressl/build +# Path for libressl +export STATICLIBSSL="${BUILD_DIR}/${VERSION_LIBRESSL}" + +function setup() { + # create and clean build directory + mkdir -p ${BUILD_DIR} + rm -Rf ${BUILD_DIR}/* + # install build environment tools + apk add linux-headers perl +} + +function download_sources() { + # todo: verify checksum / integrity of downloads! + echo "Download sources" + + pushd ${BUILD_DIR} + + curl -sSLO "${SOURCE_MUSL}${VERSION_MUSL}.tar.gz" + curl -sSLO "${SOURCE_ZLIB}${VERSION_ZLIB}.tar.gz" + curl -sSLO "${SOURCE_LIBRESSL}${VERSION_LIBRESSL}.tar.gz" + curl -sSLO "${SOURCE_CURL}${VERSION_CURL}.tar.gz" + + popd +} + +function extract_sources() { + echo "Extracting sources" + + pushd ${BUILD_DIR} + + tar -xf "${VERSION_MUSL}.tar.gz" + tar -xf "${VERSION_LIBRESSL}.tar.gz" + tar -xf "${VERSION_CURL}.tar.gz" + tar -xf "${VERSION_ZLIB}.tar.gz" + + popd +} + +function compile_musl() { + echo "Configure & build static musl" + + pushd "${BUILD_DIR}/${VERSION_MUSL}" + + make clean + ./configure --prefix=/usr/local --disable-shared + make -j4 + make install +} + +function compile_zlib() { + echo "Configure & build static zlib" + + pushd "${BUILD_DIR}/${VERSION_ZLIB}" + + make clean + ./configure --static --prefix=/usr/local + make -j4 + make install +} + +function compile_libressl() { + echo "Configure & build static libressl" + + pushd "${BUILD_DIR}/${VERSION_LIBRESSL}" + + make clean + ./configure --prefix=/usr/local --enable-shared=no + make -j4 + make install +} + +function compile_curl() { + echo "Configure & Build curl" + + pushd "${BUILD_DIR}/${VERSION_CURL}" + + make clean + + LIBS="-ldl -lpthread" LDFLAGS="-static" CFLAGS="-no-pie" PKG_CONFIG_FLAGS="--static" PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/ ./configure --disable-shared --enable-static + + make -j4 + make install + + popd +} + +echo "Building ${VERSION_CURL} with static ${VERSION_LIBRESSL}, and ${VERSION_ZLIB} ..." + +setup && download_sources && extract_sources && compile_musl && compile_zlib && compile_libressl && compile_curl + +retval=$? +echo "" +if [ $retval -eq 0 ]; then + echo "Your curl binary is located at ${BUILD_DIR}/${VERSION_CURL}/src/curl." + echo "Listing dynamically linked libraries ..." + ldd ${BUILD_DIR}/${VERSION_CURL}/src/curl + echo "" + ${BUILD_DIR}/${VERSION_CURL}/src/curl --version +else + echo "Ooops, build failed. Check output!" +fi diff --git a/test_container/mkrootfs.sh b/test_container/mkrootfs.sh new file mode 100755 index 00000000..42b4bbb3 --- /dev/null +++ b/test_container/mkrootfs.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +BUILDER_NAME=$(uuidgen) +docker build -t kuryr/demo_builder . -f Dockerfile.builder +docker run --name ${BUILDER_NAME} kuryr/demo_builder +docker cp ${BUILDER_NAME}:/usr/src/busybox/rootfs rootfs +docker rm ${BUILDER_NAME} + +# In order for ping and traceroute to work, we need to give suid to busybox +chmod +s rootfs/bin/busybox +tar -J -f rootfs.tar.xz --numeric-owner --exclude='dev/*' -C rootfs -c . +rm -fr rootfs +docker build -t kuryr/demo . -f Dockerfile diff --git a/test_container/rootfs.tar.xz b/test_container/rootfs.tar.xz new file mode 100644 index 00000000..4728073c Binary files /dev/null and b/test_container/rootfs.tar.xz differ diff --git a/test_container/server.go b/test_container/server.go new file mode 100644 index 00000000..a720224b --- /dev/null +++ b/test_container/server.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" +) + +func handler(w http.ResponseWriter, r *http.Request) { + hostname, err := os.Hostname() + log.Println("Received request") + if err == nil { + fmt.Fprintf(w, "%s: HELLO! I AM ALIVE!!!\n", hostname) + } +} + +func main() { + http.HandleFunc("/", handler) + log.Fatal(http.ListenAndServe(":8080", nil)) +}