Commit e1fe166b authored by segfault's avatar segfault

Improve ContainerManager.execute_command()

parent f93e0e42
......@@ -27,6 +27,10 @@ class ContainerError(Exception):
pass
class CommandFailedError(Exception):
pass
class APTError(Exception):
pass
......@@ -201,7 +205,7 @@ class ContainerManager(object):
try:
self.execute_command("/bin/true")
return True
except sh.ErrorReturnCode_1:
except CommandFailedError:
return False
def exists(self) -> bool:
......@@ -211,12 +215,38 @@ class ContainerManager(object):
except sh.ErrorReturnCode_1:
return False
def execute_command(self, *args):
sh.machinectl("shell", self.name, *args)
def execute_command(self, command: List[str], timeout=30, handle_stderr_line: callable = None):
exit_code = None
p = subprocess.Popen(["systemd-run", "--pipe", "--wait", "-M", self.name] + command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
bufsize=1)
start_time = time.perf_counter()
while time.perf_counter() - start_time < timeout:
exit_code = p.poll()
if exit_code is not None:
break
if handle_stderr_line:
handle_stderr_line(p.stderr.readline())
time.sleep(0.2)
if exit_code is None:
raise ContainerError("Timeout while running command '%s' (timeout %s)" % (' '.join(command), timeout))
if handle_stderr_line:
for l in p.stderr.readlines():
handle_stderr_line(l)
if exit_code != 0:
raise CommandFailedError("Command '%s' failed with exit code %s. stderr: %s" %
(' '.join(command), exit_code, p.stderr.read()))
def execute_apt(self, *args, timeout):
def handle_line(line):
def handle_stderr_line(line):
line = line.strip()
if not line:
return
......@@ -235,29 +265,8 @@ class ContainerManager(object):
elif info[0] == "pmstatus":
self.status_callback(info[3])
exit_code = None
command = ["/usr/bin/apt-get", "--option=APT::Status-Fd=2"] + list(args)
p = subprocess.Popen(["systemd-run", "--pipe", "--wait", "-M", self.name] + command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
bufsize=1)
start_time = time.perf_counter()
while time.perf_counter() - start_time < timeout:
exit_code = p.poll()
if exit_code is not None:
break
handle_line(p.stderr.readline())
time.sleep(0.2)
if exit_code is None:
raise ContainerError("Failed to run APT command '%s' (timeout %s)" % (' '.join(command), timeout))
for l in p.stderr.readlines():
handle_line(l)
if exit_code != 0:
raise APTError("APT command '%s' failed with exit code %s" % (' '.join(command), exit_code))
self.execute_command(command, timeout=timeout, handle_stderr_line=handle_stderr_line)
@contextmanager
def run(self):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment