Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aci): Add helpers to dual write to Actions and DataConditions #82492

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 133 additions & 1 deletion src/sentry/workflow_engine/migration_helpers/alert_rule.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,152 @@
import logging

from sentry.incidents.grouptype import MetricAlertFire
from sentry.incidents.models.alert_rule import AlertRule
from sentry.incidents.models.alert_rule import (
AlertRule,
AlertRuleThresholdType,
AlertRuleTrigger,
AlertRuleTriggerAction,
)
from sentry.integrations.services.integration import integration_service
from sentry.snuba.models import QuerySubscription, SnubaQuery
from sentry.users.services.user import RpcUser
from sentry.workflow_engine.models import (
Action,
AlertRuleDetector,
AlertRuleTriggerDataCondition,
AlertRuleWorkflow,
DataCondition,
DataConditionGroup,
DataConditionGroupAction,
DataSource,
Detector,
DetectorState,
DetectorWorkflow,
Workflow,
WorkflowDataConditionGroup,
)
from sentry.workflow_engine.models.data_condition import Condition
from sentry.workflow_engine.types import DetectorPriorityLevel

logger = logging.getLogger(__name__)


def get_action_type(alert_rule_trigger_action: AlertRuleTriggerAction) -> str | None:
if alert_rule_trigger_action.sentry_app_id:
return Action.Type.SENTRY_APP

elif alert_rule_trigger_action.integration_id:
integration = integration_service.get_integration(
integration_id=alert_rule_trigger_action.integration_id
)
if not integration:
return None
for type in Action.Type.choices:
if type[0] == integration.provider:
return type[1]
return None
Comment on lines +44 to +47
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this could be something like

Suggested change
for type in Action.Type.choices:
if type[0] == integration.provider:
return type[1]
return None
try:
return Action.Type(integration.provider)
except Exception:
return None

else:
return Action.Type.EMAIL


def migrate_metric_action(
alert_rule_trigger_action: AlertRuleTriggerAction,
) -> tuple[Action, DataConditionGroupAction] | None:
try:
alert_rule_trigger_data_condition = AlertRuleTriggerDataCondition.objects.get(
alert_rule_trigger=alert_rule_trigger_action.alert_rule_trigger
)
except AlertRuleTriggerDataCondition.DoesNotExist:
logger.exception(
"AlertRuleTriggerDataCondition does not exist",
extra={"alert_rule_trigger_id": alert_rule_trigger_action.alert_rule_trigger.id},
)
return None

action_type = get_action_type(alert_rule_trigger_action)
if not action_type:
logger.warning(
"Could not find a matching Action.Type for the trigger action",
extra={"alert_rule_trigger_action_id": alert_rule_trigger_action.id},
)
return None

data = {
"type": alert_rule_trigger_action.type,
"sentry_app_id": alert_rule_trigger_action.sentry_app_id,
"sentry_app_config": alert_rule_trigger_action.sentry_app_config,
}
action = Action.objects.create(
required=False,
type=action_type,
data=data,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does Action.data need json schema validation too? it's a JSONField cc @iamrajjoshi

integration_id=alert_rule_trigger_action.integration_id,
target_display=alert_rule_trigger_action.target_display,
target_identifier=alert_rule_trigger_action.target_identifier,
target_type=alert_rule_trigger_action.target_type,
legacy_notification_type=Action.LegacyNotificationType.METRIC_ALERT,
)
data_condition_group_action = DataConditionGroupAction.objects.create(
condition_group_id=alert_rule_trigger_data_condition.data_condition.condition_group.id,
action_id=action.id,
)
return action, data_condition_group_action


def migrate_metric_data_condition(
alert_rule_trigger: AlertRuleTrigger,
) -> tuple[DataCondition, AlertRuleTriggerDataCondition] | None:
try:
alert_rule_detector = AlertRuleDetector.objects.get(
alert_rule=alert_rule_trigger.alert_rule
)
except AlertRuleDetector.DoesNotExist:
logger.exception(
"AlertRuleDetector does not exist",
extra={"alert_rule_id": alert_rule_trigger.alert_rule.id},
)
return None

threshold_to_condition = {
AlertRuleThresholdType.ABOVE.value: Condition.GREATER,
AlertRuleThresholdType.BELOW.value: Condition.LESS,
# TODO add ABOVE_AND_BELOW for anomaly detection
}

data_condition_group = alert_rule_detector.detector.workflow_condition_group
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there are multiple triggers, should they each be their own DataConditionGroup? they also have separate actions which need to be connected to them via DataConditionGroupAction. from my understanding the when_condition_group does not have actions associated with it

if not data_condition_group:
logger.warning(
"Could not find data_condition_group",
extra={"detector_id": alert_rule_detector.id},
)
return None

condition_result = (
DetectorPriorityLevel.MEDIUM
if alert_rule_trigger.label == "warning"
else DetectorPriorityLevel.HIGH
)
threshold_type = alert_rule_trigger.alert_rule.threshold_type
# XXX: we read the threshold type off of the alert_rule and NOT the alert_rule_trigger
# alert_rule_trigger.threshold_type is a deprecated feature we are not moving over
if threshold_type is None:
logger.warning(
"No threshold type",
extra={"alert_rule_id": alert_rule_trigger.alert_rule.id},
)
return None

data_condition = DataCondition.objects.create(
comparison=alert_rule_trigger.alert_threshold,
condition_result=condition_result,
type=threshold_to_condition.get(threshold_type, AlertRuleThresholdType.ABOVE.value),
condition_group=data_condition_group,
)
alert_rule_trigger_data_condition = AlertRuleTriggerDataCondition.objects.create(
alert_rule_trigger=alert_rule_trigger, data_condition=data_condition
)
return data_condition, alert_rule_trigger_data_condition


def create_metric_alert_lookup_tables(
alert_rule: AlertRule,
Expand Down
16 changes: 15 additions & 1 deletion src/sentry/workflow_engine/models/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,23 @@ class Action(DefaultFieldsModel):
__repr__ = sane_repr("id", "type")

class Type(models.TextChoices):
EMAIL = "email"
SLACK = "slack"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TYTY

MSTEAMS = "msteams"
DISCORD = "discord"

PAGERDUTY = "pagerduty"
OPSGENIE = "opsgenie"

GITHUB = "github"
GITHUB_ENTERPRISE = "github_enterprise"
JIRA = "jira"
JIRA_SERVER = "jira_server"
AZURE_DEVOPS = "azure_devops"

EMAIL = "email"
SENTRY_APP = "sentry_app"

PLUGIN = "plugin"
WEBHOOK = "webhook"

class LegacyNotificationType(models.TextChoices):
Expand Down
Loading
Loading