feat: prepare ARR for controlled public deployment

This commit is contained in:
Wyndham ARR
2026-07-29 16:38:05 +08:00
commit a701de9f0e
271 changed files with 48472 additions and 0 deletions

View File

@@ -0,0 +1,379 @@
"""Alibaba Cloud OSS SDK V2 adapter for the guarded ARR object-store port."""
from __future__ import annotations
import os
import re
from dataclasses import dataclass
from typing import Any, Mapping, Optional
from urllib.parse import urlparse
from arr_storage.contracts import BackendObject, valid_object_key
from arr_storage.remote import CloudClientError
BUCKET_RE = re.compile(r"^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$")
REGION_RE = re.compile(r"^[a-z0-9][a-z0-9-]{1,62}$")
@dataclass(frozen=True)
class AliyunOssConfig:
"""Non-secret OSS routing; credentials remain in the SDK provider chain."""
region: str
bucket: str
endpoint: Optional[str] = None
def __post_init__(self) -> None:
region = self.region.strip().lower()
bucket = self.bucket.strip().lower()
endpoint = self.endpoint.strip().rstrip("/") if self.endpoint else None
if not REGION_RE.fullmatch(region) or not BUCKET_RE.fullmatch(bucket):
raise ValueError("OSS region or bucket configuration is invalid")
if endpoint is not None:
parsed = urlparse(endpoint)
if parsed.scheme != "https" or not parsed.netloc or parsed.path not in {"", "/"}:
raise ValueError("ARR_OSS_ENDPOINT must be an HTTPS endpoint origin")
object.__setattr__(self, "region", region)
object.__setattr__(self, "bucket", bucket)
object.__setattr__(self, "endpoint", endpoint)
@classmethod
def from_environment(
cls,
environment: Optional[Mapping[str, str]] = None,
) -> "AliyunOssConfig":
values = environment if environment is not None else os.environ
region = str(values.get("ARR_OSS_REGION", "")).strip()
bucket = str(values.get("ARR_OSS_BUCKET", "")).strip()
endpoint = str(values.get("ARR_OSS_ENDPOINT", "")).strip() or None
if not region or not bucket:
raise ValueError("ARR_OSS_REGION and ARR_OSS_BUCKET are required")
return cls(region=region, bucket=bucket, endpoint=endpoint)
class _OssReader:
"""Small file-like wrapper that never leaks provider exception text."""
def __init__(self, body: Any) -> None:
self._body = body
self._buffer = bytearray()
self._eof = False
iterator_factory = getattr(body, "iter_bytes", None)
try:
self._iterator = (
iter(iterator_factory(block_size=1024 * 1024))
if callable(iterator_factory)
else None
)
except Exception:
raise OSError("OSS stream initialization failed") from None
def read(self, size: int = -1) -> bytes:
if size == 0:
return b""
if self._iterator is not None:
return self._read_iterator(size)
try:
value = self._body.read(size)
except TypeError:
try:
value = self._body.read()
except Exception:
raise OSError("OSS stream read failed") from None
except Exception:
raise OSError("OSS stream read failed") from None
if not isinstance(value, bytes):
raise OSError("OSS stream returned invalid bytes")
return value
def _read_iterator(self, size: int) -> bytes:
try:
if size is None or size < 0:
chunks = [bytes(self._buffer)] if self._buffer else []
self._buffer.clear()
while not self._eof:
chunk = self._next_chunk()
if chunk:
chunks.append(chunk)
return b"".join(chunks)
while len(self._buffer) < size and not self._eof:
chunk = self._next_chunk()
if chunk:
self._buffer.extend(chunk)
value = bytes(self._buffer[:size])
del self._buffer[:size]
return value
except OSError:
raise
except Exception:
raise OSError("OSS stream read failed") from None
def _next_chunk(self) -> bytes:
if self._eof or self._iterator is None:
return b""
try:
chunk = next(self._iterator)
except StopIteration:
self._eof = True
return b""
if not isinstance(chunk, bytes):
raise OSError("OSS stream returned invalid bytes")
return chunk
def close(self) -> None:
try:
close = getattr(self._body, "close", None)
if callable(close):
close()
except Exception:
raise OSError("OSS stream close failed") from None
def __enter__(self) -> "_OssReader":
return self
def __exit__(self, exc_type: object, exc: object, traceback: object) -> None:
self.close()
class AliyunOssV2Client:
"""CloudClientPort implemented with ``alibabacloud_oss_v2``.
The bucket is required to be unversioned because OSS ignores
``forbid_overwrite`` when versioning is enabled or suspended. This is a
fail-closed invariant for the staged/committed object workflow.
"""
def __init__(
self,
config: AliyunOssConfig,
*,
sdk: Optional[Any] = None,
client: Optional[Any] = None,
) -> None:
if sdk is None:
try:
import alibabacloud_oss_v2 as sdk_module # type: ignore[import-not-found]
except ImportError:
raise CloudClientError("driver_unavailable") from None
sdk = sdk_module
self._sdk = sdk
self._config = config
self._http_session: Optional[Any] = None
if client is None:
http_session: Optional[Any] = None
try:
import requests
credentials = sdk.credentials.EnvironmentVariableCredentialsProvider()
sdk_config = sdk.config.load_default()
sdk_config.credentials_provider = credentials
sdk_config.region = config.region
if config.endpoint is not None:
sdk_config.endpoint = config.endpoint
# Never send OSS credentials through an ambient desktop/system
# proxy. Any deployment proxy must be configured explicitly.
http_session = requests.Session()
http_session.trust_env = False
sdk_config.http_client = sdk.transport.RequestsHttpClient(
session=http_session
)
client = sdk.Client(sdk_config)
except Exception as error:
if http_session is not None:
http_session.close()
self._raise_mapped(error)
self._http_session = http_session
self._client = client
@property
def bucket(self) -> str:
return self._config.bucket
def assert_immutable_writes_supported(self) -> None:
"""Reject buckets that cannot safely hold private immutable ARR data."""
try:
result = self._client.get_bucket_info(
self._sdk.GetBucketInfoRequest(bucket=self.bucket)
)
except Exception as error:
self._raise_mapped(error)
info = getattr(result, "bucket_info", None)
if info is None:
raise CloudClientError("unavailable")
location = str(getattr(info, "location", "") or "").strip().lower()
if location not in {self._config.region, f"oss-{self._config.region}"}:
raise CloudClientError("region_mismatch")
acl = str(getattr(info, "acl", "") or "").strip().lower()
# A public-read bucket is an approved deployment choice for this
# integration. Every ARR object is still written with an explicit
# private object ACL, which overrides the bucket ACL. Never accept a
# bucket that grants anonymous writes.
if acl not in {"private", "public-read"}:
raise CloudClientError("public_access_incompatible")
if getattr(info, "sse_rule", None) is None:
raise CloudClientError("encryption_incompatible")
status = str(getattr(info, "versioning", "") or "").strip().lower()
if status in {"enabled", "suspended"}:
raise CloudClientError("versioning_incompatible")
def upload_file_if_absent(
self,
object_key: str,
source: str,
mime_type: str,
metadata: Mapping[str, str],
) -> BackendObject:
self._validate_request(object_key, metadata)
try:
self._client.put_object_from_file(
self._sdk.PutObjectRequest(
bucket=self.bucket,
key=object_key,
acl="private",
content_type=mime_type,
metadata=dict(metadata),
forbid_overwrite=True,
),
source,
)
except Exception as error:
self._raise_mapped(error)
return self.stat_object(object_key)
def stat_object(self, object_key: str) -> BackendObject:
self._validate_key(object_key)
try:
result = self._client.head_object(
self._sdk.HeadObjectRequest(bucket=self.bucket, key=object_key)
)
except Exception as error:
self._raise_mapped(error)
raw_metadata = getattr(result, "metadata", None) or {}
if not isinstance(raw_metadata, Mapping) or any(
not isinstance(key, str) or not isinstance(value, str)
for key, value in raw_metadata.items()
):
raise CloudClientError("unavailable")
metadata = dict(raw_metadata)
content_type = getattr(result, "content_type", None)
if isinstance(content_type, str) and content_type:
metadata.setdefault("arr-backend-content-type", content_type)
content_length = getattr(result, "content_length", None)
if not isinstance(content_length, int) or isinstance(content_length, bool) or content_length < 0:
raise CloudClientError("unavailable")
return BackendObject(
object_key=object_key,
byte_size=content_length,
metadata=metadata,
etag=self._optional_text(getattr(result, "etag", None)),
version_id=self._optional_text(getattr(result, "version_id", None)),
)
def stream_object(self, object_key: str) -> _OssReader:
self._validate_key(object_key)
try:
result = self._client.get_object(
self._sdk.GetObjectRequest(bucket=self.bucket, key=object_key)
)
except Exception as error:
self._raise_mapped(error)
body = getattr(result, "body", None)
if body is None or not callable(getattr(body, "read", None)):
raise CloudClientError("unavailable")
return _OssReader(body)
def copy_object_if_absent(
self,
source_key: str,
destination_key: str,
metadata: Mapping[str, str],
) -> BackendObject:
self._validate_key(source_key)
self._validate_request(destination_key, metadata)
try:
self._client.copy_object(
self._sdk.CopyObjectRequest(
bucket=self.bucket,
key=destination_key,
source_bucket=self.bucket,
source_key=source_key,
acl="private",
metadata=dict(metadata),
metadata_directive="REPLACE",
content_type=metadata.get("arr-mime-type"),
forbid_overwrite=True,
)
)
except Exception as error:
self._raise_mapped(error)
return self.stat_object(destination_key)
def delete_exact_object(self, object_key: str) -> None:
self._validate_key(object_key)
try:
self._client.delete_object(
self._sdk.DeleteObjectRequest(bucket=self.bucket, key=object_key)
)
except Exception as error:
self._raise_mapped(error)
def close(self) -> None:
close = getattr(self._client, "close", None)
if callable(close):
try:
close()
except Exception:
pass
if self._http_session is not None:
try:
self._http_session.close()
except Exception:
pass
self._http_session = None
@staticmethod
def _optional_text(value: Any) -> Optional[str]:
return value if isinstance(value, str) and value else None
@staticmethod
def _validate_key(object_key: str) -> None:
if not valid_object_key(object_key):
raise CloudClientError("invalid_request")
@classmethod
def _validate_request(cls, object_key: str, metadata: Mapping[str, str]) -> None:
cls._validate_key(object_key)
if any(
not isinstance(key, str)
or not key
or not isinstance(value, str)
for key, value in metadata.items()
):
raise CloudClientError("invalid_request")
@staticmethod
def _raise_mapped(error: BaseException) -> None:
unwrapped: Any = error
unwrap = getattr(error, "unwrap", None)
if callable(unwrap):
try:
unwrapped = unwrap()
except Exception:
unwrapped = error
status = getattr(unwrapped, "status_code", None)
code = str(getattr(unwrapped, "code", "") or "")
if status == 404 or code in {"NoSuchKey", "NoSuchBucket", "NotFound"}:
kind = "not_found"
elif status in {409, 412} or code in {
"FileAlreadyExists",
"FileImmutable",
"PreconditionFailed",
}:
kind = "conflict"
elif status in {400, 401, 403}:
kind = "invalid_request" if status == 400 else "forbidden"
else:
kind = "unavailable"
raise CloudClientError(kind) from None