test_container: Add non-layered demo test image

Often our tests are based on kuryr/demo. That image used to be based on
python and alpine and was taking a very long time to pull for already
slow tests. This change makes this repository have the authoritative
version of the kuryr/demo build.

The way it is built is just by running mkrootfs.sh. But that only needs
to be run if we change the version of curl or modify server.go.
Otherwise, to run test it just needs:

    docker build -t kuryr/demo . -f Dockerfile

This build will not need to go to Docker Hub for anything so we also
eliminate one source of flakiness.

Depends-On: https://review.openstack.org/#/c/547390/
Change-Id: Iab5799e22c3b353a4f2adbaa280ab4a9904ecd95
Signed-off-by: Antoni Segura Puimedon <antonisp@celebdor.com>
This commit is contained in:
Antoni Segura Puimedon 2018-02-22 18:54:49 +01:00 committed by Daniel Mellado
parent 10a741ec62
commit c7ebfeadc6
No known key found for this signature in database
GPG Key ID: C905561547B09777
9 changed files with 408 additions and 1 deletions

View File

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

View File

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

View File

@ -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"]

View File

@ -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 <vda.linux@googlemail.com>
# 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

66
test_container/README.rst Normal file
View File

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

117
test_container/curl_builder.sh Executable file
View File

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

13
test_container/mkrootfs.sh Executable file
View File

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

Binary file not shown.

21
test_container/server.go Normal file
View File

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