Skip to content
Merged
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
2 changes: 1 addition & 1 deletion src/sentry/hybridcloud/tasks/deliver_webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ def perform_request(payload: WebhookPayload) -> None:

def perform_region_request(region: Cell, payload: WebhookPayload) -> None:
try:
client = CellSiloClient(region=region)
client = CellSiloClient(cell=region)
with metrics.timer(
"hybridcloud.deliver_webhooks.send_request",
tags={"destination_region": region.name},
Expand Down
4 changes: 2 additions & 2 deletions src/sentry/integrations/middleware/hybrid_cloud/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def get_response_from_region_silo(self, region: Cell) -> HttpResponseBase:
tags={"destination_region": region.name},
sample_rate=1.0,
):
region_client = CellSiloClient(region, retry=True)
cell_client = CellSiloClient(region, retry=True)
with MiddlewareOperationEvent(
operation_type=MiddlewareOperationType.GET_REGION_RESPONSE,
integration_name=self.provider,
Expand All @@ -135,7 +135,7 @@ def get_response_from_region_silo(self, region: Cell) -> HttpResponseBase:
}
)

http_response = region_client.proxy_request(incoming_request=self.request)
http_response = cell_client.proxy_request(incoming_request=self.request)
return http_response

def get_responses_from_region_silos(self, regions: list[Cell]) -> dict[str, RegionResult]:
Expand Down
6 changes: 3 additions & 3 deletions src/sentry/middleware/integrations/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ def unpack_payload(self, response: Response) -> Any:
raise NotImplementedError

def _dispatch_to_region(self, region_name: str) -> _AsyncResult:
region = get_cell_by_name(region_name)
client = CellSiloClient(region=region)
cell = get_cell_by_name(region_name)
client = CellSiloClient(cell=cell)
response = client.request(
method=self.request_payload["method"],
path=self.request_payload["path"],
Expand All @@ -77,7 +77,7 @@ def _dispatch_to_region(self, region_name: str) -> _AsyncResult:
json=False,
raw_response=True,
)
return _AsyncResult(region, cast(Response, response))
return _AsyncResult(cell, cast(Response, response))

def _forward_response(self, result: _AsyncResult) -> Response | None:
if not result.was_successful():
Expand Down
58 changes: 28 additions & 30 deletions src/sentry/silo/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ class SiloClientError(Exception):
"""Indicates an error in processing a cross-silo HTTP request"""


def get_region_ip_addresses() -> frozenset[ipaddress.IPv4Address | ipaddress.IPv6Address]:
def get_cell_ip_addresses() -> frozenset[ipaddress.IPv4Address | ipaddress.IPv6Address]:
"""
Infers the Region Silo IP addresses from the SENTRY_REGION_CONFIG setting.
Infers the Cell Silo IP addresses from the SENTRY_REGION_CONFIG setting.
"""
region_ip_addresses: set[ipaddress.IPv4Address | ipaddress.IPv6Address] = set()
cell_ip_addresses: set[ipaddress.IPv4Address | ipaddress.IPv6Address] = set()

