コード例 #1
0
 def connect_container_to_network(self,
                                  network_name: str,
                                  container_name_or_id: str,
                                  aliases: Optional[List] = None) -> None:
     LOG.debug(
         "Connecting container '%s' to network '%s' with aliases '%s'",
         container_name_or_id,
         network_name,
         aliases,
     )
     cmd = self._docker_cmd()
     cmd += ["network", "connect"]
     if aliases:
         cmd += ["--alias", ",".join(aliases)]
     cmd += [network_name, container_name_or_id]
     try:
         run(cmd)
     except subprocess.CalledProcessError as e:
         stdout_str = to_str(e.stdout)
         if re.match(r".*network (.*) not found.*", stdout_str):
             raise NoSuchNetwork(network_name=network_name)
         elif "No such container" in stdout_str:
             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)
コード例 #2
0
 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
         )
コード例 #3
0
 def inspect_container(self, container_name_or_id: str) -> Dict[str, Union[Dict, str]]:
     try:
         return self.client().containers.get(container_name_or_id).attrs
     except NotFound:
         raise NoSuchContainer(container_name_or_id)
     except APIError as e:
         raise ContainerException() from e
コード例 #4
0
 def stream_container_logs(self, container_name_or_id: str) -> CancellableStream:
     try:
         container = self.client().containers.get(container_name_or_id)
         return container.logs(stream=True, follow=True)
     except NotFound:
         raise NoSuchContainer(container_name_or_id)
     except APIError as e:
         raise ContainerException() from e
コード例 #5
0
 def exec_in_container(
     self,
     container_name_or_id: str,
     command: Union[List[str], str],
     interactive=False,
     detach=False,
     env_vars: Optional[Dict[str, Optional[str]]] = None,
     stdin: Optional[bytes] = None,
     user: Optional[str] = None,
     workdir: Optional[str] = None,
 ) -> Tuple[bytes, bytes]:
     LOG.debug("Executing command in container %s: %s",
               container_name_or_id, command)
     try:
         container: Container = self.client().containers.get(
             container_name_or_id)
         result = container.exec_run(
             cmd=command,
             environment=env_vars,
             user=user,
             detach=detach,
             stdin=interactive and bool(stdin),
             socket=interactive and bool(stdin),
             stdout=True,
             stderr=True,
             demux=True,
             workdir=workdir,
         )
         tty = False
         if interactive and stdin:  # result is a socket
             sock = result[1]
             sock = sock._sock if hasattr(sock, "_sock") else sock
             with sock:
                 try:
                     sock.sendall(stdin)
                     sock.shutdown(socket.SHUT_WR)
                     stdout, stderr = self._read_from_sock(sock, tty)
                     return stdout, stderr
                 except socket.timeout:
                     pass
         else:
             if detach:
                 return b"", b""
             return_code = result[0]
             if isinstance(result[1], bytes):
                 stdout = result[1]
                 stderr = b""
             else:
                 stdout, stderr = result[1]
             if return_code != 0:
                 raise ContainerException(
                     "Exec command returned with exit code %s" %
                     return_code, stdout, stderr)
             return stdout, stderr
     except ContainerError:
         raise NoSuchContainer(container_name_or_id)
     except APIError:
         raise ContainerException()
コード例 #6
0
 def unpause_container(self, container_name: str) -> None:
     LOG.debug("Unpausing container: %s", container_name)
     try:
         container = self.client().containers.get(container_name)
         container.unpause()
     except NotFound:
         raise NoSuchContainer(container_name)
     except APIError as e:
         raise ContainerException() from e
コード例 #7
0
 def stop_container(self, container_name: str, timeout: int = None) -> None:
     if timeout is None:
         timeout = self.STOP_TIMEOUT
     LOG.debug("Stopping container: %s", container_name)
     try:
         container = self.client().containers.get(container_name)
         container.stop(timeout=timeout)
     except NotFound:
         raise NoSuchContainer(container_name)
     except APIError as e:
         raise ContainerException() from e
コード例 #8
0
 def get_container_logs(self, container_name_or_id: str, safe=False) -> str:
     try:
         container = self.client().containers.get(container_name_or_id)
         return to_str(container.logs())
     except NotFound:
         if safe:
             return ""
         raise NoSuchContainer(container_name_or_id)
     except APIError as e:
         if safe:
             return ""
         raise ContainerException() from e
コード例 #9
0
 def unpause_container(self, container_name: str) -> None:
     cmd = self._docker_cmd()
     cmd += ["unpause", container_name]
     LOG.debug("Unpausing container with cmd %s", cmd)
     try:
         run(cmd)
     except subprocess.CalledProcessError as e:
         if "No such container" in to_str(e.stdout):
             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
             )
