From 470d6f8f85390eced3f48ac4855bfb46c9d071b4 Mon Sep 17 00:00:00 2001 From: Riyaz Faizullabhoy Date: Thu, 31 Mar 2016 14:02:13 -0700 Subject: [PATCH 1/6] Add docker run and build commands to DCT integration test Signed-off-by: Riyaz Faizullabhoy --- misc/dockertest.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/misc/dockertest.py b/misc/dockertest.py index 50a75095a0..80e4265319 100644 --- a/misc/dockertest.py +++ b/misc/dockertest.py @@ -58,6 +58,9 @@ NOTARY_CLIENT = "bin/notary -c cmd/notary/config.json" # Assumes the trust server will be run using compose TRUST_SERVER = "https://notary-server:4443" +# simple dockerfile to test building with trust +DOCKERFILE_FIXTURE = "FROM alpine:latest\nRUN sh" + # ---- setup ---- def download_docker(download_dir="/tmp"): @@ -148,6 +151,7 @@ _DIGEST_REGEX = re.compile(r"\b[dD]igest: sha256:([0-9a-fA-F]+)\b") _SIZE_REGEX = re.compile(r"\bsize: ([0-9]+)\b") _PULL_A_REGEX = re.compile( r"Pull \(\d+ of \d+\): .+:(.+)@sha256:([0-9a-fA-F]+)") +_BUILD_REGEX = re.compile(r"Successfully built ([0-9a-fA-F]+)") def clear_tuf(): @@ -271,6 +275,17 @@ def notary_list(fout, repo): return [line.strip().split() for line in lines[2:]] +def test_build(fout, docker_version): + """ + Build from a simple Dockerfile and ensure it works with DCT enabled + """ + # build + output = run_cmd( + "{0} build - < {1}".format(DOCKERS[docker_version], DOCKERFILE_FIXTURE), fout) + build_result = _BUILD_REGEX.findall(output) + assert len(build_result) >= 0, "build did not succeed" + + def test_pull_a(fout, docker_version, image, expected_tags): """ Pull -A on an image and ensure that all the expected tags are present @@ -335,6 +350,16 @@ def test_push(tempdir, docker_version, image, tag="", allow_push_failure=False, return return_val +def test_run(fout, docker_version): + """ + Runs a simple alpine container to ensure it works with DCT enabled + """ + # run + output = run_cmd( + "{0} run -it --rm alpine:latest echo SUCCESS".format(DOCKERS[docker_version]), fout) + assert "SUCCESS" in output, "run did not succeed" + + def test_docker_version(docker_version, repo_name="", do_after_first_push=None): """ Initialize a repo with one docker version. Test that all other docker @@ -400,6 +425,22 @@ def test_docker_version(docker_version, repo_name="", do_after_first_push=None): result["list"] = "listed expected targets successfully" + with open(os.path.join(tempdir, "build"), 'wb') as fout: + try: + test_build(fout, ver) + except subprocess.CalledProcessError: + result[ver]["build"] = "failed" + else: + result[ver]["build"] = "success" + + with open(os.path.join(tempdir, "run"), 'wb') as fout: + try: + test_run(fout, ver) + except subprocess.CalledProcessError: + result[ver]["run"] = "failed" + else: + result[ver]["run"] = "success" + with open(os.path.join(tempdir, "result.json"), 'wb') as fout: json.dump(result, fout, indent=2) From bc5eef1131aa37ab4229f4a8c87f0607852e0c8b Mon Sep 17 00:00:00 2001 From: Riyaz Faizullabhoy Date: Fri, 1 Apr 2016 09:49:53 -0700 Subject: [PATCH 2/6] Fix key rotate flag, update build and run test to use local images Signed-off-by: Riyaz Faizullabhoy --- misc/dockertest.py | 62 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/misc/dockertest.py b/misc/dockertest.py index 80e4265319..b3f5252f35 100644 --- a/misc/dockertest.py +++ b/misc/dockertest.py @@ -27,14 +27,13 @@ DOCKERS = { "1.8": "docker-1.8.3", "1.9": "docker-1.9.1", "1.10": "docker", - "1.10.2": "docker-1.10.2.RC1", } # delete any of these if you want to specify the docker binaries yourself DOWNLOAD_DOCKERS = { "1.8": ("https://get.docker.com", "docker-1.8.3"), "1.9": ("https://get.docker.com", "docker-1.9.1"), - "1.10": ("https://get.docker.com", "docker-1.10.1") + "1.10": ("https://get.docker.com", "docker-1.10.3") } # please replace with private registry if you want to test against a private @@ -58,9 +57,6 @@ NOTARY_CLIENT = "bin/notary -c cmd/notary/config.json" # Assumes the trust server will be run using compose TRUST_SERVER = "https://notary-server:4443" -# simple dockerfile to test building with trust -DOCKERFILE_FIXTURE = "FROM alpine:latest\nRUN sh" - # ---- setup ---- def download_docker(download_dir="/tmp"): @@ -123,7 +119,6 @@ _TEMPDIR = mkdtemp(prefix="docker-version-test") _TEMP_DOCKER_CONFIG_DIR = os.path.join(_TEMPDIR, "docker-config-dir") _TRUST_DIR = os.path.join(_TEMP_DOCKER_CONFIG_DIR, "trust") - _ENV = os.environ.copy() _ENV.update({ # enable content trust and use our own server @@ -201,6 +196,30 @@ def run_cmd(cmd, fileoutput): fileoutput.write("\n") +def run_cmd_with_input(cmd, input, fileoutput): + """ + Takes a string command and an input to pipe into the command execution, and returns the output even if it fails. + """ + print("$ cat " + input + " | " + cmd) + fileoutput.write("$ cat {0} | {1}\n".format(input, cmd)) + try: + ps = subprocess.Popen(['cat', input], stdout=subprocess.PIPE) + output = subprocess.check_output(cmd.split(), stdin=ps.stdout, stderr=subprocess.STDOUT, + env=_ENV) + except subprocess.CalledProcessError as ex: + print(ex.output) + fileoutput.write(ex.output) + raise + else: + if output: + print(output) + fileoutput.write(output) + return output + finally: + print() + fileoutput.write("\n") + + def rmi(fout, docker_version, image, tag): """ Ensures that an image is no longer available locally to docker. @@ -275,13 +294,20 @@ def notary_list(fout, repo): return [line.strip().split() for line in lines[2:]] -def test_build(fout, docker_version): +def test_build(fout, image, docker_version): """ Build from a simple Dockerfile and ensure it works with DCT enabled """ # build - output = run_cmd( - "{0} build - < {1}".format(DOCKERS[docker_version], DOCKERFILE_FIXTURE), fout) + # simple dockerfile to test building with trust + dockerfile = "FROM {0}:{1}\nRUN sh\n".format(image, docker_version) + tempdir_dockerfile = os.path.join(_TEMPDIR, "Dockerfile") + with open(tempdir_dockerfile, 'wb') as ftemp: + ftemp.write(dockerfile) + + output = run_cmd_with_input( + "{0} build -".format(DOCKERS[docker_version]), tempdir_dockerfile, fout) + build_result = _BUILD_REGEX.findall(output) assert len(build_result) >= 0, "build did not succeed" @@ -350,13 +376,13 @@ def test_push(tempdir, docker_version, image, tag="", allow_push_failure=False, return return_val -def test_run(fout, docker_version): +def test_run(fout, image, docker_version): """ Runs a simple alpine container to ensure it works with DCT enabled """ # run output = run_cmd( - "{0} run -it --rm alpine:latest echo SUCCESS".format(DOCKERS[docker_version]), fout) + "{0} run -it --rm {1}:{2} echo SUCCESS".format(DOCKERS[docker_version], image, docker_version), fout) assert "SUCCESS" in output, "run did not succeed" @@ -427,19 +453,19 @@ def test_docker_version(docker_version, repo_name="", do_after_first_push=None): with open(os.path.join(tempdir, "build"), 'wb') as fout: try: - test_build(fout, ver) + test_build(fout, image, docker_version) except subprocess.CalledProcessError: - result[ver]["build"] = "failed" + result[docker_version]["build"] = "failed" else: - result[ver]["build"] = "success" + result[docker_version]["build"] = "success" with open(os.path.join(tempdir, "run"), 'wb') as fout: try: - test_run(fout, ver) + test_run(fout, image, docker_version) except subprocess.CalledProcessError: - result[ver]["run"] = "failed" + result[docker_version]["run"] = "failed" else: - result[ver]["run"] = "success" + result[docker_version]["run"] = "success" with open(os.path.join(tempdir, "result.json"), 'wb') as fout: json.dump(result, fout, indent=2) @@ -452,7 +478,7 @@ def rotate_to_server_snapshot(fout, image): Uses the notary client to rotate the snapshot key to be server-managed. """ run_cmd( - "{0} -d {1} key rotate {2} -t snapshot -r".format( + "{0} -d {1} key rotate {2} snapshot -r".format( NOTARY_CLIENT, _TRUST_DIR, image), fout) run_cmd( From ce6737d6939c4df570a6df30d7ce53cfcfadea00 Mon Sep 17 00:00:00 2001 From: Riyaz Faizullabhoy Date: Fri, 1 Apr 2016 11:04:15 -0700 Subject: [PATCH 3/6] Clear tuf data, and pass build context directly to docker build Signed-off-by: Riyaz Faizullabhoy --- misc/dockertest.py | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/misc/dockertest.py b/misc/dockertest.py index b3f5252f35..a9f3fabbe1 100644 --- a/misc/dockertest.py +++ b/misc/dockertest.py @@ -196,30 +196,6 @@ def run_cmd(cmd, fileoutput): fileoutput.write("\n") -def run_cmd_with_input(cmd, input, fileoutput): - """ - Takes a string command and an input to pipe into the command execution, and returns the output even if it fails. - """ - print("$ cat " + input + " | " + cmd) - fileoutput.write("$ cat {0} | {1}\n".format(input, cmd)) - try: - ps = subprocess.Popen(['cat', input], stdout=subprocess.PIPE) - output = subprocess.check_output(cmd.split(), stdin=ps.stdout, stderr=subprocess.STDOUT, - env=_ENV) - except subprocess.CalledProcessError as ex: - print(ex.output) - fileoutput.write(ex.output) - raise - else: - if output: - print(output) - fileoutput.write(output) - return output - finally: - print() - fileoutput.write("\n") - - def rmi(fout, docker_version, image, tag): """ Ensures that an image is no longer available locally to docker. @@ -298,6 +274,7 @@ def test_build(fout, image, docker_version): """ Build from a simple Dockerfile and ensure it works with DCT enabled """ + clear_tuf() # build # simple dockerfile to test building with trust dockerfile = "FROM {0}:{1}\nRUN sh\n".format(image, docker_version) @@ -305,8 +282,8 @@ def test_build(fout, image, docker_version): with open(tempdir_dockerfile, 'wb') as ftemp: ftemp.write(dockerfile) - output = run_cmd_with_input( - "{0} build -".format(DOCKERS[docker_version]), tempdir_dockerfile, fout) + output = run_cmd( + "{0} build {1}".format(DOCKERS[docker_version], _TEMPDIR), fout) build_result = _BUILD_REGEX.findall(output) assert len(build_result) >= 0, "build did not succeed" @@ -380,6 +357,7 @@ def test_run(fout, image, docker_version): """ Runs a simple alpine container to ensure it works with DCT enabled """ + clear_tuf() # run output = run_cmd( "{0} run -it --rm {1}:{2} echo SUCCESS".format(DOCKERS[docker_version], image, docker_version), fout) From b00a8641d6b060e85aa103b39b782d5a73dca716 Mon Sep 17 00:00:00 2001 From: Riyaz Faizullabhoy Date: Fri, 1 Apr 2016 21:26:07 -0700 Subject: [PATCH 4/6] allow for configurable notary server Signed-off-by: Riyaz Faizullabhoy --- misc/dockertest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/dockertest.py b/misc/dockertest.py index a9f3fabbe1..9e5986e45b 100644 --- a/misc/dockertest.py +++ b/misc/dockertest.py @@ -54,8 +54,8 @@ DEFAULT_DOCKER_CONFIG = os.path.expanduser("~/.docker") # the root of the notary repo after binaries are built NOTARY_CLIENT = "bin/notary -c cmd/notary/config.json" -# Assumes the trust server will be run using compose -TRUST_SERVER = "https://notary-server:4443" +# Assumes the trust server will be run using compose if DOCKER_CONTENT_TRUST_SERVER is not specified +TRUST_SERVER = os.getenv('DOCKER_CONTENT_TRUST_SERVER', "https://notary-server:4443") # ---- setup ---- From 36ad744e7930f607422c01b39a39cea0c275bd4e Mon Sep 17 00:00:00 2001 From: Riyaz Faizullabhoy Date: Mon, 4 Apr 2016 11:13:32 -0700 Subject: [PATCH 5/6] Also allow for non-localhost notary server for notary client Signed-off-by: Riyaz Faizullabhoy --- misc/dockertest.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/misc/dockertest.py b/misc/dockertest.py index 9e5986e45b..fbfaa42be3 100644 --- a/misc/dockertest.py +++ b/misc/dockertest.py @@ -50,12 +50,17 @@ REPO_PREFIX = "docker_test" # Assumes default docker config dir DEFAULT_DOCKER_CONFIG = os.path.expanduser("~/.docker") +# Assumes the trust server will be run using compose if DOCKER_CONTENT_TRUST_SERVER is not specified +DEFAULT_NOTARY_SERVER = "https://notary-server:4443" +TRUST_SERVER = os.getenv('DOCKER_CONTENT_TRUST_SERVER', DEFAULT_NOTARY_SERVER) + # Assumes the test will be run with `python misc/dockertest.py` from # the root of the notary repo after binaries are built -NOTARY_CLIENT = "bin/notary -c cmd/notary/config.json" - -# Assumes the trust server will be run using compose if DOCKER_CONTENT_TRUST_SERVER is not specified -TRUST_SERVER = os.getenv('DOCKER_CONTENT_TRUST_SERVER', "https://notary-server:4443") +# also overrides the notary server location if need be +if TRUST_SERVER != DEFAULT_NOTARY_SERVER: + NOTARY_CLIENT = "bin/notary -s {0}".format(TRUST_SERVER) +else: + NOTARY_CLIENT = "bin/notary -c cmd/notary/config.json" # ---- setup ---- @@ -100,7 +105,7 @@ def setup(): shutil.copytree(defaulttlsdir, tlsdir) # make sure that the cert is in the right place for local notary - if TRUST_SERVER == "https://notary-server:4443": + if TRUST_SERVER == DEFAULT_NOTARY_SERVER: tlsdir = os.path.join(tlsdir, "notary-server:4443") if not os.path.isdir(tlsdir): try: From 222d76834bc1db612505737b40a3c3ed2a68c1a5 Mon Sep 17 00:00:00 2001 From: Riyaz Faizullabhoy Date: Tue, 5 Apr 2016 10:13:48 -0700 Subject: [PATCH 6/6] Sleep for 1s after push, add comment about certs Signed-off-by: Riyaz Faizullabhoy --- misc/dockertest.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/misc/dockertest.py b/misc/dockertest.py index fbfaa42be3..e7f47e6121 100644 --- a/misc/dockertest.py +++ b/misc/dockertest.py @@ -13,7 +13,7 @@ import re import shutil import subprocess from tempfile import mkdtemp -from time import time +from time import sleep, time import urllib from urlparse import urljoin @@ -52,6 +52,11 @@ DEFAULT_DOCKER_CONFIG = os.path.expanduser("~/.docker") # Assumes the trust server will be run using compose if DOCKER_CONTENT_TRUST_SERVER is not specified DEFAULT_NOTARY_SERVER = "https://notary-server:4443" + +# please enter a custom trust server location if you do not wish to use a local +# docker-compose instantiation. If testing against Docker Hub's notary server or +# another trust server, please also ensure that this script does not pick up incorrect TLS +# certificates from ~/.notary/config.json by default TRUST_SERVER = os.getenv('DOCKER_CONTENT_TRUST_SERVER', DEFAULT_NOTARY_SERVER) # Assumes the test will be run with `python misc/dockertest.py` from @@ -254,6 +259,9 @@ def push(fout, docker_version, image, tag): sha = _DIGEST_REGEX.search(output).group(1) size = _SIZE_REGEX.search(output).group(1) + # sleep for 1s after pushing, just to let things propagate :) + time.sleep(1) + # list targets = notary_list(fout, image) for target in targets: