apply: RemoteException() in item.fix() does not show the command that's caused it
The actual failed command (zpool create -o ashift=12 -o autotrim=on video raidz2 ...) is not shown, instead we get an empty line.
Am confuse, whereβs the empty line youβre referring to? π€ It shows the zpool command at the bottom of the screenshot.
@vain it's below all the other listed commands, but before the printed exception.
I dug a little.
Item.run() runs the command, then appends the result to the list. The exception gets thrown by self.node.run(), we never get to the point where itβs added to the list.
The whole thing is run from a worker pool in apply_items(). The pool catches the exception by calling handle_exception(), which then proceeds to call handle_apply_result() and thatβs the function that prints the executed commands so far.
Now, IIUC, the problem youβre describing is: In handle_apply_result() the list item._command_results does not contain the command thatβs currently being executed β because this list only ever contains commands (and their results) that finished as expected (e.g., exit code 1 is fine if itβs called with may_fail=True).
I guess what you ideally want to see is something like this (this is a made-up example):
β mynode mybundle zfs_pool:tank failed
β
ββ zpool list tank (return code: 1; no output)
β
ββ lsblk -rndo fstype /dev/sda (RemoteException)
β
β File "/home/user/git/bundlewrap/bundlewrap/concurrency.py", line 133, in run
β result = self._get_result()
β File "/home/user/git/bundlewrap/bundlewrap/concurrency.py", line 73, in _get_result
β raise exception
β File "/usr/lib/python3.13/concurrent/futures/thread.py", line 59, in run
β result = self.fn(*self.args, **self.kwargs)
β File "/home/user/git/bundlewrap/bundlewrap/items/__init__.py", line 645, in apply
β self.fix(status_before)
β ~~~~~~~~^^^^^^^^^^^^^^^
β File "/home/user/git/bundlewrap/bundlewrap/items/zfs_pool.py", line 60, in fix
β res = self.run("lsblk -rndo fstype {}".format(quote(device)))
β File "/home/user/git/bundlewrap/bundlewrap/items/__init__.py", line 679, in run
β result = self.node.run(command, **kwargs)
β File "/home/user/git/bundlewrap/bundlewrap/node.py", line 935, in run
β return operations.run(
β ~~~~~~~~~~~~~~^
β self.hostname,
β ^^^^^^^^^^^^^^
β ...<8 lines>...
β user=user,
β ^^^^^^^^^^
β )
β ^
β File "/home/user/git/bundlewrap/bundlewrap/operations.py", line 366, in run
β raise RemoteException(error_msg)
β RemoteException("Non-zero return code (32) running 'lsblk -rndo fstype /dev/sda' on '1.2.3.4':\n\nlsblk: /dev/sda: not a block device\n\n\n")
β΅
The line lsblk -rndo fstype /dev/sda (RemoteException) is currently not included in the output. Instead, we print a β΅ which terminates the list of successfully run commands. (This might look like an empty line.)
First idea: We would need more bookkeeping to implement this. Something like βthe command currently being run is β¦β. This could be tricky, because weβre talking about a generic exception handler here β there might not be any command being currently run.
Next idea: Instead, it might be better to handle the RemoteException directly in Node.run(), record the failure there, then re-throw the exception (to signal to the rest of bw that it has failed). And then figure out how to suppress printing this exception in handle_apply_result() to avoid duplicating the output β but show other not-yet-printed exceptions. Ugh.
Next idea: How about printing this instead:
β mynode mybundle zfs_pool:tank failed
β
ββ zpool list tank (return code: 1; no output)
β΅
Exception after the commands above have been run:
β·
β File "/home/user/git/bundlewrap/bundlewrap/concurrency.py", line 133, in run
β result = self._get_result()
β File "/home/user/git/bundlewrap/bundlewrap/concurrency.py", line 73, in _get_result
β raise exception
β File "/usr/lib/python3.13/concurrent/futures/thread.py", line 59, in run
β result = self.fn(*self.args, **self.kwargs)
β File "/home/user/git/bundlewrap/bundlewrap/items/__init__.py", line 645, in apply
β self.fix(status_before)
β ~~~~~~~~^^^^^^^^^^^^^^^
β File "/home/user/git/bundlewrap/bundlewrap/items/zfs_pool.py", line 60, in fix
β res = self.run("lsblk -rndo fstype {}".format(quote(device)))
β File "/home/user/git/bundlewrap/bundlewrap/items/__init__.py", line 679, in run
β result = self.node.run(command, **kwargs)
β File "/home/user/git/bundlewrap/bundlewrap/node.py", line 935, in run
β return operations.run(
β ~~~~~~~~~~~~~~^
β self.hostname,
β ^^^^^^^^^^^^^^
β ...<8 lines>...
β user=user,
β ^^^^^^^^^^
β )
β ^
β File "/home/user/git/bundlewrap/bundlewrap/operations.py", line 366, in run
β raise RemoteException(error_msg)
β RemoteException("Non-zero return code (32) running 'lsblk -rndo fstype /dev/sda' on '1.2.3.4':\n\nlsblk: /dev/sda: not a block device\n\n\n")
β΅
Would this be better?
I'd really like to have the former, because i think that's where users expect to see the command that has been run. I'd imagine the latter leading to reports like "zpool list failed with this exception: ..."
The latter would definitely be an improvement over the current situation, but it's definitely not what i would want.