Исправлены ошибки, выявленные на тесте инфраструктуры

This commit is contained in:
mi
2026-07-15 13:18:30 +03:00
parent 6d1cb3d6d9
commit caf6bf0516
8 changed files with 137 additions and 19 deletions
+9 -3
View File
@@ -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
View File
@@ -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] = []
+21 -1
View File
@@ -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 = (