diff --git a/src/sentry/rules/conditions/event_attribute.py b/src/sentry/rules/conditions/event_attribute.py index 3ac593eabb3ea2..b73f823419735a 100644 --- a/src/sentry/rules/conditions/event_attribute.py +++ b/src/sentry/rules/conditions/event_attribute.py @@ -39,6 +39,8 @@ "stacktrace.package": Columns.STACK_PACKAGE, "unreal.crashtype": Columns.UNREAL_CRASH_TYPE, "app.in_foreground": Columns.APP_IN_FOREGROUND, + "os.distribution.name": Columns.OS_DISTRIBUTION_NAME, + "os.distribution.version": Columns.OS_DISTRIBUTION_VERSION, } @@ -112,8 +114,8 @@ def _get_attribute_values(self, event: GroupEvent, attr: str) -> Sequence[object return value return [value] - elif len(path) != 2: - return [] + elif len(path) < 2: + return [] # all attribute paths below have at least 2 elements elif path[0] == "exception": if path[1] not in ("type", "value"): @@ -211,6 +213,23 @@ def _get_attribute_values(self, event: GroupEvent, attr: str) -> Sequence[object response = {} return [response.get(path[1])] + elif len(path) < 3: + return [] # all attribute paths below have at least 3 elements + + elif path[0] == "os": + if path[1] in ("distribution"): + if path[2] in ("name", "version"): + contexts = event.data["contexts"] + os_context = contexts.get("os") + if os_context is None: + os_context = {} + + distribution = os_context.get(path[1]) + if distribution is None: + distribution = {} + + return [distribution.get(path[2])] + return [] return [] return [] diff --git a/src/sentry/snuba/events.py b/src/sentry/snuba/events.py index 73480b1eb5fead..47e7aee6650d6e 100644 --- a/src/sentry/snuba/events.py +++ b/src/sentry/snuba/events.py @@ -600,6 +600,22 @@ class Columns(Enum): issue_platform_name="contexts[app.in_foreground]", alias="app.in_foreground", ) + OS_DISTRIBUTION_NAME = Column( + group_name="events.contexts[os.distribution.name]", + event_name="contexts[os.distribution.name]", + transaction_name="contexts[os.distribution.name]", + discover_name="contexts[os.distribution.name]", + issue_platform_name="contexts[os.distribution.name]", + alias="os.distribution.name", + ) + OS_DISTRIBUTION_VERSION = Column( + group_name="events.contexts[os.distribution.version]", + event_name="contexts[os.distribution.version]", + transaction_name="contexts[os.distribution.version]", + discover_name="contexts[os.distribution.version]", + issue_platform_name="contexts[os.distribution.version]", + alias="os.distribution.version", + ) # Transactions specific columns TRANSACTION_OP = Column( group_name=None, diff --git a/tests/sentry/rules/conditions/test_event_attribute.py b/tests/sentry/rules/conditions/test_event_attribute.py index 096813f0b9feb2..4734c2bd5387fc 100644 --- a/tests/sentry/rules/conditions/test_event_attribute.py +++ b/tests/sentry/rules/conditions/test_event_attribute.py @@ -59,6 +59,12 @@ def get_event(self, **kwargs): "unreal": { "crash_type": "crash", }, + "os": { + "distribution": { + "name": "ubuntu", + "version": "22.04", + } + }, }, "threads": { "values": [ @@ -757,6 +763,47 @@ def test_app_in_foreground(self): ) self.assertDoesNotPass(rule, event) + def test_os_distribution_only(self): + event = self.get_event() + rule = self.get_rule( + data={"match": MatchType.EQUAL, "attribute": "os.distribution", "value": "irrelevant"} + ) + self.assertDoesNotPass(rule, event) + + def test_os_distribution_name_and_version(self): + event = self.get_event() + rule = self.get_rule( + data={"match": MatchType.EQUAL, "attribute": "os.distribution.name", "value": "ubuntu"} + ) + self.assertPasses(rule, event) + + rule = self.get_rule( + data={ + "match": MatchType.EQUAL, + "attribute": "os.distribution.version", + "value": "22.04", + } + ) + self.assertPasses(rule, event) + + rule = self.get_rule( + data={ + "match": MatchType.EQUAL, + "attribute": "os.distribution.name", + "value": "slackware", + } + ) + self.assertDoesNotPass(rule, event) + + rule = self.get_rule( + data={ + "match": MatchType.EQUAL, + "attribute": "os.distribution.version", + "value": "20.04", + } + ) + self.assertDoesNotPass(rule, event) + def test_unreal_crash_type(self): event = self.get_event() rule = self.get_rule(