MCPcopy Create free account
hub / github.com/cloudflare/cloudflared / CloudflaredProcess

Class CloudflaredProcess

component-tests/util.py:18–74  ·  view source on GitHub ↗

Wrapper around a Popen process that continuously drains stdout and stderr in background threads to prevent OS pipe buffers from filling up and blocking the child process. Captured output is logged when the process is cleaned up.

Source from the content-addressed store, hash-verified

16from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS, GRACEFUL_SHUTDOWN_TIMEOUT, READER_THREAD_JOIN_TIMEOUT
17
18class CloudflaredProcess:
19 """
20 Wrapper around a Popen process that continuously drains stdout and stderr
21 in background threads to prevent OS pipe buffers from filling up and
22 blocking the child process. Captured output is logged when the process
23 is cleaned up.
24 """
25
26 def __init__(self, cmd, allow_input, capture_output):
27 output = subprocess.PIPE if capture_output else subprocess.DEVNULL
28 stdin = subprocess.PIPE if allow_input else None
29 self.process = subprocess.Popen(cmd, stdin=stdin, stdout=output, stderr=subprocess.STDOUT)
30
31 self._capture_output = capture_output
32 self._stdout_lines = []
33 self._threads = []
34 if capture_output:
35 self._threads.append(self._start_reader(self.process.stdout, self._stdout_lines))
36
37 @staticmethod
38 def _start_reader(pipe, sink):
39 def _drain():
40 for line in pipe:
41 sink.append(line)
42 pipe.close()
43 t = threading.Thread(target=_drain, daemon=True)
44 t.start()
45 return t
46
47 def terminate(self):
48 """Terminate the process if it is still running."""
49 if self.process.poll() is None:
50 self.process.terminate()
51
52 def cleanup(self):
53 """Terminate, wait for exit, join reader threads, and log output."""
54 self.terminate()
55 try:
56 self.process.wait(timeout=GRACEFUL_SHUTDOWN_TIMEOUT)
57 except subprocess.TimeoutExpired:
58 self.process.kill()
59 self.process.wait()
60 for t in self._threads:
61 t.join(timeout=READER_THREAD_JOIN_TIMEOUT)
62 if self._capture_output:
63 stdout = b"".join(self._stdout_lines).decode("utf-8", errors="replace")
64 if stdout:
65 LOGGER.info(f"cloudflared stdout:\n{stdout}")
66
67 @property
68 def stdout_lines(self):
69 return self._stdout_lines
70
71 # Proxy common Popen attributes so callers can still use the wrapper
72 # as if it were a Popen (e.g. send_signal, stdin, pid, returncode).
73 def __getattr__(self, name):
74 return getattr(self.process, name)
75

Callers 2

__enter__Method · 0.90

Calls

no outgoing calls

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…