def test_get_container_ip_for_network(self, docker_client: ContainerClient, dummy_container, create_network): network_name = f"test-network-{short_uid()}" network_id = create_network(network_name) safe_run([ "docker", "network", "connect", network_id, dummy_container.container_id ]) docker_client.start_container(dummy_container.container_id) result_bridge_network = docker_client.get_container_ipv4_for_network( container_name_or_id=dummy_container.container_id, container_network="bridge").strip() assert is_ipv4_address(result_bridge_network) bridge_network = docker_client.inspect_network( "bridge")["IPAM"]["Config"][0]["Subnet"] assert ipaddress.IPv4Address( result_bridge_network) in ipaddress.IPv4Network(bridge_network) result_custom_network = docker_client.get_container_ipv4_for_network( container_name_or_id=dummy_container.container_id, container_network=network_name).strip() assert is_ipv4_address(result_custom_network) assert result_custom_network != result_bridge_network custom_network = docker_client.inspect_network( network_name)["IPAM"]["Config"][0]["Subnet"] assert ipaddress.IPv4Address( result_custom_network) in ipaddress.IPv4Network(custom_network)
def has_docker(self) -> bool: """Check if system has docker available""" try: safe_run(self._docker_cmd() + ["ps"]) return True except Exception: return False
def test_run_container_automatic_pull(self, docker_client: ContainerClient): try: safe_run([config.DOCKER_CMD, "rmi", "alpine"]) except CalledProcessError: pass message = "test message" stdout, _ = docker_client.run_container("alpine", command=["echo", message], remove=True) assert message == stdout.decode(config.DEFAULT_ENCODING).strip()
def copy_into_container( self, container_name: str, local_path: str, container_path: str ) -> None: """Copy contents of the given local path into the container""" cmd = self._docker_cmd() cmd += ["cp", local_path, f"{container_name}:{container_path}"] LOG.debug("Copying into container with cmd: %s", cmd) safe_run(cmd)
def test_get_container_entrypoint_not_pulled_image( self, docker_client: ContainerClient): try: docker_client.get_image_cmd("alpine", pull=False) safe_run([config.DOCKER_CMD, "rmi", "alpine"]) except ContainerException: pass entrypoint = docker_client.get_image_entrypoint("alpine") assert "" == entrypoint
def test_get_container_command_not_pulled_image( self, docker_client: ContainerClient): try: docker_client.get_image_cmd("alpine", pull=False) safe_run([config.DOCKER_CMD, "rmi", "alpine"]) except ContainerException: pass command = docker_client.get_image_cmd("alpine") assert ["/bin/sh"] == command
def test_run_container_non_existent_image(self, docker_client: ContainerClient): try: safe_run([config.DOCKER_CMD, "rmi", "alpine"]) except CalledProcessError: pass with pytest.raises(NoSuchImage): stdout, _ = docker_client.run_container( "localstack_non_existing_image_for_tests", command=["echo", "test"], remove=True )
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: safe_run(cmd) except subprocess.CalledProcessError as e: raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr )
def test_pull_docker_image(self, docker_client: ContainerClient): try: docker_client.get_image_cmd("alpine", pull=False) safe_run([config.DOCKER_CMD, "rmi", "alpine"]) except ContainerException: pass with pytest.raises(NoSuchImage): docker_client.get_image_cmd("alpine", pull=False) docker_client.pull_image("alpine") assert ["/bin/sh"] == docker_client.get_image_cmd("alpine", pull=False)
def test_pull_docker_image_with_tag(self, docker_client: ContainerClient): try: docker_client.get_image_cmd("alpine") safe_run([config.DOCKER_CMD, "rmi", "alpine"]) except ContainerException: pass with pytest.raises(NoSuchImage): docker_client.get_image_cmd("alpine") docker_client.pull_image("alpine:3.13") assert "/bin/sh" == docker_client.get_image_cmd("alpine:3.13").strip() assert "alpine:3.13" in docker_client.inspect_image( "alpine:3.13")["RepoTags"]
def test_docker_image_names(self, docker_client: ContainerClient): try: safe_run([config.DOCKER_CMD, "rmi", "alpine"]) except CalledProcessError: pass assert "alpine:latest" not in docker_client.get_docker_image_names() assert "alpine" not in docker_client.get_docker_image_names() docker_client.pull_image("alpine") assert "alpine:latest" in docker_client.get_docker_image_names() assert "alpine:latest" not in docker_client.get_docker_image_names(include_tags=False) assert "alpine" in docker_client.get_docker_image_names(include_tags=False) assert "alpine" in docker_client.get_docker_image_names() assert "alpine" not in docker_client.get_docker_image_names(strip_latest=False)
def stop_container(self, container_name: str) -> None: """Stops container with given name""" cmd = self._docker_cmd() cmd += ["stop", "-t0", container_name] LOG.debug("Stopping container with cmd %s", cmd) try: safe_run(cmd) except subprocess.CalledProcessError as e: if "No such container" in e.stdout.decode(config.DEFAULT_ENCODING): raise NoSuchContainer(container_name, stdout=e.stdout, stderr=e.stderr) else: raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr )
def test_get_network_multiple_networks(self, docker_client: ContainerClient, dummy_container, create_network): network_name = f"test-network-{short_uid()}" network_id = create_network(network_name) safe_run([ "docker", "network", "connect", network_id, dummy_container.container_id ]) docker_client.start_container(dummy_container.container_id) networks = docker_client.get_networks(dummy_container.container_id) assert network_name in networks assert "bridge" in networks assert len(networks) == 2
def list_containers(self, filter: Union[List[str], str, None] = None) -> List[dict]: """List all containers matching the given filters Returns a list of dicts with keys id, image, name, labels, status """ filter = [filter] if isinstance(filter, str) else filter cmd = self._docker_cmd() cmd += ["ps", "-a"] options = [] if filter: options += [y for filter_item in filter for y in ["--filter", filter_item]] cmd += options cmd.append("--format") cmd.append( '{"id":"{{ .ID }}","image":"{{ .Image }}","name":"{{ .Names }}",' '"labels":"{{ .Labels }}","status":"{{ .State }}"}' ) try: cmd_result = safe_run(cmd).strip() except subprocess.CalledProcessError as e: raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr ) container_list = [] if cmd_result: container_list = [json.loads(line) for line in cmd_result.splitlines()] return container_list
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": subprocess.PIPE, } if stdin: kwargs["stdin"] = True try: process = safe_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 = e.stderr.decode(config.DEFAULT_ENCODING) 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 remove_container(self, container_name: str, force=True) -> None: """Removes container with given name""" cmd = self._docker_cmd() + ["rm"] if force: cmd.append("-f") cmd.append(container_name) LOG.debug("Removing container with cmd %s", cmd) try: safe_run(cmd) except subprocess.CalledProcessError as e: if "No such container" in e.stdout.decode(config.DEFAULT_ENCODING): raise NoSuchContainer(container_name, stdout=e.stdout, stderr=e.stderr) else: raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr )
def test_pull_docker_image_with_hash(self, docker_client: ContainerClient): try: docker_client.get_image_cmd("alpine") safe_run([config.DOCKER_CMD, "rmi", "alpine"]) except ContainerException: pass with pytest.raises(NoSuchImage): docker_client.get_image_cmd("alpine") docker_client.pull_image( "alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a" ) assert ("/bin/sh" == docker_client.get_image_cmd( "alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a" ).strip()) assert ( "alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a" in docker_client.inspect_image( "alpine@sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a" )["RepoDigests"])
def create_network(): """ Uses the factory as fixture pattern to wrap the creation of networks as a factory that removes the networks after the fixture is cleaned up. """ networks = list() def _create_network(network_name: str): network_id = safe_run([config.DOCKER_CMD, "network", "create", network_name]).strip() networks.append(network_id) return network_id yield _create_network for network in networks: try: LOG.debug("Removing network %s", network) safe_run([config.DOCKER_CMD, "network", "remove", network]) except CalledProcessError: pass
def get_container_logs(self, container_name_or_id: str, safe=False) -> str: cmd = self._docker_cmd() cmd += ["logs", container_name_or_id] try: return safe_run(cmd) except subprocess.CalledProcessError as e: if not safe: return "" if "No such container" in e.stdout.decode(config.DEFAULT_ENCODING): raise NoSuchContainer(container_name_or_id, stdout=e.stdout, stderr=e.stderr) else: raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr )
def create_container(self, image_name: str, **kwargs) -> str: cmd = self._build_run_create_cmd("create", image_name, **kwargs) LOG.debug("Create container with cmd: %s", cmd) try: container_id = safe_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 e.stdout.decode(config.DEFAULT_ENCODING): raise NoSuchImage(image_name, stdout=e.stdout, stderr=e.stderr) raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr )
def get_image_cmd(self, docker_image: str) -> str: """Get the command for the given image""" cmd = self._docker_cmd() cmd += [ "image", "inspect", '--format="{{ .Config.Cmd }}"', docker_image, ] try: run_result = safe_run(cmd) except subprocess.CalledProcessError as e: if "No such image" in e.stdout.decode(config.DEFAULT_ENCODING): raise NoSuchImage(docker_image, stdout=e.stdout, stderr=e.stderr) else: raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr ) entry_point = run_result.strip('"[]\n\r ') return entry_point
def get_container_status(self, container_name: str) -> DockerContainerStatus: """Returns the status of the container with the given name""" cmd = self._docker_cmd() cmd += [ "ps", "-a", "--filter", f"name={container_name}", "--format", "{{ .Status }} - {{ .Names }}", ] cmd_result = safe_run(cmd) # filter empty / invalid lines from docker ps output cmd_result = next((line for line in cmd_result.splitlines() if container_name in line), "") container_status = cmd_result.strip().lower() if len(container_status) == 0: return DockerContainerStatus.NON_EXISTENT elif container_status.startswith("up "): return DockerContainerStatus.UP else: return DockerContainerStatus.DOWN
def get_network(self, container_name: str) -> str: """Returns the network mode of the container with the given name""" LOG.debug("Getting container network: %s", container_name) cmd = self._docker_cmd() cmd += [ "inspect", container_name, "--format", "{{ .HostConfig.NetworkMode }}", ] try: cmd_result = safe_run(cmd) except subprocess.CalledProcessError as e: if "No such container" in e.stdout.decode(config.DEFAULT_ENCODING): raise NoSuchContainer(container_name, stdout=e.stdout, stderr=e.stderr) else: raise ContainerException( "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr ) container_network = cmd_result.strip() return container_network
def try_install(): safe_run([plugin_binary, "install", "-b", plugin])
def try_install(): output = safe_run([plugin_binary, "install", "-b", plugin]) LOG.debug("Plugin installation output: %s", output)
def _create_network(network_name: str): network_id = safe_run( [config.DOCKER_CMD, "network", "create", network_name]).strip() networks.append(network_id) return network_id