test: add test that ensures mount output is part of the exception

While debugging a failure of osbuild-composer [0] on fc39 it was
noticed that a mount failure does not include the output of
the mount command:
```
  File "/usr/lib/python3.12/site-packages/osbuild/mounts.py", line 78, in mount
    path = client.call("mount", args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/osbuild/host.py", line 348, in call
    ret, _ = self.call_with_fds(method, args)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/osbuild/host.py", line 384, in call_with_fds
    raise error
osbuild.host.RemoteError: CalledProcessError: Command '['mount', '-t', 'xfs', '-o', 'ro,norecovery', '--source', '/dev/rootvg/applv', '--target', '/tmp/tmpjtfmth56/app']' returned non-zero exit status 32.
   File "/usr/lib/python3.12/site-packages/osbuild/host.py", line 268, in serve
    reply, reply_fds = self._handle_message(msg, fds)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/osbuild/host.py", line 301, in _handle_message
    ret, fds = self.dispatch(name, args, fds)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/osbuild/mounts.py", line 111, in dispatch
    r = self.mount(args)
        ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/osbuild/mounts.py", line 160, in mount
    subprocess.run(
  File "/usr/lib64/python3.12/subprocess.py", line 571, in run
    raise CalledProcessError(retcode, process.args,
```
which makes diagnostic errors harder of course. This commit adds
a test that ensures that mount output is visbile and also changes
the code to include it.

[0] https://github.com/osbuild/osbuild-composer/pull/3820
This commit is contained in:
Michael Vogt 2023-12-08 13:52:23 +01:00
parent e73f4e57dd
commit 4026d4dc10
2 changed files with 32 additions and 1 deletions

View file

@ -57,7 +57,16 @@ class MountGuard(contextlib.AbstractContextManager):
if options:
args += ["-o", ",".join(options)]
subprocess.run(["mount"] + args + [source, target], check=True)
r = subprocess.run(["mount"] + args + [source, target],
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
encoding="utf-8",
check=False)
if r.returncode != 0:
code = r.returncode
msg = r.stdout.strip()
raise RuntimeError(f"{msg} (code: {code})")
self.mounts += [{"source": source, "target": target}]
def umount(self):

22
test/mod/test_util_mnt.py Normal file
View file

@ -0,0 +1,22 @@
import os
import subprocess
import pytest
from osbuild.util.mnt import mount, MountGuard
@pytest.mark.skipif(os.getuid() != 0, reason="root only")
def test_mount_failure_msg(tmp_path):
with pytest.raises(RuntimeError) as e:
mount("/dev/invalid-src", tmp_path)
assert "special device /dev/invalid-src does not exist" in str(e.value)
@pytest.mark.skipif(os.getuid() != 0, reason="root only")
def test_mount_guard_failure_msg(tmp_path):
with pytest.raises(RuntimeError) as e:
with MountGuard() as mg:
mg.mount("/dev/invalid-src", tmp_path)
assert "special device /dev/invalid-src does not exist" in str(e.value)