Skip to content

Commit e8b6c4a

Browse files
authored
Fix #531: count partial executions of workflows (#541)
* Fix #531: count partial executions of workflows * Rename partial to aborted * Count incomplete workflows * Use whiteboard tag in metrics names
1 parent af49778 commit e8b6c4a

File tree

5 files changed

+73
-0
lines changed

5 files changed

+73
-0
lines changed

jbi/actions/default.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import logging
1111
from typing import Callable, Optional
1212

13+
from statsd.defaults.env import statsd
14+
1315
from jbi import ActionResult, Operation
1416
from jbi.actions import steps as steps_module
1517
from jbi.environment import get_settings
@@ -95,15 +97,30 @@ def __init__(self, steps: dict[Operation, list[Callable]], **parameters):
9597

9698
def __call__(self, context: ActionContext) -> ActionResult:
9799
"""Called from `runner` when the action is used."""
100+
has_produced_request = False
101+
98102
for step in self.steps[context.operation]:
99103
context = context.update(current_step=step.__name__)
100104
try:
101105
context = step(context=context, **self.parameters)
102106
except IncompleteStepError as exc:
103107
# Step did not execute all its operations.
104108
context = exc.context
109+
statsd.incr(
110+
f"jbi.action.{context.action.whiteboard_tag}.incomplete.count"
111+
)
112+
except Exception:
113+
if has_produced_request:
114+
# Count the number of workflows that produced at least one request,
115+
# but could not complete entirely with success.
116+
statsd.incr(
117+
f"jbi.action.{context.action.whiteboard_tag}.aborted.count"
118+
)
119+
raise
105120

106121
step_responses = context.responses_by_step[step.__name__]
122+
if step_responses:
123+
has_produced_request = True
107124
for response in step_responses:
108125
logger.debug(
109126
"Received %s",

jbi/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ class RunnerContext(Context, extra=Extra.forbid):
359359
class ActionContext(Context, extra=Extra.forbid):
360360
"""Logging context from actions"""
361361

362+
action: Action
362363
rid: str
363364
operation: Operation
364365
current_step: Optional[str]

jbi/runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def execute_action(
7474
linked_issue_key: Optional[str] = bug.extract_from_see_also()
7575

7676
action_context = ActionContext(
77+
action=action,
7778
rid=request.rid,
7879
bug=bug,
7980
event=event,

tests/fixtures/factories.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def comment_factory(**overrides):
114114
def action_context_factory(**overrides):
115115
return ActionContext.parse_obj(
116116
{
117+
"action": action_factory(),
117118
"rid": token_hex(16),
118119
"operation": Operation.IGNORE,
119120
"bug": bug_factory(),

tests/unit/actions/test_default.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
from unittest import mock
33

44
import pytest
5+
import requests
56
import responses
67

8+
from jbi import Operation
79
from jbi.actions import default
810
from jbi.environment import get_settings
911
from jbi.models import ActionContext
12+
from tests.fixtures import factories
1013

1114

1215
def test_default_invalid_init():
@@ -95,3 +98,53 @@ def test_default_returns_callable_with_data(
9598
assert handled
9699
assert details["responses"][0] == {"key": "k"}
97100
assert details["responses"][1] == sentinel
101+
102+
103+
def test_counter_is_incremented_when_workflows_was_aborted(
104+
mocked_bugzilla, mocked_jira
105+
):
106+
context_create_example: ActionContext = factories.action_context_factory(
107+
operation=Operation.CREATE,
108+
action=factories.action_factory(whiteboard_tag="fnx"),
109+
)
110+
mocked_bugzilla.get_bug.return_value = context_create_example.bug
111+
mocked_jira.create_or_update_issue_remote_links.side_effect = requests.HTTPError(
112+
"Unauthorized"
113+
)
114+
callable_object = default.init(jira_project_key=context_create_example.jira.project)
115+
116+
with mock.patch("jbi.actions.default.statsd") as mocked:
117+
with pytest.raises(requests.HTTPError):
118+
callable_object(context=context_create_example)
119+
120+
mocked.incr.assert_called_with("jbi.action.fnx.aborted.count")
121+
122+
123+
def test_counter_is_incremented_when_workflows_was_incomplete(
124+
mocked_bugzilla, mocked_jira
125+
):
126+
context_create_example: ActionContext = factories.action_context_factory(
127+
operation=Operation.CREATE,
128+
action=factories.action_factory(whiteboard_tag="fnx"),
129+
bug=factories.bug_factory(resolution="WONTFIX"),
130+
)
131+
mocked_bugzilla.get_bug.return_value = context_create_example.bug
132+
133+
callable_object = default.init(
134+
jira_project_key=context_create_example.jira.project,
135+
steps={
136+
"new": [
137+
"create_issue",
138+
"maybe_update_issue_resolution",
139+
]
140+
},
141+
resolution_map={
142+
# Not matching WONTFIX, `maybe_` step will not complete
143+
"DUPLICATE": "Duplicate",
144+
},
145+
)
146+
147+
with mock.patch("jbi.actions.default.statsd") as mocked:
148+
callable_object(context=context_create_example)
149+
150+
mocked.incr.assert_called_with("jbi.action.fnx.incomplete.count")

0 commit comments

Comments
 (0)