コード例 #10
0
 def copy_from_container(self, container_name: str, local_path: str,
                         container_path: str) -> None:
     cmd = self._docker_cmd()
     cmd += ["cp", f"{container_name}:{container_path}", local_path]
     LOG.debug("Copying from container with cmd: %s", cmd)
     try:
         run(cmd)
     except subprocess.CalledProcessError as e:
         if "No such container" in to_str(e.stdout):
             raise NoSuchContainer(container_name)
         raise ContainerException(
             "Docker process returned with errorcode %s" % e.returncode,
             e.stdout, e.stderr)
コード例 #11
0
 def remove_container(self, container_name: str, force=True, check_existence=False) -> None:
     LOG.debug("Removing container: %s", container_name)
     if check_existence and container_name not in self.get_running_container_names():
         LOG.debug("Aborting removing due to check_existence check")
         return
     try:
         container = self.client().containers.get(container_name)
         container.remove(force=force)
     except NotFound:
         if not force:
             raise NoSuchContainer(container_name)
     except APIError as e:
         raise ContainerException() from e
コード例 #12
0
 def copy_into_container(
     self, container_name: str, local_path: str, container_path: str
 ) -> None:  # TODO behave like https://docs.docker.com/engine/reference/commandline/cp/
     LOG.debug("Copying file %s into %s:%s", local_path, container_name, container_path)
     try:
         container = self.client().containers.get(container_name)
         target_exists, target_isdir = self._container_path_info(container, container_path)
         target_path = container_path if target_isdir else os.path.dirname(container_path)
         with Util.tar_path(local_path, container_path, is_dir=target_isdir) as tar:
             container.put_archive(target_path, tar)
     except NotFound:
         raise NoSuchContainer(container_name)
     except APIError as e:
         raise ContainerException() from e
コード例 #13
0
 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 run(cmd)
     except subprocess.CalledProcessError as e:
         if safe:
             return ""
         if "No such container" in to_str(e.stdout):
             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
             )
コード例 #14
0
 def commit(
     self,
     container_name_or_id: str,
     image_name: str,
     image_tag: str,
 ):
     LOG.debug("Creating image from container %s as %s:%s",
               container_name_or_id, image_name, image_tag)
     try:
         container = self.client().containers.get(container_name_or_id)
         container.commit(repository=image_name, tag=image_tag)
     except NotFound:
         raise NoSuchContainer(container_name_or_id)
     except APIError:
         raise ContainerException()
コード例 #15
0
 def copy_from_container(
     self,
     container_name: str,
     local_path: str,
     container_path: str,
 ) -> None:
     LOG.debug("Copying file from %s:%s to %s", container_name, container_path, local_path)
     try:
         container = self.client().containers.get(container_name)
         bits, _ = container.get_archive(container_path)
         Util.untar_to_path(bits, local_path)
     except NotFound:
         raise NoSuchContainer(container_name)
     except APIError as e:
         raise ContainerException() from e
コード例 #16
0
 def disconnect_container_from_network(self, network_name: str,
                                       container_name_or_id: str) -> None:
     LOG.debug("Disconnecting container '%s' from network '%s'",
               container_name_or_id, network_name)
     try:
         try:
             network = self.client().networks.get(network_name)
         except NotFound:
             raise NoSuchNetwork(network_name)
         try:
             network.disconnect(container_name_or_id)
         except NotFound:
             raise NoSuchContainer(container_name_or_id)
     except APIError:
         raise ContainerException()
コード例 #17
0
 def stop_container(self, container_name: str, timeout: int = None) -> None:
     if timeout is None:
         timeout = self.STOP_TIMEOUT
     cmd = self._docker_cmd()
     cmd += ["stop", "--time", str(timeout), container_name]
     LOG.debug("Stopping container with cmd %s", cmd)
     try:
         run(cmd)
     except subprocess.CalledProcessError as e:
         if "No such container" in to_str(e.stdout):
             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
             )
コード例 #18
0
 def remove_container(self, container_name: str, force=True, check_existence=False) -> None:
     if check_existence and container_name not in self.get_running_container_names():
         return
     cmd = self._docker_cmd() + ["rm"]
     if force:
         cmd.append("-f")
     cmd.append(container_name)
     LOG.debug("Removing container with cmd %s", cmd)
     try:
         run(cmd)
     except subprocess.CalledProcessError as e:
         if "No such container" in to_str(e.stdout):
             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
             )
