Skip to content

Commit

Permalink
v1.12.1
Browse files Browse the repository at this point in the history
  • Loading branch information
joeyorlando authored Nov 6, 2024
2 parents 7dc55a9 + 53ac2bc commit ffb32d7
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,28 @@
def populate_slack_channel(apps, schema_editor):
ChannelFilter = apps.get_model("alerts", "ChannelFilter")
SlackChannel = apps.get_model("slack", "SlackChannel")
AlertReceiveChannel = apps.get_model("alerts", "AlertReceiveChannel")
Organization = apps.get_model("user_management", "Organization")

logger.info("Starting migration to populate slack_channel field.")

queryset = ChannelFilter.objects.filter(
_slack_channel_id__isnull=False,
alert_receive_channel__organization__slack_team_identity__isnull=False,
)
total_channel_filters = queryset.count()
updated_channel_filters = 0
missing_channel_filters = 0
channel_filters_to_update = []

logger.info(f"Total channel filters to process: {total_channel_filters}")

for channel_filter in queryset:
slack_id = channel_filter._slack_channel_id
slack_team_identity = channel_filter.alert_receive_channel.organization.slack_team_identity

try:
slack_channel = SlackChannel.objects.get(slack_id=slack_id, slack_team_identity=slack_team_identity)
channel_filter.slack_channel = slack_channel
channel_filters_to_update.append(channel_filter)

updated_channel_filters += 1
logger.info(
f"ChannelFilter {channel_filter.id} updated with SlackChannel {slack_channel.id} "
f"(slack_id: {slack_id})."
)
except SlackChannel.DoesNotExist:
missing_channel_filters += 1
logger.warning(
f"SlackChannel with slack_id {slack_id} and slack_team_identity {slack_team_identity} "
f"does not exist for ChannelFilter {channel_filter.id}."
)

if channel_filters_to_update:
ChannelFilter.objects.bulk_update(channel_filters_to_update, ["slack_channel"])
logger.info(f"Bulk updated {len(channel_filters_to_update)} ChannelFilters with their Slack channel.")

logger.info(
f"Finished migration. Total channel filters processed: {total_channel_filters}. "
f"Channel filters updated: {updated_channel_filters}. Missing SlackChannels: {missing_channel_filters}."
)
sql = f"""
UPDATE {ChannelFilter._meta.db_table} AS cf
JOIN {AlertReceiveChannel._meta.db_table} AS arc ON arc.id = cf.alert_receive_channel_id
JOIN {Organization._meta.db_table} AS org ON org.id = arc.organization_id
JOIN {SlackChannel._meta.db_table} AS sc ON sc.slack_id = cf._slack_channel_id
AND sc.slack_team_identity_id = org.slack_team_identity_id
SET cf.slack_channel_id = sc.id
WHERE cf._slack_channel_id IS NOT NULL
AND org.slack_team_identity_id IS NOT NULL;
"""

with schema_editor.connection.cursor() as cursor:
cursor.execute(sql)
updated_rows = cursor.rowcount # Number of rows updated

logger.info(f"Bulk updated {updated_rows} ChannelFilters with their Slack channel.")
logger.info("Finished migration to populate slack_channel field.")


class Migration(migrations.Migration):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,30 @@
def populate_slack_channel(apps, schema_editor):
ResolutionNoteSlackMessage = apps.get_model("alerts", "ResolutionNoteSlackMessage")
SlackChannel = apps.get_model("slack", "SlackChannel")
AlertGroup = apps.get_model("alerts", "AlertGroup")
AlertReceiveChannel = apps.get_model("alerts", "AlertReceiveChannel")
Organization = apps.get_model("user_management", "Organization")

logger.info("Starting migration to populate slack_channel field.")

queryset = ResolutionNoteSlackMessage.objects.filter(
_slack_channel_id__isnull=False,
alert_group__channel__organization__slack_team_identity__isnull=False,
)
total_resolution_notes = queryset.count()
updated_resolution_notes = 0
missing_resolution_notes = 0
resolution_notes_to_update = []

