class Runner: """ This class is in charge of loading test suites and runs them on different environments """ STOP_TIMEOUT = 3 def __init__(self, cfg): from docker.client import Client from docker.utils import kwargs_from_env self.config = cfg docker_kwargs = kwargs_from_env() docker_kwargs['tls'].assert_hostname = False self.docker = Client(**docker_kwargs) def run(self, build, *tests): """ Run all the test suites passed in as parameters on the given build This method will start a container of the build, run the tests and stop it """ from docker.utils import create_host_config print("Running tests on {}".format(build.name)) ports = self.config['environment']['ports'] host = self.config['global'].get('docker_host', os.getenv('DOCKER_HOST').split('/')[-1].split(':')[0]) container = self.docker.create_container( image=build.docker_tag, command='/bin/bash -c "nc -l 8080"', ports=ports, host_config=create_host_config(port_bindings=dict(zip(ports, [None] * len(ports)))) ).get('Id') self.docker.start(container) info = self.docker.inspect_container(container) port_bindings = {port: bind[0]['HostPort'] for port, bind in info['NetworkSettings']['Ports'].items()} for test in tests: test.run(host, port_bindings, build.context) self.docker.stop(container, timeout=self.STOP_TIMEOUT) log_file_path = os.path.join(self.config['global'].get('logs_dir', '/tmp'), '{}.log'.format(build.name)) with open(log_file_path, 'wb') as logs: logs.write(self.docker.logs(container, stdout=True, stderr=True, stream=False)) print("Container logs wrote to {}".format(log_file_path))
def _run_build(build): """ The REPOSITORY_DIR will contain a directory for each Project instance. Each Project instance directory will have a directory called "clone" which is the master clone. Then, each time a build is queued, we create a local clone from that repository named after the current build's commit sha. REPOSITORY_DIR/ django/ clone/ a18bc/ .../ Once the local clone is created, we build the docker image, create the container and run the tests. """ project = build.project # docker client c = Client(build.host) project_dir = os.path.join(REPOSITORY_DIR, project.slug) main_repo_path = os.path.join(project_dir, 'clone') if not os.path.exists(main_repo_path): # assert project repo present os.makedirs(project_dir) # clone repo repo = Repo.clone_from(project.repository, main_repo_path) else: repo = Repo(main_repo_path) remote = repo.remote() remote.pull() # build_path is a local clone of the project and it's named after the # current build's commit sha build_path = os.path.join(project_dir, build.commit_sha) if not os.path.exists(build_path): repo = Repo.clone_from(main_repo_path, build_path) else: repo = Repo(build_path) g = repo.git g.checkout(build.commit_sha) image_name = ':'.join([project.slug, build.commit_sha]) img_id, res = c.build(build_path) # create image c.tag(img_id, repository=project.slug, tag=build.commit_sha) # run build command from new image and report output container = c.create_container(image_name, project.build_command, stdin_open=True, tty=True) container_id = container.get('Id') c.start(container_id) return_code = c.wait(container_id) out = c.logs(container_id) build.result = out build.return_code = return_code build.save()