-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #78 from sul-dlss/honeybadger-alert-on-nonzero-scr…
…ipt-exit send Honeybadger alert on nonzero script exit
- Loading branch information
Showing
8 changed files
with
156 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
[run] | ||
cov-report = term,html | ||
omit = tests/* | ||
concurrency = multiprocessing | ||
parallel = true | ||
sigterm = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import dotenv | ||
import logging | ||
import os | ||
import sys | ||
|
||
from honeybadger import honeybadger | ||
from subprocess import run, CalledProcessError | ||
|
||
|
||
# This must be invoked before the logger is invoked for the first time | ||
def configure() -> None: | ||
dotenv.load_dotenv() | ||
|
||
# TODO: HB and logging config copied from speech_to_text.py, may eventually want | ||
# to centralize more and start organizing codebase as a package? | ||
honeybadger.configure( | ||
api_key=os.environ.get("HONEYBADGER_API_KEY", ""), | ||
environment=os.environ.get("HONEYBADGER_ENV", "stage"), | ||
) | ||
|
||
logging.basicConfig( | ||
level=logging.INFO, | ||
format="%(asctime)s :: %(levelname)s :: %(message)s", | ||
datefmt="%Y-%m-%dT%H:%M:%S%z", | ||
) | ||
|
||
|
||
# advantages of this vs using the `honeybadger exec` subcommand | ||
# of the honeybadger gem/command (see https://docs.honeybadger.io/lib/ruby/gem-reference/cli/): | ||
# 1. a little more control over what the error reporting sends along | ||
# 2. more honest reporting of error code: in my testing, a script that exited with a | ||
# return code of '1' was reported as having a return code of '256' by honeybadger | ||
# exec (but was correctly reported by this, when compared to running the command by itself and doing `echo $?`) | ||
# 3. this script will bubble up the return code of the wrapped script, whereas honeybadger exec always returns 0, | ||
# even when the wrapped script exits non-zero | ||
# 4. one or two fewer dependencies to add to the docker image (maybe ruby, depending on the base image; definitely | ||
# the honeybadger gem regardless, whereas we already have the python package in our python deps) | ||
# | ||
# disadvantages of this approach: a little more code of our own to maintain | ||
def run_with_error_reporting(cmd_with_args: list) -> int: | ||
returncode: int | ||
|
||
try: | ||
completed_process = run(cmd_with_args, check=True) | ||
returncode = completed_process.returncode | ||
|
||
logging.info(completed_process) | ||
except KeyboardInterrupt: | ||
logging.info(f"exiting {sys.argv[0]}") | ||
sys.exit() | ||
except CalledProcessError as e: | ||
returncode = e.returncode | ||
|
||
error_context = { | ||
"message": str(e), | ||
"cmd": e.cmd, | ||
"returncode": e.returncode, | ||
} | ||
logging.error(error_context) | ||
honeybadger.notify(e, context=error_context) | ||
|
||
return returncode | ||
|
||
|
||
if __name__ == "__main__": | ||
configure() | ||
|
||
cmd_with_args = sys.argv[1:] # argv[0] is this script's name | ||
logging.info(f"command and args: {cmd_with_args}") | ||
|
||
# bubble up the exit code from the wrapped call | ||
sys.exit(run_with_error_reporting(cmd_with_args)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import error_reporting_wrapper | ||
import pytest | ||
|
||
from subprocess import run, CalledProcessError, CompletedProcess | ||
|
||
from unittest.mock import patch | ||
|
||
|
||
def test_error_reporting_wrapper_exit_zero(): | ||
completed_process = run(["cat", "Dockerfile"], check=True) | ||
assert completed_process.returncode == 0, "return code for successful command is 0" | ||
|
||
|
||
def test_error_reporting_wrapper_exit_nonzero(): | ||
completed_process = run(["cat", "foooooo"]) | ||
assert completed_process.returncode == 1, ( | ||
"return code for unsuccessful command is bubbled up through wrapper" | ||
) | ||
|
||
|
||
@patch("error_reporting_wrapper.honeybadger") | ||
def test_run_with_error_reporting_on_error_honeybadger(mock_honeybadger): | ||
returncode = error_reporting_wrapper.run_with_error_reporting(["cat", "foooooo"]) | ||
|
||
mock_honeybadger.notify.assert_called_once() | ||
args, kwargs = mock_honeybadger.notify.call_args | ||
context = kwargs["context"] | ||
assert isinstance(args[0], CalledProcessError) | ||
assert "returned non-zero exit status" in context["message"] | ||
assert context["cmd"] == ["cat", "foooooo"] | ||
assert context["returncode"] == 1 | ||
assert returncode == 1 | ||
|
||
|
||
# ignore utcnow warning from within honeybadger | ||
@pytest.mark.filterwarnings("ignore:datetime.datetime.utcnow") | ||
@patch("error_reporting_wrapper.logging") | ||
def test_run_with_error_reporting_on_error_logging(mock_logging): | ||
returncode = error_reporting_wrapper.run_with_error_reporting(["cat", "foooooo"]) | ||
|
||
mock_logging.error.assert_called_once() | ||
args, kwargs = mock_logging.error.call_args | ||
assert "returned non-zero exit status" in args[0]["message"] | ||
assert args[0]["cmd"] == ["cat", "foooooo"] | ||
assert args[0]["returncode"] == 1 | ||
assert returncode == 1 | ||
|
||
|
||
@patch("error_reporting_wrapper.logging") | ||
@patch("error_reporting_wrapper.honeybadger") | ||
def test_run_with_error_reporting_on_success(mock_honeybadger, mock_logging): | ||
returncode = error_reporting_wrapper.run_with_error_reporting(["cat", "Dockerfile"]) | ||
|
||
mock_honeybadger.notify.assert_not_called() | ||
mock_logging.error.assert_not_called() | ||
mock_logging.info.assert_called_once() | ||
args = mock_logging.info.call_args.args | ||
assert isinstance(args[0], CompletedProcess) | ||
assert returncode == 0 |