feat(ql3): add strong cluster run management

This commit is contained in:
whyour
2026-08-12 07:28:43 +08:00
parent e38b143dbb
commit c0ab62e64a
58 changed files with 3087 additions and 105 deletions
@@ -15,13 +15,14 @@ export interface PostgresSchemaContractFunction {
export interface PostgresSchemaContract {
readonly schema: 'ql3';
readonly contractName: 'control-core';
readonly contractVersion: 54;
readonly migrationId: 'pg-0055-run-attempt-log-retention';
readonly contractVersion: 55;
readonly migrationId: 'pg-0056-run-management-boundary';
readonly minimumServerMajor: 16;
readonly maximumServerMajor: 18;
readonly capabilities: Readonly<{
run_core: 1;
run_attempt_log_retention: 1;
run_management_boundary: 1;
run_dispatch_lease: 1;
run_retry_policy: 1;
project_policy: 1;
@@ -101,8 +102,8 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
Object.freeze({
schema: 'ql3',
contractName: 'control-core',
contractVersion: 54,
migrationId: 'pg-0055-run-attempt-log-retention',
contractVersion: 55,
migrationId: 'pg-0056-run-management-boundary',
minimumServerMajor: 16,
maximumServerMajor: 18,
capabilities: Object.freeze({
@@ -143,6 +144,7 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
project_tool_definition_snapshot: 1,
run_core: 1,
run_attempt_log_retention: 1,
run_management_boundary: 1,
run_dispatch_lease: 1,
run_retry_policy: 1,
security_audit: 1,
@@ -2323,6 +2325,15 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
volatility: 'volatile',
configuration: Object.freeze(['search_path=pg_catalog, ql3']),
}),
Object.freeze({
name: 'lock_run_management_policy_fence',
identityArguments:
'character varying, character varying, character varying, integer, integer',
owner: 'ql3_migration',
securityDefiner: true,
volatility: 'volatile',
configuration: Object.freeze(['search_path=pg_catalog, ql3']),
}),
Object.freeze({
name: 'commit_plugin_package_task_reconciliation',
identityArguments:
@@ -19,6 +19,7 @@ export const POSTGRES_SCHEMA_READINESS_ERROR_CODES = [
'admin_role_invalid',
'automation_manager_role_invalid',
'approval_manager_role_invalid',
'run_manager_role_invalid',
'package_manager_role_invalid',
'package_executor_role_invalid',
'worker_credential_manager_role_invalid',
@@ -1361,6 +1362,37 @@ const REQUIRED_APPROVAL_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze(
),
);
const REQUIRED_RUN_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze(
Object.fromEntries(
postgresqlControlSchemaContract.tables.map(({ name }) => [
name,
Object.freeze(
name === 'schema_migrations' ||
name === 'schema_capabilities' ||
name === 'projects' ||
name === 'project_role_bindings' ||
name === 'task_definitions' ||
name === 'task_definition_revisions' ||
name === 'task_execution_revisions'
? { ...NO_TABLE_PRIVILEGES, select: true }
: name === 'runs' ||
name === 'run_attempts' ||
name === 'run_events' ||
name === 'security_audit_events'
? { ...NO_TABLE_PRIVILEGES, select: true, insert: true }
: name === 'plugin_package_identity_keyset_ledger'
? {
...NO_TABLE_PRIVILEGES,
select: true,
insert: true,
update: true,
}
: NO_TABLE_PRIVILEGES,
),
]),
),
);
const REQUIRED_WORKER_CREDENTIAL_MANAGER_PRIVILEGES: RequiredPrivileges =
Object.freeze(
Object.fromEntries(
@@ -1448,6 +1480,7 @@ const REQUIRED_RUNTIME_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
enforce_plugin_package_stage_provenance: false,
lock_active_plugin_package_project: false,
lock_approval_policy_fence: false,
lock_run_management_policy_fence: true,
plugin_package_automation_start_allowed: true,
plugin_package_workflow_admission_snapshot: true,
plugin_package_workflow_task_attempt_snapshot: true,
@@ -1464,6 +1497,7 @@ const REQUIRED_PACKAGE_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
enforce_plugin_package_stage_provenance: false,
lock_active_plugin_package_project: false,
lock_approval_policy_fence: true,
lock_run_management_policy_fence: false,
plugin_package_automation_start_allowed: false,
plugin_package_workflow_admission_snapshot: false,
plugin_package_workflow_task_attempt_snapshot: false,
@@ -1480,6 +1514,7 @@ const REQUIRED_PACKAGE_EXECUTOR_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges
enforce_plugin_package_stage_provenance: false,
lock_active_plugin_package_project: true,
lock_approval_policy_fence: true,
lock_run_management_policy_fence: false,
plugin_package_automation_start_allowed: false,
plugin_package_workflow_admission_snapshot: false,
plugin_package_workflow_task_attempt_snapshot: false,
@@ -1500,6 +1535,12 @@ const REQUIRED_APPROVAL_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges
lock_approval_policy_fence: true,
});
const REQUIRED_RUN_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
Object.freeze({
...NO_FUNCTION_PRIVILEGES,
lock_run_management_policy_fence: true,
});
function safeInteger(value: unknown): number | null {
if (typeof value === 'number' && Number.isSafeInteger(value)) return value;
if (typeof value === 'string' && /^(0|[1-9]\d*)$/.test(value)) {
@@ -1864,6 +1905,7 @@ async function assertRole(
| 'admin_role_invalid'
| 'automation_manager_role_invalid'
| 'approval_manager_role_invalid'
| 'run_manager_role_invalid'
| 'package_manager_role_invalid'
| 'package_executor_role_invalid'
| 'worker_credential_manager_role_invalid'
@@ -2120,6 +2162,30 @@ export async function assertPostgresApprovalManagerSchemaReady(
});
}
export async function assertPostgresRunManagerSchemaReady(
queryable: PostgresMigrationQueryable,
contract: PostgresSchemaContract = postgresqlControlSchemaContract,
): Promise<PostgresSchemaReadinessReport> {
const server = await readServer(queryable, contract);
const migrationIds = await assertHistory(queryable);
await assertCapability(queryable, contract);
await assertSchemaContract(queryable, contract);
await assertRole(
queryable,
contract,
REQUIRED_RUN_MANAGER_PRIVILEGES,
REQUIRED_RUN_MANAGER_FUNCTION_PRIVILEGES,
'run_manager_role_invalid',
);
return Object.freeze({
ready: true,
...server,
contractName: contract.contractName,
contractVersion: contract.contractVersion,
migrationIds,
});
}
export async function assertPostgresPackageManagerSchemaReady(
queryable: PostgresMigrationQueryable,
contract: PostgresSchemaContract = postgresqlControlSchemaContract,