feat(cloud): add targeted task governance foundation
Tests / Test passed: 659

This commit is contained in:
2026-07-13 22:21:12 +08:00
parent a3ba94be04
commit 2cd314b183
41 changed files with 2099 additions and 15 deletions
@@ -19,9 +19,11 @@ from cloud.db_models import (
ScheduledTaskRow,
TaskAttemptRow,
AuthAuditRow,
HostGovernancePolicyRow,
LoginThrottleRow,
UserRow,
UserSessionRow,
UserSubmissionPolicyRow,
)
from cloud.observability import current_correlation_id
from core.models import utc_now
@@ -807,6 +809,86 @@ class SQLAlchemyCloudRepository:
removed += len(stale_throttles)
return removed
# -------------------------------------------------------------- governance
def get_user_submission_policy(self, user_id: str) -> Any | None:
with self._sessions() as session:
row = session.get(UserSubmissionPolicyRow, user_id)
return _user_submission_policy_from_row(row) if row is not None else None
def upsert_user_submission_policy(
self,
*,
user_id: str,
submission_enabled: bool,
allowed_host_ids: tuple[str, ...] | None,
allowed_device_targets: tuple[tuple[str, str], ...] | None,
updated_at: datetime,
) -> Any:
with self._sessions.begin() as session:
if session.get(UserRow, user_id) is None:
raise KeyError(f"unknown user {user_id!r}")
row = session.get(UserSubmissionPolicyRow, user_id)
if row is None:
row = UserSubmissionPolicyRow(
user_id=user_id,
revision=1,
submission_enabled=1 if submission_enabled else 0,
allowed_host_ids_json=_dump_optional_list(allowed_host_ids),
allowed_device_targets_json=_dump_optional_list(
allowed_device_targets
),
updated_at=_iso(updated_at),
)
session.add(row)
else:
row.revision += 1
row.submission_enabled = 1 if submission_enabled else 0
row.allowed_host_ids_json = _dump_optional_list(allowed_host_ids)
row.allowed_device_targets_json = _dump_optional_list(
allowed_device_targets
)
row.updated_at = _iso(updated_at)
session.flush()
return _user_submission_policy_from_row(row)
def get_host_governance_policy(self, host_id: str) -> Any | None:
with self._sessions() as session:
row = session.get(HostGovernancePolicyRow, host_id)
return _host_governance_policy_from_row(row) if row is not None else None
def upsert_host_governance_policy(
self,
*,
host_id: str,
self_submission_enabled: bool,
max_active_tasks: int | None,
daily_token_budget: int | None,
updated_at: datetime,
) -> Any:
with self._sessions.begin() as session:
if session.get(HostRow, host_id) is None:
raise KeyError(f"unknown host {host_id!r}")
row = session.get(HostGovernancePolicyRow, host_id)
if row is None:
row = HostGovernancePolicyRow(
host_id=host_id,
revision=1,
self_submission_enabled=1 if self_submission_enabled else 0,
max_active_tasks=max_active_tasks,
daily_token_budget=daily_token_budget,
updated_at=_iso(updated_at),
)
session.add(row)
else:
row.revision += 1
row.self_submission_enabled = 1 if self_submission_enabled else 0
row.max_active_tasks = max_active_tasks
row.daily_token_budget = daily_token_budget
row.updated_at = _iso(updated_at)
session.flush()
return _host_governance_policy_from_row(row)
def list_reserved_device_ids(self, *, now: datetime) -> set[str]:
with self._sessions() as session:
device_ids = session.scalars(
@@ -1218,6 +1300,8 @@ def _task_from_row(row: ScheduledTaskRow) -> Any:
constraints=TaskConstraints(
driver_type=constraints_data.get("driver_type"),
capability_tags=list(constraints_data.get("capability_tags") or []),
target_host_id=constraints_data.get("target_host_id"),
target_device_id=constraints_data.get("target_device_id"),
),
status=row.status,
assigned_device_id=row.assigned_device_id,
@@ -1232,6 +1316,66 @@ def _task_from_row(row: ScheduledTaskRow) -> Any:
)
def _dump_optional_list(value: tuple[Any, ...] | None) -> str | None:
return json.dumps(value, ensure_ascii=False) if value is not None else None
def _load_optional_string_tuple(value: str | None) -> tuple[str, ...] | None:
if value is None:
return None
try:
parsed = json.loads(value)
except (TypeError, ValueError):
parsed = []
return tuple(item for item in parsed if isinstance(item, str))
def _load_optional_device_targets(
value: str | None,
) -> tuple[tuple[str, str], ...] | None:
if value is None:
return None
try:
parsed = json.loads(value)
except (TypeError, ValueError):
parsed = []
return tuple(
(item[0], item[1])
for item in parsed
if isinstance(item, list | tuple)
and len(item) == 2
and all(isinstance(part, str) for part in item)
)
def _user_submission_policy_from_row(row: UserSubmissionPolicyRow) -> Any:
from cloud.governance import UserSubmissionPolicy
return UserSubmissionPolicy(
user_id=row.user_id,
revision=row.revision,
submission_enabled=bool(row.submission_enabled),
allowed_host_ids=_load_optional_string_tuple(row.allowed_host_ids_json),
allowed_device_targets=_load_optional_device_targets(
row.allowed_device_targets_json
),
updated_at=_parse_dt(row.updated_at) or utc_now(),
)
def _host_governance_policy_from_row(row: HostGovernancePolicyRow) -> Any:
from cloud.governance import HostGovernancePolicy
return HostGovernancePolicy(
host_id=row.host_id,
revision=row.revision,
self_submission_enabled=bool(row.self_submission_enabled),
max_active_tasks=row.max_active_tasks,
daily_token_budget=row.daily_token_budget,
updated_at=_parse_dt(row.updated_at) or utc_now(),
)
def _task_attempt_from_row(row: TaskAttemptRow) -> Any:
from cloud.repository import TaskAttemptRecord