Skip to content

Commit

Permalink
feat(uptime): Add option to restrict issue creation via host id (#77435)
Browse files Browse the repository at this point in the history
This will allow us to disable issue creation for specific hosting
providers using the `host_provider_id` field of the UptimeSubscription.

Resolves #76875
  • Loading branch information
evanpurkhiser authored Sep 13, 2024
1 parent df05cc2 commit 5d5feb7
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 4 deletions.
13 changes: 13 additions & 0 deletions src/sentry/options/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -2713,3 +2713,16 @@
default=False,
flags=FLAG_AUTOMATOR_MODIFIABLE,
)

# Restrict uptime issue creation for specific host provider identifiers. Items
# in this list map to the `host_provider_id` column in the UptimeSubscription
# table.
#
# This may be used to stop issue creation in the event that a network / hosting
# provider blocks the uptime checker causing false positives.
register(
"uptime.restrict-issue-creation-by-hosting-provider-id",
type=Sequence,
default=[],
flags=FLAG_ALLOW_EMPTY | FLAG_AUTOMATOR_MODIFIABLE,
)
2 changes: 2 additions & 0 deletions src/sentry/testutils/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -1938,6 +1938,7 @@ def create_uptime_subscription(
subscription_id: str | None,
status: UptimeSubscription.Status,
url: str,
host_provider_id: str,
interval_seconds: int,
timeout_ms: int,
date_updated: datetime,
Expand All @@ -1947,6 +1948,7 @@ def create_uptime_subscription(
subscription_id=subscription_id,
status=status.value,
url=url,
host_provider_id=host_provider_id,
interval_seconds=interval_seconds,
timeout_ms=timeout_ms,
date_updated=date_updated,
Expand Down
5 changes: 5 additions & 0 deletions src/sentry/testutils/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,17 +651,22 @@ def create_uptime_subscription(
subscription_id: str | None = None,
status: UptimeSubscription.Status = UptimeSubscription.Status.ACTIVE,
url="http://sentry.io/",
host_provider_id: str | None = None,
interval_seconds=60,
timeout_ms=100,
date_updated: None | datetime = None,
) -> UptimeSubscription:
if date_updated is None:
date_updated = timezone.now()
if host_provider_id is None:
host_provider_id = "TEST"

return Factories.create_uptime_subscription(
type=type,
subscription_id=subscription_id,
status=status,
url=url,
host_provider_id=host_provider_id,
interval_seconds=interval_seconds,
timeout_ms=timeout_ms,
date_updated=date_updated,
Expand Down
23 changes: 19 additions & 4 deletions src/sentry/uptime/consumers/results_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
CheckResult,
)

from sentry import features
from sentry import features, options
from sentry.conf.types.kafka_definition import Topic
from sentry.remote_subscriptions.consumers.result_consumer import (
ResultProcessor,
Expand Down Expand Up @@ -258,9 +258,24 @@ def handle_result_for_project_active_mode(
if not self.has_reached_status_threshold(project_subscription, result["status"]):
return

if features.has(
"organizations:uptime-create-issues", project_subscription.project.organization
):
issue_creation_flag_enabled = features.has(
"organizations:uptime-create-issues",
project_subscription.project.organization,
)

# Do not create uptime issue occurences for
restricted_host_provider_ids = options.get(
"uptime.restrict-issue-creation-by-hosting-provider-id"
)
issue_creation_restricted_by_provider = (
project_subscription.uptime_subscription.host_provider_id
in restricted_host_provider_ids
)

if issue_creation_restricted_by_provider:
metrics.incr("uptime.result_processor.restricted_by_provider", sample_rate=1.0)

if issue_creation_flag_enabled and not issue_creation_restricted_by_provider:
create_issue_platform_occurrence(result, project_subscription)
metrics.incr("uptime.result_processor.active.sent_occurrence", sample_rate=1.0)
logger.info(
Expand Down
37 changes: 37 additions & 0 deletions tests/sentry/uptime/consumers/test_results_consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from sentry.issues.grouptype import UptimeDomainCheckFailure
from sentry.models.group import Group, GroupStatus
from sentry.testutils.cases import UptimeTestCase
from sentry.testutils.helpers.options import override_options
from sentry.uptime.consumers.results_consumer import (
AUTO_DETECTED_ACTIVE_SUBSCRIPTION_INTERVAL,
ONBOARDING_MONITOR_PERIOD,
Expand Down Expand Up @@ -125,6 +126,42 @@ def test(self):
self.project_subscription.refresh_from_db()
assert self.project_subscription.uptime_status == UptimeStatus.FAILED

def test_restricted_host_provider_id(self):
"""
Test that we do NOT create an issue when the host provider identifier
has been restricted using the
`restrict-issue-creation-by-hosting-provider-id` option.
"""
result = self.create_uptime_result(
self.subscription.subscription_id,
scheduled_check_time=datetime.now() - timedelta(minutes=5),
)
with (
mock.patch("sentry.uptime.consumers.results_consumer.metrics") as metrics,
self.feature("organizations:uptime-create-issues"),
mock.patch(
"sentry.uptime.consumers.results_consumer.ACTIVE_FAILURE_THRESHOLD",
new=1,
),
override_options({"uptime.restrict-issue-creation-by-hosting-provider-id": ["TEST"]}),
):
self.send_result(result)
metrics.incr.assert_has_calls(
[
call("uptime.result_processor.restricted_by_provider", sample_rate=1.0),
],
any_order=True,
)

# Issue is not created
hashed_fingerprint = md5(str(self.project_subscription.id).encode("utf-8")).hexdigest()
with pytest.raises(Group.DoesNotExist):
Group.objects.get(grouphash__hash=hashed_fingerprint)

# subscription status is still updated
self.project_subscription.refresh_from_db()
assert self.project_subscription.uptime_status == UptimeStatus.FAILED

def test_reset_fail_count(self):
with (
mock.patch("sentry.uptime.consumers.results_consumer.metrics") as metrics,
Expand Down

0 comments on commit 5d5feb7

Please sign in to comment.