Skip to content

Commit c022565

Browse files
committed
feat: code owner theme and squad attributes
Add code_owner_squad and code_owner_theme custom attributes. See 0004-code-owner-theme-and-squad.rst ADR for details on the decision. ARCHBOM-1759
1 parent d3889fc commit c022565

File tree

7 files changed

+282
-42
lines changed

7 files changed

+282
-42
lines changed

CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ Removed
2121

2222
* Removed the old location of ``CodeOwnerMonitoringMiddleware``. It had moved in a past commit. Although technically a breaking change, all references in the Open edX platform have already been updated to point to the new location.
2323

24+
Added
25+
-----
26+
27+
* Added new ``code_owner_theme`` and ``code_owner_squad`` custom attributes. This is useful in cases where the ``code_owner`` combines a theme and squad name, because monitoring can instead reference ``code_owner_squad`` to be resilient to theme name updates. For the decision doc, see edx_django_utils/monitoring/docs/decisions/0004-code-owner-theme-and-squad.rst.
28+
2429
[3.16.0] - 2021-03-24
2530
---------------------
2631

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Code Owner Theme and Squad
2+
==========================
3+
4+
Status
5+
------
6+
7+
Accepted
8+
9+
Context
10+
-------
11+
12+
As detailed in the `Monitoring by Code Owner ADR`_, we added a ``code_owner`` custom attribute for monitoring by code owner. The value for this attribute had the format 'theme-squad'.
13+
14+
The problems with this configuration is that for theme name changes, or when squads transfer themes, any monitoring referencing the full name would also need to be updated.
15+
16+
.. _Monitoring by Code Owner ADR: https://github.com/edx/edx-platform/blob/master/lms/djangoapps/monitoring/docs/decisions/0001-monitoring-by-code-owner.rst
17+
18+
Decision
19+
--------
20+
21+
We will add a ``code_owner_squad`` custom attribute. Monitoring will now be able to refer to ``code_owner_squad`` with a value of 'squad', and will be unaffected by theme name changes.
22+
23+
Additionally, we are adding ``code_owner_theme`` for similar convenience if there is a need for theme-based monitoring.
24+
25+
We will leave the original ``code_owner`` custom attribute for backward compatability, and for cases where theme and squad are not used.
26+
27+
Consequences
28+
------------
29+
30+
* Theme name changes may now result in a one time fix for those that were relying on ``code_owner``. Monitoring can switch from ``code_owner`` to ``code_owner_squad``, rather than a change for every theme update in the future.

edx_django_utils/monitoring/docs/how_tos/add_code_owner_custom_attribute_to_an_ida.rst

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@ Add Code_Owner Custom Attribute to an IDA
55
:local:
66
:depth: 2
77

8-
What is the code_owner custom attribute?
9-
----------------------------------------
8+
What are the code owner custom attributes?
9+
------------------------------------------
1010

11-
The code_owner custom attribute can be used to create custom dashboards and alerts for monitoring the things that you own. It was originally introduced for the LMS, as is described in this `ADR on monitoring by code owner`_.
11+
The code owner custom attributes can be used to create custom dashboards and alerts for monitoring the things that you own. It was originally introduced for the LMS, as is described in this `ADR on monitoring by code owner`_.
12+
13+
The code owner custom attributes consist of:
14+
15+
* code_owner: The owner name. When themes and squads are used, this will be the theme and squad names joined by a hyphen.
16+
* code_owner_theme: The theme name of the owner.
17+
* code_owner_squad: The squad name of the owner. Use this to avoid issues when theme name changes.
1218

1319
You can now easily add this same attribute to any IDA so that your dashboards and alerts can work across multiple IDAs at once.
1420

15-
If you want to know about custom attributes in general, see: using_custom_attributes.rst.
21+
If you want to know about custom attributes in general, see :doc:`using_custom_attributes`.
1622

1723
.. _ADR on monitoring by code owner: https://github.com/edx/edx-platform/blob/master/lms/djangoapps/monitoring/docs/decisions/0001-monitoring-by-code-owner.rst
1824

@@ -24,7 +30,7 @@ You simply need to add ``edx_django_utils.monitoring.CodeOwnerMonitoringMiddlewa
2430
Handling celery tasks
2531
---------------------
2632

27-
Celery tasks require use of a special decorator to set the ``code_owner`` custom attribute because no middleware will be run.
33+
Celery tasks require use of a special decorator to set the ``code_owner`` custom attributes because no middleware will be run.
2834

