Files
wyndham-ARR/arr_ingestion/artifacts.py
2026-07-29 16:38:05 +08:00

91 lines
3.6 KiB
Python

"""Artifact materialization boundaries used by ARR delivery validation."""
from __future__ import annotations
import os
import shutil
from pathlib import Path
from typing import Protocol
from arr_ingestion.contracts import IngestionError
class ArtifactStore(Protocol):
def materialize(self, object_key: str, destination: Path, max_bytes: int) -> None:
"""Copy one immutable object to a new private local file."""
class LocalObjectStore:
"""Controlled local implementation for tests and offline vertical slices."""
def __init__(self, root: Path) -> None:
self._root = root.resolve()
if not self._root.is_dir():
raise ValueError("object store root must be an existing directory")
def materialize(self, object_key: str, destination: Path, max_bytes: int) -> None:
source = self._root.joinpath(*object_key.split("/"))
try:
resolved = source.resolve(strict=True)
resolved.relative_to(self._root)
except (FileNotFoundError, OSError, ValueError):
raise IngestionError("ARTIFACT_NOT_FOUND", "delivery artifact is unavailable") from None
if source.is_symlink() or not resolved.is_file():
raise IngestionError("ARTIFACT_NOT_FOUND", "delivery artifact is unavailable")
destination.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
descriptor = os.open(destination, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
copied = 0
try:
with resolved.open("rb") as source_handle, os.fdopen(descriptor, "wb") as target:
descriptor = -1
while True:
chunk = source_handle.read(1024 * 1024)
if not chunk:
break
copied += len(chunk)
if copied > max_bytes:
raise IngestionError(
"ARTIFACT_TOO_LARGE", "delivery artifact exceeds its size limit"
)
target.write(chunk)
target.flush()
os.fsync(target.fileno())
except Exception:
destination.unlink(missing_ok=True)
raise
finally:
if descriptor >= 0:
os.close(descriptor)
class DirectoryArtifactPublisher:
"""Small helper for synthetic/local flows; production uses the OSS adapter."""
def __init__(self, root: Path) -> None:
self._root = root.resolve()
self._root.mkdir(parents=True, exist_ok=True, mode=0o700)
def put_file(self, source: Path, object_key: str) -> Path:
destination = self._root.joinpath(*object_key.split("/"))
try:
destination.resolve().relative_to(self._root)
except ValueError:
raise IngestionError("ARTIFACT_REFERENCE_INVALID", "object key is invalid") from None
destination.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
if destination.exists():
raise IngestionError("ARTIFACT_CONFLICT", "object key already exists")
descriptor = os.open(destination, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
try:
with source.open("rb") as source_handle, os.fdopen(descriptor, "wb") as target:
descriptor = -1
shutil.copyfileobj(source_handle, target, length=1024 * 1024)
target.flush()
os.fsync(target.fileno())
except Exception:
destination.unlink(missing_ok=True)
raise
finally:
if descriptor >= 0:
os.close(descriptor)
return destination