import subprocess import sys import time import unicodedata from threading import Thread from termcolor import colored 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 BOXES = [ ("AntonioMeireles/coreos-stable --box-version=835.8.0", False), ("AntonioMeireles/coreos-stable --box-version=766.3.0", False), ("AntonioMeireles/coreos-stable --box-version=681.0.0", False), ("AntonioMeireles/coreos-stable --box-version=607.0.0", False), ("AntonioMeireles/coreos-stable --box-version=557.2.0", False), ("yungsang/coreos --box-version=1.3.8", False), ("yungsang/coreos --box-version=1.3.7", False), ("yungsang/coreos --box-version=1.2.9", False), ("yungsang/coreos --box-version=1.1.5", False), ("yungsang/coreos --box-version=1.0.0", False), ("yungsang/coreos --box-version=1.0.0", False), ("yungsang/coreos --box-version=0.9.10", False), ("yungsang/coreos --box-version=0.9.6", False), ("yungsang/coreos --box-version=0.9.1", True), ("yungsang/coreos --box-version=0.3.1", True), ] class CommandFailedException(Exception): pass class SpinOutputter(Thread): def __init__(self, initial_message): super(SpinOutputter, self).__init__() self.previous_line = '' self.next_line = initial_message self.running = True self.daemon = True @staticmethod def spinning_cursor(): while 1: for cursor in '|/-\\': yield cursor def set_next(self, text): first_line = text.split('\n')[0].strip() first_line = remove_control_characters(first_line) self.next_line = first_line[:80] def _clear_line(self): sys.stdout.write('\r') sys.stdout.write(' ' * (len(self.previous_line) + 2)) sys.stdout.flush() sys.stdout.write('\r') sys.stdout.flush() self.previous_line = '' def stop(self): self._clear_line() self.running = False def run(self): spinner = SpinOutputter.spinning_cursor() while self.running: self._clear_line() sys.stdout.write('\r') sys.stdout.flush() sys.stdout.write(next(spinner)) sys.stdout.write(" ") sys.stdout.write(colored(self.next_line, attrs=['dark'])) sys.stdout.flush() self.previous_line = self.next_line time.sleep(0.25) def _run_and_wait(command, error_allowed=False): # Run the command itself. outputter = SpinOutputter('Running command %s' % command) outputter.start() output = '' process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) for line in iter(process.stdout.readline, ''): output += line outputter.set_next(line) result = process.wait() outputter.stop() if result != 0 and not error_allowed: print colored('>>> Command `%s` Failed:' % command, 'red') print output raise CommandFailedException() return output def _indent(text, amount): return ''.join((' ' * amount) + line for line in text.splitlines(True)) def _run_box(box, requires_v1, registry): print colored('>>> Box: %s' % box, attrs=['bold']) print colored('>>> Starting box', 'yellow') _run_and_wait(['vagrant', 'destroy', '-f'], error_allowed=True) _run_and_wait(['rm', 'Vagrantfile'], error_allowed=True) _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']) 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 _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('>>> Testing login', 'cyan') _run_and_wait(['vagrant', 'ssh', '-c', 'docker login --username=devtable --password=password --email=foo %s' % registry]) print colored('>>> Testing push', 'cyan') _run_and_wait(['vagrant', 'ssh', '-c', 'docker push %s/devtable/testrepo' % registry]) 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]) print colored('>>> Testing pull', 'cyan') _run_and_wait(['vagrant', 'ssh', '-c', 'docker pull %s/devtable/testrepo' % registry]) print colored('>>> Tearing down box', 'magenta') _run_and_wait(['vagrant', 'destroy', '-f'], error_allowed=True) print colored('>>> Successfully tested box %s' % box, 'green') print "" 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: try: _run_box(box, requires_v1, registry) except CommandFailedException: sys.exit(-1) if __name__ == "__main__": test_clients(sys.argv[1] if len(sys.argv) > 1 else '10.0.2.2:5000')