Skip to content
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
5 changes: 2 additions & 3 deletions src/sentry/feedback/endpoints/error_page_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from sentry.models.userreport import UserReport
from sentry.services import eventstore
from sentry.signals import user_feedback_received
from sentry.types.region import get_local_region
from sentry.types.region import get_local_locality
from sentry.utils import json
from sentry.utils.db import atomic_transaction
from sentry.utils.http import is_valid_origin, origin_from_request
Expand Down Expand Up @@ -219,8 +219,7 @@ def dispatch(self, request: HttpRequest) -> HttpResponse:
elif request.method == "POST":
return self._smart_response(request, {"errors": dict(form.errors)}, status=400)

region = get_local_region()
endpoint = region.to_url(request.get_full_path())
endpoint = get_local_locality().to_url(request.get_full_path())
show_branding = (
ProjectOption.objects.get_value(
project=key.project, key="feedback:branding", default="1"
Expand Down
4 changes: 2 additions & 2 deletions src/sentry/models/avatars/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from sentry.models.files.control_file import ControlFile
from sentry.models.files.file import File
from sentry.silo.base import SiloMode
from sentry.types.region import get_local_region
from sentry.types.region import get_local_locality
from sentry.utils.cache import cache
from sentry.utils.db import atomic_transaction

Expand Down Expand Up @@ -122,7 +122,7 @@ def absolute_url(self) -> str:
url_base = options.get("system.url-prefix")
silo_limit = getattr(cls._meta, "silo_limit", None)
if silo_limit is not None and SiloMode.REGION in silo_limit.modes:
url_base = get_local_region().to_url("")
url_base = get_local_locality().to_url("")

return urljoin(url_base, f"/{self.url_path}/{self.ident}/")

Expand Down
5 changes: 2 additions & 3 deletions src/sentry/releases/endpoints/project_releases_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@
from sentry.api.base import region_silo_endpoint
from sentry.api.bases.project import ProjectEndpoint, StrictProjectPermission
from sentry.models.options.project_option import ProjectOption
from sentry.types.region import get_local_region
from sentry.types.region import get_local_locality


def _get_webhook_url(project, plugin_id, token):
region = get_local_region()
return region.to_url(
return get_local_locality().to_url(
reverse(
"sentry-release-hook",
kwargs={
Expand Down
10 changes: 5 additions & 5 deletions src/sentry/testutils/region.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def swap_to_default_region(self) -> Generator[None]:
def swap_to_region_by_name(self, region_name: str) -> Generator[None]:
"""Swap to the specified region when entering region mode."""

region = self.get_by_name(region_name)
region = self.get_cell_by_name(region_name)
if region is None:
raise Exception("specified swap region not found")
with override_settings(SENTRY_REGION=region.name):
Expand All @@ -88,15 +88,15 @@ def localities(self) -> frozenset[Locality]:
for r in self._tmp_state.regions
)

def get_by_name(self, region_name: str) -> Region | None:
def get_cell_by_name(self, region_name: str) -> Region | None:
match = (r for r in self._tmp_state.regions if r.name == region_name)
try:
return next(match)
except StopIteration:
return None

def get_locality_by_name(self, locality_name: str) -> Locality | None:
region = self.get_by_name(locality_name)
region = self.get_cell_by_name(locality_name)
if region is None:
return None
return Locality(
Expand All @@ -107,7 +107,7 @@ def get_locality_by_name(self, locality_name: str) -> Locality | None:
)

def get_locality_for_cell(self, cell_name: str) -> Locality | None:
region = self.get_by_name(cell_name)
region = self.get_cell_by_name(cell_name)
if region is None:
return None
return Locality(
Expand All @@ -118,7 +118,7 @@ def get_locality_for_cell(self, cell_name: str) -> Locality | None:
)

def get_cells_for_locality(self, locality_name: str) -> Iterable[Region]:
region = self.get_by_name(locality_name)
region = self.get_cell_by_name(locality_name)
if region is None:
return ()
return (region,)
Expand Down
63 changes: 33 additions & 30 deletions src/sentry/types/region.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class Locality:
"""Whether the locality is visible in API responses."""

def to_url(self, path: str) -> str:
"""Resolve a path into a customer facing URL on this locality.

In monolith mode, there is likely only the historical simulated locality.
The public URL of the simulated locality is the same as the application base URL.
"""
from sentry.api.utils import generate_region_url

if SiloMode.get_current_mode() == SiloMode.MONOLITH:
Expand Down Expand Up @@ -101,22 +106,6 @@ def validate(self) -> None:

REGION_ID.validate(self.snowflake_id)

def to_url(self, path: str) -> str:
"""Resolve a path into a customer facing URL on this region's silo.

In monolith mode, there is likely only the historical simulated
region. The public URL of the simulated region is the same
as the application base URL.
"""
from sentry.api.utils import generate_region_url

if SiloMode.get_current_mode() == SiloMode.MONOLITH:
base_url = options.get("system.url-prefix")
else:
base_url = generate_region_url(self.name)

return urljoin(base_url, path)

def is_historic_monolith_region(self) -> bool:
"""Check whether this is a historic monolith region.

Expand Down Expand Up @@ -154,10 +143,10 @@ class RegionDirectory:

def __init__(
self,
regions: Collection[Region],
cells: Collection[Region],
localities: Collection[Locality],
) -> None:
self._cells = frozenset(regions)
self._cells = frozenset(cells)
self._by_name = {r.name: r for r in self._cells}
self._localities = frozenset(localities)
self._localities_by_name = {loc.name: loc for loc in self._localities}
Expand All @@ -171,18 +160,22 @@ def regions(self) -> frozenset[Region]:
def localities(self) -> frozenset[Locality]:
return self._localities

def get_by_name(self, region_name: str) -> Region | None:
def get_cell_by_name(self, region_name: str) -> Region | None:
return self._by_name.get(region_name)

def get_locality_by_name(self, locality_name: str) -> Locality | None:
return self._localities_by_name.get(locality_name)

def get_regions(self, category: RegionCategory | None = None) -> Iterable[Region]:
def get_cells(self, category: RegionCategory | None = None) -> Iterable[Region]:
return (r for r in self.regions if (category is None or r.category == category))

def get_region_names(self, category: RegionCategory | None = None) -> Iterable[str]:
def get_cell_names(self, category: RegionCategory | None = None) -> Iterable[str]:
return (r.name for r in self.regions if (category is None or r.category == category))

def get_region_names(self, category: RegionCategory | None = None) -> Iterable[str]:
"""Deprecated. Use get_cell_names."""
return self.get_cell_names(category)

def get_locality_for_cell(self, cell_name: str) -> Locality | None:
return self._cell_to_locality.get(cell_name)

Expand Down Expand Up @@ -328,11 +321,11 @@ def get_global_directory() -> RegionDirectory:
def get_cell_by_name(name: str) -> Region:
"""Look up a cell by name."""
global_regions = get_global_directory()
region = global_regions.get_by_name(name)
region = global_regions.get_cell_by_name(name)
if region is not None:
return region
else:
region_names = list(global_regions.get_region_names(RegionCategory.MULTI_TENANT))
region_names = list(global_regions.get_cell_names(RegionCategory.MULTI_TENANT))
raise RegionResolutionError(
f"No cell with name: {name!r} "
f"(expected one of {region_names!r} or a single-tenant name)"
Expand All @@ -358,7 +351,7 @@ def get_region_by_name(name: str) -> Region:


def is_region_name(name: str) -> bool:
return get_global_directory().get_by_name(name) is not None
return get_global_directory().get_cell_by_name(name) is not None


def subdomain_is_region(request: HttpRequest) -> bool:
Expand Down Expand Up @@ -388,6 +381,15 @@ def get_region_for_organization(organization_id_or_slug: str) -> Region:
return get_region_by_name(name=mapping.region_name)


def get_local_locality() -> Locality:
"""Get the locality for the cell this server instance is running in."""
cell = get_local_region()
locality = get_global_directory().get_locality_for_cell(cell.name)
if locality is None:
raise RegionResolutionError(f"No locality found for cell {cell.name!r}")
return locality


def get_local_region() -> Region:
"""Get the region in which this server instance is running.

Expand Down Expand Up @@ -469,15 +471,20 @@ def find_regions_for_sentry_app(sentry_app: SentryApp) -> set[str]:
return {r[0] for r in regions}


def find_all_cell_names() -> Iterable[str]:
return get_global_directory().get_cell_names()


def find_all_region_names() -> Iterable[str]:
return get_global_directory().get_region_names()
"""Deprecated. Use find_all_cell_names."""
return find_all_cell_names()


def find_all_multitenant_region_names() -> list[str]:
"""
Return all visible multi_tenant regions.
"""
regions = get_global_directory().get_regions(RegionCategory.MULTI_TENANT)
regions = get_global_directory().get_cells(RegionCategory.MULTI_TENANT)
return list([r.name for r in regions if r.visible])


Expand All @@ -490,7 +497,3 @@ def find_all_multitenant_locality_names() -> list[str]:
for loc in get_global_directory().localities
if loc.category == RegionCategory.MULTI_TENANT and loc.visible
]


def find_all_region_addresses() -> Iterable[str]:
return (r.address for r in get_global_directory().regions)
5 changes: 2 additions & 3 deletions src/sentry/utils/linksign.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from sentry import features, options
from sentry.models.organization import Organization
from sentry.types.region import get_local_region
from sentry.types.region import get_local_locality
from sentry.users.services.user.model import RpcUser
from sentry.users.services.user.service import user_service
from sentry.utils.numbers import base36_decode, base36_encode
Expand All @@ -36,8 +36,7 @@ def generate_signed_link(
path = reverse(viewname, args=args, kwargs=kwargs)
item = "{}|{}|{}".format(options.get("system.url-prefix"), path, base36_encode(user_id))
signature = ":".join(get_signer().sign(item).rsplit(":", 2)[1:])
region = get_local_region()
signed_link = f"{region.to_url(path)}?_={base36_encode(user_id)}:{signature}"
signed_link = f"{get_local_locality().to_url(path)}?_={base36_encode(user_id)}:{signature}"
if referrer:
signed_link = signed_link + "&" + urlencode({"referrer": referrer})
return signed_link
Expand Down
31 changes: 19 additions & 12 deletions tests/sentry/core/endpoints/test_organization_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from sentry.models.organizationmember import OrganizationMember
from sentry.testutils.cases import APITestCase
from sentry.testutils.silo import assume_test_silo_mode_of, control_silo_test, create_test_regions
from sentry.types.region import Region, get_region_by_name
from sentry.types.region import Region, get_global_directory, get_region_by_name
from sentry.utils.security.orgauthtoken_token import generate_token, hash_token


Expand All @@ -27,7 +27,9 @@ def create_internal_integration_for_org(self, org, user, scopes: list[str]):
return (internal_integration, integration_token)

def create_auth_token_for_org(self, org: Organization, region: Region, scopes: list[str]):
org_auth_token_str = generate_token(org.slug, region.to_url(""))
locality = get_global_directory().get_locality_for_cell(region.name)
assert locality is not None
org_auth_token_str = generate_token(org.slug, locality.to_url(""))
self.create_org_auth_token(
organization_id=org.id,
scope_list=scopes,
Expand All @@ -51,8 +53,9 @@ def test_org_member_has_access(self) -> None:
response = self.get_response(self.org.slug)

assert response.status_code == 200
us_region = get_region_by_name("us")
assert response.data == {"url": us_region.to_url(""), "name": us_region.name}
us_locality = get_global_directory().get_locality_for_cell("us")
assert us_locality is not None
assert response.data == {"url": us_locality.to_url(""), "name": us_locality.name}

def test_non_org_member_has_no_access(self) -> None:
non_member_user = self.create_user()
Expand All @@ -67,8 +70,9 @@ def test_org_auth_token_access_with_org_read(self) -> None:
)
response = self.send_get_request_with_auth(self.org.slug, org_auth_token_str)

us_region = get_region_by_name("us")
assert response.data == {"url": us_region.to_url(""), "name": us_region.name}
us_locality = get_global_directory().get_locality_for_cell("us")
assert us_locality is not None
assert response.data == {"url": us_locality.to_url(""), "name": us_locality.name}
assert response.status_code == 200

def test_org_auth_token_access_with_incorrect_scopes(self) -> None:
Expand Down Expand Up @@ -99,8 +103,9 @@ def test_integration_token_access(self) -> None:
response = self.send_get_request_with_auth(self.org.slug, token.token)

assert response.status_code == 200
us_region = get_region_by_name("us")
assert response.data == {"name": us_region.name, "url": us_region.to_url("")}
us_locality = get_global_directory().get_locality_for_cell("us")
assert us_locality is not None
assert response.data == {"name": us_locality.name, "url": us_locality.to_url("")}

def test_integration_token_with_invalid_scopes(self) -> None:
integration, token = self.create_internal_integration_for_org(self.org, self.org_owner, [])
Expand All @@ -127,8 +132,9 @@ def test_user_auth_token_for_owner(self) -> None:
response = self.send_get_request_with_auth(self.org.slug, user_auth_token.token)

assert response.status_code == 200
us_region = get_region_by_name("us")
assert response.data == {"url": us_region.to_url(""), "name": us_region.name}
us_locality = get_global_directory().get_locality_for_cell("us")
assert us_locality is not None
assert response.data == {"url": us_locality.to_url(""), "name": us_locality.name}

def test_user_auth_token_for_member(self) -> None:
org_user = self.create_user()
Expand All @@ -141,8 +147,9 @@ def test_user_auth_token_for_member(self) -> None:
response = self.send_get_request_with_auth(self.org.slug, user_auth_token.token)

assert response.status_code == 200
us_region = get_region_by_name("us")
assert response.data == {"url": us_region.to_url(""), "name": us_region.name}
us_locality = get_global_directory().get_locality_for_cell("us")
assert us_locality is not None
assert response.data == {"url": us_locality.to_url(""), "name": us_locality.name}

def test_user_auth_token_for_non_member(self) -> None:
user_auth_token = self.create_user_auth_token(
Expand Down
5 changes: 2 additions & 3 deletions tests/sentry/feedback/endpoints/test_error_page_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from sentry.models.userreport import UserReport
from sentry.testutils.cases import TestCase
from sentry.testutils.helpers.datetime import before_now
from sentry.types.region import get_local_region
from sentry.types.region import get_local_locality


class ErrorPageEmbedTest(TestCase):
Expand Down Expand Up @@ -83,8 +83,7 @@ def test_endpoint_reflects_region_url(self) -> None:
assert resp["Access-Control-Allow-Origin"] == "*"
self.assertTemplateUsed(resp, "sentry/error-page-embed.html")

region = get_local_region()
region_url = region.to_url(self.path_with_qs)
region_url = get_local_locality().to_url(self.path_with_qs)
body = resp.content.decode("utf8")
assert f'endpoint = /**/"{region_url}";/**/' in body

Expand Down
10 changes: 6 additions & 4 deletions tests/sentry/middleware/integrations/parsers/test_jira.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
from sentry.testutils.outbox import assert_no_webhook_payloads, assert_webhook_payloads_for_mailbox
from sentry.testutils.region import override_regions
from sentry.testutils.silo import control_silo_test
from sentry.types.region import Region, RegionCategory
from sentry.types.region import Locality, Region, RegionCategory

region = Region("us", 1, "http://us.testserver", RegionCategory.MULTI_TENANT)
eu_region = Region("eu", 2, "http://eu.testserver", RegionCategory.MULTI_TENANT)
locality = Locality("us", frozenset(["us"]), RegionCategory.MULTI_TENANT)
eu_locality = Locality("eu", frozenset(["eu"]), RegionCategory.MULTI_TENANT)

region_config = (region, eu_region)

Expand Down Expand Up @@ -79,7 +81,7 @@ def test_get_response_routing_to_control(self) -> None:
def test_get_response_routing_to_region_sync(self) -> None:
responses.add(
responses.POST,
region.to_url("/extensions/jira/issue/LR-123/"),
locality.to_url("/extensions/jira/issue/LR-123/"),
body="region response",
status=200,
)
Expand All @@ -101,7 +103,7 @@ def test_get_response_routing_to_region_sync(self) -> None:
def test_get_response_routing_to_region_sync_retry_errors(self) -> None:
responses.add(
responses.POST,
region.to_url("/extensions/jira/issue/LR-123/"),
locality.to_url("/extensions/jira/issue/LR-123/"),
body="region response",
status=503,
)
Expand Down Expand Up @@ -188,7 +190,7 @@ def test_get_response_invalid_path(self) -> None:
def test_get_response_multiple_regions(self) -> None:
responses.add(
responses.POST,
eu_region.to_url("/extensions/jira/issue/LR-123/"),
eu_locality.to_url("/extensions/jira/issue/LR-123/"),
body="region response",
status=200,
)
Expand Down
Loading
Loading