Browse Source

chore(hybrid-cloud): Split db for deletion tests (#54745)

Alberto Leal 1 year ago
parent
commit
2a9d532e3c

+ 6 - 7
src/sentry/deletions/base.py

@@ -1,7 +1,11 @@
+from __future__ import annotations
+
 import logging
 import re
 
 from sentry.constants import ObjectStatus
+from sentry.services.hybrid_cloud.user.model import RpcUser
+from sentry.services.hybrid_cloud.user.service import user_service
 from sentry.utils import metrics
 from sentry.utils.query import bulk_delete_objects
 
@@ -237,14 +241,9 @@ class ModelDeletionTask(BaseDeletionTask):
                     },
                 )
 
-    def get_actor(self):
-        from sentry.models import User
-
+    def get_actor(self) -> RpcUser | None:
         if self.actor_id:
-            try:
-                return User.objects.get_from_cache(id=self.actor_id)
-            except User.DoesNotExist:
-                pass
+            return user_service.get_user(user_id=self.actor_id)
         return None
 
     def mark_deletion_in_progress(self, instance_list):

+ 8 - 18
src/sentry/deletions/defaults/organizationintegration.py

@@ -1,4 +1,6 @@
 from sentry.constants import ObjectStatus
+from sentry.models.integrations.organization_integration import OrganizationIntegration
+from sentry.services.hybrid_cloud.repository import repository_service
 
 from ..base import ModelDeletionTask, ModelRelation
 
@@ -18,22 +20,10 @@ class OrganizationIntegrationDeletionTask(ModelDeletionTask):
 
         return relations
 
-    def delete_instance(self, instance):
-        from sentry.models import ProjectCodeOwners, Repository, RepositoryProjectPathConfig
-
-        # Dissociate repos from the integration being deleted. integration
-        Repository.objects.filter(
-            organization_id=instance.organization_id, integration_id=instance.integration_id
-        ).update(integration_id=None)
-
-        # Delete Code Owners with a Code Mapping using the OrganizationIntegration
-        ProjectCodeOwners.objects.filter(
-            repository_project_path_config__in=RepositoryProjectPathConfig.objects.filter(
-                organization_integration_id=instance.id
-            ).values_list("id", flat=True)
-        ).delete()
-
-        # Delete the Code Mappings
-        RepositoryProjectPathConfig.objects.filter(organization_integration_id=instance.id).delete()
-
+    def delete_instance(self, instance: OrganizationIntegration):
+        repository_service.disassociate_organization_integration(
+            organization_id=instance.organization_id,
+            organization_integration_id=instance.id,
+            integration_id=instance.integration_id,
+        )
         return super().delete_instance(instance)

+ 1 - 1
src/sentry/models/integrations/organization_integration.py

@@ -55,7 +55,7 @@ class OrganizationIntegration(DefaultFieldsModel):
         ):
             for outbox in self.outboxes_for_update():
                 outbox.save()
-            super().delete(*args, **kwds)
+            return super().delete(*args, **kwds)
 
     @staticmethod
     def services_in(config: Mapping[str, Any]) -> List[PagerDutyServiceDict]:

+ 3 - 2
src/sentry/models/project.py

@@ -35,6 +35,7 @@ from sentry.locks import locks
 from sentry.models.grouplink import GroupLink
 from sentry.models.options.option import OptionMixin
 from sentry.models.outbox import OutboxCategory, OutboxScope, RegionOutbox, outbox_context
+from sentry.services.hybrid_cloud.notifications import notifications_service
 from sentry.services.hybrid_cloud.user import RpcUser
 from sentry.services.hybrid_cloud.user.service import user_service
 from sentry.snuba.models import SnubaQuery
@@ -554,10 +555,10 @@ class Project(Model, PendingDeletionMixin, OptionMixin, SnowflakeIdMixin):
         )
 
     def delete(self, **kwargs):
-        from sentry.models import NotificationSetting
 
         # There is no foreign key relationship so we have to manually cascade.
-        NotificationSetting.objects.remove_for_project(self)
+        notifications_service.remove_notification_settings_for_project(project_id=self.id)
+
         with outbox_context(transaction.atomic(router.db_for_write(Project))):
             Project.outbox_for_update(self.id, self.organization_id).save()
             return super().delete(**kwargs)

+ 2 - 2
src/sentry/notifications/manager.py

