Исправлены ошибки, выявленные на тесте инфраструктуры
This commit is contained in:
+9
-3
@@ -149,11 +149,17 @@ INFRATEST_CHECK_MIGRATIONS=true
|
||||
|
||||
## CORS
|
||||
|
||||
При `INFRATEST_CHECK_CORS=true` quarantine должен отвечать на preflight:
|
||||
При `INFRATEST_CHECK_CORS=true` quarantine должен отвечать на preflight по
|
||||
**vHosted** URL (`<bucket>.<s3_domain>`). У Selectel CORS не работает на
|
||||
path-style адресации, даже если presigned PUT с сервера проходит.
|
||||
|
||||
Требования к правилу CORS на `han-chat-quarantine`:
|
||||
|
||||
- бакет с включённой Virtual-Hosted адресацией;
|
||||
- origin — точное значение `PUBLIC_WEB_URL`, без wildcard;
|
||||
- method — `PUT`;
|
||||
- headers — `Content-Type`, `x-amz-*`.
|
||||
- method — `PUT` (можно также `GET`, `HEAD`, `POST`);
|
||||
- headers — минимум `content-type`; wildcard `x-amz-*` в панели Selectel обычно
|
||||
**не работает**, надёжнее указать `*` или перечислить заголовки явно.
|
||||
|
||||
Чтобы временно исключить CORS из диагностики:
|
||||
|
||||
|
||||
+22
-9
@@ -93,6 +93,15 @@ def sanitize(value: Any) -> str:
|
||||
return text[:1000]
|
||||
|
||||
|
||||
def virtual_hosted_bucket_url(endpoint: str, bucket: str, key: str = "") -> str:
|
||||
"""Собирает vHosted URL бакета для CORS preflight (Selectel не поддерживает CORS на path-style)."""
|
||||
parsed = urlsplit(endpoint.rstrip("/"))
|
||||
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
|
||||
raise ConfigError("SELECTEL_S3_ENDPOINT_URL должен быть абсолютным https URL")
|
||||
path = f"/{key.lstrip('/')}" if key else "/"
|
||||
return urlunsplit((parsed.scheme, f"{bucket}.{parsed.netloc}", path, "", ""))
|
||||
|
||||
|
||||
def require(values: dict[str, str], name: str) -> str:
|
||||
value = values.get(name, "").strip()
|
||||
if not value:
|
||||
@@ -288,6 +297,7 @@ class InfraTest:
|
||||
connect_timeout=self.settings.timeout_seconds,
|
||||
read_timeout=self.settings.timeout_seconds,
|
||||
retries={"max_attempts": 2},
|
||||
s3={"addressing_style": "virtual"},
|
||||
),
|
||||
}
|
||||
if self.settings.s3_region:
|
||||
@@ -742,14 +752,12 @@ class InfraTest:
|
||||
return "GET quarantine разрешён; запись, удаление и другие бакеты запрещены"
|
||||
|
||||
def _check_cors(self, key: str) -> str:
|
||||
url = self.api_s3.generate_presigned_url(
|
||||
"put_object",
|
||||
Params={
|
||||
"Bucket": self.settings.buckets["quarantine"],
|
||||
"Key": key,
|
||||
"ContentType": "application/octet-stream",
|
||||
},
|
||||
ExpiresIn=300,
|
||||
# Selectel обрабатывает CORS только на vHosted URL; path-style presigned URL
|
||||
# возвращает 405 даже при корректной конфигурации бакета.
|
||||
url = virtual_hosted_bucket_url(
|
||||
self.settings.s3_endpoint,
|
||||
self.settings.buckets["quarantine"],
|
||||
key,
|
||||
)
|
||||
response = requests.options(
|
||||
url,
|
||||
@@ -771,7 +779,12 @@ class InfraTest:
|
||||
allow_methods = response.headers.get("Access-Control-Allow-Methods", "")
|
||||
if "PUT" not in allow_methods.upper():
|
||||
raise AssertionError("CORS не разрешает PUT")
|
||||
return "CORS разрешает presigned PUT только с PUBLIC_WEB_URL"
|
||||
allow_headers = response.headers.get("Access-Control-Allow-Headers", "")
|
||||
if "content-type" not in allow_headers.lower():
|
||||
raise AssertionError(
|
||||
f"CORS не разрешает content-type: {allow_headers!r}"
|
||||
)
|
||||
return "CORS разрешает PUT с PUBLIC_WEB_URL через vHosted URL"
|
||||
|
||||
def _cleanup(self) -> None:
|
||||
failures: list[str] = []
|
||||
|
||||
@@ -9,7 +9,14 @@ from contextlib import redirect_stdout
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from infratest import ConfigError, Reporter, Settings, as_bool, sanitize
|
||||
from infratest import (
|
||||
ConfigError,
|
||||
Reporter,
|
||||
Settings,
|
||||
as_bool,
|
||||
sanitize,
|
||||
virtual_hosted_bucket_url,
|
||||
)
|
||||
|
||||
|
||||
def valid_env(ca_path: Path) -> str:
|
||||
@@ -102,6 +109,19 @@ class SettingsTests(unittest.TestCase):
|
||||
Settings.from_env_file(env_file)
|
||||
|
||||
|
||||
class UrlTests(unittest.TestCase):
|
||||
def test_virtual_hosted_bucket_url(self) -> None:
|
||||
url = virtual_hosted_bucket_url(
|
||||
"https://s3.storage.selcloud.ru",
|
||||
"han-chat-quarantine",
|
||||
"infratest/key.bin",
|
||||
)
|
||||
self.assertEqual(
|
||||
url,
|
||||
"https://han-chat-quarantine.s3.storage.selcloud.ru/infratest/key.bin",
|
||||
)
|
||||
|
||||
|
||||
class SafetyTests(unittest.TestCase):
|
||||
def test_sanitize_redacts_secret_and_url_query(self) -> None:
|
||||
value = (
|
||||
|
||||
Reference in New Issue
Block a user