diff --git a/contrib/docker-integration/Dockerfile b/contrib/docker-integration/Dockerfile new file mode 100644 index 000000000..37da40581 --- /dev/null +++ b/contrib/docker-integration/Dockerfile @@ -0,0 +1,46 @@ +FROM debian:jessie + +MAINTAINER Docker Distribution Team + +# compile and runtime deps +# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + # For DIND + ca-certificates \ + curl \ + iptables \ + procps \ + e2fsprogs \ + xz-utils \ + # For build + build-essential \ + file \ + git \ + net-tools \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Docker +ENV VERSION 1.7.0-rc1 +RUN curl -L -o /usr/local/bin/docker https://test.docker.com/builds/Linux/x86_64/docker-${VERSION} \ + && chmod +x /usr/local/bin/docker + +# Install DIND +RUN curl -L -o /dind https://raw.githubusercontent.com/docker/docker/master/hack/dind \ + && chmod +x /dind + +# Install bats +RUN cd /usr/local/src/ \ + && git clone https://github.com/sstephenson/bats.git \ + && cd bats \ + && ./install.sh /usr/local + +# Install docker-compose +RUN curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose \ + && chmod +x /usr/local/bin/docker-compose + +RUN mkdir -p /go/src/github.com/docker/distribution +WORKDIR /go/src/github.com/docker/distribution/contrib/docker-integration + +VOLUME /var/lib/docker + +ENTRYPOINT ["/dind"] diff --git a/contrib/docker-integration/Makefile b/contrib/docker-integration/Makefile deleted file mode 100644 index 38a595bc4..000000000 --- a/contrib/docker-integration/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -.PHONY: build test - -build: - docker-compose build - -start: build - docker-compose up -d - -stop: - docker-compose stop - -clean: - docker-compose kill - docker-compose rm -f - -install: - sh ./install_certs.sh localhost - sh ./install_certs.sh localregistry - -test: - @echo "!!!!Ensure /etc/hosts entry is updated for localregistry and make install has been run" - sh ./test_docker.sh localregistry - -all: build diff --git a/contrib/docker-integration/README.md b/contrib/docker-integration/README.md index af0779fed..e12bec1ad 100644 --- a/contrib/docker-integration/README.md +++ b/contrib/docker-integration/README.md @@ -1,32 +1,88 @@ -# Docker Registry Multi-Configuration Testing +# Docker Registry Integration Testing -This compose configuration is intended to setup a testing environment for Docker +These integration tests cover interactions between the Docker daemon and the +registry server. All tests are run using the docker cli. + +The compose configuration is intended to setup a testing environment for Docker using multiple registry configurations. These configurations include different combinations of a v1 and v2 registry as well as TLS configurations. -### Limitations +## Running inside of Docker +### Get integration container +The container image to run the integation tests will need to be pulled or built +locally. -Currently this setup is configured to use localhost as the hostname which -limits the ease of testing within Docker since localhost is always treated -as an insecure registry. To treat localhost as secure the Docker code must -be modified. Without localhost as secure, the test cases will not distinguish -between a TLS configuration with a CA and self-signed. +*Building locally* +``` +$ docker build -t distribution/docker-integration . +``` + +### Run script + +Invoke the tests within Docker through the `run.sh` script. + +``` +$ ./run.sh +``` + +Run with aufs driver and tmp volume +**NOTE: Using a volume will prevent multiple runs from needing to +re-pull images** +``` +$ DOCKER_GRAPHDRIVER=aufs DOCKER_VOLUME=/tmp/volume ./run.sh +``` + +### Example developer flow + +These tests are useful for developing both as a registry and docker +core developer. The following setup may be used to do integration +testing between development versions + +Insert into your `.zshrc` or `.bashrc` + +``` +# /usr/lib/docker for Docker-in-Docker +# Set this directory to make each invocation run much faster, without +# the need to repull images. +export DOCKER_VOLUME=$HOME/.docker-test-volume + +# Use overlay for all Docker testing, try aufs if overlay not supported +export DOCKER_GRAPHDRIVER=overlay + +# Name this according to personal preference +function rdtest() { + if [ "$1" != "" ]; then + DOCKER_BINARY=$GOPATH/src/github.com/docker/docker/bundles/$1/binary/docker + if [ ! -f $DOCKER_BINARY ]; then + current_version=`cat $GOPATH/src/github.com/docker/docker/VERSION` + echo "$DOCKER_BINARY does not exist" + echo "Current checked out docker version: $current_version" + echo "Checkout desired version and run 'make binary' from $GOPATH/src/github.com/docker/docker" + return 1 + fi + fi + + $GOPATH/src/github.com/docker/distribution/contrib/docker-integration/run.sh +} +``` + +Run with Docker release version +``` +$ rdtest +``` + +Run using local development version of docker +``` +$ cd $GOPATH/src/github.com/docker/docker +$ make binary +$ rdtest `cat VERSION` +``` + +## Running manually outside of Docker ### Install Docker Compose -1. Open a new terminal on the host with your `distribution` source. - -2. Get the `docker-compose` binary. - - $ sudo wget https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname -s`-`uname -m` -O /usr/local/bin/docker-compose - - This command installs the binary in the `/usr/local/bin` directory. - -3. Add executable permissions to the binary. - - $ sudo chmod +x /usr/local/bin/docker-compose - -## Usage +[Docker Compose Installation Guide](http://docs.docker.com/compose/install/) ### Start compose setup ``` @@ -34,7 +90,8 @@ docker-compose up ``` ### Install Certificates -The certificates must be installed in /etc/docker/cert.d in order to use TLS client auth and use the CA certificate. +The certificates must be installed in /etc/docker/cert.d in order to use TLS +client auth and use the CA certificate. ``` sudo sh ./install_certs.sh ``` @@ -52,6 +109,16 @@ docker push localhost:5441/hello-world # Perform login using user `testuser` and password `passpassword` ``` +### Set /etc/hosts entry +Find the non-localhost ip address of local machine + +### Run bats +Run the bats tests after updating /etc/hosts, installing the certificates, and +running the `docker-compose` script. +``` +bats -p . +``` + ## Configurations Port | V2 | V1 | TLS | Authentication @@ -59,6 +126,7 @@ Port | V2 | V1 | TLS | Authentication 5000 | yes | yes | no | none 5001 | no | yes | no | none 5002 | yes | no | no | none +5011 | no | yes | yes | none 5440 | yes | yes | yes | none 5441 | yes | yes | yes | basic (testuser/passpassword) 5442 | yes | yes | yes | TLS client diff --git a/contrib/docker-integration/run.sh b/contrib/docker-integration/run.sh new file mode 100755 index 000000000..c36c5147d --- /dev/null +++ b/contrib/docker-integration/run.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -e + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" + +# Root directory of Distribution +DISTRIBUTION_ROOT=$(cd ../..; pwd -P) + +volumeMount="" +if [ "$DOCKER_VOLUME" != "" ]; then + volumeMount="-v ${DOCKER_VOLUME}:/var/lib/docker" +fi + +dockerMount="" +if [ "$DOCKER_BINARY" != "" ]; then + dockerMount="-v ${DOCKER_BINARY}:/usr/local/bin/docker" +fi + +# Image containing the integration tests environment. +INTEGRATION_IMAGE=${INTEGRATION_IMAGE:-distribution/docker-integration} + +# Make sure we upgrade the integration environment. +docker pull $INTEGRATION_IMAGE + +# Start the integration tests in a Docker container. +ID=$(docker run -d -t --privileged $volumeMount $dockerMount \ + -v ${DISTRIBUTION_ROOT}:/go/src/github.com/docker/distribution \ + -e "STORAGE_DRIVER=$DOCKER_GRAPHDRIVER" \ + -e "EXEC_DRIVER=$EXEC_DRIVER" \ + ${INTEGRATION_IMAGE} \ + ./test_runner.sh "$@") + +# Clean it up when we exit. +trap "docker rm -f -v $ID > /dev/null" EXIT + +docker logs -f $ID diff --git a/contrib/docker-integration/test_docker.sh b/contrib/docker-integration/test_docker.sh deleted file mode 100644 index e66b65f7b..000000000 --- a/contrib/docker-integration/test_docker.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/sh - -hostname=$1 -if [ "$hostname" = "" ]; then - hostname="localhost" -fi - -docker pull hello-world - -# TLS Configuration chart -# Username/Password: testuser/passpassword -# | ca | client | basic | notes -# 5440 | yes | no | no | Tests CA certificate -# 5441 | yes | no | yes | Tests basic auth over TLS -# 5442 | yes | yes | no | Tests client auth with client CA -# 5443 | yes | yes | no | Tests client auth without client CA -# 5444 | yes | yes | yes | Tests using basic auth + tls auth -# 5445 | no | no | no | Tests insecure using TLS -# 5446 | no | no | yes | Tests sending credentials to server with insecure TLS -# 5447 | no | yes | no | Tests client auth to insecure -# 5448 | yes | no | no | Bad SSL version -docker tag -f hello-world $hostname:5440/hello-world -docker push $hostname:5440/hello-world -if [ $? -ne 0 ]; then - echo "Fail to push" - exit 1 -fi - -docker login -u testuser -p passpassword -e distribution@docker.com $hostname:5441 -if [ $? -ne 0 ]; then - echo "Failed to login" - exit 1 -fi -docker tag -f hello-world $hostname:5441/hello-world -docker push $hostname:5441/hello-world -if [ $? -ne 0 ]; then - echo "Fail to push" - exit 1 -fi - -docker tag -f hello-world $hostname:5442/hello-world -docker push $hostname:5442/hello-world -if [ $? -ne 0 ]; then - echo "Fail to push" - exit 1 -fi - -docker tag -f hello-world $hostname:5443/hello-world -docker push $hostname:5443/hello-world -if [ $? -eq 0 ]; then - echo "Expected failure" - exit 1 -fi - -docker login -u testuser -p passpassword -e distribution@docker.com $hostname:5444 -if [ $? -ne 0 ]; then - echo "Failed to login" - exit 1 -fi -docker tag -f hello-world $hostname:5444/hello-world -docker push $hostname:5444/hello-world -if [ $? -ne 0 ]; then - echo "Fail to push" - exit 1 -fi - -docker tag -f hello-world $hostname:5445/hello-world -docker push $hostname:5445/hello-world -if [ $? -eq 0 ]; then - echo "Expected failure with insecure registry" - exit 1 -fi - -docker login -u testuser -p passpassword -e distribution@docker.com $hostname:5446 -if [ $? -ne 0 ]; then - echo "Failed to login" - exit 1 -fi -docker tag -f hello-world $hostname:5446/hello-world -docker push $hostname:5446/hello-world -if [ $? -eq 0 ]; then - echo "Expected failure with insecure registry" - exit 1 -fi - -docker tag -f hello-world $hostname:5447/hello-world -docker push $hostname:5447/hello-world -if [ $? -eq 0 ]; then - echo "Expected failure with insecure registry" - exit 1 -fi - -docker tag -f hello-world $hostname:5448/hello-world -docker push $hostname:5448/hello-world -if [ $? -eq 0 ]; then - echo "Expected failure contacting with sslv3" - exit 1 -fi diff --git a/contrib/docker-integration/test_runner.sh b/contrib/docker-integration/test_runner.sh new file mode 100755 index 000000000..2c958c5eb --- /dev/null +++ b/contrib/docker-integration/test_runner.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -e + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" + +# Load the helpers. +#. helpers.bash + +TESTS=${@:-.} + +# Drivers to use for Docker engines the tests are going to create. +STORAGE_DRIVER=${STORAGE_DRIVER:-overlay} +EXEC_DRIVER=${EXEC_DRIVER:-native} + + +function execute() { + >&2 echo "++ $@" + eval "$@" +} + +# Set IP address in /etc/hosts for localregistry +IP=$(ifconfig eth0|grep "inet addr:"| cut -d: -f2 | awk '{ print $1}') +execute echo "$IP localregistry" >> /etc/hosts + +# Setup certificates +execute sh install_certs.sh localregistry + +# Start the docker engine. +execute docker --daemon --log-level=panic \ + --storage-driver="$STORAGE_DRIVER" --exec-driver="$EXEC_DRIVER" & +DOCKER_PID=$! + +# Wait for it to become reachable. +tries=10 +until docker version &> /dev/null; do + (( tries-- )) + if [ $tries -le 0 ]; then + echo >&2 "error: daemon failed to start" + exit 1 + fi + sleep 1 +done + +execute time docker-compose build + +execute docker-compose up -d + +# Run the tests. +execute time bats -p $TESTS + diff --git a/contrib/docker-integration/tls.bats b/contrib/docker-integration/tls.bats new file mode 100644 index 000000000..20358b723 --- /dev/null +++ b/contrib/docker-integration/tls.bats @@ -0,0 +1,96 @@ +# Registry host name, should be set to non-localhost address and match +# DNS name in nginx/ssl certificates and what is installed in /etc/docker/cert.d +hostname="localregistry" + +image="hello-world:latest" + +# Login information, should match values in nginx/test.passwd +user="testuser" +password="passpassword" +email="distribution@docker.com" + +function setup() { + docker pull $image +} + +# has_digest enforces the last output line is "Digest: sha256:..." +# the input is the name of the array containing the output lines +function has_digest() { + name=$1[@] + lines=("${!name}") + length=${#lines[@]} + digest_idx=$((length-1)) + value=${lines[$digest_idx]} + result=$(echo "$value"|cut -d':' -f1,2) + [ "$result" = "Digest: sha256" ] +} + +function login() { + run docker login -u $user -p $password -e $email $1 + [ "$status" -eq 0 ] + # First line is WARNING about credential save + [ "${lines[1]}" = "Login Succeeded" ] +} + +@test "Test valid certificates" { + docker tag -f $image $hostname:5440/$image + run docker push $hostname:5440/$image + [ "$status" -eq 0 ] + has_digest lines +} + +@test "Test basic auth" { + login $hostname:5441 + docker tag -f $image $hostname:5441/$image + run docker push $hostname:5441/$image + [ "$status" -eq 0 ] + has_digest lines +} + +@test "Test TLS client auth" { + docker tag -f $image $hostname:5442/$image + run docker push $hostname:5442/$image + [ "$status" -eq 0 ] + has_digest lines +} + +@test "Test TLS client with invalid certificate authority fails" { + docker tag -f $image $hostname:5443/$image + run docker push $hostname:5443/$image + [ "$status" -ne 0 ] +} + +@test "Test basic auth with TLS client auth" { + login $hostname:5444 + docker tag -f $image $hostname:5444/$image + run docker push $hostname:5444/$image + [ "$status" -eq 0 ] + has_digest lines +} + +@test "Test unknown certificate authority fails" { + docker tag -f $image $hostname:5445/$image + run docker push $hostname:5445/$image + [ "$status" -ne 0 ] +} + +@test "Test basic auth with unknown certificate authority fails" { + run login $hostname:5446 + [ "$status" -ne 0 ] + docker tag -f $image $hostname:5446/$image + run docker push $hostname:5446/$image + [ "$status" -ne 0 ] +} + +@test "Test TLS client auth to server with unknown certificate authority fails" { + docker tag -f $image $hostname:5447/$image + run docker push $hostname:5447/$image + [ "$status" -ne 0 ] +} + +@test "Test failure to connect to server fails to fallback to SSLv3" { + docker tag -f $image $hostname:5448/$image + run docker push $hostname:5448/$image + [ "$status" -ne 0 ] +} +