feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,273 @@
#!/usr/bin/env node
const fs = require('node:fs');
const {
createPostgresDatabaseOpener,
} = require('../packages/ql3-cluster-postgres/dist/entrypoints/runtime.js');
const {
PostgresPluginPackageLifecyclePlanReader,
} = require('../packages/ql3-cluster-postgres/dist/entrypoints/packageManager.js');
const {
createClusterPluginPackageLifecycleManagementService,
} = require('../packages/ql3-cluster-admin/dist/plugin-package/lifecycle/pluginPackageLifecycleManagement.js');
const {
runClusterPluginPackageLifecycleExecution,
runClusterPluginPackageLifecyclePlan,
} = require('../packages/ql3-cluster-admin/dist/plugin-package/lifecycle/pluginPackageLifecycleExecutor.js');
const PHASES = new Set(['plan', 'propose', 'decide', 'execute']);
const REQUESTER = Object.freeze({
type: 'user',
id: 'ha-lifecycle-owner',
});
const REVIEWER = Object.freeze({
type: 'user',
id: 'ha-lifecycle-reviewer',
});
function requiredEnvironment(name) {
const value = process.env[name];
if (!value) throw new Error(`${name} is required`);
return value;
}
function positiveInteger(value, label) {
if (!/^[1-9]\d*$/.test(value ?? '')) {
throw new Error(`${label} is invalid`);
}
const parsed = Number(value);
if (!Number.isSafeInteger(parsed)) {
throw new Error(`${label} is invalid`);
}
return parsed;
}
function databaseOpener(role, connectionString, applicationName) {
return createPostgresDatabaseOpener({
role,
connection: {
connectionString,
tls: { mode: 'disable' },
},
pool: {
applicationName,
maxConnections: 2,
connectionTimeoutMs: 2_000,
},
onPoolError() {},
});
}
function identifiers(ordinal) {
const suffix = String(ordinal).padStart(2, '0');
return Object.freeze({
approvalRequestId: `approval-managed-lifecycle-${ordinal}`,
approvalAuditEventId:
`34000000-0000-4000-8000-0000000001${suffix}`,
decisionId: `decision-managed-lifecycle-${ordinal}`,
decisionAuditEventId:
`35000000-0000-4000-8000-0000000001${suffix}`,
consumptionId: `consume-managed-lifecycle-${ordinal}`,
dispatchId: `dispatch-managed-lifecycle-${ordinal}`,
executionAuditEventId:
`36000000-0000-4000-8000-0000000001${suffix}`,
});
}
async function withDatabase(opener, callback) {
const database = await opener();
let failure;
try {
return await callback(database);
} catch (error) {
failure = error;
throw error;
} finally {
try {
await database.close();
} catch (closeError) {
if (failure !== undefined) {
throw new AggregateError(
[failure, closeError],
'lifecycle crash actor failed and PostgreSQL did not close',
);
}
throw closeError;
}
}
}
async function loadPlan(managerOpener, actionRef) {
return withDatabase(managerOpener, async (database) => {
const plan = await new PostgresPluginPackageLifecyclePlanReader(
database.pool,
).findByActionRef(actionRef);
if (!plan) throw new Error('durable lifecycle plan is absent');
return plan;
});
}
function principal(subject, authenticationId, plan) {
return Object.freeze({
subject,
authenticationId,
authenticatedAtMs: plan.plannedAtMs,
expiresAtMs: plan.expiresAtMs,
assurance: 'multi_factor',
});
}
async function runPhase(options) {
const ids = identifiers(options.ordinal);
const executorOpener = databaseOpener(
'package-executor',
options.executorUrl,
`ql3-ha-lifecycle-crash-${options.action}-${options.phase}-executor`,
);
const managerOpener = databaseOpener(
'package-manager',
options.managerUrl,
`ql3-ha-lifecycle-crash-${options.action}-${options.phase}-manager`,
);
if (options.phase === 'plan') {
const result = await runClusterPluginPackageLifecyclePlan({
openDatabase: executorOpener,
actionRef: options.actionRef,
action: options.action,
projectId: options.projectId,
packageName: options.packageName,
requestedBy: REQUESTER,
confirmAuthorization() {},
});
return Object.freeze({
status: result.status,
plan: result.plan,
});
}
const plan = await loadPlan(managerOpener, options.actionRef);
if (options.phase === 'propose') {
return withDatabase(managerOpener, async (database) => {
const service = createClusterPluginPackageLifecycleManagementService({
pool: database.pool,
now: () => plan.plannedAtMs + 1,
});
const result = await service.propose({
actionRef: options.actionRef,
approvalRequestId: ids.approvalRequestId,
approvalAuditEventId: ids.approvalAuditEventId,
principal: principal(
REQUESTER,
`managed-lifecycle-owner-auth-${options.ordinal}`,
plan,
),
});
return Object.freeze({
status: result.approvalStatus,
approvalRequest: result.approvalRequest,
});
});
}
if (options.phase === 'decide') {
return withDatabase(managerOpener, async (database) => {
const service = createClusterPluginPackageLifecycleManagementService({
pool: database.pool,
now: () => plan.plannedAtMs + 2,
});
const result = await service.decide({
actionRef: options.actionRef,
approvalRequestId: ids.approvalRequestId,
expectedVersion: 1,
decisionId: ids.decisionId,
auditEventId: ids.decisionAuditEventId,
decision: 'approved',
reasonCode: 'reviewed',
principal: principal(
REVIEWER,
`managed-lifecycle-reviewer-auth-${options.ordinal}`,
plan,
),
});
return Object.freeze({
status: result.status,
approvalRequest: result.request,
});
});
}
const result = await runClusterPluginPackageLifecycleExecution({
openDatabase: executorOpener,
actionRef: options.actionRef,
approvalRequestId: ids.approvalRequestId,
consumptionId: ids.consumptionId,
dispatchId: ids.dispatchId,
auditEventId: ids.executionAuditEventId,
confirmAuthorization() {},
});
return Object.freeze({
status: result.status,
receipt: result.receipt,
});
}
function writeMarker(markerPath, options, status) {
const descriptor = fs.openSync(markerPath, 'wx', 0o600);
try {
fs.writeSync(
descriptor,
JSON.stringify({
schema:
'qinglong/postgresql-plugin-package-lifecycle-process-crash@v1',
action: options.action,
phase: options.phase,
status,
pid: process.pid,
}),
);
fs.fsyncSync(descriptor);
} finally {
fs.closeSync(descriptor);
}
}
async function main() {
const phase = process.argv[2];
if (!PHASES.has(phase)) {
throw new Error('phase must be plan, propose, decide or execute');
}
const action = requiredEnvironment('QL3_LIFECYCLE_CRASH_ACTION');
if (action !== 'disable' && action !== 'enable') {
throw new Error('QL3_LIFECYCLE_CRASH_ACTION is invalid');
}
const options = Object.freeze({
phase,
action,
ordinal: positiveInteger(
requiredEnvironment('QL3_LIFECYCLE_CRASH_ORDINAL'),
'QL3_LIFECYCLE_CRASH_ORDINAL',
),
actionRef: requiredEnvironment('QL3_LIFECYCLE_CRASH_ACTION_REF'),
projectId: requiredEnvironment('QL3_LIFECYCLE_CRASH_PROJECT_ID'),
packageName: requiredEnvironment('QL3_LIFECYCLE_CRASH_PACKAGE_NAME'),
managerUrl: requiredEnvironment('QL3_LIFECYCLE_CRASH_MANAGER_URL'),
executorUrl: requiredEnvironment('QL3_LIFECYCLE_CRASH_EXECUTOR_URL'),
});
const result = await runPhase(options);
if (process.env.QL3_LIFECYCLE_CRASH_KILL_AFTER_DURABLE === 'true') {
const markerPath = requiredEnvironment(
'QL3_LIFECYCLE_CRASH_MARKER_PATH',
);
writeMarker(markerPath, options, result.status);
process.kill(process.pid, 'SIGKILL');
throw new Error('SIGKILL did not terminate lifecycle crash actor');
}
process.stdout.write(`${JSON.stringify(result)}\n`);
}
main().catch((error) => {
process.stderr.write(
`ql3 lifecycle crash actor failed: ${
error instanceof Error ? error.stack ?? error.message : String(error)
}\n`,
);
process.exitCode = 1;
});