@@ -260,12 +260,12 @@ class NotificationsManager(BaseManager["NotificationSetting"]):  # noqa: F821
         self._filter(team_ids=[team.id], provider=provider, type=type).delete()
 
     def remove_for_project(
-        self, project: Project, type: NotificationSettingTypes | None = None
+        self, project_id: int, type: NotificationSettingTypes | None = None
     ) -> None:
         """Bulk delete all Notification Settings for a PROJECT, optionally by type."""
         self._filter(
             scope_type=NotificationScopeType.PROJECT,
-            scope_identifier=project.id,
+            scope_identifier=project_id,
             type=type,
         ).delete()
 

+ 3 - 0
src/sentry/services/hybrid_cloud/notifications/impl.py

@@ -175,6 +175,9 @@ class DatabaseBackedNotificationsService(NotificationsService):
         assert organization_id, "organization_id must be a positive integer"
         NotificationSetting.objects.remove_for_organization(organization_id=organization_id)
 
+    def remove_notification_settings_for_project(self, *, project_id: int) -> None:
+        NotificationSetting.objects.remove_for_project(project_id=project_id)
+
     def serialize_many(
         self,
         *,

+ 5 - 0
src/sentry/services/hybrid_cloud/notifications/service.py

@@ -130,6 +130,11 @@ class NotificationsService(RpcService):
     def remove_notification_settings_for_organization(self, *, organization_id: int) -> None:
         pass
 
+    @rpc_method
+    @abstractmethod
+    def remove_notification_settings_for_project(self, *, project_id: int) -> None:
+        pass
+
 
 notifications_service: NotificationsService = cast(
     NotificationsService, NotificationsService.create_delegation()

+ 27 - 0
src/sentry/services/hybrid_cloud/repository/impl.py

@@ -7,6 +7,8 @@ from django.db import IntegrityError, router, transaction
 from sentry.api.serializers import serialize
 from sentry.constants import ObjectStatus
 from sentry.models import Repository
+from sentry.models.integrations.repository_project_path_config import RepositoryProjectPathConfig
+from sentry.models.projectcodeowners import ProjectCodeOwners
 from sentry.services.hybrid_cloud.repository import RepositoryService, RpcRepository
 from sentry.services.hybrid_cloud.repository.model import RpcCreateRepository
 from sentry.services.hybrid_cloud.repository.serial import serialize_repository
@@ -95,3 +97,28 @@ class DatabaseBackedRepositoryService(RepositoryService):
                 integration_id=integration_id,
                 provider=provider,
             ).update(status=ObjectStatus.ACTIVE)
+
+    def disassociate_organization_integration(
+        self,
+        *,
+        organization_id: int,
+        organization_integration_id: int,
+        integration_id: int,
+    ) -> None:
+        with transaction.atomic(router.db_for_write(Repository)):
+            # Disassociate repos from the organization integration being deleted
+            Repository.objects.filter(
+                organization_id=organization_id, integration_id=integration_id
+            ).update(integration_id=None)
+
+            # Delete Code Owners with a Code Mapping using the OrganizationIntegration
+            ProjectCodeOwners.objects.filter(
+                repository_project_path_config__in=RepositoryProjectPathConfig.objects.filter(
+                    organization_integration_id=organization_integration_id
+                ).values_list("id", flat=True)
+            ).delete()
+
+            # Delete the Code Mappings
+            RepositoryProjectPathConfig.objects.filter(
+                organization_integration_id=organization_integration_id
+            ).delete()

+ 14 - 0
src/sentry/services/hybrid_cloud/repository/service.py

@@ -81,5 +81,19 @@ class RepositoryService(RpcService):
         """
         pass
 
+    @regional_rpc_method(resolve=ByOrganizationId())
+    @abstractmethod
+    def disassociate_organization_integration(
+        self,
+        *,
+        organization_id: int,
+        organization_integration_id: int,
+        integration_id: int,
+    ) -> None:
+        """
+        Disassociates all repositories, code owners, and code mapping associated with the given organization integration.
+        """
+        pass
+
 
 repository_service = cast(RepositoryService, RepositoryService.create_delegation())

+ 1 - 1
src/sentry/testutils/factories.py

@@ -1100,7 +1100,7 @@ class Factories:
         }
 
     @staticmethod
-    @assume_test_silo_mode(SiloMode.CONTROL)
+    @assume_test_silo_mode(SiloMode.REGION)
     def create_service_hook(actor=None, org=None, project=None, events=None, url=None, **kwargs):
         if not actor:
             actor = Factories.create_user()

Some files were not shown because too many files changed in this diff