Просмотр исходного кода

migration(monitors): Backfill MonitorEnvironments Correctly (#45724)

Richard Ortenberg 2 лет назад
Родитель
Сommit
f7c50e9ca6

+ 1 - 1
migrations_lockfile.txt

@@ -6,5 +6,5 @@ To resolve this, rebase against latest master and regenerate your migration. Thi
 will then be regenerated, and you should be able to merge without conflicts.
 
 nodestore: 0002_nodestore_no_dictfield
-sentry: 0379_create_notificationaction_model
+sentry: 0380_backfill_monitor_env_initial
 social_auth: 0001_initial

+ 74 - 0
src/sentry/migrations/0380_backfill_monitor_env_initial.py

@@ -0,0 +1,74 @@
+# Generated by Django 2.2.28 on 2023-03-09 23:03
+
+from django.db import migrations
+
+from sentry.new_migrations.migrations import CheckedMigration
+from sentry.utils.query import RangeQuerySetWrapperWithProgressBar
+
+DEFAULT_ENVIRONMENT_NAME = "production"
+
+
+def backfill_monitor_environments(apps, schema_editor):
+    Monitor = apps.get_model("sentry", "Monitor")
+    Environment = apps.get_model("sentry", "Environment")
+    EnvironmentProject = apps.get_model("sentry", "EnvironmentProject")
+    MonitorEnvironment = apps.get_model("sentry", "MonitorEnvironment")
+
+    queryset = RangeQuerySetWrapperWithProgressBar(
+        Monitor.objects.filter(monitorenvironment__isnull=True).values_list(
+            "id", "organization_id", "project_id", "status", "next_checkin", "last_checkin"
+        ),
+        result_value_getter=lambda item: item[0],
+    )
+
+    for monitor_id, organization_id, project_id, status, next_checkin, last_checkin in queryset:
+        environment = Environment.objects.get_or_create(
+            name=DEFAULT_ENVIRONMENT_NAME, organization_id=organization_id
+        )[0]
+
+        EnvironmentProject.objects.get_or_create(
+            project_id=project_id, environment=environment, defaults={"is_hidden": None}
+        )
+
+        monitorenvironment_defaults = {
+            "status": status,
+            "next_checkin": next_checkin,
+            "last_checkin": last_checkin,
+        }
+
+        MonitorEnvironment.objects.get_or_create(
+            monitor_id=monitor_id, environment=environment, defaults=monitorenvironment_defaults
+        )
+
+
+class Migration(CheckedMigration):
+    # This flag is used to mark that a migration shouldn't be automatically run in production. For
+    # the most part, this should only be used for operations where it's safe to run the migration
+    # after your code has deployed. So this should not be used for most operations that alter the
+    # schema of a table.
+    # Here are some things that make sense to mark as dangerous:
+    # - Large data migrations. Typically we want these to be run manually by ops so that they can
+    #   be monitored and not block the deploy for a long period of time while they run.
+    # - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to
+    #   have ops run this and not block the deploy. Note that while adding an index is a schema
+    #   change, it's completely safe to run the operation after the code has deployed.
+    is_dangerous = False
+
+    dependencies = [
+        ("sentry", "0379_create_notificationaction_model"),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            backfill_monitor_environments,
+            migrations.RunPython.noop,
+            hints={
+                "tables": [
+                    "sentry_monitor",
+                    "sentry_monitorenvironment",
+                    "sentry_environment",
+                    "sentry_environmentproject",
+                ]
+            },
+        ),
+    ]

+ 41 - 0
tests/sentry/migrations/test_0380_backfill_monitor_env_initial.py

@@ -0,0 +1,41 @@
+from datetime import timedelta
+
+from django.utils import timezone
+
+from sentry.models import Environment, EnvironmentProject
+from sentry.monitors.models import Monitor, MonitorEnvironment, MonitorType, ScheduleType
+from sentry.testutils.cases import TestMigrations
+
+DEFAULT_ENVIRONMENT_NAME = "production"
+
+
+class MigrateMonitorEnvironmentBackfillInitialTest(TestMigrations):
+    migrate_from = "0379_create_notificationaction_model"
+    migrate_to = "0380_backfill_monitor_env_initial"
+
+    def setup_before_migration(self, apps):
+        self.monitor = Monitor.objects.create(
+            organization_id=self.organization.id,
+            project_id=self.project.id,
+            next_checkin=timezone.now() - timedelta(minutes=1),
+            type=MonitorType.CRON_JOB,
+            config={"schedule": "* * * * *", "schedule_type": ScheduleType.CRONTAB},
+        )
+
+    def test(self):
+        environment = Environment.objects.get(name=DEFAULT_ENVIRONMENT_NAME)
+
+        assert environment is not None
+        assert environment.name == DEFAULT_ENVIRONMENT_NAME
+
+        environment_project = EnvironmentProject.objects.get(
+            environment=environment, project_id=self.monitor.project_id
+        )
+        assert environment_project is not None
+
+        monitor_environment = MonitorEnvironment.objects.filter(monitor=self.monitor)[0]
+
+        assert monitor_environment is not None
+        assert monitor_environment.monitor == self.monitor
+        assert monitor_environment.environment.name == "production"
+        assert monitor_environment.status == self.monitor.status