feat(cloud): report planner transport status
This commit is contained in:
@@ -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")
|
||||
@@ -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(
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user