This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user