def response_reader(): """Read remote command responses from the socket.""" for stream_type, data in frames_iter(socket, tty=False): response = data.decode("utf-8") if stream_type == 1: # stream_type == 1 means it's from the command's stdout logger.debug("received: %s", response.encode("utf-8")) try: msg = json.loads(response) if "result" in msg and "file" in msg["result"]: container_file = Path(msg["result"]["file"]) volume_file = container_path_to_volume_path( container_file) msg["result"]["file"] = str(volume_file) response = json.dumps(msg) + "\n" except Exception as e: logger.warning( "Cannot parse gftp response '%s': %s", response.encode("utf-8"), e, ) # Return the response as is sys.stdout.write(response) sys.stdout.flush() elif stream_type == 2: # it's from the command's stderr logger.debug("stderr: %s", response.strip()) else: raise ValueError( f"Unexpected stream type in a frame header: {stream_type}")
def _read_from_sock(self, sock: socket, tty: bool): """Reads multiplexed messages from a socket returned by attach_socket. Uses the protocol specified here: https://docs.docker.com/engine/api/v1.41/#operation/ContainerAttach """ stdout = b"" stderr = b"" for frame_type, frame_data in frames_iter(sock, tty): if frame_type == STDOUT: stdout += frame_data elif frame_type == STDERR: stderr += frame_data else: raise ContainerException("Invalid frame type when reading from socket") return stdout, stderr
def read(self) -> str: """ Generator yielding lines read from socket. :return: Lines read from socket (str) """ read_buffer = "" for stream, frame in frames_iter(socket=self.socket, tty=False): read_buffer += frame.decode("utf8") if read_buffer.endswith("\n"): yield read_buffer.strip("\n") read_buffer = "" if read_buffer: yield read_buffer.strip("\n")
def read(self) -> str: """ Generator yielding lines read from named pipe. Retry mechanism tries to avoid breaking prematurely while waiting on the other end of the pipe to send data. :return: Lines read from named pipe (str) """ read_buffer = "" max_peek_tries = 3 for stream, frame in frames_iter(socket=self.socket, tty=False): read_buffer += frame.decode("utf8") if read_buffer.endswith("\n"): yield read_buffer.strip("\n") read_buffer = "" peek_tries = 0 while peek_tries <= max_peek_tries: _, n_bytes_left, _ = win32pipe.PeekNamedPipe(self.socket._handle, 2) if n_bytes_left: break else: self.logger.debug("No bytes left to read. Retrying...") self.logger.debug("peek_tries: %s", peek_tries) peek_tries += 1 time.sleep(1) continue else: self.logger.debug("No more bytes left to read") if read_buffer: yield read_buffer.strip("\n") break
def read_socket(sock, timeout: int = None) -> Tuple[bytes, bytes]: """ Reads from a docker socket, and returns everything :param sock: Docker socket to read from :param timeout: Number of seconds after which we return all data collected :return: A tuple of stdout, stderr """ start_time = time.time() out = [b"", b""] for frame in frames_iter(sock, tty=False): frame = demux_adaptor(*frame) # If we hit the timeout, return anyawy if time.time() >= start_time + timeout: return tuple(out) assert frame != (None, None) if frame[0] is not None: out[0] += frame[0] else: out[1] += frame[1] return tuple(out)