fix(supervise): get bot-bottle.db off the data plane (supervise + egress proposals over RPC) #471
@@ -29,7 +29,7 @@ from ..git_gate import GitGate, GitGatePlan
|
||||
from ..log import die
|
||||
from ..manifest import Manifest, ManifestBottle
|
||||
from ..supervisor.plan import SupervisePlan
|
||||
from ..orchestrator.supervisor.supervise import Supervise
|
||||
from ..orchestrator.supervisor import Supervisor
|
||||
from . import BottleSpec
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ def prepare_supervise(bottle: ManifestBottle, slug: str) -> SupervisePlan | None
|
||||
return None
|
||||
supervise_dir = supervise_state_dir(slug)
|
||||
supervise_dir.mkdir(parents=True, exist_ok=True)
|
||||
return Supervise().prepare(slug, supervise_dir)
|
||||
return Supervisor().prepare(slug, supervise_dir)
|
||||
|
||||
|
||||
def merge_provision_env_vars(provision: AgentProvisionPlan) -> AgentProvisionPlan:
|
||||
|
||||
@@ -5,10 +5,7 @@ from __future__ import annotations
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from .store.config_store import ConfigStore
|
||||
except ImportError:
|
||||
from config_store import ConfigStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from .store.config_store import ConfigStore
|
||||
|
||||
|
||||
class StaleImageError(Exception):
|
||||
|
||||
@@ -6,16 +6,10 @@ import os
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from ...supervisor.types import Proposal, Response
|
||||
from ...paths import host_db_path
|
||||
from ...store.db_store import DbStore
|
||||
from ...store.migrations import TableMigrations
|
||||
except ImportError:
|
||||
from supervisor.types import Proposal, Response # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from paths import host_db_path # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from db_store import DbStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from ...supervisor.types import Proposal, Response
|
||||
from ...paths import host_db_path
|
||||
from ...store.db_store import DbStore
|
||||
from ...store.migrations import TableMigrations
|
||||
|
||||
|
||||
class QueueStore(DbStore):
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""The orchestrator's view of the supervise plane.
|
||||
|
||||
Bundles the orchestrator-owned supervise surface: the queue/audit I/O and diff
|
||||
rendering (`queue`) and the `Supervise` lifecycle (`supervise`). For
|
||||
convenience it re-exports the neutral vocabulary (`bot_bottle.supervisor`'s
|
||||
types + `SupervisePlan`) so orchestrator-side callers — service, CLI, tests —
|
||||
import from one place.
|
||||
rendering (`queue`), and the `Supervisor` lifecycle (host-side database
|
||||
staging). For convenience it re-exports the neutral vocabulary
|
||||
(`bot_bottle.supervisor`'s types + `SupervisePlan`) so orchestrator-side
|
||||
callers — service, CLI, tests — import from one place.
|
||||
|
||||
The data plane must NOT import this package; it imports `bot_bottle.supervisor`
|
||||
(neutral) directly and reaches the queue over the control-plane RPC.
|
||||
@@ -12,8 +12,12 @@ The data plane must NOT import this package; it imports `bot_bottle.supervisor`
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from pathlib import Path
|
||||
|
||||
from ...supervisor.types import * # noqa: F401,F403 — re-export the neutral vocabulary
|
||||
from ...supervisor.plan import SupervisePlan
|
||||
from ..store.store_manager import StoreManager
|
||||
from .queue import (
|
||||
archive_all_proposals,
|
||||
archive_proposal,
|
||||
@@ -31,4 +35,22 @@ from .queue import (
|
||||
write_proposal,
|
||||
write_response,
|
||||
)
|
||||
from .supervise import Supervise
|
||||
|
||||
|
||||
class Supervisor(ABC):
|
||||
"""Per-bottle supervise lifecycle. Encapsulates host-side database staging;
|
||||
the gateway's start/stop lifecycle is backend-specific.
|
||||
|
||||
`prepare` migrates the orchestrator's `bot-bottle.db` and returns the
|
||||
neutral `SupervisePlan`. It touches the orchestrator store manager, so it
|
||||
lives here rather than in the neutral `bot_bottle.supervisor` package; the
|
||||
backend (which drives launch) may import it — backend → orchestrator is an
|
||||
allowed direction, unlike gateway → orchestrator."""
|
||||
|
||||
def prepare(self, slug: str, stage_dir: Path) -> SupervisePlan:
|
||||
"""Stage the host database. Returns the plan; `internal_network` must be
|
||||
set by the launch step before .start runs."""
|
||||
del stage_dir
|
||||
mgr = StoreManager.instance()
|
||||
mgr.migrate()
|
||||
return SupervisePlan(slug=slug, db_path=mgr.db_path)
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
"""The `Supervise` lifecycle — host-side database staging (PRD 0013 / 0070).
|
||||
|
||||
`Supervise.prepare` migrates the orchestrator's `bot-bottle.db` and returns the
|
||||
neutral `SupervisePlan`. It touches the orchestrator store manager, so it lives
|
||||
here rather than in the neutral `bot_bottle.supervisor` package; the backend
|
||||
(which drives launch) may import it — backend → orchestrator is an allowed
|
||||
direction, unlike gateway → orchestrator.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from pathlib import Path
|
||||
|
||||
from ..store.store_manager import StoreManager
|
||||
from ...supervisor.plan import SupervisePlan
|
||||
|
||||
|
||||
class Supervise(ABC):
|
||||
"""Per-bottle supervise daemon. Encapsulates host-side database staging;
|
||||
the gateway's start/stop lifecycle is backend-specific."""
|
||||
|
||||
def prepare(
|
||||
self,
|
||||
slug: str,
|
||||
stage_dir: Path,
|
||||
) -> SupervisePlan:
|
||||
"""Stage the host database. Returns the plan; `internal_network` must be
|
||||
set by the launch step before .start runs."""
|
||||
del stage_dir
|
||||
mgr = StoreManager.instance()
|
||||
mgr.migrate()
|
||||
return SupervisePlan(
|
||||
slug=slug,
|
||||
db_path=mgr.db_path,
|
||||
)
|
||||
@@ -5,16 +5,10 @@ from __future__ import annotations
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from ..supervisor.types import AuditEntry
|
||||
from ..paths import host_db_path
|
||||
from .db_store import DbStore
|
||||
from .migrations import TableMigrations
|
||||
except ImportError:
|
||||
from supervisor.types import AuditEntry # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from paths import host_db_path # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from db_store import DbStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from ..supervisor.types import AuditEntry
|
||||
from ..paths import host_db_path
|
||||
from .db_store import DbStore
|
||||
from .migrations import TableMigrations
|
||||
|
||||
|
||||
class AuditStore(DbStore):
|
||||
|
||||
@@ -4,14 +4,9 @@ from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from .db_store import DbStore
|
||||
from .migrations import TableMigrations
|
||||
from ..paths import host_db_path
|
||||
except ImportError:
|
||||
from db_store import DbStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from paths import host_db_path # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from .db_store import DbStore
|
||||
from .migrations import TableMigrations
|
||||
from ..paths import host_db_path
|
||||
|
||||
|
||||
DEFAULT_CACHED_IMAGE_STALE_WARNING_DAYS = 1
|
||||
|
||||
@@ -6,10 +6,7 @@ import sqlite3
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from .migrations import TableMigrations
|
||||
except ImportError:
|
||||
from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
||||
from .migrations import TableMigrations
|
||||
|
||||
|
||||
class DbVersionError(Exception):
|
||||
|
||||
@@ -11,6 +11,6 @@ tier's private code, importable by `bot_bottle.gateway` **and**
|
||||
status / poll-status constants, and the shared daemon constants.
|
||||
* `plan` — `SupervisePlan`, the launch-time staging DTO.
|
||||
|
||||
The orchestrator-only half (queue I/O, diff rendering, the `Supervise`
|
||||
The orchestrator-only half (queue I/O, diff rendering, the `Supervisor`
|
||||
lifecycle) lives under `bot_bottle.orchestrator.supervisor`.
|
||||
"""
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""`SupervisePlan` — the launch-time supervise staging DTO.
|
||||
|
||||
A pure value type (no store access), so it lives in the neutral package: the
|
||||
backend builds it at launch and the orchestrator's `Supervise.prepare` returns
|
||||
backend builds it at launch and the orchestrator's `Supervisor.prepare` returns
|
||||
it. The behaviour that fills it in — staging the host database — is the
|
||||
orchestrator's, in `bot_bottle.orchestrator.supervisor.supervise`.
|
||||
orchestrator's, in `bot_bottle.orchestrator.supervisor.Supervisor`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -14,7 +14,7 @@ from pathlib import Path
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SupervisePlan:
|
||||
"""Output of `Supervise.prepare`; consumed by `.start`.
|
||||
"""Output of `Supervisor.prepare`; consumed by `.start`.
|
||||
|
||||
`db_path` is the host database bind-mounted into the gateway at
|
||||
/run/supervise/bot-bottle.db. `internal_network` is empty at prepare time;
|
||||
|
||||
@@ -352,7 +352,7 @@ class TestToolConstants(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class _StubSupervise(supervise.Supervise):
|
||||
class _StubSupervise(supervise.Supervisor):
|
||||
"""Concrete Supervise subclass for testing the prepare template."""
|
||||
|
||||
def start(self, plan): # type: ignore
|
||||
|
||||
Reference in New Issue
Block a user