-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
fix(objectstore): Fix proxy in siloed mode #106172
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
Changes from all commits
398d2fb
d6dbd7f
8232da3
b31e9c5
bfee19d
21149be
27f6173
c363730
2a4ebfc
6438815
51e54af
7701dd6
cbbfcf3
ad6f046
621f4a1
8765254
d4829ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,21 @@ | ||
| from dataclasses import asdict | ||
|
|
||
| import pytest | ||
| import requests | ||
| from django.db import connections | ||
| from django.urls import reverse | ||
| from objectstore_client import Client, RequestError, Session, Usecase | ||
| from pytest_django.live_server_helper import LiveServer | ||
|
|
||
| from sentry.silo.base import SiloMode, SingleProcessSiloModeState | ||
| from sentry.testutils.asserts import assert_status_code | ||
| from sentry.testutils.cases import TransactionTestCase | ||
| from sentry.testutils.helpers.features import with_feature | ||
| from sentry.testutils.silo import region_silo_test | ||
| from sentry.testutils.region import override_regions | ||
| from sentry.testutils.silo import create_test_regions, region_silo_test | ||
| from sentry.testutils.skips import requires_objectstore | ||
| from sentry.types.region import Region | ||
| from sentry.utils import json | ||
|
|
||
|
|
||
| @pytest.fixture(scope="function") | ||
|
|
@@ -101,3 +109,120 @@ def test_large_payload(self): | |
|
|
||
| retrieved = session.get(object_key) | ||
| assert retrieved.payload.read() == data | ||
|
|
||
|
|
||
| test_region = create_test_regions("us")[0] | ||
|
|
||
|
|
||
| @region_silo_test(regions=(test_region,)) | ||
| @requires_objectstore | ||
| @with_feature("organizations:objectstore-endpoint") | ||
| @pytest.mark.usefixtures("local_live_server") | ||
| class OrganizationObjectstoreEndpointWithControlSiloTest(TransactionTestCase): | ||
| endpoint = "sentry-api-0-organization-objectstore" | ||
| live_server: LiveServer | ||
|
|
||
| def setUp(self) -> None: | ||
| super().setUp() | ||
| self.login_as(user=self.user) | ||
| self.organization = self.create_organization(owner=self.user) | ||
| self.api_key = self.create_api_key( | ||
| organization=self.organization, | ||
| scope_list=["org:admin"], | ||
| ) | ||
|
|
||
| def tearDown(self) -> None: | ||
| for conn in connections.all(): | ||
| conn.close() | ||
| super().tearDown() | ||
|
Comment on lines
+134
to
+137
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding this because I was getting in a previous CI run.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is likely being caused by the the live_server that runs in a thread. |
||
|
|
||
| def get_endpoint_url(self) -> str: | ||
| path = reverse( | ||
| self.endpoint, | ||
| kwargs={ | ||
| "organization_id_or_slug": self.organization.id, | ||
| "path": "", | ||
| }, | ||
| ) | ||
| return path | ||
|
|
||
| def test_health(self): | ||
| config = asdict(test_region) | ||
| config["address"] = self.live_server.url | ||
| with override_regions([Region(**config)]): | ||
| with SingleProcessSiloModeState.enter(SiloMode.CONTROL): | ||
| response = self.client.get( | ||
| self.get_endpoint_url() + "health", | ||
| follow=True, | ||
| ) | ||
| assert response.status_code == 200 | ||
| # consume body to close connection | ||
| b"".join(response.streaming_content) # type: ignore[attr-defined] | ||
|
|
||
| def test_full_cycle(self): | ||
|
|
||
| config = asdict(test_region) | ||
| config["address"] = self.live_server.url | ||
| auth_header = self.create_basic_auth_header(self.api_key.key).decode() | ||
|
|
||
| with override_regions([Region(**config)]): | ||
| with SingleProcessSiloModeState.enter(SiloMode.CONTROL): | ||
| base_url = f"{self.get_endpoint_url()}v1/objects/test/org={self.organization.id}/" | ||
|
|
||
| response = self.client.post( | ||
| base_url, | ||
| data=b"test data", | ||
| HTTP_AUTHORIZATION=auth_header, | ||
| content_type="application/octet-stream", | ||
| follow=True, | ||
| ) | ||
| assert_status_code(response, 201) | ||
| object_key = json.loads(b"".join(response.streaming_content))["key"] # type: ignore[attr-defined] | ||
| assert object_key is not None | ||
|
|
||
| response = self.client.get( | ||
| f"{base_url}{object_key}", | ||
| HTTP_AUTHORIZATION=auth_header, | ||
| follow=True, | ||
| ) | ||
| assert_status_code(response, 200) | ||
| retrieved_data = b"".join(response.streaming_content) # type: ignore[attr-defined] | ||
| assert retrieved_data == b"test data" | ||
|
|
||
| response = self.client.put( | ||
| f"{base_url}{object_key}", | ||
| data=b"new data", | ||
| content_type="application/octet-stream", | ||
| HTTP_AUTHORIZATION=auth_header, | ||
| follow=True, | ||
| ) | ||
| assert_status_code(response, 200) | ||
| new_key = json.loads(b"".join(response.streaming_content))["key"] # type: ignore[attr-defined] | ||
| assert new_key == object_key | ||
|
|
||
| response = self.client.get( | ||
| f"{base_url}{object_key}", | ||
| HTTP_AUTHORIZATION=auth_header, | ||
| follow=True, | ||
| ) | ||
| assert_status_code(response, 200) | ||
| retrieved = b"".join(response.streaming_content) # type: ignore[attr-defined] | ||
| assert retrieved == b"new data" | ||
|
|
||
| response = self.client.delete( | ||
| f"{base_url}{object_key}", | ||
| HTTP_AUTHORIZATION=auth_header, | ||
| follow=True, | ||
| ) | ||
| assert_status_code(response, 204) | ||
| # consume body to close connection | ||
| b"".join(response.streaming_content) # type: ignore[attr-defined] | ||
|
|
||
| response = self.client.get( | ||
| f"{base_url}{object_key}", | ||
| HTTP_AUTHORIZATION=auth_header, | ||
| follow=True, | ||
| ) | ||
| assert_status_code(response, 404) | ||
| # consume body to close connection | ||
| b"".join(response.streaming_content) # type: ignore[attr-defined] | ||
Uh oh!
There was an error while loading. Please reload this page.