TypeError in logging when using retcode 75 in actions #900

Closed
opened 2024-07-20 04:34:57 +00:00 by 2rs2ts · 2 comments

What I'm trying to do and why

I have some before_backup actions to check to make sure that the files I'm about to back up aren't in use.

before_backup:
    - bash -c 'if nc -v -w 1 -z myservice.local 8443 ; then exit 75 ; fi'

During my last backup, I got an exception because the test exited with the 75 retcode: TypeError: unsupported operand type(s) for +: 'CalledProcessError' and 'str'. After a couple retries, though, the nc command started failing (a good thing!) and the backup went as normal. So, I think this is just a problem with the logging code.

Steps to reproduce

Have a before_backup command that returns the 75 exit code; maybe having it also write something to stdout is necessary, I'm not sure.

Actual behavior

This is from my cron error mail; I think verbosity 2 isn't needed here, but I can repro with higher verbosity if requested.

/root/.config/borgmatic/config.yaml: Running 2 commands for pre-backup hook
Connection to myservice.local 8443 port [tcp/*] succeeded!
my-borg-db: Error running actions for repository
--- Logging error ---
Traceback (most recent call last):
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 621, in log_error_records
raise error
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 145, in run_configuration
yield from run_actions(
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 330, in run_actions
yield from borgmatic.actions.create.run_create(
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/actions/create.py", line 69, in run_create
borgmatic.hooks.command.execute_hook(
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/hooks/command.py", line 69, in execute_hook
execute.execute_command(
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/execute.py", line 312, in execute_command
log_outputs(
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/execute.py", line 210, in log_outputs
raise subprocess.CalledProcessError(
subprocess.CalledProcessError: Command 'bash -c 'if nc -v -w 1 -z myservice.local 8443 ; then exit 75 ; fi'' returned non-zero exit status 75.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/local/lib/python3.11/logging/__init__.py", line 1113, in emit
stream.write(msg + self.terminator)
~~~~^~~~~~~~~~~~~~~~~
TypeError: unsupported operand type(s) for +: 'CalledProcessError' and 'str'
Call stack:
File "/root/.local/bin/borgmatic", line 8, in <module>
sys.exit(main())
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 922, in main
or list(collect_configuration_run_summary_logs(configs, config_paths, arguments))
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 804, in collect_configuration_run_summary_logs
results = list(run_configuration(config_filename, config, config_paths, arguments))
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 160, in run_configuration
tuple( # Consume the generator so as to trigger logging.
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 642, in log_error_records
yield log_record(levelno=levelno, levelname=level_name, msg=error)
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 599, in log_record
logger.handle(record)
File "/usr/local/lib/python3.11/logging/__init__.py", line 1644, in handle
self.callHandlers(record)
File "/usr/local/lib/python3.11/logging/__init__.py", line 1706, in callHandlers
hdlr.handle(record)
File "/usr/local/lib/python3.11/logging/__init__.py", line 978, in handle
self.emit(record)
File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/logger.py", line 76, in emit
self.log_level_to_handler[record.levelno].emit(record)
Message: CalledProcessError(75, "bash -c 'if nc -v -w 1 -z myservice.local 8443 ; then exit 75 ; fi'", 'Connection to myservice.local 8443 port [tcp/*] succeeded!')
Arguments: ()
my-borg-db: Retrying... attempt 1/3

Expected behavior

It Just Works™

Other notes / implementation ideas

This before_backup test has worked for forever, I haven't changed it in a long time. However, I recently upgraded a bunch of stuff in the jail I use for borgmatic, including:

  • the python version (3.9 something to 3.11.9)
  • borg version (not sure what it was before, might have been 1.1.x or 1.2.x)
  • borgmatic version (also not sure what it was at before, but I'm pretty sure it was a 1.8.x version)
  • the FreeBSD base for the jail (I think it was 13.2-RELEASE-p2, but it might have been a 13.1 version instead.)

I also switched from pip installing both borg and borgmatic, to using the BSD port for borg and pipx for borgmatic (pipx per your documentation's recommendation.)

As for how this is invoked, it's a cronjob, run as root, that does the following:

jexec ioc-borg /root/.local/bin/borgmatic --verbosity 1 --syslog-verbosity 1 --stats

I should note that on a macOS host that does a different backup job, I did not experience this issue. The test it does is different ([ ! -f ~/Library/myservice/myservice.pid ] || exit 75), but it still makes use of the retcode 75 trick and it uses the same log verbosity. That host is also running python 3.11 and borgmatic 1.8.13, but it's on borg 1.4.0, with all of these installed by homebrew (though I'm pretty sure the python version being used there is actually coming from pyenv globals.)

So, this could be a problem of my jail's setup, I'm not sure, but I double-checked and I don't think anything is amiss there; if it were something like the borgmatic code using the logging module from some other python version, I'd expect there to be a lot more wrong and for my backups to fail. I feel like it's more likely something to do with the way the logging module is used that's sensitive to something about the difference in the before_backup commands... maybe the fact that the nc-based one has output while the [-based one does not?

borgmatic version

1.8.13

borgmatic installation method

pipx install

Borg version

borg 1.2.8

Python version

Python 3.11.9

Database version (if applicable)

No response

Operating system and version

FreeBSD 13.2-RELEASE-p4

### What I'm trying to do and why I have some `before_backup` actions to check to make sure that the files I'm about to back up aren't in use. ```yaml before_backup: - bash -c 'if nc -v -w 1 -z myservice.local 8443 ; then exit 75 ; fi' ``` During my last backup, I got an exception because the test exited with the 75 retcode: `TypeError: unsupported operand type(s) for +: 'CalledProcessError' and 'str'`. After a couple retries, though, the `nc` command started failing (a good thing!) and the backup went as normal. So, I think this is just a problem with the logging code. ### Steps to reproduce Have a `before_backup` command that returns the 75 exit code; maybe having it also write something to stdout is necessary, I'm not sure. ### Actual behavior This is from my cron error mail; I think verbosity 2 isn't needed here, but I can repro with higher verbosity if requested. ``` /root/.config/borgmatic/config.yaml: Running 2 commands for pre-backup hook Connection to myservice.local 8443 port [tcp/*] succeeded! my-borg-db: Error running actions for repository --- Logging error --- Traceback (most recent call last): File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 621, in log_error_records raise error File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 145, in run_configuration yield from run_actions( File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 330, in run_actions yield from borgmatic.actions.create.run_create( File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/actions/create.py", line 69, in run_create borgmatic.hooks.command.execute_hook( File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/hooks/command.py", line 69, in execute_hook execute.execute_command( File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/execute.py", line 312, in execute_command log_outputs( File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/execute.py", line 210, in log_outputs raise subprocess.CalledProcessError( subprocess.CalledProcessError: Command 'bash -c 'if nc -v -w 1 -z myservice.local 8443 ; then exit 75 ; fi'' returned non-zero exit status 75. During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/local/lib/python3.11/logging/__init__.py", line 1113, in emit stream.write(msg + self.terminator) ~~~~^~~~~~~~~~~~~~~~~ TypeError: unsupported operand type(s) for +: 'CalledProcessError' and 'str' Call stack: File "/root/.local/bin/borgmatic", line 8, in <module> sys.exit(main()) File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 922, in main or list(collect_configuration_run_summary_logs(configs, config_paths, arguments)) File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 804, in collect_configuration_run_summary_logs results = list(run_configuration(config_filename, config, config_paths, arguments)) File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 160, in run_configuration tuple( # Consume the generator so as to trigger logging. File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 642, in log_error_records yield log_record(levelno=levelno, levelname=level_name, msg=error) File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/commands/borgmatic.py", line 599, in log_record logger.handle(record) File "/usr/local/lib/python3.11/logging/__init__.py", line 1644, in handle self.callHandlers(record) File "/usr/local/lib/python3.11/logging/__init__.py", line 1706, in callHandlers hdlr.handle(record) File "/usr/local/lib/python3.11/logging/__init__.py", line 978, in handle self.emit(record) File "/root/.local/pipx/venvs/borgmatic/lib/python3.11/site-packages/borgmatic/logger.py", line 76, in emit self.log_level_to_handler[record.levelno].emit(record) Message: CalledProcessError(75, "bash -c 'if nc -v -w 1 -z myservice.local 8443 ; then exit 75 ; fi'", 'Connection to myservice.local 8443 port [tcp/*] succeeded!') Arguments: () my-borg-db: Retrying... attempt 1/3 ``` ### Expected behavior It Just Works™ ### Other notes / implementation ideas This `before_backup` test has worked for forever, I haven't changed it in a long time. However, I recently upgraded a bunch of stuff in the jail I use for borgmatic, including: * the python version (3.9 something to 3.11.9) * borg version (not sure what it was before, might have been 1.1.x or 1.2.x) * borgmatic version (also not sure what it was at before, but I'm pretty sure it was a 1.8.x version) * the FreeBSD base for the jail (I think it was 13.2-RELEASE-p2, but it might have been a 13.1 version instead.) I also switched from `pip install`ing both borg and borgmatic, to using the BSD port for borg and `pipx` for borgmatic (`pipx` per your documentation's recommendation.) As for how this is invoked, it's a cronjob, run as root, that does the following: ```bash jexec ioc-borg /root/.local/bin/borgmatic --verbosity 1 --syslog-verbosity 1 --stats ``` **I should note that on a macOS host that does a different backup job, I did not experience this issue.** The test it does is different (`[ ! -f ~/Library/myservice/myservice.pid ] || exit 75`), but it still makes use of the retcode 75 trick and it uses the same log verbosity. That host is also running python 3.11 and borgmatic 1.8.13, but it's on borg 1.4.0, with all of these installed by homebrew (though I'm pretty sure the python version being used there is actually coming from pyenv globals.) So, this could be a problem of my jail's setup, I'm not sure, but I double-checked and I don't think anything is amiss there; if it were something like the borgmatic code using the `logging` module from some other python version, I'd expect there to be a lot more wrong and for my backups to fail. I feel like it's more likely something to do with the way the logging module is used that's sensitive to something about the difference in the `before_backup` commands... maybe the fact that the `nc`-based one has output while the `[`-based one does not? ### borgmatic version 1.8.13 ### borgmatic installation method pipx install ### Borg version borg 1.2.8 ### Python version Python 3.11.9 ### Database version (if applicable) _No response_ ### Operating system and version FreeBSD 13.2-RELEASE-p4
Owner

Thank you for the super detailed bug report. The good news is this was already fixed in main prior to your ticket being filed and will be part of the next release! (In case you're curious, here are the changesets: d44dc93509 and 27e7ece2f5)

Thank you for the super detailed bug report. The good news is this was already fixed in main prior to your ticket being filed and will be part of the next release! (In case you're curious, here are the changesets: d44dc93509dc2baa4af36181fae0765438906ce5 and 27e7ece2f5e192be7b024598d1933f66eadc14ed)
Owner

Released in borgmatic 1.8.14!

Released in borgmatic 1.8.14!
Sign in to join this conversation.
No milestone
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
borgmatic-collective/borgmatic#900
No description provided.