diff --git a/test/clients/client.py b/test/clients/client.py index 3b1698ac2..62112b062 100644 --- a/test/clients/client.py +++ b/test/clients/client.py @@ -13,7 +13,7 @@ class Client(object): """ Client defines the interface for all clients being tested. """ @abstractmethod - def setup_client(self, registry_host): + def setup_client(self, registry_host, verify_tls): """ Returns the commands necessary to setup the client inside the VM. """ @@ -47,18 +47,21 @@ class Client(object): class DockerClient(Client): - def __init__(self, requires_v1=False): + def __init__(self, requires_v1=False, requires_email=False): self.requires_v1 = requires_v1 + self.requires_email = requires_email - 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') + def setup_client(self, registry_host, verify_tls): + if not verify_tls: + 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 Command(cp_command) + yield Command('sudo systemctl daemon-reload') - 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: @@ -73,8 +76,13 @@ class DockerClient(Client): yield Command('docker version') def login(self, registry_host, username, password): - yield Command('docker login --username=%s --password=%s %s' % - (username, password, registry_host)) + email_param = "" + if self.requires_email: + # cli will block forever if email is not set for version under 1.10.3 + email_param = "--email=none " + + yield Command('docker login --username=%s --password=%s %s %s' % + (username, password, email_param, registry_host)) def push(self, registry_host, namespace, name): yield Command('docker push %s/%s/%s' % (registry_host, namespace, name)) @@ -92,8 +100,12 @@ class DockerClient(Client): class PodmanClient(Client): - def setup_client(self, registry_host): + def __init__(self): + self.verify_tls = False + + def setup_client(self, registry_host, verify_tls): yield FileCopy('Dockerfile.test', '/home/vagrant/Dockerfile') + self.verify_tls = verify_tls def populate_test_image(self, registry_host, namespace, name): yield Command('sudo podman build -t %s/%s/%s /home/vagrant/' % (registry_host, namespace, name)) @@ -102,18 +114,18 @@ class PodmanClient(Client): 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)) + yield Command('sudo podman login --tls-verify=%s --username=%s --password=%s %s' % + (self.verify_tls, 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)) + yield Command('sudo podman push --tls-verify=%s %s/%s/%s' % (self.verify_tls, 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)) + yield Command('sudo podman pull --tls-verify=%s %s/%s/%s' % (self.verify_tls, 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 5fb98584e..26a4719aa 100644 --- a/test/clients/clients_test.py +++ b/test/clients/clients_test.py @@ -11,8 +11,9 @@ from termcolor import colored 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") + return "".join(ch for ch in unicode(s, 'utf-8') if unicodedata.category(ch)[0] != "C") # These tuples are the box&version and the client to use. @@ -28,26 +29,27 @@ BOXES = [ ("kleesc/coreos --box-version=1235.6.0", DockerClient()), # docker 1.12.3 ("kleesc/coreos --box-version=1185.5.0", DockerClient()), # docker 1.11.2 - ("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 + ("kleesc/coreos --box-version=1122.3.0", DockerClient(requires_email=True)), # docker 1.10.3 + ("kleesc/coreos --box-version=899.17.0", DockerClient(requires_email=True)), # docker 1.9.1 + ("kleesc/coreos --box-version=835.13.0", DockerClient(requires_email=True)), # docker 1.8.3 + ("kleesc/coreos --box-version=766.5.0", DockerClient(requires_email=True)), # docker 1.7.1 + ("kleesc/coreos --box-version=717.3.0", DockerClient(requires_email=True)), # docker 1.6.2 + ("kleesc/coreos --box-version=647.2.0", DockerClient(requires_email=True)), # docker 1.5.0 + ("kleesc/coreos --box-version=557.2.0", DockerClient(requires_email=True)), # docker 1.4.1 + ("kleesc/coreos --box-version=522.6.0", DockerClient(requires_email=True)), # docker 1.3.3 - ("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=1.3.7", DockerClient(requires_email=True)), # docker 1.3.2 + ("yungsang/coreos --box-version=1.2.9", DockerClient(requires_email=True)), # docker 1.2.0 + ("yungsang/coreos --box-version=1.1.5", DockerClient(requires_email=True)), # docker 1.1.2 + ("yungsang/coreos --box-version=1.0.0", DockerClient(requires_email=True)), # docker 1.0.1 + ("yungsang/coreos --box-version=0.9.10", DockerClient(requires_email=True)), # docker 1.0.0 + ("yungsang/coreos --box-version=0.9.6", DockerClient(requires_email=True)), # 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)), + ("yungsang/coreos --box-version=0.9.1", DockerClient(requires_v1=True)), # docker 0.10.0 + ("yungsang/coreos --box-version=0.3.1", DockerClient(requires_v1=True)), ] + def _check_vagrant(): vagrant_command = 'vagrant' vagrant = any(os.access(os.path.join(path, vagrant_command), os.X_OK) @@ -56,6 +58,28 @@ def _check_vagrant(): return (vagrant, 'vagrant-scp' in vagrant_plugins) +def _load_ca(box, ca_cert): + if 'coreos' in box: + yield FileCopy(ca_cert, '/home/core/ca.pem') + yield Command('sudo cp /home/core/ca.pem /etc/ssl/certs/ca.pem') + yield Command('sudo update-ca-certificates') + yield Command('sudo systemctl daemon-reload') + elif 'centos' in box: + yield FileCopy(ca_cert, '/home/vagrant/ca.pem') + yield Command('sudo cp /home/vagrant/ca.pem /etc/pki/ca-trust/source/anchors/') + yield Command('sudo update-ca-trust enable') + yield Command('sudo update-ca-trust extract') + else: + raise Exception("unknown box for loading CA cert") + + +# extra steps to initialize the system +def _init_system(box): + if 'coreos' in box: + # disable the update-engine so that it's easier to debug + yield Command('sudo systemctl stop update-engine') + + class CommandFailedException(Exception): pass @@ -122,7 +146,12 @@ def _run_and_wait(command, error_allowed=False): result = process.wait() outputter.stop() - if result != 0 and not error_allowed: + failed = result != 0 and not error_allowed + # vagrant scp doesn't report auth failure as non-0 exit + failed = failed or (len(command) > 1 and command[1] == 'scp' and + 'authentication failures' in output) + + if failed: print colored('>>> Command `%s` Failed:' % command, 'red') print output raise CommandFailedException() @@ -134,17 +163,47 @@ def _indent(text, amount): return ''.join((' ' * amount) + line for line in text.splitlines(True)) +def scp_to_vagrant(source, destination): + '''scp_to_vagrant copies the file from source to destination in the default + vagrant box without vagrant scp, which may fail on some coreos boxes. + ''' + config = _run_and_wait(['vagrant', 'ssh-config']) + config_lines = config.split('\n') + params = ['scp'] + for i in xrange(len(config_lines)): + if 'Host default' in config_lines[i]: + config_i = i + 1 + while config_i < len(config_lines): + if config_lines[config_i].startswith(' '): + params += ['-o', '='.join(config_lines[config_i].split())] + else: + break + + config_i += 1 + break + + params.append(source) + params.append('core@localhost:' + destination) + return _run_and_wait(params) + + 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]) + try: + last_result = _run_and_wait(['vagrant', 'scp', command.source, command.destination]) + except CommandFailedException as e: + print colored('>>> Retry FileCopy command without vagrant scp...', 'red') + # sometimes the vagrant scp fails because of invalid ssh configuration. + last_result = scp_to_vagrant(command.source, command.destination) return last_result -def _run_box(box, client, registry): + +def _run_box(box, client, registry, ca_cert): vagrant, vagrant_scp = _check_vagrant() if not vagrant: print("vagrant command not found") @@ -166,8 +225,15 @@ def _run_box(box, client, registry): _run_and_wait(['vagrant', 'init'] + box.split(' ')) _run_and_wait(['vagrant', 'up', '--provider', 'virtualbox']) - print colored('>>> Setting up runtime', 'yellow') - _run_commands(client.setup_client(registry)) + _run_commands(_init_system(box)) + + if ca_cert: + print colored('>>> Setting up runtime with cert ' + ca_cert, 'yellow') + _run_commands(_load_ca(box, ca_cert)) + _run_commands(client.setup_client(registry, verify_tls=True)) + else: + print colored('>>> Setting up runtime with insecure HTTP(S)', 'yellow') + _run_commands(client.setup_client(registry, verify_tls=False)) print colored('>>> Client version', 'cyan') runtime_version = _run_commands(client.print_version()) @@ -197,14 +263,16 @@ def _run_box(box, client, registry): print colored('>>> Successfully tested box %s' % box, 'green') print "" -def test_clients(registry='10.0.2.2:5000'): + +def test_clients(registry='10.0.2.2:5000', ca_cert=''): print colored('>>> Running against registry ', attrs=['bold']) + colored(registry, 'cyan') for box, client in BOXES: try: - _run_box(box, client, registry) + _run_box(box, client, registry, ca_cert) except CommandFailedException: sys.exit(-1) if __name__ == "__main__": - test_clients(sys.argv[1] if len(sys.argv) > 1 else '10.0.2.2:5000') + test_clients(sys.argv[1] if len(sys.argv) > 1 else '10.0.2.2:5000', sys.argv[2] + if len(sys.argv) > 2 else '')