66 lines
2.3 KiB
Python
66 lines
2.3 KiB
Python
"""Fail-closed runtime admission for per-Workspace KnowledgeFS cutover."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Protocol
|
|
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.orm import Session, sessionmaker
|
|
|
|
from models.knowledge_fs_cutover import (
|
|
KnowledgeFSWorkspaceCutoverLedger,
|
|
KnowledgeFSWorkspaceCutoverPhase,
|
|
)
|
|
from services.knowledge_fs.product_remote import KnowledgeFSOperationUnavailableError
|
|
|
|
_TRAFFIC_PHASES = frozenset(
|
|
{
|
|
KnowledgeFSWorkspaceCutoverPhase.CUTOVER,
|
|
KnowledgeFSWorkspaceCutoverPhase.OBSERVING,
|
|
KnowledgeFSWorkspaceCutoverPhase.READY_FOR_CLEANUP,
|
|
}
|
|
)
|
|
|
|
|
|
class KnowledgeFSWorkspaceRuntimeGatePort(Protocol):
|
|
"""Workspace-scoped admission checked before product traffic or token issuance."""
|
|
|
|
def require_product_routes(self, *, tenant_id: str) -> None: ...
|
|
|
|
def require_capability_v2(self, *, tenant_id: str) -> None: ...
|
|
|
|
|
|
class SQLKnowledgeFSWorkspaceRuntimeGate:
|
|
"""Read the atomic cutover row and reject missing or partial rollout state."""
|
|
|
|
def __init__(self, session_maker: sessionmaker[Session]) -> None:
|
|
self._session_maker = session_maker
|
|
|
|
def require_product_routes(self, *, tenant_id: str) -> None:
|
|
self._require_complete_cutover(tenant_id=tenant_id)
|
|
|
|
def require_capability_v2(self, *, tenant_id: str) -> None:
|
|
self._require_complete_cutover(tenant_id=tenant_id)
|
|
|
|
def _require_complete_cutover(self, *, tenant_id: str) -> None:
|
|
with self._session_maker() as session:
|
|
ledger = session.scalar(
|
|
sa.select(KnowledgeFSWorkspaceCutoverLedger).where(
|
|
KnowledgeFSWorkspaceCutoverLedger.tenant_id == tenant_id
|
|
)
|
|
)
|
|
if (
|
|
ledger is None
|
|
or ledger.phase not in _TRAFFIC_PHASES
|
|
or ledger.cutover_at is None
|
|
or ledger.rolled_back_at is not None
|
|
or not ledger.product_routes_enabled
|
|
or not ledger.capability_v2_enabled
|
|
or not ledger.integrated_mode_enabled
|
|
or not ledger.legacy_acl_read_only
|
|
):
|
|
raise KnowledgeFSOperationUnavailableError("KnowledgeFS Workspace is not cut over for product traffic")
|
|
|
|
|
|
__all__ = ["KnowledgeFSWorkspaceRuntimeGatePort", "SQLKnowledgeFSWorkspaceRuntimeGate"]
|