コード例 #19
0
 def get_container_ip(self, container_name_or_id: str) -> str:
     cmd = self._docker_cmd()
     cmd += [
         "inspect",
         "--format",
         "{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}",
         container_name_or_id,
     ]
     try:
         result = run(cmd).strip()
         return result.split(" ")[0] if result else ""
     except subprocess.CalledProcessError as e:
         if "No such object" in to_str(e.stdout):
             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
             )
コード例 #20
0
 def connect_container_to_network(
     self, network_name: str, container_name_or_id: str, aliases: Optional[List] = None
 ) -> None:
     LOG.debug(
         "Connecting container '%s' to network '%s' with aliases '%s'",
         container_name_or_id,
         network_name,
         aliases,
     )
     try:
         network = self.client().networks.get(network_name)
     except NotFound:
         raise NoSuchNetwork(network_name)
     try:
         network.connect(container=container_name_or_id, aliases=aliases)
     except NotFound:
         raise NoSuchContainer(container_name_or_id)
     except APIError as e:
         raise ContainerException() from e
コード例 #21
0
 def disconnect_container_from_network(
     self, network_name: str, container_name_or_id: str
 ) -> None:
     LOG.debug(
         "Disconnecting container '%s' from network '%s'", container_name_or_id, network_name
     )
     cmd = self._docker_cmd() + ["network", "disconnect", network_name, container_name_or_id]
     try:
         run(cmd)
     except subprocess.CalledProcessError as e:
         stdout_str = to_str(e.stdout)
         if re.match(r".*network (.*) not found.*", stdout_str):
             raise NoSuchNetwork(network_name=network_name)
         elif "No such container" in stdout_str:
             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
             )
コード例 #22
0
 def commit(
     self,
     container_name_or_id: str,
     image_name: str,
     image_tag: str,
 ):
     cmd = self._docker_cmd()
     cmd += ["commit", container_name_or_id, f"{image_name}:{image_tag}"]
     LOG.debug(
         "Creating image from container %s as %s:%s", container_name_or_id, image_name, image_tag
     )
     try:
         run(cmd)
     except subprocess.CalledProcessError as e:
         if "No such container" in to_str(e.stdout):
             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
             )
コード例 #23
0
    def start_container(
        self,
        container_name_or_id: str,
        stdin=None,
        interactive: bool = False,
        attach: bool = False,
        flags: Optional[str] = None,
    ) -> Tuple[bytes, bytes]:
        LOG.debug("Starting container %s", container_name_or_id)
        try:
            container = self.client().containers.get(container_name_or_id)
            stdout = to_bytes(container_name_or_id)
            stderr = b""
            if interactive or attach:
                params = {"stdout": 1, "stderr": 1, "stream": 1}
                if interactive:
                    params["stdin"] = 1
                sock = container.attach_socket(params=params)
                sock = sock._sock if hasattr(sock, "_sock") else sock
                result_queue = queue.Queue()
                thread_started = threading.Event()
                start_waiting = threading.Event()

                # Note: We need to be careful about potential race conditions here - .wait() should happen right
                #   after .start(). Hence starting a thread and asynchronously waiting for the container exit code
                def wait_for_result(*_):
                    _exit_code = -1
                    try:
                        thread_started.set()
                        start_waiting.wait()
                        _exit_code = container.wait()["StatusCode"]
                    except APIError as e:
                        _exit_code = 1
                        raise ContainerException(str(e))
                    finally:
                        result_queue.put(_exit_code)

                # start listener thread
                start_worker_thread(wait_for_result)
                thread_started.wait()
                # start container
                container.start()
                # start awaiting container result
                start_waiting.set()

                # handle container input/output
                # under windows, the socket has no __enter__ / cannot be used as context manager
                # therefore try/finally instead of with here
                try:
                    if stdin:
                        sock.sendall(to_bytes(stdin))
                        sock.shutdown(socket.SHUT_WR)
                    stdout, stderr = self._read_from_sock(sock, False)
                except socket.timeout:
                    LOG.debug(
                        f"Socket timeout when talking to the I/O streams of Docker container '{container_name_or_id}'"
                    )
                finally:
                    sock.close()

                # get container exit code
                exit_code = result_queue.get()
                if exit_code:
                    raise ContainerException(
                        f"Docker container returned with exit code {exit_code}",
                        stdout=stdout,
                        stderr=stderr,
                    )
            else:
                container.start()
            return stdout, stderr
        except NotFound:
            raise NoSuchContainer(container_name_or_id)
        except APIError as e:
            raise ContainerException() from e
コード例 #24
0
 def inspect_container(
         self, container_name_or_id: str) -> Dict[str, Union[Dict, str]]:
     try:
         return self._inspect_object(container_name_or_id)
     except NoSuchObject as e:
         raise NoSuchContainer(container_name_or_id=e.object_id)