logger.info(f"Total resolution note slack messages to process: {total_resolution_notes}")

for resolution_note in queryset:
slack_id = resolution_note._slack_channel_id
slack_team_identity = resolution_note.alert_group.channel.organization.slack_team_identity

try:
slack_channel = SlackChannel.objects.get(slack_id=slack_id, slack_team_identity=slack_team_identity)
resolution_note.slack_channel = slack_channel
resolution_notes_to_update.append(resolution_note)

updated_resolution_notes += 1
logger.info(
f"ResolutionNoteSlackMessage {resolution_note.id} updated with SlackChannel {slack_channel.id} "
f"(slack_id: {slack_id})."
)
except SlackChannel.DoesNotExist:
missing_resolution_notes += 1
logger.warning(
f"SlackChannel with slack_id {slack_id} and slack_team_identity {slack_team_identity} "
f"does not exist for ResolutionNoteSlackMessage {resolution_note.id}."
)

if resolution_notes_to_update:
ResolutionNoteSlackMessage.objects.bulk_update(resolution_notes_to_update, ["slack_channel"])
logger.info(
f"Bulk updated {len(resolution_notes_to_update)} ResolutionNoteSlackMessage with their Slack channel."
)

logger.info(
f"Finished migration. Total resolution note slack messages processed: {total_resolution_notes}. "
f"Resolution note slack messages updated: {updated_resolution_notes}. "
f"Missing SlackChannels: {missing_resolution_notes}."
)

sql = f"""
UPDATE {ResolutionNoteSlackMessage._meta.db_table} AS rsm
JOIN {AlertGroup._meta.db_table} AS ag ON ag.id = rsm.alert_group_id
JOIN {AlertReceiveChannel._meta.db_table} AS arc ON arc.id = ag.channel_id
JOIN {Organization._meta.db_table} AS org ON org.id = arc.organization_id
JOIN {SlackChannel._meta.db_table} AS sc ON sc.slack_id = rsm._slack_channel_id
AND sc.slack_team_identity_id = org.slack_team_identity_id
SET rsm.slack_channel_id = sc.id
WHERE rsm._slack_channel_id IS NOT NULL
AND org.slack_team_identity_id IS NOT NULL;
"""

with schema_editor.connection.cursor() as cursor:
cursor.execute(sql)
updated_rows = cursor.rowcount # Number of rows updated

logger.info(f"Bulk updated {updated_rows} ResolutionNoteSlackMessage records with their Slack channel.")
logger.info("Finished migration to populate slack_channel field.")


class Migration(migrations.Migration):
Expand Down
3 changes: 3 additions & 0 deletions engine/apps/api/serializers/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class OrganizationSerializer(EagerLoadingMixin, serializers.ModelSerializer):

rbac_enabled = serializers.BooleanField(read_only=True, source="is_rbac_permissions_enabled")
grafana_incident_enabled = serializers.BooleanField(read_only=True, source="is_grafana_incident_enabled")
grafana_irm_enabled = serializers.BooleanField(read_only=True, source="is_grafana_irm_enabled")

SELECT_RELATED = ["slack_team_identity", "slack_channel"]

Expand All @@ -39,13 +40,15 @@ class Meta:
"slack_channel",
"rbac_enabled",
"grafana_incident_enabled",
"grafana_irm_enabled",
"direct_paging_prefer_important_policy",
]
read_only_fields = [
"stack_slug",
"slack_team_identity",
"rbac_enabled",
"grafana_incident_enabled",
"grafana_irm_enabled",
]


