Skip to content

Commit

Permalink
Do not place the whole exception object in event_log
Browse files Browse the repository at this point in the history
Exception object cannot be serialized and sent through websocket.
Catch JobTemplateNotFoundException explicitly

Fixes AAP-11969: ansible-rulebook worker mode fails to handle
JobTemplateNotFoundException
  • Loading branch information
bzwei committed May 8, 2023
1 parent 00bf527 commit cf41e2c
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 30 deletions.
43 changes: 23 additions & 20 deletions ansible_rulebook/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from .event_filter.insert_meta_info import main as insert_meta
from .exception import (
ControllerApiException,
JobTemplateNotFoundException,
PlaybookNotFoundException,
PlaybookStatusNotFoundException,
ShutdownException,
Expand Down Expand Up @@ -813,32 +814,34 @@ async def event_callback(event: dict) -> None:
)
if controller_job["status"] != "failed":
break
except ControllerApiException as ex:
except (ControllerApiException, JobTemplateNotFoundException) as ex:
logger.error(ex)
controller_job = {}
controller_job["status"] = "failed"
controller_job["created"] = run_at()
controller_job["error"] = str(ex)

await event_log.put(
dict(
type="Action",
action="run_job_template",
action_uuid=str(uuid.uuid4()),
activation_id=settings.identifier,
job_template_name=name,
organization=organization,
job_id=job_id,
ruleset=ruleset,
ruleset_uuid=source_ruleset_uuid,
rule=source_rule_name,
rule_uuid=source_rule_uuid,
status=controller_job["status"],
run_at=controller_job["created"],
url=_controller_job_url(controller_job),
matching_events=_get_events(variables),
rule_run_at=rule_run_at,
)
a_log = dict(
type="Action",
action="run_job_template",
action_uuid=str(uuid.uuid4()),
activation_id=settings.identifier,
job_template_name=name,
organization=organization,
job_id=job_id,
ruleset=ruleset,
ruleset_uuid=source_ruleset_uuid,
rule=source_rule_name,
rule_uuid=source_rule_uuid,
status=controller_job["status"],
run_at=controller_job["created"],
url=_controller_job_url(controller_job),
matching_events=_get_events(variables),
rule_run_at=rule_run_at,
)
if "error" in controller_job:
a_log["reason"] = dict(error=controller_job["error"])
await event_log.put(a_log)

if set_facts or post_events:
logger.debug("set_facts")
Expand Down
5 changes: 5 additions & 0 deletions ansible_rulebook/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,8 @@ class JobTemplateNotFoundException(Exception):
class WebSocketExchangeException(Exception):

pass


class UnsupportedActionException(Exception):

pass
23 changes: 14 additions & 9 deletions ansible_rulebook/rule_set_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@

from ansible_rulebook.builtin import actions as builtin_actions
from ansible_rulebook.conf import settings
from ansible_rulebook.exception import ShutdownException
from ansible_rulebook.exception import (
ShutdownException,
UnsupportedActionException,
)
from ansible_rulebook.messages import Shutdown
from ansible_rulebook.rule_types import (
Action,
Expand Down Expand Up @@ -310,7 +313,7 @@ async def _call_action(

logger.info("call_action %s", action)

result = None
error = None
if action in builtin_actions:
try:
if action == "run_job_template":
Expand Down Expand Up @@ -397,15 +400,15 @@ async def _call_action(
str(e),
pformat(variables_copy),
)
result = dict(error=e)
error = e
except MessageNotHandledException as e:
logger.error(
"Message cannot be handled: %s err %s", action_args, str(e)
)
result = dict(error=e)
error = e
except MessageObservedException as e:
logger.info("MessageObservedException: %s", action_args)
result = dict(error=e)
error = e
except ShutdownException as e:
if self.shutdown:
logger.info(
Expand All @@ -418,15 +421,17 @@ async def _call_action(
raise
except Exception as e:
logger.error("Error calling action %s, err %s", action, str(e))
result = dict(error=e)
error = e
except BaseException as e:
logger.error(e)
raise
else:
logger.error("Action %s not supported", action)
result = dict(error=f"Action {action} not supported")
error = UnsupportedActionException(
f"Action {action} not supported"
)

if result:
if error:
await self.event_log.put(
dict(
type="Action",
Expand All @@ -435,7 +440,7 @@ async def _call_action(
playbook_name=action_args.get("name"),
status="failed",
run_at=str(datetime.utcnow()),
reason=result,
reason=dict(error=str(error)),
)
)

Expand Down
40 changes: 39 additions & 1 deletion tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@
from freezegun import freeze_time

from ansible_rulebook.engine import run_rulesets, start_source
from ansible_rulebook.exception import VarsKeyMissingException
from ansible_rulebook.exception import (
ControllerApiException,
JobTemplateNotFoundException,
VarsKeyMissingException,
)
from ansible_rulebook.job_template_runner import job_template_runner
from ansible_rulebook.messages import Shutdown
from ansible_rulebook.util import load_inventory
Expand Down Expand Up @@ -2105,6 +2109,40 @@ async def test_46_job_template():
assert action["action"] == "run_job_template"


JOB_TEMPLATE_ERRORS = [
("api error", ControllerApiException("api error")),
("jt does not exist", JobTemplateNotFoundException("jt does not exist")),
]


@pytest.mark.parametrize("err_msg,err", JOB_TEMPLATE_ERRORS)
@pytest.mark.asyncio
async def test_46_job_template_exception(err_msg, err):
ruleset_queues, event_log = load_rulebook("examples/46_job_template.yml")

queue = ruleset_queues[0][1]
rs = ruleset_queues[0][0]
with SourceTask(rs.sources[0], "sources", {}, queue):
with patch(
"ansible_rulebook.builtin.job_template_runner.run_job_template",
side_effect=err,
):
await run_rulesets(
event_log,
ruleset_queues,
dict(),
load_inventory("playbooks/inventory.yml"),
)

while not event_log.empty():
event = event_log.get_nowait()
if event["type"] == "Action":
action = event

assert action["action"] == "run_job_template"
assert action["reason"] == {"error": err_msg}


@pytest.mark.asyncio
async def test_77_default_events_ttl():
ruleset_queues, event_log = load_rulebook(
Expand Down

0 comments on commit cf41e2c

Please sign in to comment.