PD migrator: populate important OnCall notification rules (#5226)

# What this PR does

Makes so that user notification rules from PD are migrated into both
"default" and "important" OnCall user notification rules instead of just
"default".

Before this PR:

<img width="631" alt="Screenshot 2024-11-04 at 16 54 28"
src="https://github.com/user-attachments/assets/1d768736-e752-4c76-bb42-ec4b67260210">

After this PR:

<img width="631" alt="Screenshot 2024-11-04 at 16 54 22"
src="https://github.com/user-attachments/assets/9fdcf895-7eb7-4e7f-842e-87b3dd3fc5ab">

## Which issue(s) this PR closes

Related to [Slack
thread](https://raintank-corp.slack.com/archives/C07HMCM59TK/p1730306579122409?thread_ts=1730303532.031559&cid=C07HMCM59TK)

<!--
*Note*: If you want the issue to be auto-closed once the PR is merged,
change "Related to" to "Closes" in the line above.
If you have more than one GitHub issue that this PR closes, be sure to
preface
each issue link with a [closing
keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue).
This ensures that the issue(s) are auto-closed once the PR has been
merged.
-->

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
    show up in the autogenerated release notes.
This commit is contained in:
Vadim Stepanov 2024-11-05 09:57:49 +00:00 committed by GitHub
parent 686ebbfb37
commit 4871b3a781
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 108 additions and 16 deletions

View file

@ -172,8 +172,9 @@ Configuration is done via environment variables passed to the docker container.
The tool is capable of migrating user notification rules from PagerDuty to Grafana OnCall.
Notification rules from the `"When a high-urgency incident is assigned to me..."` section in PagerDuty settings are
taken into account and will be migrated to default notification rules in Grafana OnCall for each user. Note that delays
between notification rules may be slightly different in Grafana OnCall, see [Limitations](#limitations) for more info.
taken into account and will be migrated to both default and important notification rules in Grafana OnCall
for each user. Note that delays between notification rules may be slightly different in Grafana OnCall,
see [Limitations](#limitations) for more info.
When running the migration, existing notification rules in Grafana OnCall will be deleted for every affected user.

View file

@ -27,21 +27,25 @@ def migrate_notification_rules(user: dict) -> None:
rule for rule in user["notification_rules"] if rule["urgency"] == "high"
]
oncall_rules = transform_notification_rules(
notification_rules, user["oncall_user"]["id"]
)
for important in (False, True):
oncall_rules = transform_notification_rules(
notification_rules, user["oncall_user"]["id"], important
)
for rule in oncall_rules:
OnCallAPIClient.create("personal_notification_rules", rule)
for rule in oncall_rules:
OnCallAPIClient.create("personal_notification_rules", rule)
if oncall_rules:
# delete old notification rules if any new rules were created
for rule in user["oncall_user"]["notification_rules"]:
OnCallAPIClient.delete("personal_notification_rules/{}".format(rule["id"]))
if oncall_rules:
# delete old notification rules if any new rules were created
for rule in user["oncall_user"]["notification_rules"]:
if rule["important"] == important:
OnCallAPIClient.delete(
"personal_notification_rules/{}".format(rule["id"])
)
def transform_notification_rules(
notification_rules: list[dict], user_id: str
notification_rules: list[dict], user_id: str, important: bool
) -> list[dict]:
"""
Transform PagerDuty user notification rules to Grafana OnCall personal notification rules.
@ -58,7 +62,9 @@ def transform_notification_rules(
previous_delay = notification_rules[idx - 1]["start_delay_in_minutes"]
delay -= previous_delay
oncall_notification_rules += transform_notification_rule(rule, delay, user_id)
oncall_notification_rules += transform_notification_rule(
rule, delay, user_id, important
)
oncall_notification_rules = remove_duplicate_rules_between_waits(
oncall_notification_rules
@ -68,12 +74,12 @@ def transform_notification_rules(
def transform_notification_rule(
notification_rule: dict, delay: int, user_id: str
notification_rule: dict, delay: int, user_id: str, important: bool
) -> list[dict]:
contact_method_type = notification_rule["contact_method"]["type"]
oncall_type = PAGERDUTY_TO_ONCALL_CONTACT_METHOD_MAP[contact_method_type]
notify_rule = {"user_id": user_id, "type": oncall_type, "important": False}
notify_rule = {"user_id": user_id, "type": oncall_type, "important": important}
if not delay:
return [notify_rule]
@ -82,6 +88,6 @@ def transform_notification_rule(
"user_id": user_id,
"type": "wait",
"duration": transform_wait_delay(delay),
"important": "False",
"important": important,
}
return [wait_rule, notify_rule]

View file

@ -0,0 +1,85 @@
from unittest.mock import call, patch
from lib.oncall.api_client import OnCallAPIClient
from lib.pagerduty.resources.notification_rules import migrate_notification_rules
@patch.object(OnCallAPIClient, "delete")
@patch.object(OnCallAPIClient, "create")
def test_migrate_notification_rules(api_client_create_mock, api_client_delete_mock):
migrate_notification_rules(
{
"notification_rules": [
{
"contact_method": {"type": "sms_contact_method"},
"start_delay_in_minutes": 0,
"urgency": "high",
},
{
"contact_method": {"type": "push_notification_contact_method"},
"start_delay_in_minutes": 5,
"urgency": "high",
},
],
"oncall_user": {
"id": "EXISTING_USER_ID",
"notification_rules": [
{"id": "EXISTING_RULE_ID_1", "important": False},
{"id": "EXISTING_RULE_ID_2", "important": True},
],
},
}
)
assert api_client_create_mock.call_args_list == [
call(
"personal_notification_rules",
{
"user_id": "EXISTING_USER_ID",
"type": "notify_by_sms",
"important": False,
},
),
call(
"personal_notification_rules",
{
"user_id": "EXISTING_USER_ID",
"type": "wait",
"duration": 300,
"important": False,
},
),
call(
"personal_notification_rules",
{
"user_id": "EXISTING_USER_ID",
"type": "notify_by_mobile_app",
"important": False,
},
),
call(
"personal_notification_rules",
{"user_id": "EXISTING_USER_ID", "type": "notify_by_sms", "important": True},
),
call(
"personal_notification_rules",
{
"user_id": "EXISTING_USER_ID",
"type": "wait",
"duration": 300,
"important": True,
},
),
call(
"personal_notification_rules",
{
"user_id": "EXISTING_USER_ID",
"type": "notify_by_mobile_app",
"important": True,
},
),
]
assert api_client_delete_mock.call_args_list == [
call("personal_notification_rules/EXISTING_RULE_ID_1"),
call("personal_notification_rules/EXISTING_RULE_ID_2"),
]