137 lines
4.3 KiB
Python
137 lines
4.3 KiB
Python
from collections.abc import Mapping
|
|
from dataclasses import dataclass, field
|
|
from datetime import UTC, datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
import docker
|
|
from docker import DockerClient
|
|
|
|
from adapter.config.loader import load_config
|
|
from adapter.config.model import AppConfig
|
|
from adapter.docker.runtime import DockerSandboxRuntime
|
|
from adapter.observability.factory import build_observability
|
|
from adapter.observability.runtime import ObservabilityRuntime
|
|
from adapter.sandbox.reconciliation import SandboxSessionReconciler
|
|
from repository.sandbox_lock import ProcessLocalSandboxLifecycleLocker
|
|
from repository.sandbox_session import InMemorySandboxSessionRepository
|
|
from usecase.interface import Clock
|
|
from usecase.sandbox import CleanupExpiredSandboxes, CreateSandbox, DeleteSandbox
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class AppRepositories:
|
|
sandbox_session: InMemorySandboxSessionRepository
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class AppUsecases:
|
|
create_sandbox: CreateSandbox
|
|
cleanup_expired_sandboxes: CleanupExpiredSandboxes
|
|
delete_sandbox: DeleteSandbox
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class AppContainer:
|
|
config: AppConfig
|
|
observability: ObservabilityRuntime
|
|
repositories: AppRepositories
|
|
usecases: AppUsecases
|
|
sandbox_reconciler: SandboxSessionReconciler = field(repr=False)
|
|
_docker_client: DockerClient = field(repr=False)
|
|
_is_shutdown: bool = field(default=False, init=False, repr=False)
|
|
|
|
def shutdown(self) -> None:
|
|
if self._is_shutdown:
|
|
return
|
|
|
|
self._is_shutdown = True
|
|
errors: list[Exception] = []
|
|
|
|
for action in (self._docker_client.close, self.observability.shutdown):
|
|
try:
|
|
action()
|
|
except Exception as exc:
|
|
errors.append(exc)
|
|
|
|
if errors:
|
|
raise ExceptionGroup('shutdown failed', errors)
|
|
|
|
|
|
class SystemClock(Clock):
|
|
def now(self) -> datetime:
|
|
return datetime.now(tz=UTC)
|
|
|
|
|
|
def build_container(
|
|
config_path: Path | str | None = None,
|
|
env_path: Path | str | None = None,
|
|
environ: Mapping[str, str] | None = None,
|
|
config: AppConfig | None = None,
|
|
) -> AppContainer:
|
|
app_config = config
|
|
if app_config is None:
|
|
app_config = load_config(
|
|
config_path=config_path,
|
|
env_path=env_path,
|
|
environ=environ,
|
|
)
|
|
|
|
observability = build_observability(app_config)
|
|
clock = SystemClock()
|
|
docker_client = docker.DockerClient(base_url=app_config.docker.base_url)
|
|
|
|
sandbox_repository = InMemorySandboxSessionRepository()
|
|
sandbox_locker = ProcessLocalSandboxLifecycleLocker()
|
|
sandbox_runtime = DockerSandboxRuntime(
|
|
app_config.sandbox,
|
|
docker_client,
|
|
observability.metrics,
|
|
observability.tracer,
|
|
)
|
|
sandbox_reconciler = SandboxSessionReconciler(
|
|
state_source=sandbox_runtime,
|
|
registry=sandbox_repository,
|
|
logger=observability.logger,
|
|
metrics=observability.metrics,
|
|
tracer=observability.tracer,
|
|
)
|
|
|
|
repositories = AppRepositories(sandbox_session=sandbox_repository)
|
|
usecases = AppUsecases(
|
|
create_sandbox=CreateSandbox(
|
|
repository=sandbox_repository,
|
|
locker=sandbox_locker,
|
|
runtime=sandbox_runtime,
|
|
clock=clock,
|
|
logger=observability.logger,
|
|
metrics=observability.metrics,
|
|
tracer=observability.tracer,
|
|
ttl=timedelta(seconds=app_config.sandbox.ttl_seconds),
|
|
),
|
|
cleanup_expired_sandboxes=CleanupExpiredSandboxes(
|
|
repository=sandbox_repository,
|
|
locker=sandbox_locker,
|
|
runtime=sandbox_runtime,
|
|
clock=clock,
|
|
logger=observability.logger,
|
|
metrics=observability.metrics,
|
|
tracer=observability.tracer,
|
|
),
|
|
delete_sandbox=DeleteSandbox(
|
|
repository=sandbox_repository,
|
|
locker=sandbox_locker,
|
|
runtime=sandbox_runtime,
|
|
logger=observability.logger,
|
|
metrics=observability.metrics,
|
|
tracer=observability.tracer,
|
|
),
|
|
)
|
|
|
|
return AppContainer(
|
|
config=app_config,
|
|
observability=observability,
|
|
repositories=repositories,
|
|
usecases=usecases,
|
|
sandbox_reconciler=sandbox_reconciler,
|
|
_docker_client=docker_client,
|
|
)
|