feat(cloud): report planner transport status

This commit is contained in:
2026-07-13 23:15:46 +08:00
parent 40efa53411
commit 5b64efab53
15 changed files with 71 additions and 6 deletions
@@ -51,6 +51,9 @@ class HostRow(Base):
display_name: Mapped[str | None] = mapped_column(String, nullable=True)
enrolled_at: Mapped[str | None] = mapped_column(String, nullable=True)
revoked_at: Mapped[str | None] = mapped_column(String, nullable=True)
planner_transport: Mapped[str] = mapped_column(
String, nullable=False, default="direct", server_default=text("'direct'")
)
class DeviceEnrollmentRow(Base):
@@ -177,6 +177,7 @@ def create_internal_router(
devices,
address=payload.address,
allow_device_takeover=allow_device_takeover,
planner_transport=payload.planner_transport,
)
policy = pool.store.get_host_governance_policy(host_id)
policy_revision = policy.revision if policy is not None else 0
@@ -39,6 +39,7 @@ class HeartbeatRequest(BaseModel):
address: str | None = None
devices: list[DeviceSnapshotModel] = Field(default_factory=list)
policy_revision: int = Field(default=0, ge=0)
planner_transport: Literal["direct", "cloud"] = "direct"
class HostGovernancePolicyModel(BaseModel):
@@ -0,0 +1,23 @@
"""Record each Host's planner transport for governance visibility."""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "0006_host_planner_transport"
down_revision = "0005_cloud_token_usage"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"host_registrations",
sa.Column("planner_transport", sa.String(), nullable=False, server_default="direct"),
)
def downgrade() -> None:
op.drop_column("host_registrations", "planner_transport")
+8 -1
View File
@@ -34,6 +34,7 @@ class HostRegistration:
host_id: str
address: str | None
last_seen_at: datetime
planner_transport: Literal["direct", "cloud"] = "direct"
@dataclass(frozen=True)
@@ -61,6 +62,7 @@ class DevicePool:
snapshot: list[Device],
*,
address: str | None = None,
planner_transport: Literal["direct", "cloud"] = "direct",
allow_device_takeover: bool = False,
) -> None:
"""Push a host's current device snapshot into the pool.
@@ -70,7 +72,12 @@ class DevicePool:
other hosts are untouched.
"""
now = utc_now()
self.store.upsert_host(host_id, address=address, last_seen_at=now)
self.store.upsert_host(
host_id,
address=address,
last_seen_at=now,
planner_transport=planner_transport,
)
devices = [self._to_pooled(device, host_id, now) for device in snapshot]
if allow_device_takeover:
self.store.replace_host_devices(
@@ -154,6 +154,7 @@ class CloudRepository(Protocol):
*,
address: str | None,
last_seen_at: datetime,
planner_transport: Literal["direct", "cloud"] = "direct",
) -> None: ...
def replace_host_devices(
+1 -1
View File
@@ -9,7 +9,7 @@ from alembic.runtime.migration import MigrationContext
from cloud.database import create_database_engine, normalize_database_url
HEAD_REVISION = "0005_cloud_token_usage"
HEAD_REVISION = "0006_host_planner_transport"
class SchemaVersionError(RuntimeError):
+1
View File
@@ -240,6 +240,7 @@ def create_cloud_router(
host_id=h.host_id,
address=h.address,
last_seen_at=h.last_seen_at.isoformat() if h.last_seen_at else "",
planner_transport=h.planner_transport,
)
for h in pool.list_hosts()
]
@@ -86,6 +86,7 @@ class HostResponse(BaseModel):
host_id: str
address: str | None = None
last_seen_at: str
planner_transport: Literal["direct", "cloud"] = "direct"
class PluginRegistrationRequest(BaseModel):
@@ -266,6 +266,7 @@ class SQLAlchemyCloudRepository:
*,
address: str | None,
last_seen_at: datetime,
planner_transport: str = "direct",
) -> None:
with self._sessions.begin() as session:
row = session.get(HostRow, host_id)
@@ -275,12 +276,14 @@ class SQLAlchemyCloudRepository:
host_id=host_id,
address=address,
last_seen_at=_iso(last_seen_at),
planner_transport=planner_transport,
)
)
return
if address is not None:
row.address = address
row.last_seen_at = _iso(last_seen_at)
row.planner_transport = planner_transport
def replace_host_devices(
self,
@@ -1419,6 +1422,9 @@ def _host_from_row(row: HostRow) -> Any:
host_id=row.host_id,
address=row.address,
last_seen_at=_parse_dt(row.last_seen_at) or utc_now(),
planner_transport=(
row.planner_transport if row.planner_transport in {"direct", "cloud"} else "direct"
),
)