def scoped_containers_fixture(docker_project: Project, request): now = datetime.utcnow() if request.config.getoption("--use-running-containers"): containers = docker_project.containers( ) # type: List[Container] else: if any(docker_project.containers()): raise ContainersAlreadyExist( 'pytest-docker-compose tried to start containers but there are' ' already running containers: %s, you probably scoped your' ' tests wrong' % docker_project.containers()) containers = docker_project.up() if not containers: raise ValueError( "`docker-compose` didn't launch any containers!") container_getter = ContainerGetter(docker_project) yield container_getter if request.config.getoption("--verbose"): for container in sorted(containers, key=lambda c: c.name): header = "Logs from {name}:".format(name=container.name) print(header, '\n', "=" * len(header)) print( container.logs(since=now).decode("utf-8", errors="replace") or "(no logs)", '\n') if not request.config.getoption("--use-running-containers"): docker_project.down( ImageType.none, request.config.getoption( "--docker-compose-remove-volumes"))
def docker_containers( docker_project: Project) -> Iterator[Dict[str, Container]]: """ Spins up a the containers for the Docker project and returns them. Note that this fixture's scope is a single test; the containers will be stopped after the test is finished. This is intentional; stopping the containers destroys local storage, so that the next test can start with fresh containers. """ containers: List[Container] = docker_project.up(DOCKER_SERVICES) if not containers: raise ValueError("`docker-compose` didn't launch any containers!") containers_by_name = dict([(c.name, c) for c in containers]) yield containers_by_name # Send container logs to stdout, so that they get included in # the test report. # https://docs.pytest.org/en/latest/capture.html for container in sorted(containers, key=lambda c: c.name): header = "Logs from {0}:".format(container.name) logger.info(header) logger.info("=" * len(header)) logger.info(container.logs().decode("utf-8", errors="replace") or "(no logs)") logger.info('') docker_project.down(ImageType.none, False)
def _containers_down( cls, docker_project: Project, docker_containers: typing.Iterable[Container], ) -> None: """ Brings down containers that were launched using :py:meth:`_containers_up`. """ # Send container logs to stdout, so that they get included in # the test report. # https://docs.pytest.org/en/latest/capture.html for container in sorted(docker_containers, key=lambda c: c.name): header = "Logs from {name}:".format(name=container.name) print(header) print("=" * len(header)) print(container.logs().decode("utf-8", errors="replace") or "(no logs)") print() docker_project.down(ImageType.none, False)
async def docker_compose(loop, request, docker_project: Project, postgres_override_addr): async def check_postgres(url): conn = await asyncpg.connect(url, loop=loop) await conn.close() checks = { ('postgres', 'POSTGRES_DSN', postgres_override_addr, 'postgresql://[email protected]:%d/postgres' '' % COMPOSE_POSTGRES_PORT, check_postgres), } result = {} fns = [] to_start = [] for svc, name, override, url, fn in checks: if override: result[name] = override else: to_start.append(svc) fns.append((fn, url)) result[name] = url if not to_start: yield result else: containers = docker_project.up(to_start, remove_orphans=True) if not containers: raise ValueError("`docker-compose` didn't launch any containers!") try: timeout = 60 start_time = time.time() print() print('Waiting for docker services...') last_err = None while start_time + timeout > time.time(): try: await asyncio.gather(*[fn(url) for fn, url in fns], loop=loop) break except Exception as err: last_err = err await asyncio.sleep(1, loop=loop) else: last_err_type = type(last_err) raise TimeoutError(f'Unable to start all container services' f' within {timeout} seconds. Last error:' f' {last_err} ({last_err_type})') print('Docker services are ready') yield result finally: # Send container logs to stdout, so that they get included in # the test report. # https://docs.pytest.org/en/latest/capture.html for container in sorted(containers, key=lambda c: c.name): if request.config.getoption('show_docker_logs'): header = f"Logs from {container.name}:" print(header) print("=" * len(header)) print(container.logs().decode("utf-8", errors="replace") or "(no logs)") print() docker_project.down(ImageType.none, False)
async def compose(loop, request, docker_project: Project, postgres_override_addr, rabbit_override_addr): async def check_postgres(url): conn = await asyncpg.connect(url, loop=loop) await conn.close() async def check_rabbit(url): transport, protocol = await aioamqp.from_url(url, loop=loop) await protocol.close() # async def check_redis(url): # conn = await aioredis.create_connection(url) # conn.close() # async def check_http(url): # async with aiohttp.ClientSession(loop=loop) as ses: # resp = await ses.get(url) # assert resp.status == 200 checks = { ( 'postgres', 'DB_URL', postgres_override_addr, COMPOSE_POSTGRES_URL, check_postgres ), ( 'rabbit', 'RABBIT_URL', rabbit_override_addr, COMPOSE_RABBIT_URL, check_rabbit ), } result = {} fns = [] to_start = [] urls = [] for svc, name, override, url, fn in checks: if override: result[name] = override else: to_start.append(svc) urls.append(url) fns.append((fn, url)) result[name] = url if not to_start: yield result else: containers = docker_project.up(to_start) if not containers: raise ValueError("`docker-compose` didn't launch any containers!") try: timeout = 60 start_time = time.time() print() print('Waiting for docker services...') print('\n'.join(urls)) last_err = None while start_time + timeout > time.time(): try: await asyncio.gather(*[fn(url) for fn, url in fns], loop=loop) break except Exception as err: last_err = err await asyncio.sleep(1, loop=loop) else: last_err_type = type(last_err) raise TimeoutError(f'Unable to start all container services' f' within {timeout} seconds. Last error:' f' {last_err} ({last_err_type})') print('Docker services are ready') yield result finally: # Send container logs to stdout, so that they get included in # the test report. # https://docs.pytest.org/en/latest/capture.html for container in sorted(containers, key=lambda c: c.name): if request.config.getoption('show_docker_logs'): header = f"Logs from {container.name}:" print(header) print("=" * len(header)) print( container.logs().decode("utf-8", errors="replace") or "(no logs)" ) print() docker_project.down(ImageType.none, False)