From d7038e75607d08e33c1a60ce0ed3247bd7099366 Mon Sep 17 00:00:00 2001 From: Manuel Vogel Date: Fri, 9 Feb 2018 20:11:30 +0100 Subject: [PATCH] Feat/swarm 3 acc test infra (#39) * Set up test infrastructure with local registry and custom images. * Updated travis docker version and usage of new test infra. * Made acc tests constantly output test results. * Tmp acc test files are ignored. * Fixed tests with new infra. * Allowing insecure registries for acc tests. Added fallback for v1 registries. * Added private image cleanup after tests. * Refined acc test structure to confirm tf provider standards with make testacc. --- .gitignore | 2 + .travis.yml | 18 ++++++- GNUmakefile | 2 +- docker/data_source_docker_registry_image.go | 32 ++++++++++-- docker/resource_docker_container_test.go | 8 +-- docker/resource_docker_image_test.go | 1 + scripts/runAccTests.sh | 57 +++++++++++++++++++++ scripts/testing/Dockerfile_v1 | 6 +++ scripts/testing/Dockerfile_v2 | 6 +++ scripts/testing/configs.json | 3 ++ scripts/testing/secrets.json | 3 ++ scripts/testing/server.js | 17 ++++++ scripts/testing/server_v2.js | 20 ++++++++ scripts/testing/setup_private_registry.sh | 38 ++++++++++++++ 14 files changed, 201 insertions(+), 12 deletions(-) create mode 100755 scripts/runAccTests.sh create mode 100644 scripts/testing/Dockerfile_v1 create mode 100644 scripts/testing/Dockerfile_v2 create mode 100644 scripts/testing/configs.json create mode 100644 scripts/testing/secrets.json create mode 100644 scripts/testing/server.js create mode 100644 scripts/testing/server_v2.js create mode 100755 scripts/testing/setup_private_registry.sh diff --git a/.gitignore b/.gitignore index 5982d2c6..ae320bf7 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ website/vendor # Test exclusions !command/test-fixtures/**/*.tfstate !command/test-fixtures/**/.terraform/ +scripts/testing/auth +scripts/testing/certs diff --git a/.travis.yml b/.travis.yml index 1ba1993a..86537792 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,18 @@ dist: trusty -sudo: false +sudo: required +services: + - docker language: go go: -- 1.8.1 +- 1.9.1 + +before_install: +- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - +- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" +- sudo apt-get update +- sudo apt-get -y install docker-ce +- docker version +- export TRAVIS="true" install: # This script is used by the Travis build to install a cookie for @@ -11,9 +21,13 @@ install: # See: https://github.com/golang/go/issues/12933 - bash scripts/gogetcookie.sh - go get github.com/kardianos/govendor +- sudo sed 's/DOCKER_OPTS="/DOCKER_OPTS="--insecure-registry=127.0.0.1:5000 /g' -i /etc/default/docker +- sudo cat /etc/default/docker +- sudo service docker restart script: - make test +- make testacc - make vendor-status - make vet diff --git a/GNUmakefile b/GNUmakefile index c9eacb45..f9f5a59f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -12,7 +12,7 @@ test: fmtcheck xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4 testacc: fmtcheck - TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m + @sh -c "'$(CURDIR)/scripts/runAccTests.sh'" vet: @echo "go vet ." diff --git a/docker/data_source_docker_registry_image.go b/docker/data_source_docker_registry_image.go index ee0d3ba9..bd6d6150 100644 --- a/docker/data_source_docker_registry_image.go +++ b/docker/data_source_docker_registry_image.go @@ -1,11 +1,14 @@ package docker import ( + "crypto/tls" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" + "os" + "strconv" "strings" "github.com/hashicorp/terraform/helper/schema" @@ -60,10 +63,13 @@ func dataSourceDockerRegistryImageRead(d *schema.ResourceData, meta interface{}) password = auth.Password } - digest, err := getImageDigest(pullOpts.Registry, pullOpts.Repository, pullOpts.Tag, username, password) + digest, err := getImageDigest(pullOpts.Registry, pullOpts.Repository, pullOpts.Tag, username, password, false) if err != nil { - return fmt.Errorf("Got error when attempting to fetch image version from registry: %s", err) + digest, err = getImageDigest(pullOpts.Registry, pullOpts.Repository, pullOpts.Tag, username, password, true) + if err != nil { + return fmt.Errorf("Got error when attempting to fetch image version from registry: %s", err) + } } d.SetId(digest) @@ -72,11 +78,23 @@ func dataSourceDockerRegistryImageRead(d *schema.ResourceData, meta interface{}) return nil } -func getImageDigest(registry, image, tag, username, password string) (string, error) { +func getImageDigest(registry, image, tag, username, password string, fallback bool) (string, error) { client := http.DefaultClient - req, err := http.NewRequest("GET", "https://"+registry+"/v2/"+image+"/manifests/"+tag, nil) + // Allow insecure registries only for ACC tests + // cuz we don't have a valid certs for this case + if env, okEnv := os.LookupEnv("TF_ACC"); okEnv { + if i, errConv := strconv.Atoi(env); errConv == nil && i >= 1 { + cfg := &tls.Config{ + InsecureSkipVerify: true, + } + client.Transport = &http.Transport{ + TLSClientConfig: cfg, + } + } + } + req, err := http.NewRequest("GET", "https://"+registry+"/v2/"+image+"/manifests/"+tag, nil) if err != nil { return "", fmt.Errorf("Error creating registry request: %s", err) } @@ -87,6 +105,10 @@ func getImageDigest(registry, image, tag, username, password string) (string, er // Set this header so that we get the v2 manifest back from the registry. req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v2+json") + if fallback { + // Fallback to this header if the registry does not support the v2 manifest like gcr.io + req.Header.Set("Accept", "application/vnd.docker.distribution.manifest.v1+prettyjws") + } resp, err := client.Do(req) @@ -153,7 +175,7 @@ func getImageDigest(registry, image, tag, username, password string) (string, er return "", fmt.Errorf("Bad credentials: " + resp.Status) } - // Some unexpected status was given, return an error + // Some unexpected status was given, return an error default: return "", fmt.Errorf("Got bad response from registry: " + resp.Status) } diff --git a/docker/resource_docker_container_test.go b/docker/resource_docker_container_test.go index f7746d97..b992bf60 100644 --- a/docker/resource_docker_container_test.go +++ b/docker/resource_docker_container_test.go @@ -195,12 +195,12 @@ func TestAccDockerContainer_customized(t *testing.T) { return fmt.Errorf("Container does not have correct number of extra host entries, got %d", len(c.HostConfig.ExtraHosts)) } - if c.HostConfig.ExtraHosts[0] != "testhost2:10.0.2.0" { - return fmt.Errorf("Container has incorrect extra host string: %q", c.HostConfig.ExtraHosts[0]) + if c.HostConfig.ExtraHosts[0] != "testhost:10.0.1.0" { + return fmt.Errorf("Container has incorrect extra host string at 0: %q", c.HostConfig.ExtraHosts[0]) } - if c.HostConfig.ExtraHosts[1] != "testhost:10.0.1.0" { - return fmt.Errorf("Container has incorrect extra host string: %q", c.HostConfig.ExtraHosts[1]) + if c.HostConfig.ExtraHosts[1] != "testhost2:10.0.2.0" { + return fmt.Errorf("Container has incorrect extra host string at 1: %q", c.HostConfig.ExtraHosts[1]) } if _, ok := c.NetworkSettings.Networks["test"]; !ok { diff --git a/docker/resource_docker_image_test.go b/docker/resource_docker_image_test.go index faf28121..8d7d50ae 100644 --- a/docker/resource_docker_image_test.go +++ b/docker/resource_docker_image_test.go @@ -195,6 +195,7 @@ data "docker_registry_image" "foo_private" { resource "docker_image" "foo_private" { provider = "docker.private" name = "${data.docker_registry_image.foo_private.name}" + keep_locally = true pull_triggers = ["${data.docker_registry_image.foo_private.sha256_digest}"] } ` diff --git a/scripts/runAccTests.sh b/scripts/runAccTests.sh new file mode 100755 index 00000000..f6415a96 --- /dev/null +++ b/scripts/runAccTests.sh @@ -0,0 +1,57 @@ +#!/bin/bash +set -e + +log() { + echo "" + echo "##################################" + echo "-------> $1" + echo "##################################" +} + +setup() { + export DOCKER_REGISTRY_ADDRESS="127.0.0.1:5000" + export DOCKER_REGISTRY_USER="testuser" + export DOCKER_REGISTRY_PASS="testpwd" + export DOCKER_PRIVATE_IMAGE="127.0.0.1:5000/my-private-service:v1" + sh scripts/testing/setup_private_registry.sh +} + +run() { + # Run the acc test suite + TF_ACC=1 go test ./docker -v -timeout 120m + + # for a single test + # TF_LOG=INFO TF_ACC=1 go test -v github.com/terraform-providers/terraform-provider-docker/docker -run ^TestAccDockerContainer_basic$ -timeout 360s + + # keep the return for the scripts to fail and clean properly + return $? +} + +cleanup() { + unset DOCKER_REGISTRY_ADDRESS DOCKER_REGISTRY_USER DOCKER_REGISTRY_PASS DOCKER_PRIVATE_IMAGE + echo "### unsetted env ###" + rm -f scripts/testing/auth/htpasswd + rm -f scripts/testing/certs/registry_auth.* + echo "### removed auth and certs ###" + docker stop private_registry + echo "### stopped private registry ###" + docker rmi -f $(docker images -aq 127.0.0.1:5000/my-private-service) + echo "### removed my-private-service images ###" + # consider running this manually to clean up the + # updateabe configs and secrets + #docker config rm $(docker config ls -q) + #docker secret rm $(docker secret ls -q) +} + +## main +log "setup" && setup +log "run" && run && echo $? +if [ $? -ne 0 ]; then + log "cleanup" && cleanup + exit 1 +fi +# we only clean on local envs. travis fails from time to time there +# cuz it cannot remove the images +if [ "$TRAVIS" != "true" ]; then + log "cleanup" && cleanup +fi \ No newline at end of file diff --git a/scripts/testing/Dockerfile_v1 b/scripts/testing/Dockerfile_v1 new file mode 100644 index 00000000..80f70de1 --- /dev/null +++ b/scripts/testing/Dockerfile_v1 @@ -0,0 +1,6 @@ +FROM node:6.12.3-slim +EXPOSE 8080 +COPY configs.json . +COPY secrets.json . +COPY server.js . +CMD node server.js diff --git a/scripts/testing/Dockerfile_v2 b/scripts/testing/Dockerfile_v2 new file mode 100644 index 00000000..dbee88c4 --- /dev/null +++ b/scripts/testing/Dockerfile_v2 @@ -0,0 +1,6 @@ +FROM node:6.12.3-slim +EXPOSE 8080 +COPY configs.json . +COPY secrets.json . +COPY server_v2.js . +CMD node server_v2.js diff --git a/scripts/testing/configs.json b/scripts/testing/configs.json new file mode 100644 index 00000000..3253c079 --- /dev/null +++ b/scripts/testing/configs.json @@ -0,0 +1,3 @@ +{ + "prefix": "123" +} \ No newline at end of file diff --git a/scripts/testing/secrets.json b/scripts/testing/secrets.json new file mode 100644 index 00000000..4c3a3a4a --- /dev/null +++ b/scripts/testing/secrets.json @@ -0,0 +1,3 @@ +{ + "key": "QWERTY" +} \ No newline at end of file diff --git a/scripts/testing/server.js b/scripts/testing/server.js new file mode 100644 index 00000000..c080b226 --- /dev/null +++ b/scripts/testing/server.js @@ -0,0 +1,17 @@ +var http = require('http'); +var configs = require('./configs') +var secrets = require('./secrets') + +var handleRequest = function(request, response) { + console.log('Received request for URL: ' + request.url); + + if(request.url === '/health') { + response.writeHead(200); + response.end('ok'); + } else { + response.writeHead(200); + response.end(configs.prefix + ' - Hello World!'); + } +}; +var www = http.createServer(handleRequest); +www.listen(8080); diff --git a/scripts/testing/server_v2.js b/scripts/testing/server_v2.js new file mode 100644 index 00000000..27533b73 --- /dev/null +++ b/scripts/testing/server_v2.js @@ -0,0 +1,20 @@ +var http = require('http'); +var configs = require('./configs') +var secrets = require('./secrets') + +var handleRequest = function(request, response) { + console.log('Received request for URL: ' + request.url); + + if(request.url === '/health') { + response.writeHead(200); + response.end('ok'); + } else if(request.url === '/newroute') { + response.writeHead(200); + response.end('new Route!'); + } else { + response.writeHead(200); + response.end(configs.prefix + ' - Hello World!'); + } +}; +var www = http.createServer(handleRequest); +www.listen(8080); diff --git a/scripts/testing/setup_private_registry.sh b/scripts/testing/setup_private_registry.sh new file mode 100755 index 00000000..d0feaa76 --- /dev/null +++ b/scripts/testing/setup_private_registry.sh @@ -0,0 +1,38 @@ +set -e + +# Create private registry +## Create self signed certs +mkdir -p scripts/testing/certs +openssl req \ + -newkey rsa:2048 \ + -nodes \ + -x509 \ + -days 365 \ + -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=127.0.0.1" \ + -keyout scripts/testing/certs/registry_auth.key \ + -out scripts/testing/certs/registry_auth.crt +## Create auth +mkdir -p scripts/testing/auth +# Start registry +docker run --entrypoint htpasswd registry:2 -Bbn testuser testpwd > scripts/testing/auth/htpasswd +docker run -d -p 5000:5000 --rm --name private_registry \ + -v "$(pwd)"/scripts/testing/auth:/auth \ + -e "REGISTRY_AUTH=htpasswd" \ + -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ + -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \ + -v "$(pwd)"/scripts/testing/certs:/certs \ + -e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry_auth.crt" \ + -e "REGISTRY_HTTP_TLS_KEY=/certs/registry_auth.key" \ + registry:2 +# wait a bit for travis... +sleep 5 +# Login to private registry +docker login -u testuser -p testpwd 127.0.0.1:5000 +# Build private images +docker build -t my-private-service ./scripts/testing -f ./scripts/testing/Dockerfile_v1 +docker tag my-private-service 127.0.0.1:5000/my-private-service:v1 +docker build -t my-private-service ./scripts/testing -f ./scripts/testing/Dockerfile_v2 +docker tag my-private-service 127.0.0.1:5000/my-private-service:v2 +# Push private images into private registry +docker push 127.0.0.1:5000/my-private-service:v1 +docker push 127.0.0.1:5000/my-private-service:v2