|
@@ -1,5 +1,6 @@
|
|
|
from __future__ import annotations
|
|
|
|
|
|
+import logging
|
|
|
from collections import namedtuple
|
|
|
from collections.abc import Callable
|
|
|
from datetime import timedelta
|
|
@@ -10,6 +11,7 @@ from uuid import uuid4
|
|
|
from django.conf import settings
|
|
|
from django.core.cache import cache
|
|
|
from django.db import IntegrityError, models, router, transaction
|
|
|
+from django.db.models import QuerySet
|
|
|
from django.db.models.signals import post_delete, post_save
|
|
|
from django.utils import timezone
|
|
|
|
|
@@ -30,13 +32,16 @@ from sentry.db.models import (
|
|
|
)
|
|
|
from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey
|
|
|
from sentry.db.models.manager import BaseManager
|
|
|
+from sentry.incidents.utils.constants import INCIDENTS_SNUBA_SUBSCRIPTION_TYPE
|
|
|
+from sentry.incidents.utils.types import AlertRuleActivationConditionType
|
|
|
from sentry.models.actor import Actor
|
|
|
from sentry.models.notificationaction import AbstractNotificationAction, ActionService, ActionTarget
|
|
|
from sentry.models.organization import Organization
|
|
|
+from sentry.models.project import Project
|
|
|
from sentry.models.team import Team
|
|
|
from sentry.services.hybrid_cloud.user.service import user_service
|
|
|
from sentry.snuba.models import QuerySubscription
|
|
|
-from sentry.snuba.subscriptions import delete_snuba_subscription
|
|
|
+from sentry.snuba.subscriptions import bulk_create_snuba_subscriptions, delete_snuba_subscription
|
|
|
from sentry.utils import metrics
|
|
|
from sentry.utils.retries import TimedRetryPolicy
|
|
|
|
|
@@ -66,6 +71,9 @@ def invoke_alert_subscription_callback(
|
|
|
return callback(subscription)
|
|
|
|
|
|
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
+
|
|
|
+
|
|
|
@region_silo_only_model
|
|
|
class IncidentProject(Model):
|
|
|
__relocation_scope__ = RelocationScope.Excluded
|
|
@@ -427,6 +435,52 @@ class AlertRuleManager(BaseManager["AlertRule"]):
|
|
|
for sub_id in subscription_ids
|
|
|
)
|
|
|
|
|
|
+ def conditionally_subscribe_project_to_alert_rules(
|
|
|
+ self,
|
|
|
+ project: Project,
|
|
|
+ activation_condition: AlertRuleActivationConditionType,
|
|
|
+ query_extra: str,
|
|
|
+ trigger: str,
|
|
|
+ ) -> list[QuerySubscription]:
|
|
|
+ """
|
|
|
+ Subscribes a project to an alert rule given activation condition
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ project_alert_rules: QuerySet[AlertRule] = self.filter(
|
|
|
+ projects=project,
|
|
|
+ monitor_type=AlertRuleMonitorType.ACTIVATED.value,
|
|
|
+ )
|
|
|
+ created_subscriptions = []
|
|
|
+ for alert_rule in project_alert_rules:
|
|
|
+ if alert_rule.activation_conditions.filter(
|
|
|
+ condition_type=activation_condition.value
|
|
|
+ ).exists():
|
|
|
+ logger.info(
|
|
|
+ "Attempt subscribe project to activated alert rule",
|
|
|
+ extra={
|
|
|
+ "trigger": trigger,
|
|
|
+ "query_extra": query_extra,
|
|
|
+ "condition": activation_condition,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ created_subscriptions.extend(
|
|
|
+ alert_rule.subscribe_projects(
|
|
|
+ projects=[project],
|
|
|
+ monitor_type=AlertRuleMonitorType.ACTIVATED,
|
|
|
+ query_extra=query_extra,
|
|
|
+ )
|
|
|
+ )
|
|
|
+ return created_subscriptions
|
|
|
+ except Exception as e:
|
|
|
+ logger.exception(
|
|
|
+ "Failed to subscribe project to activated alert rule",
|
|
|
+ extra={
|
|
|
+ "trigger": trigger,
|
|
|
+ "exception": e,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ return []
|
|
|
+
|
|
|
|
|
|
@region_silo_only_model
|
|
|
class AlertRuleExcludedProjects(Model):
|
|
@@ -567,6 +621,36 @@ class AlertRule(Model):
|
|
|
|
|
|
return old_pk
|
|
|
|
|
|
+ def subscribe_projects(
|
|
|
+ self,
|
|
|
+ projects: list[Project],
|
|
|
+ monitor_type: AlertRuleMonitorType = AlertRuleMonitorType.CONTINUOUS,
|
|
|
+ query_extra: str | None = None,
|
|
|
+ ) -> list[QuerySubscription]:
|
|
|
+ """
|
|
|
+ Subscribes a list of projects to the alert rule instance
|
|
|
+ :return: The list of created subscriptions
|
|
|
+ """
|
|
|
+
|
|
|
+ logger.info(
|
|
|
+ "Subscribing projects to alert rule",
|
|
|
+ extra={
|
|
|
+ "alert_rule.monitor_type": self.monitor_type,
|
|
|
+ "conditional_monitor_type": monitor_type.value,
|
|
|
+ "query_extra": query_extra,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ # NOTE: AlertRuleMonitorType.ACTIVATED will be conditionally subscribed given activation triggers
|
|
|
+ # On activated subscription, additional query parameters will be added to the constructed query in Snuba
|
|
|
+ if self.monitor_type == monitor_type.value:
|
|
|
+ return bulk_create_snuba_subscriptions(
|
|
|
+ projects,
|
|
|
+ INCIDENTS_SNUBA_SUBSCRIPTION_TYPE,
|
|
|
+ self.snuba_query,
|
|
|
+ query_extra,
|
|
|
+ )
|
|
|
+ return []
|
|
|
+
|
|
|
|
|
|
class TriggerStatus(Enum):
|
|
|
ACTIVE = 0
|
|
@@ -687,11 +771,6 @@ class AlertRuleTrigger(Model):
|
|
|
unique_together = (("alert_rule", "label"),)
|
|
|
|
|
|
|
|
|
-class AlertRuleActivationConditionType(Enum):
|
|
|
- RELEASE_CREATION = 0
|
|
|
- DEPLOY_CREATION = 1
|
|
|
-
|
|
|
-
|
|
|
@region_silo_only_model
|
|
|
class AlertRuleActivationCondition(Model):
|
|
|
"""
|