def run(self, service_instance: DockerServiceInstance, rm=True, detach=True): if not isinstance(service_instance, DockerServiceInstance): raise TypeError( 'ServiceInstance should be of type DockerServiceInstance instead of {}' .format(type(service_instance))) with create_docker_client( service_instance.target_host.get_host()) as client: if isinstance(service_instance.docker_image.registry, RemoteDockerRegistry): client.login( registry=service_instance.docker_image.registry.host, username=service_instance.docker_image.registry.username, password=service_instance.docker_image.registry.password) import docker.errors # FIXME try: # always detach from container and just stream logs if detach=False container = client.containers.run( service_instance.docker_image.get_image_uri(), name=service_instance.name, auto_remove=rm, ports=service_instance.ports_mapping, detach=True) if not detach: try: # infinite loop of logs while container running or if everything ok for log in self.logs(container, stream=True): logger.debug(log) if not self._is_service_running( client, service_instance): raise DockerRunnerException( "The container died unexpectedly.") except KeyboardInterrupt: logger.info('Interrupted. Stopping the container') container.stop() else: if not self._is_service_running(client, service_instance): if not rm: for log in self.logs(container, stdout=False, stderr=True): raise DockerRunnerException( "The container died unexpectedly.", log) else: # Can't get logs from removed container raise DockerRunnerException( "The container died unexpectedly. Try to run the container " "with detach=False or rm=False args to get more info." ) return container except docker.errors.ContainerError as e: if e.stderr: print(e.stderr.decode(), file=sys.stderr) raise
def _build_image(self, context_dir): tag = '{}:{}'.format(self.name, self.tag) logger.debug('Building docker image %s from %s...', tag, context_dir) with create_docker_client() as client: if not self.force_overwrite: try: client.images.get(tag) raise ValueError( f'Image {tag} already exists. Change name or set force_overwrite=True.' ) except errors.ImageNotFound: pass else: try: client.images.remove( tag) # to avoid spawning dangling images except errors.ImageNotFound: pass try: _, logs = client.images.build(path=context_dir, tag=tag) logger.info('Build successful') _print_docker_logs(logs) except errors.BuildError as e: _print_docker_logs(e.build_log) raise
def login_to_registry(client: docker.DockerClient, registry: DockerRegistry): """ Logs in to Docker registry (if it is remote). Corresponding credentials should be specified as environment variables per registry: e.g., if registry host is "168.32.25.1:5000" then "168_32_25_1_5000_USERNAME" and "168_32_25_1_5000_PASSWORD" variables should be specified :param client: Docker client instance :param registry: Docker registry descriptor :return: nothing """ if isinstance(registry, RemoteDockerRegistry): host_for_env = registry.host.replace('.', '_').replace(':', '_') username_var = f'{host_for_env}_username'.upper() username = os.getenv(username_var) password_var = f'{host_for_env}_password'.upper() password = os.getenv(password_var) if username and password: client.login(registry=registry.host, username=username, password=password) logger.info('Logged in to remote registry at host %s', registry.host) else: logger.warning( 'Skipped logging in to remote registry at host %s because no credentials given. ' + 'You could specify credentials as %s and %s environment variables.', registry.host, username_var, password_var)
def run(self, instance: DockerContainer, image: DockerImage, env: DockerEnv, rm=True, detach=True, **kwargs): if not (isinstance(instance, DockerContainer) and isinstance( image, DockerImage) and isinstance(env, DockerEnv)): raise TypeError( 'DockerRunner works with DockerContainer, DockerImage and DockerHost only' ) with env.daemon.client() as client: image.registry.login(client) try: # always detach from container and just stream logs if detach=False container = client.containers.run(image.uri, name=instance.name, auto_remove=rm, ports=instance.port_mapping, detach=True, **instance.params, **kwargs) instance.container_id = container.id if not detach: try: # infinite loop of logs while container running or if everything ok for log in self._logs(container, stream=True): logger.debug(log) self._sleep() if not self._is_service_running(client, instance.name): raise DockerRunnerException( "The container died unexpectedly.") except KeyboardInterrupt: logger.info('Interrupted. Stopping the container') container.stop() else: self._sleep(.5) if not self._is_service_running(client, instance.name): if not rm: for log in self._logs(container, stdout=False, stderr=True): raise DockerRunnerException( "The container died unexpectedly.", log) else: # Can't get logs from removed container raise DockerRunnerException( "The container died unexpectedly. Try to run the container " "with detach=False or rm=False args to get more info." ) except docker.errors.ContainerError as e: if e.stderr: print(e.stderr.decode(), file=sys.stderr) raise
def _build_image(self, context_dir, env: DockerEnv) -> docker.models.images.Image: tag = self.params.uri logger.debug('Building docker image %s from %s...', tag, context_dir) with env.daemon.client() as client: self.params.registry.login(client) if not self.force_overwrite: if self.params.exists(client): raise ValueError( f'Image {tag} already exists at {self.params.registry}. ' f'Change name or set force_overwrite=True.') else: self.params.delete(client) # to avoid spawning dangling images try: image, logs = client.images.build(path=context_dir, tag=tag, rm=True) logger.info('Built docker image %s', tag) _print_docker_logs(logs) self.params.registry.push(client, tag) return image except errors.BuildError as e: _print_docker_logs(e.build_log, logging.ERROR) raise
def prebuild_image(prebuild_path, name_template, python_version, *, push=False): tag = name_template.format(python_version) if image_exists_at_dockerhub(tag): logger.info('Skipped building image %s: already exists', tag) return with tempfile.TemporaryDirectory() as tmpdir: context_dir = os.path.join(tmpdir, 'context') logger.info('Building image %s on %s ...', tag, context_dir) shutil.copytree(prebuild_path, context_dir) _generate_dockerfile(context_dir, python_version) try: with create_docker_client() as client: client.images.build(path=context_dir, tag=tag, rm=True) logger.info('Successfully built image %s', tag) except Exception as e: logger.error('Failed to build image %s: %s', tag, e) return if push: with create_docker_client() as client: DockerIORegistry().login(client) client.images.push(tag)
def _build_image(self, context_dir): tag = self.params.get_uri() logger.debug('Building docker image %s from %s...', tag, context_dir) with create_docker_client() as client: login_to_registry(client, self.params.registry) if not self.force_overwrite: try: client.images.get(tag) raise ValueError( f'Image {tag} already exists. Change name or set force_overwrite=True.' ) except errors.ImageNotFound: pass else: try: client.images.remove( tag) # to avoid spawning dangling images except errors.ImageNotFound: pass try: _, logs = client.images.build(path=context_dir, tag=tag, rm=True) logger.info('Built image %s', tag) _print_docker_logs(logs) if isinstance(self.params.registry, RemoteDockerRegistry): client.images.push(tag) logger.info( 'Pushed image %s to remote registry at host %s', tag, self.params.registry.host) return Image(tag, params=self.params) except errors.BuildError as e: _print_docker_logs(e.build_log, logging.ERROR) raise
def push(self, client, tag): client.images.push(tag) logger.info('Pushed image %s to docker.io', tag)
def push(self, client, tag): client.images.push(tag) logger.info('Pushed image %s to remote registry at host %s', tag, self.host)