for cell in get_global_directory().cells:
url = urllib3.util.parse_url(cell.address)
Expand All @@ -58,39 +58,37 @@ def get_region_ip_addresses() -> frozenset[ipaddress.IPv4Address | ipaddress.IPv
except Exception:
metrics.incr(
"hybrid_cloud.silo_client.ip_address_resolution_error",
tags={"region": cell.name},
tags={"cell": cell.name},
)
sentry_sdk.capture_exception(
CellResolutionError(f"Unable to resolve region host for: {url.host}")
CellResolutionError(f"Unable to resolve cell host for: {url.host}")
)
continue
region_ip_addresses.add(ipaddress.ip_address(force_str(ip, strings_only=True)))
cell_ip_addresses.add(ipaddress.ip_address(force_str(ip, strings_only=True)))
else:
sentry_sdk.capture_exception(
CellResolutionError(f"Unable to parse url to host for: {cell.address}")
)

return frozenset(region_ip_addresses)
return frozenset(cell_ip_addresses)


def validate_region_ip_address(ip: str) -> bool:
def validate_cell_ip_address(ip: str) -> bool:
"""
Checks if the provided IP address is a Region Silo IP address.
Checks if the provided IP address is a Cell Silo IP address.
"""
allowed_region_ip_addresses = get_region_ip_addresses()
if not allowed_region_ip_addresses:
allowed_cell_ip_addresses = get_cell_ip_addresses()
if not allowed_cell_ip_addresses:
sentry_sdk.capture_exception(
CellResolutionError(f"allowed_region_ip_addresses is empty for: {ip}")
CellResolutionError(f"allowed_cell_ip_addresses is empty for: {ip}")
)
return False

ip_address = ipaddress.ip_address(force_str(ip, strings_only=True))
result = ip_address in allowed_region_ip_addresses
result = ip_address in allowed_cell_ip_addresses

if not result:
sentry_sdk.capture_exception(
CellResolutionError(f"Disallowed Region Silo IP address: {ip}")
)
sentry_sdk.capture_exception(CellResolutionError(f"Disallowed Cell Silo IP address: {ip}"))
return result


Expand All @@ -99,11 +97,11 @@ class CellSiloClient(BaseApiClient):

access_modes = [SiloMode.CONTROL]

metrics_prefix = "silo_client.region"
logger = logging.getLogger("sentry.silo.client.region")
silo_client_name = "region"
metrics_prefix = "silo_client.cell"
logger = logging.getLogger("sentry.silo.client.cell")
silo_client_name = "cell"

def __init__(self, region: Cell, retry: bool = False) -> None:
def __init__(self, cell: Cell, retry: bool = False) -> None:
super().__init__()
if SiloMode.get_current_mode() not in self.access_modes:
access_mode_str = ", ".join(str(m) for m in self.access_modes)
Expand All @@ -112,12 +110,12 @@ def __init__(self, region: Cell, retry: bool = False) -> None:
f"Only available in: {access_mode_str}"
)

if not isinstance(region, Cell):
raise SiloClientError(f"Invalid region provided. Received {type(region)} type instead.")
if not isinstance(cell, Cell):
raise SiloClientError(f"Invalid cell provided. Received {type(cell)} type instead.")

# Ensure the region is registered
self.region = get_cell_by_name(region.name)
self.base_url = self.region.address
# Ensure the cell is registered
self.cell = get_cell_by_name(cell.name)
self.base_url = self.cell.address
self.retry = retry

def proxy_request(self, incoming_request: HttpRequest) -> HttpResponse:
Expand Down Expand Up @@ -168,11 +166,11 @@ def request(
prefix_hash: str | None = None,
) -> Any:
"""
Sends a request to the region silo.
Sends a request to the cell silo.
If prefix_hash is provided, the request will be retries up to REQUEST_ATTEMPTS_LIMIT times.
"""
if prefix_hash is not None:
hash = sha256(f"{prefix_hash}{self.region.name}{method}{path}".encode()).hexdigest()
hash = sha256(f"{prefix_hash}{self.cell.name}{method}{path}".encode()).hexdigest()
self.check_request_attempts(hash=hash, method=method, path=path)
return self._request(
method=method,
Expand All @@ -188,15 +186,15 @@ def request(
def build_session(self) -> SafeSession:
"""
Generates a safe Requests session for the API client to use.
This injects a custom is_ipaddress_permitted function to allow only connections to Region Silo IP addresses.
This injects a custom is_ipaddress_permitted function to allow only connections to Cell Silo IP addresses.
"""
if not self.retry:
return build_session(
is_ipaddress_permitted=validate_region_ip_address,
is_ipaddress_permitted=validate_cell_ip_address,
)

return build_session(
is_ipaddress_permitted=validate_region_ip_address,
is_ipaddress_permitted=validate_cell_ip_address,
max_retries=Retry(
total=options.get("hybridcloud.regionsiloclient.retries"),
backoff_factor=0.1,
Expand Down
6 changes: 3 additions & 3 deletions src/sentry/testutils/hybrid_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,8 @@ def use_split_dbs() -> bool:


@contextmanager
def override_allowed_region_silo_ip_addresses(*allowed_ip_addresses):
with patch("sentry.silo.client.get_region_ip_addresses") as mock_get_region_ip_addresses:
def override_allowed_cell_silo_ip_addresses(*allowed_ip_addresses):
with patch("sentry.silo.client.get_cell_ip_addresses") as mock_get_cell_ip_addresses:
override_value = frozenset(ipaddress.ip_address(str(ip)) for ip in allowed_ip_addresses)
mock_get_region_ip_addresses.return_value = override_value
mock_get_cell_ip_addresses.return_value = override_value
yield
Loading
Loading