2935
Here is an example::
3036

@@ -47,23 +53,29 @@ An untested potential alternative to the decorator is documented in the `Code Ow
4753
Configuring your app settings
4854
-----------------------------
4955

50-
Once the Middleware is made available, simply set the Django Setting ``CODE_OWNER_MAPPINGS`` appropriately.
56+
Once the Middleware is made available, simply set the Django Settings ``CODE_OWNER_MAPPINGS`` and ``CODE_OWNER_THEMES`` appropriately.
5157

5258
The following example shows how you can include an optional config for a catch-all using ``'*'``. Although you might expect this example to use Python, it is intentionally illustrated in YAML because the catch-all requires special care in YAML.
5359

5460
::
5561

5662
# YAML format of example CODE_OWNER_MAPPINGS
5763
CODE_OWNER_MAPPINGS:
58-
team-red:
64+
theme-x-team-red:
5965
- xblock_django
6066
- openedx.core.djangoapps.xblock
61-
team-blue:
67+
theme-x-team-blue:
6268
- '*' # IMPORTANT: you must surround * with quotes in yml
6369

70+
# YAML format of example CODE_OWNER_THEMES
71+
CODE_OWNER_THEMES:
72+
theme-x:
73+
- theme-x-team-red
74+
- theme-x-team-blue
75+
6476
How to find and fix code_owner mappings
6577
---------------------------------------
6678

67-
If you are missing the `code_owner` custom attribute on a particular Transaction or Error, or if `code_owner` is matching the catch-all, but you want to add a more specific mapping, you can use the other `code_owner supporting attributes`_ to determine what the appropriate mappings should be.
79+
If you are missing the `code_owner` custom attributes on a particular Transaction or Error, or if `code_owner` is matching the catch-all, but you want to add a more specific mapping, you can use the other `code_owner supporting attributes`_ to determine what the appropriate mappings should be.
6880

6981
.. _code_owner supporting attributes: https://github.com/edx/edx-django-utils/blob/7db8301af21760f8bca188b3c6c95a8ae873baf7/edx_django_utils/monitoring/code_owner/middleware.py#L28-L34

edx_django_utils/monitoring/internal/code_owner/middleware.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88

99
from ..transactions import get_current_transaction
1010
from ..utils import set_custom_attribute
11-
from .utils import _get_catch_all_code_owner, get_code_owner_from_module, is_code_owner_mappings_configured
11+
from .utils import (
12+
_get_catch_all_code_owner,
13+
get_code_owner_from_module,
14+
is_code_owner_mappings_configured,
15+
set_code_owner_custom_attributes
16+
)
1217

1318
log = logging.getLogger(__name__)
1419

@@ -52,7 +57,7 @@ def _set_code_owner_attribute(self, request):
5257
code_owner = _get_catch_all_code_owner()
5358

5459
if code_owner:
55-
set_custom_attribute('code_owner', code_owner)
60+
set_code_owner_custom_attributes(code_owner)
5661

5762
def _get_module_from_request(self, request):
5863
"""

edx_django_utils/monitoring/internal/code_owner/utils.py

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,22 @@ def set_code_owner_attribute_from_module(module):
149149
code_owner = _get_catch_all_code_owner()
150150

151151
if code_owner:
152-
set_custom_attribute('code_owner', code_owner)
152+
set_code_owner_custom_attributes(code_owner)
153+
154+
155+
def set_code_owner_custom_attributes(code_owner):
156+
"""
157+
Sets custom metrics for code_owner, code_owner_theme, and code_owner_squad
158+
"""
159+
if not code_owner: # pragma: no cover
160+
return
161+
set_custom_attribute('code_owner', code_owner)
162+
theme = _get_theme_from_code_owner(code_owner)
163+
if theme:
164+
set_custom_attribute('code_owner_theme', theme)
165+
squad = _get_squad_from_code_owner(code_owner)
166+
if squad:
167+
set_custom_attribute('code_owner_squad', squad)
153168

154169

155170
def set_code_owner_attribute(wrapped_function):
@@ -182,12 +197,108 @@ def new_function(*args, **kwargs):
182197

183198
def clear_cached_mappings():
184199
"""
185-
Clears the cached path to code owner mappings. Useful for testing.
200+
Clears the cached code owner mappings. Useful for testing.
186201
"""
187202
global _PATH_TO_CODE_OWNER_MAPPINGS
188203
_PATH_TO_CODE_OWNER_MAPPINGS = None
204+
global _CODE_OWNER_TO_THEME_AND_SQUAD_MAPPINGS
205+
_CODE_OWNER_TO_THEME_AND_SQUAD_MAPPINGS = None
189206

190207

191208
# TODO: Retire this once edx-platform import_shims is no longer used.
209+
# Note: This should be ready for removal because import_shims has been removed.
192210
# See https://github.com/edx/edx-platform/tree/854502b560bda74ef898501bb2a95ce238cf794c/import_shims
193211
_OPTIONAL_MODULE_PREFIX_PATTERN = re.compile(r'^(lms|common|openedx\.core)\.djangoapps\.')
212+
213+
214+
# Cached lookup table for code owner theme and squad given a code owner.
215+
# - Although code owner is "theme-squad", a hyphen may also be in the theme or squad name, so this ensures we get both
216+
# correctly from config.
217+
# Do not access this directly, but instead use get_code_owner_theme_squad_mappings.
218+
_CODE_OWNER_TO_THEME_AND_SQUAD_MAPPINGS = None
219+
220+
221+
def get_code_owner_theme_squad_mappings():
222+
"""
223+
Returns the contents of the CODE_OWNER_THEMES Django Setting, processed
224+
for efficient lookup by path.
225+
226+
Returns:
227+
(dict): dict mapping code owners to a dict containing the squad and theme, or
228+
an empty dict if there are no configured mappings.
229+
230+
Example return value::
231+
232+
{
233+
'theme-x-team-red': {
234+
'theme': 'theme-x',
235+
'squad': 'team-red',
236+
},
237+
'theme-x-team-blue': {
238+
'theme': 'theme-x',
239+
'squad': 'team-blue',
240+
},
241+
}
242+
243+
"""
244+
global _CODE_OWNER_TO_THEME_AND_SQUAD_MAPPINGS
245+
246+
# Return cached processed mappings if already processed
247+
if _CODE_OWNER_TO_THEME_AND_SQUAD_MAPPINGS is not None:
248+
return _CODE_OWNER_TO_THEME_AND_SQUAD_MAPPINGS
249+
250+
# Uses temporary variable to build mappings to avoid multi-threading issue with a partially
251+
# processed map. Worst case, it is processed more than once at start-up.
252+
code_owner_to_theme_and_squad_mapping = {}
253+
254+
# .. setting_name: CODE_OWNER_THEMES
255+
# .. setting_default: None
256+
# .. setting_description: Used for monitoring and reporting of ownership. Use a
257+
# dict with keys of code owner themes and values as a list of code owner names
258+
# including theme and squad, separated with a hyphen.
259+
code_owner_themes = getattr(settings, 'CODE_OWNER_THEMES', {})
260+
261+
try:
262+
for theme in code_owner_themes:
263+
code_owner_list = code_owner_themes[theme]
264+
for code_owner in code_owner_list:
265+
squad = code_owner.split(theme + '-', 1)[1]
266+
code_owner_details = {
267+
'theme': theme,
268+
'squad': squad,
269+
}
270+
code_owner_to_theme_and_squad_mapping[code_owner] = code_owner_details
271+
except TypeError as e:
272+
log.exception('Error processing CODE_OWNER_THEMES setting. {}'.format(e))
273+
raise e
274+
275+
_CODE_OWNER_TO_THEME_AND_SQUAD_MAPPINGS = code_owner_to_theme_and_squad_mapping
276+
return _CODE_OWNER_TO_THEME_AND_SQUAD_MAPPINGS
277+
278+
279+
def _get_theme_from_code_owner(code_owner):
280+
"""
281+
Returns theme for a code_owner (e.g. 'theme-my-squad' => 'theme')
282+
"""
283+
mappings = get_code_owner_theme_squad_mappings()
284+
if mappings is None: # pragma: no cover
285+
return None
286+
287+
if code_owner in mappings:
288+
return mappings[code_owner]['theme']
289+
290+
return None
291+
292+
293+
def _get_squad_from_code_owner(code_owner):
294+
"""
295+
Returns squad for a code_owner (e.g. 'theme-my-squad' => 'my-squad')
296+
"""
297+
mappings = get_code_owner_theme_squad_mappings()
298+
if mappings is None: # pragma: no cover
299+
return None
300+
301+
if code_owner in mappings:
302+
return mappings[code_owner]['squad']
303+
304+
return None

0 commit comments

Comments
 (0)