Expand Down
33 changes: 29 additions & 4 deletions engine/apps/api/tests/test_organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,23 @@ def test_get_organization(

client = APIClient()
url = reverse("api-internal:api-organization")
expected_result = {
response = client.get(url, format="json", **make_user_auth_headers(user, token))

assert response.status_code == status.HTTP_200_OK
assert response.json() == {
"pk": organization.public_primary_key,
"name": organization.org_title,
"stack_slug": organization.stack_slug,
"slack_team_identity": None,
"slack_channel": None,
"rbac_enabled": organization.is_rbac_permissions_enabled,
"grafana_incident_enabled": organization.is_grafana_incident_enabled,
"grafana_irm_enabled": organization.is_grafana_irm_enabled,
"direct_paging_prefer_important_policy": organization.direct_paging_prefer_important_policy,
"is_resolution_note_required": False,
"env_status": mock_env_status,
"banner": mock_banner,
}
response = client.get(url, format="json", **make_user_auth_headers(user, token))
assert response.status_code == status.HTTP_200_OK
assert response.json() == expected_result


@pytest.mark.django_db
Expand All @@ -70,6 +71,30 @@ def test_get_organization_rbac_enabled(make_organization_and_user_with_plugin_to
assert response.json()["rbac_enabled"] == organization.is_rbac_permissions_enabled


# NOTE: we need to patch the following because when is_grafana_irm_enabled is True, it alters how
# API authz works. For the purpose of this test, we don't care about testing that behaviour (it's already tested),
# just want to test the serializer essentially.
@patch("apps.api.permissions.user_is_authorized", return_value=True)
@pytest.mark.django_db
@pytest.mark.parametrize("is_grafana_irm_enabled", [True, False])
def test_get_organization_grafana_irm_enabled(
_mock_user_is_authorized,
make_organization_and_user_with_plugin_token,
make_user_auth_headers,
is_grafana_irm_enabled,
):
organization, user, token = make_organization_and_user_with_plugin_token()
organization.is_grafana_irm_enabled = is_grafana_irm_enabled
organization.save()

client = APIClient()
url = reverse("api-internal:api-organization")

response = client.get(url, format="json", **make_user_auth_headers(user, token))
assert response.status_code == status.HTTP_200_OK
assert response.json()["grafana_irm_enabled"] is is_grafana_irm_enabled


@pytest.mark.django_db
def test_update_organization_settings(make_organization_and_user_with_plugin_token, make_user_auth_headers):
organization, user, token = make_organization_and_user_with_plugin_token()
Expand Down
55 changes: 17 additions & 38 deletions engine/apps/schedules/migrations/0019_auto_20241021_1735.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,47 +9,26 @@
def populate_slack_channel(apps, schema_editor):
OnCallSchedule = apps.get_model("schedules", "OnCallSchedule")
SlackChannel = apps.get_model("slack", "SlackChannel")
Organization = apps.get_model("user_management", "Organization")

logger.info("Starting migration to populate slack_channel field.")

queryset = OnCallSchedule.objects.filter(channel__isnull=False, organization__slack_team_identity__isnull=False)
total_schedules = queryset.count()
updated_schedules = 0
missing_channels = 0
schedules_to_update = []

logger.info(f"Total schedules to process: {total_schedules}")

for schedule in queryset:
slack_id = schedule.channel
slack_team_identity = schedule.organization.slack_team_identity

try:
slack_channel = SlackChannel.objects.get(slack_id=slack_id, slack_team_identity=slack_team_identity)

schedule.slack_channel = slack_channel
schedules_to_update.append(schedule)

updated_schedules += 1
logger.info(
f"Schedule {schedule.id} updated with SlackChannel {slack_channel.id} (slack_id: {slack_id})."
)
except SlackChannel.DoesNotExist:
missing_channels += 1
logger.warning(
f"SlackChannel with slack_id {slack_id} and slack_team_identity {slack_team_identity} "
f"does not exist for Schedule {schedule.id}."
)

if schedules_to_update:
OnCallSchedule.objects.bulk_update(schedules_to_update, ["slack_channel"])
logger.info(f"Bulk updated {len(schedules_to_update)} OnCallSchedules with their Slack channel.")

logger.info(
f"Finished migration. Total schedules processed: {total_schedules}. "
f"Schedules updated: {updated_schedules}. Missing SlackChannels: {missing_channels}."
)

sql = f"""
UPDATE {OnCallSchedule._meta.db_table} AS ocs
JOIN {Organization._meta.db_table} AS org ON org.id = ocs.organization_id
JOIN {SlackChannel._meta.db_table} AS sc ON sc.slack_id = ocs.channel
AND sc.slack_team_identity_id = org.slack_team_identity_id
SET ocs.slack_channel_id = sc.id
WHERE ocs.channel IS NOT NULL
AND org.slack_team_identity_id IS NOT NULL;
"""

with schema_editor.connection.cursor() as cursor:
cursor.execute(sql)
updated_rows = cursor.rowcount # Number of rows updated

logger.info(f"Bulk updated {updated_rows} OnCallSchedules with their Slack channel.")
logger.info("Finished migration to populate slack_channel field.")

class Migration(migrations.Migration):

Expand Down
52 changes: 15 additions & 37 deletions engine/apps/user_management/migrations/0026_auto_20241017_1919.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,21 @@ def populate_default_slack_channel(apps, schema_editor):

logger.info("Starting migration to populate default_slack_channel field.")

queryset = Organization.objects.filter(general_log_channel_id__isnull=False, slack_team_identity__isnull=False)
total_orgs = queryset.count()
updated_orgs = 0
missing_channels = 0
organizations_to_update = []

logger.info(f"Total organizations to process: {total_orgs}")

for org in queryset:
slack_id = org.general_log_channel_id
slack_team_identity = org.slack_team_identity

try:
slack_channel = SlackChannel.objects.get(slack_id=slack_id, slack_team_identity=slack_team_identity)

org.default_slack_channel = slack_channel
organizations_to_update.append(org)

updated_orgs += 1
logger.info(
f"Organization {org.id} updated with SlackChannel {slack_channel.id} (slack_id: {slack_id})."
)
except SlackChannel.DoesNotExist:
missing_channels += 1
logger.warning(
f"SlackChannel with slack_id {slack_id} and slack_team_identity {slack_team_identity} "
f"does not exist for Organization {org.id}."
)

if organizations_to_update:
Organization.objects.bulk_update(organizations_to_update, ["default_slack_channel"])
logger.info(f"Bulk updated {len(organizations_to_update)} organizations with their default Slack channel.")

logger.info(
f"Finished migration. Total organizations processed: {total_orgs}. "
f"Organizations updated: {updated_orgs}. Missing SlackChannels: {missing_channels}."
)
sql = f"""
UPDATE {Organization._meta.db_table} AS org
JOIN {SlackChannel._meta.db_table} AS sc ON sc.slack_id = org.general_log_channel_id
AND sc.slack_team_identity_id = org.slack_team_identity_id
SET org.default_slack_channel_id = sc.id
WHERE org.general_log_channel_id IS NOT NULL
AND org.slack_team_identity_id IS NOT NULL;
"""

with schema_editor.connection.cursor() as cursor:
cursor.execute(sql)
updated_rows = cursor.rowcount # Number of rows updated

logger.info(f"Bulk updated {updated_rows} organizations with their default Slack channel.")
logger.info("Finished migration to populate default_slack_channel field.")

class Migration(migrations.Migration):

Expand Down
2 changes: 1 addition & 1 deletion engine/common/insight_log/insight_logs_enabled_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ def is_insight_logs_enabled(organization: "Organization") -> bool:
f"ONCALL_BACKEND_REGION={settings.ONCALL_BACKEND_REGION} "
f"cluster_slug={organization.cluster_slug}"
)
return not settings.IS_OPEN_SOURCE and settings.ONCALL_BACKEND_REGION == organization.cluster_slug
return not settings.IS_OPEN_SOURCE
5 changes: 3 additions & 2 deletions tools/migrators/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
Loading

0 comments on commit ffb32d7

Please sign in to comment.