From 9e6f2c5be9d5ffb71daa3d1479465bec46902395 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Fri, 25 Jan 2019 17:28:35 -0500 Subject: [PATCH 1/2] Interface out the clients test in prep for supporting other kinds of clients --- .../50-insecure-registry.conf | 0 .../Dockerfile.test | 1 + test/clients/__init__.py | 0 test/clients/client.py | 89 +++++++++++++ .../clients_test.py | 121 +++++++++--------- 5 files changed, 153 insertions(+), 58 deletions(-) rename test/{dockerclients => clients}/50-insecure-registry.conf (100%) rename test/{dockerclients => clients}/Dockerfile.test (75%) create mode 100644 test/clients/__init__.py create mode 100644 test/clients/client.py rename test/{dockerclients => clients}/clients_test.py (50%) diff --git a/test/dockerclients/50-insecure-registry.conf b/test/clients/50-insecure-registry.conf similarity index 100% rename from test/dockerclients/50-insecure-registry.conf rename to test/clients/50-insecure-registry.conf diff --git a/test/dockerclients/Dockerfile.test b/test/clients/Dockerfile.test similarity index 75% rename from test/dockerclients/Dockerfile.test rename to test/clients/Dockerfile.test index b5728c23e..f773e18ff 100644 --- a/test/dockerclients/Dockerfile.test +++ b/test/clients/Dockerfile.test @@ -2,3 +2,4 @@ FROM quay.io/quay/busybox RUN date > somefile RUN date +%s%N > anotherfile RUN date +"%T.%N" > thirdfile +RUN echo "testing 123" > testfile diff --git a/test/clients/__init__.py b/test/clients/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/clients/client.py b/test/clients/client.py new file mode 100644 index 000000000..27ddf9070 --- /dev/null +++ b/test/clients/client.py @@ -0,0 +1,89 @@ +from abc import ABCMeta, abstractmethod +from collections import namedtuple +from six import add_metaclass + +Command = namedtuple('Command', ['command']) +FileCopy = namedtuple('FileCopy', ['source', 'destination']) + + +@add_metaclass(ABCMeta) +class Client(object): + """ Client defines the interface for all clients being tested. """ + + @abstractmethod + def setup_client(self, registry_host): + """ Returns the commands necessary to setup the client inside the VM. + """ + + @abstractmethod + def populate_test_image(self, registry_host, namespace, name): + """ Returns the commands necessary to populate the test image. """ + + @abstractmethod + def print_version(self): + """ Returns the commands necessary to print the version of the client. """ + + @abstractmethod + def login(self, registry_host, username, password): + """ Returns the commands necessary to login. """ + + @abstractmethod + def push(self, registry_host, namespace, name): + """ Returns the commands necessary to test pushing. """ + + @abstractmethod + def pre_pull_cleanup(self, registry_host, namespace, name): + """ Returns the commands necessary to cleanup before pulling. """ + + @abstractmethod + def pull(self, registry_host, namespace, name): + """ Returns the commands necessary to test pulling. """ + + @abstractmethod + def verify(self, registry_host, namespace, name): + """ Returns the commands necessary to verify the pulled image. """ + + +class DockerClient(Client): + def __init__(self, requires_v1=False): + self.requires_v1 = requires_v1 + + def setup_client(self, registry_host): + cp_command = ('sudo cp /home/core/50-insecure-registry.conf ' + + '/etc/systemd/system/docker.service.d/50-insecure-registry.conf') + + yield Command('sudo mkdir -p /etc/systemd/system/docker.service.d/') + yield FileCopy('50-insecure-registry.conf', '/home/core') + yield FileCopy('Dockerfile.test', '/home/core/Dockerfile') + yield Command(cp_command) + yield Command('sudo systemctl daemon-reload') + + def populate_test_image(self, registry_host, namespace, name): + if self.requires_v1: + # These versions of Docker don't support the new TLS cert on quay.io, so we need to pull + # from v1.quay.io and then retag so the build works. + yield Command('docker pull v1.quay.io/quay/busybox') + yield Command('docker tag v1.quay.io/quay/busybox quay.io/quay/busybox') + + yield Command('docker build -t %s/%s/%s .' % (registry_host, namespace, name)) + + def print_version(self): + yield Command('docker version') + + def login(self, registry_host, username, password): + yield Command('docker login --username=%s --password=%s %s' % + (username, password, registry_host)) + + def push(self, registry_host, namespace, name): + yield Command('docker push %s/%s/%s' % (registry_host, namespace, name)) + + def pre_pull_cleanup(self, registry_host, namespace, name): + prefix = 'v1.' if self.requires_v1 else '' + yield Command('docker rmi -f %s/%s/%s' % (registry_host, namespace, name)) + yield Command('docker rmi -f %squay.io/quay/busybox' % prefix) + + def pull(self, registry_host, namespace, name): + yield Command('docker pull %s/%s/%s' % (registry_host, namespace, name)) + + def verify(self, registry_host, namespace, name): + yield Command('docker run %s/%s/%s echo testfile' % (registry_host, namespace, name)) diff --git a/test/dockerclients/clients_test.py b/test/clients/clients_test.py similarity index 50% rename from test/dockerclients/clients_test.py rename to test/clients/clients_test.py index af2ab46d1..00805c58d 100644 --- a/test/dockerclients/clients_test.py +++ b/test/clients/clients_test.py @@ -9,45 +9,48 @@ from threading import Thread from termcolor import colored +from test.clients.client import DockerClient, Command, FileCopy + def remove_control_characters(s): return "".join(ch for ch in unicode(s) if unicodedata.category(ch)[0]!="C") -# These tuples are the box&version and whether it requires V1 registry protocol + +# These tuples are the box&version and the client to use. BOXES = [ - ("kleesc/coreos --box-version=1911.4.0", False), # docker 18.06.1 - ("kleesc/coreos --box-version=1800.7.0", False), # docker 18.03.1 - ("kleesc/coreos --box-version=1688.5.3", False), # docker 17.12.1 - ("kleesc/coreos --box-version=1632.3.0", False), # docker 17.09.1 - ("kleesc/coreos --box-version=1576.5.0", False), # docker 17.09.0 - ("kleesc/coreos --box-version=1520.9.0", False), # docker 1.12.6 - ("kleesc/coreos --box-version=1235.6.0", False), # docker 1.12.3 - ("kleesc/coreos --box-version=1185.5.0", False), # docker 1.11.2 - - ("kleesc/coreos --box-version=1122.3.0", False), # docker 1.10.3 - ("kleesc/coreos --box-version=899.17.0", False), # docker 1.9.1 - ("kleesc/coreos --box-version=835.13.0", False), # docker 1.8.3 - ("kleesc/coreos --box-version=766.5.0", False), # docker 1.7.1 - ("kleesc/coreos --box-version=717.3.0", False), # docker 1.6.2 - ("kleesc/coreos --box-version=647.2.0", False), # docker 1.5.0 - ("kleesc/coreos --box-version=557.2.0", False), # docker 1.4.1 - ("kleesc/coreos --box-version=522.6.0", False), # docker 1.3.3 + ("kleesc/coreos --box-version=1911.4.0", DockerClient()), # docker 18.06.1 + ("kleesc/coreos --box-version=1800.7.0", DockerClient()), # docker 18.03.1 + ("kleesc/coreos --box-version=1688.5.3", DockerClient()), # docker 17.12.1 + ("kleesc/coreos --box-version=1632.3.0", DockerClient()), # docker 17.09.1 + ("kleesc/coreos --box-version=1576.5.0", DockerClient()), # docker 17.09.0 + ("kleesc/coreos --box-version=1520.9.0", DockerClient()), # docker 1.12.6 + ("kleesc/coreos --box-version=1235.6.0", DockerClient()), # docker 1.12.3 + ("kleesc/coreos --box-version=1185.5.0", DockerClient()), # docker 1.11.2 - ("yungsang/coreos --box-version=1.3.7", False), # docker 1.3.2 - ("yungsang/coreos --box-version=1.2.9", False), # docker 1.2.0 - ("yungsang/coreos --box-version=1.1.5", False), # docker 1.1.2 - ("yungsang/coreos --box-version=1.0.0", False), # docker 1.0.1 - ("yungsang/coreos --box-version=0.9.10", False), # docker 1.0.0 - ("yungsang/coreos --box-version=0.9.6", False), # docker 0.11.1 + ("kleesc/coreos --box-version=1122.3.0", DockerClient()), # docker 1.10.3 + ("kleesc/coreos --box-version=899.17.0", DockerClient()), # docker 1.9.1 + ("kleesc/coreos --box-version=835.13.0", DockerClient()), # docker 1.8.3 + ("kleesc/coreos --box-version=766.5.0", DockerClient()), # docker 1.7.1 + ("kleesc/coreos --box-version=717.3.0", DockerClient()), # docker 1.6.2 + ("kleesc/coreos --box-version=647.2.0", DockerClient()), # docker 1.5.0 + ("kleesc/coreos --box-version=557.2.0", DockerClient()), # docker 1.4.1 + ("kleesc/coreos --box-version=522.6.0", DockerClient()), # docker 1.3.3 - ("yungsang/coreos --box-version=0.9.1", True), # docker 0.10.0 - ("yungsang/coreos --box-version=0.3.1", True), + ("yungsang/coreos --box-version=1.3.7", DockerClient()), # docker 1.3.2 + ("yungsang/coreos --box-version=1.2.9", DockerClient()), # docker 1.2.0 + ("yungsang/coreos --box-version=1.1.5", DockerClient()), # docker 1.1.2 + ("yungsang/coreos --box-version=1.0.0", DockerClient()), # docker 1.0.1 + ("yungsang/coreos --box-version=0.9.10", DockerClient()), # docker 1.0.0 + ("yungsang/coreos --box-version=0.9.6", DockerClient()), # docker 0.11.1 + + ("yungsang/coreos --box-version=0.9.1", DockerClient(True)), # docker 0.10.0 + ("yungsang/coreos --box-version=0.3.1", DockerClient(True)), ] def _check_vagrant(): vagrant_command = 'vagrant' - vagrant = any(os.access(os.path.join(path, vagrant_command), os.X_OK) for path in os.environ.get('PATH').split(':')) + vagrant = any(os.access(os.path.join(path, vagrant_command), os.X_OK) + for path in os.environ.get('PATH').split(':')) vagrant_plugins = subprocess.check_output([vagrant_command, 'plugin', 'list']) - return (vagrant, 'vagrant-scp' in vagrant_plugins) @@ -128,7 +131,18 @@ def _run_and_wait(command, error_allowed=False): def _indent(text, amount): return ''.join((' ' * amount) + line for line in text.splitlines(True)) -def _run_box(box, requires_v1, registry): + +def _run_commands(commands): + last_result = None + for command in commands: + if isinstance(command, Command): + last_result = _run_and_wait(['vagrant', 'ssh', '-c', command.command]) + else: + last_result = _run_and_wait(['vagrant', 'scp', command.source, command.destination]) + + return last_result + +def _run_box(box, client, registry): vagrant, vagrant_scp = _check_vagrant() if not vagrant: print("vagrant command not found") @@ -138,6 +152,11 @@ def _run_box(box, requires_v1, registry): print("vagrant-scp plugin not installed") return + namespace = 'devtable' + repo_name = 'testrepo%s' % int(time.time()) + username = 'devtable' + password = 'password' + print colored('>>> Box: %s' % box, attrs=['bold']) print colored('>>> Starting box', 'yellow') _run_and_wait(['vagrant', 'destroy', '-f'], error_allowed=True) @@ -145,44 +164,30 @@ def _run_box(box, requires_v1, registry): _run_and_wait(['vagrant', 'init'] + box.split(' ')) _run_and_wait(['vagrant', 'up', '--provider', 'virtualbox']) - print colored('>>> Setting up Docker', 'yellow') - _run_and_wait(['vagrant', 'ssh', '-c', 'sudo mkdir -p /etc/systemd/system/docker.service.d/']) - _run_and_wait(['vagrant', 'scp', '50-insecure-registry.conf', '/home/core']) - _run_and_wait(['vagrant', 'scp', 'Dockerfile.test', '/home/core/Dockerfile']) + print colored('>>> Setting up runtime', 'yellow') + _run_commands(client.setup_client(registry)) - cp_command = ('sudo cp /home/core/50-insecure-registry.conf ' + - '/etc/systemd/system/docker.service.d/50-insecure-registry.conf') - _run_and_wait(['vagrant', 'ssh', '-c', cp_command]) - _run_and_wait(['vagrant', 'ssh', '-c', 'sudo systemctl daemon-reload']) - - print colored('>>> Docker version', 'cyan') - docker_version = _run_and_wait(['vagrant', 'ssh', '-c', 'docker version']) + print colored('>>> Client version', 'cyan') + docker_version = _run_commands(client.print_version()) print _indent(docker_version, 4) - print colored('>>> Building test image', 'yellow') - if requires_v1: - # These versions of Docker don't support the new TLS cert on quay.io, so we need to pull - # from v1.quay.io and then retag so the build works. - _run_and_wait(['vagrant', 'ssh', '-c', 'docker pull v1.quay.io/quay/busybox']) - _run_and_wait(['vagrant', 'ssh', '-c', - 'docker tag v1.quay.io/quay/busybox quay.io/quay/busybox']) - - _run_and_wait(['vagrant', 'ssh', '-c', 'docker build -t %s/devtable/testrepo .' % registry]) + print colored('>>> Populating test image', 'yellow') + _run_commands(client.populate_test_image(registry, namespace, repo_name)) print colored('>>> Testing login', 'cyan') - _run_and_wait(['vagrant', 'ssh', '-c', - 'docker login --username=devtable --password=password %s' % registry]) + _run_commands(client.login(registry, username, password)) print colored('>>> Testing push', 'cyan') - _run_and_wait(['vagrant', 'ssh', '-c', 'docker push %s/devtable/testrepo' % registry]) + _run_commands(client.push(registry, namespace, repo_name)) print colored('>>> Removing all images', 'yellow') - prefix = 'v1.' if requires_v1 else '' - _run_and_wait(['vagrant', 'ssh', '-c', 'docker rmi -f %s/devtable/testrepo' % registry]) - _run_and_wait(['vagrant', 'ssh', '-c', 'docker rmi -f %squay.io/quay/busybox' % prefix]) + _run_commands(client.pre_pull_cleanup(registry, namespace, repo_name)) print colored('>>> Testing pull', 'cyan') - _run_and_wait(['vagrant', 'ssh', '-c', 'docker pull %s/devtable/testrepo' % registry]) + _run_commands(client.pull(registry, namespace, repo_name)) + + print colored('>>> Verifying', 'cyan') + _run_commands(client.verify(registry, namespace, repo_name)) print colored('>>> Tearing down box', 'magenta') _run_and_wait(['vagrant', 'destroy', '-f'], error_allowed=True) @@ -192,9 +197,9 @@ def _run_box(box, requires_v1, registry): def test_clients(registry='10.0.2.2:5000'): print colored('>>> Running against registry ', attrs=['bold']) + colored(registry, 'cyan') - for box, requires_v1 in BOXES: + for box, client in BOXES: try: - _run_box(box, requires_v1, registry) + _run_box(box, client, registry) except CommandFailedException: sys.exit(-1) From 72ef93b73c64c9729fc8966f31fc76ff822577df Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Fri, 25 Jan 2019 17:59:17 -0500 Subject: [PATCH 2/2] Add a podman client test --- test/clients/client.py | 30 ++++++++++++++++++++++++++++++ test/clients/clients_test.py | 8 +++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/test/clients/client.py b/test/clients/client.py index 27ddf9070..3b1698ac2 100644 --- a/test/clients/client.py +++ b/test/clients/client.py @@ -3,6 +3,8 @@ from collections import namedtuple from six import add_metaclass Command = namedtuple('Command', ['command']) + +# NOTE: FileCopy is done via `scp`, instead of `ssh` which is how Command is run. FileCopy = namedtuple('FileCopy', ['source', 'destination']) @@ -87,3 +89,31 @@ class DockerClient(Client): def verify(self, registry_host, namespace, name): yield Command('docker run %s/%s/%s echo testfile' % (registry_host, namespace, name)) + + +class PodmanClient(Client): + def setup_client(self, registry_host): + yield FileCopy('Dockerfile.test', '/home/vagrant/Dockerfile') + + def populate_test_image(self, registry_host, namespace, name): + yield Command('sudo podman build -t %s/%s/%s /home/vagrant/' % (registry_host, namespace, name)) + + def print_version(self): + yield Command('sudo podman version') + + def login(self, registry_host, username, password): + yield Command('sudo podman login --tls-verify=false --username=%s --password=%s %s' % + (username, password, registry_host)) + + def push(self, registry_host, namespace, name): + yield Command('sudo podman push --tls-verify=false %s/%s/%s' % (registry_host, namespace, name)) + + def pre_pull_cleanup(self, registry_host, namespace, name): + yield Command('sudo podman rmi -f %s/%s/%s' % (registry_host, namespace, name)) + yield Command('sudo podman rmi -f quay.io/quay/busybox') + + def pull(self, registry_host, namespace, name): + yield Command('sudo podman pull --tls-verify=false %s/%s/%s' % (registry_host, namespace, name)) + + def verify(self, registry_host, namespace, name): + yield Command('sudo podman run %s/%s/%s echo testfile' % (registry_host, namespace, name)) diff --git a/test/clients/clients_test.py b/test/clients/clients_test.py index 00805c58d..5fb98584e 100644 --- a/test/clients/clients_test.py +++ b/test/clients/clients_test.py @@ -9,7 +9,7 @@ from threading import Thread from termcolor import colored -from test.clients.client import DockerClient, Command, FileCopy +from test.clients.client import DockerClient, PodmanClient, Command, FileCopy def remove_control_characters(s): return "".join(ch for ch in unicode(s) if unicodedata.category(ch)[0]!="C") @@ -17,6 +17,8 @@ def remove_control_characters(s): # These tuples are the box&version and the client to use. BOXES = [ + ("kleesc/centos7-podman --box-version=0.11.1.1", PodmanClient()), # podman 0.11.1.1 + ("kleesc/coreos --box-version=1911.4.0", DockerClient()), # docker 18.06.1 ("kleesc/coreos --box-version=1800.7.0", DockerClient()), # docker 18.03.1 ("kleesc/coreos --box-version=1688.5.3", DockerClient()), # docker 17.12.1 @@ -168,8 +170,8 @@ def _run_box(box, client, registry): _run_commands(client.setup_client(registry)) print colored('>>> Client version', 'cyan') - docker_version = _run_commands(client.print_version()) - print _indent(docker_version, 4) + runtime_version = _run_commands(client.print_version()) + print _indent(runtime_version, 4) print colored('>>> Populating test image', 'yellow') _run_commands(client.populate_test_image(registry, namespace, repo_name))