def remove_image(self, image: str, force: bool = True): LOG.debug("Removing image %s %s", image, "(forced)" if force else "") try: self.client().images.remove(image=image, force=force) except ImageNotFound: if not force: raise NoSuchImage(image)
def _run_async_cmd( self, cmd: List[str], stdin: bytes, container_name: str, image_name=None ) -> Tuple[bytes, bytes]: kwargs = { "inherit_env": True, "asynchronous": True, "stderr": subprocess.PIPE, "outfile": self.default_run_outfile or subprocess.PIPE, } if stdin: kwargs["stdin"] = True try: process = run(cmd, **kwargs) stdout, stderr = process.communicate(input=stdin) if process.returncode != 0: raise subprocess.CalledProcessError( process.returncode, cmd, stdout, stderr, ) else: return stdout, stderr except subprocess.CalledProcessError as e: stderr_str = to_str(e.stderr) if "Unable to find image" in stderr_str: raise NoSuchImage(image_name or "", stdout=e.stdout, stderr=e.stderr) if "No such container" in stderr_str: raise NoSuchContainer(container_name, stdout=e.stdout, stderr=e.stderr) raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr )
def inspect_image(self, image_name: str, pull: bool = True) -> Dict[str, Union[Dict, str]]: try: return self._inspect_object(image_name) except NoSuchObject as e: if pull: self.pull_image(image_name) return self.inspect_image(image_name, pull=False) raise NoSuchImage(image_name=e.object_id)
def push_image(self, docker_image: str) -> None: LOG.debug("Pushing Docker image: %s", docker_image) try: result = self.client().images.push(docker_image) # some SDK clients (e.g., 5.0.0) seem to return an error string, instead of raising if isinstance(result, (str, bytes)) and '"errorDetail"' in to_str(result): if "image does not exist locally" in to_str(result): raise NoSuchImage(docker_image) if "is denied" in to_str(result): raise AccessDenied(docker_image) if "connection refused" in to_str(result): raise RegistryConnectionError(result) raise ContainerException(result) except ImageNotFound: raise NoSuchImage(docker_image) except APIError as e: raise ContainerException() from e
def tag_image(self, source_ref: str, target_name: str) -> None: try: LOG.debug("Tagging Docker image '%s' as '%s'", source_ref, target_name) image = self.client().images.get(source_ref) image.tag(target_name) except APIError as e: if e.status_code == 404: raise NoSuchImage(source_ref) raise ContainerException("Unable to tag Docker image") from e
def pull_image(self, docker_image: str) -> None: LOG.debug("Pulling Docker image: %s", docker_image) # some path in the docker image string indicates a custom repository try: self.client().images.pull(docker_image) except ImageNotFound: raise NoSuchImage(docker_image) except APIError as e: raise ContainerException() from e
def inspect_image(self, image_name: str, pull: bool = True) -> Dict[str, Union[Dict, str]]: try: return self.client().images.get(image_name).attrs except NotFound: if pull: self.pull_image(image_name) return self.inspect_image(image_name, pull=False) raise NoSuchImage(image_name) except APIError as e: raise ContainerException() from e
def tag_image(self, source_ref: str, target_name: str) -> None: cmd = self._docker_cmd() cmd += ["tag", source_ref, target_name] LOG.debug("Tagging Docker image %s as %s", source_ref, target_name) try: run(cmd) except subprocess.CalledProcessError as e: if "No such image" in to_str(e.stdout): raise NoSuchImage(source_ref) raise ContainerException( f"Docker process returned with error code {e.returncode}", e.stdout, e.stderr) from e
def pull_image(self, docker_image: str) -> None: cmd = self._docker_cmd() cmd += ["pull", docker_image] LOG.debug("Pulling image with cmd: %s", cmd) try: run(cmd) except subprocess.CalledProcessError as e: if "pull access denied" in to_str(e.stdout): raise NoSuchImage(docker_image) raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr)
def remove_image(self, image: str, force: bool = True) -> None: cmd = self._docker_cmd() cmd += ["rmi", image] if force: cmd += ["--force"] LOG.debug("Removing image %s %s", image, "(forced)" if force else "") try: run(cmd) except subprocess.CalledProcessError as e: if "No such image" in to_str(e.stdout): raise NoSuchImage(image, stdout=e.stdout, stderr=e.stderr) else: raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr)
def push_image(self, docker_image: str) -> None: cmd = self._docker_cmd() cmd += ["push", docker_image] LOG.debug("Pushing image with cmd: %s", cmd) try: run(cmd) except subprocess.CalledProcessError as e: if "is denied" in to_str(e.stdout): raise AccessDenied(docker_image) if "does not exist" in to_str(e.stdout): raise NoSuchImage(docker_image) if "connection refused" in to_str(e.stdout): raise RegistryConnectionError(e.stdout) raise ContainerException( f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr) from e
def create_container(self, image_name: str, **kwargs) -> str: cmd, env_file = self._build_run_create_cmd("create", image_name, **kwargs) LOG.debug("Create container with cmd: %s", cmd) try: container_id = run(cmd) # Note: strip off Docker warning messages like "DNS setting (--dns=127.0.0.1) may fail in containers" container_id = container_id.strip().split("\n")[-1] return container_id.strip() except subprocess.CalledProcessError as e: if "Unable to find image" in to_str(e.stdout): raise NoSuchImage(image_name, stdout=e.stdout, stderr=e.stderr) raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr ) finally: Util.rm_env_vars_file(env_file)
def create_container( self, image_name: str, *, name: Optional[str] = None, entrypoint: Optional[str] = None, remove: bool = False, interactive: bool = False, tty: bool = False, detach: bool = False, command: Optional[Union[List[str], str]] = None, mount_volumes: Optional[List[SimpleVolumeBind]] = None, ports: Optional[PortMappings] = None, env_vars: Optional[Dict[str, str]] = None, user: Optional[str] = None, cap_add: Optional[List[str]] = None, cap_drop: Optional[List[str]] = None, security_opt: Optional[List[str]] = None, network: Optional[str] = None, dns: Optional[str] = None, additional_flags: Optional[str] = None, workdir: Optional[str] = None, ) -> str: LOG.debug("Creating container with attributes: %s", locals()) extra_hosts = None if additional_flags: env_vars, ports, mount_volumes, extra_hosts, network = Util.parse_additional_flags( additional_flags, env_vars, ports, mount_volumes, network ) try: kwargs = {} if cap_add: kwargs["cap_add"] = cap_add if cap_drop: kwargs["cap_drop"] = cap_drop if security_opt: kwargs["security_opt"] = security_opt if dns: kwargs["dns"] = [dns] if ports: kwargs["ports"] = ports.to_dict() if workdir: kwargs["working_dir"] = workdir mounts = None if mount_volumes: mounts = Util.convert_mount_list_to_dict(mount_volumes) def create_container(): return self.client().containers.create( image=image_name, command=command, auto_remove=remove, name=name, stdin_open=interactive, tty=tty, entrypoint=entrypoint, environment=env_vars, detach=detach, user=user, network=network, volumes=mounts, extra_hosts=extra_hosts, **kwargs, ) try: container = create_container() except ImageNotFound: self.pull_image(image_name) container = create_container() return container.id except ImageNotFound: raise NoSuchImage(image_name) except APIError as e: raise ContainerException() from e