mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 10:32:40 +08:00
feat(ql3): persist package secret approval plans
This commit is contained in:
+310
@@ -0,0 +1,310 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
PLUGIN_PACKAGE_API_VERSION,
|
||||
PLUGIN_PACKAGE_KIND,
|
||||
planPluginPackageInstall,
|
||||
} = require('@qinglong/runtime-core/plugin-package');
|
||||
const {
|
||||
createPluginPackageInstall,
|
||||
createPluginPackageLock,
|
||||
pluginPackageActivationIntentDigest,
|
||||
pluginPackageInstallActionDigest,
|
||||
pluginPackageInstallPlanDigest,
|
||||
transitionPluginPackageInstall,
|
||||
} = require('@qinglong/runtime-core/plugin-package-install');
|
||||
const {
|
||||
createPluginPackageInstallProposal,
|
||||
} = require('@qinglong/runtime-core/plugin-package-proposal');
|
||||
const {
|
||||
createPluginPackageResourceGeneration,
|
||||
} = require('@qinglong/runtime-core/plugin-package-resource-generation');
|
||||
const {
|
||||
createPluginPackageSecretBindingPlan,
|
||||
} = require('@qinglong/runtime-core/plugin-package-secret-binding-plan');
|
||||
const {
|
||||
PluginPackageSecretBindingApprovalPlanConflictError,
|
||||
PluginPackageSecretBindingApprovalPlanUnavailableError,
|
||||
createPluginPackageSecretBindingApprovalPlan,
|
||||
} = require('@qinglong/runtime-core/plugin-package-secret-binding-approval-plan');
|
||||
const { createSecretRef } = require('@qinglong/runtime-core/secret-reference');
|
||||
const {
|
||||
PostgresPluginPackageSecretBindingApprovalPlanReader,
|
||||
PostgresPluginPackageSecretBindingApprovalPlanRepository,
|
||||
} = require('../dist/plugin-package/secret-binding/pluginPackageSecretBindingApprovalPlanRepository');
|
||||
|
||||
function fixture() {
|
||||
const manifest = {
|
||||
apiVersion: PLUGIN_PACKAGE_API_VERSION,
|
||||
kind: PLUGIN_PACKAGE_KIND,
|
||||
metadata: {
|
||||
name: 'example-monitor',
|
||||
displayName: 'Example Monitor',
|
||||
version: '1.0.0',
|
||||
description: 'PostgreSQL Secret binding plan fixture',
|
||||
license: 'Apache-2.0',
|
||||
},
|
||||
spec: {
|
||||
compatibility: {
|
||||
qinglong: '>=3.0.0-0 <4.0.0',
|
||||
architectures: ['arm64'],
|
||||
deploymentProfiles: ['cluster-control'],
|
||||
},
|
||||
runtimes: [],
|
||||
resources: {
|
||||
memory: { recommended: '32Mi' },
|
||||
disk: { install: '4Mi', working: '8Mi' },
|
||||
},
|
||||
permissions: {
|
||||
network: { allowedHosts: [] },
|
||||
secrets: [{ name: 'TOKEN', required: true }],
|
||||
tools: ['secret.use'],
|
||||
},
|
||||
contents: { tasks: [], workflows: [], prompts: [], tools: [] },
|
||||
},
|
||||
};
|
||||
const environment = {
|
||||
qinglongVersion: '3.0.0-alpha.0',
|
||||
architecture: 'arm64',
|
||||
deploymentProfile: 'cluster-control',
|
||||
runtimes: [],
|
||||
availableMemoryBytes: 128 * 1024 * 1024,
|
||||
availableDiskBytes: 256 * 1024 * 1024,
|
||||
};
|
||||
const installPlan = planPluginPackageInstall(manifest, environment);
|
||||
const actionInput = {
|
||||
lockId: 'lock-secret-binding-1',
|
||||
projectId: 'project-1',
|
||||
manifest,
|
||||
plan: installPlan,
|
||||
environment,
|
||||
source: {
|
||||
kind: 'offline',
|
||||
locator: `offline:sha256:${'a'.repeat(64)}`,
|
||||
artifactDigest: 'a'.repeat(64),
|
||||
artifactBytes: 2048,
|
||||
contentDigest: 'b'.repeat(64),
|
||||
},
|
||||
architecture: 'arm64',
|
||||
deploymentProfile: 'cluster-control',
|
||||
targetGeneration: 1,
|
||||
};
|
||||
const proposal = createPluginPackageInstallProposal({
|
||||
actionRef: 'proposal:secret-binding-install-v1',
|
||||
actionInput,
|
||||
proposedBy: { type: 'user', id: 'cluster-owner' },
|
||||
proposalFence: { projectVersion: 1, bindingVersion: 1 },
|
||||
createdAtMs: 90,
|
||||
});
|
||||
const lock = createPluginPackageLock({
|
||||
...actionInput,
|
||||
approval: {
|
||||
requestId: 'approval-secret-binding-install-v1',
|
||||
requestVersion: 1,
|
||||
dispatchId: 'dispatch-secret-binding-install-v1',
|
||||
actionDigest: pluginPackageInstallActionDigest(actionInput),
|
||||
previewDigest: pluginPackageInstallPlanDigest(installPlan),
|
||||
approvedBy: { type: 'user', id: 'install-reviewer' },
|
||||
approvedAtMs: 100,
|
||||
expiresAtMs: 1_000,
|
||||
fence: { projectVersion: 1, bindingVersion: 1 },
|
||||
},
|
||||
createdAtMs: 200,
|
||||
});
|
||||
const queued = createPluginPackageInstall(lock, {
|
||||
installationId: 'install-secret-binding-1',
|
||||
mutationId: 'mutation-secret-binding-create',
|
||||
occurredAtMs: 201,
|
||||
});
|
||||
const staged = transitionPluginPackageInstall(lock, queued, {
|
||||
type: 'stage_completed',
|
||||
mutationId: 'mutation-secret-binding-stage',
|
||||
occurredAtMs: 202,
|
||||
stageRef: 'stage-secret-binding-1',
|
||||
artifactDigest: lock.source.artifactDigest,
|
||||
manifestDigest: lock.manifestDigest,
|
||||
contentDigest: lock.source.contentDigest,
|
||||
evidenceDigest: 'c'.repeat(64),
|
||||
});
|
||||
const activating = transitionPluginPackageInstall(lock, staged, {
|
||||
type: 'activation_started',
|
||||
mutationId: 'mutation-secret-binding-activate',
|
||||
occurredAtMs: 203,
|
||||
});
|
||||
const record = transitionPluginPackageInstall(lock, activating, {
|
||||
type: 'activation_committed',
|
||||
mutationId: 'mutation-secret-binding-commit',
|
||||
occurredAtMs: 204,
|
||||
activationRef: 'activation-secret-binding-1',
|
||||
intentDigest: pluginPackageActivationIntentDigest(lock, activating),
|
||||
generation: 1,
|
||||
contentDigest: lock.source.contentDigest,
|
||||
});
|
||||
const generation = createPluginPackageResourceGeneration({
|
||||
installationId: record.installationId,
|
||||
projectId: record.projectId,
|
||||
packageName: record.packageName,
|
||||
lockDigest: record.lockDigest,
|
||||
generation: record.targetGeneration,
|
||||
previousActiveLockDigest: record.previousActiveLockDigest,
|
||||
contentDigest: lock.source.contentDigest,
|
||||
contents: manifest.spec.contents,
|
||||
});
|
||||
const bindingPlan = createPluginPackageSecretBindingPlan({
|
||||
generation,
|
||||
manifest,
|
||||
assignments: [
|
||||
{
|
||||
name: 'TOKEN',
|
||||
secretRef: createSecretRef({
|
||||
projectId: 'project-1',
|
||||
name: 'runtime-token',
|
||||
version: 2,
|
||||
}),
|
||||
},
|
||||
],
|
||||
plannedAtMs: 300,
|
||||
});
|
||||
const approvalPlan = createPluginPackageSecretBindingApprovalPlan({
|
||||
actionRef: 'secret-binding:example-monitor-v1',
|
||||
bindingPlan,
|
||||
requestedBy: { type: 'user', id: 'cluster-owner' },
|
||||
expiresAtMs: 900,
|
||||
});
|
||||
return { approvalPlan, lock, proposal, record };
|
||||
}
|
||||
|
||||
test('reads and creates one exact approval plan only through the narrow database function', async () => {
|
||||
const { approvalPlan } = fixture();
|
||||
const calls = [];
|
||||
const repository =
|
||||
new PostgresPluginPackageSecretBindingApprovalPlanRepository({
|
||||
async query(text, parameters) {
|
||||
calls.push({ text, parameters });
|
||||
if (
|
||||
text.includes('create_plugin_package_secret_binding_approval_plan')
|
||||
) {
|
||||
return { rows: [{ status: 'created' }] };
|
||||
}
|
||||
return { rows: [{ planJson: approvalPlan }] };
|
||||
},
|
||||
});
|
||||
|
||||
const result = await repository.create(approvalPlan);
|
||||
assert.deepEqual(result, { status: 'created', plan: approvalPlan });
|
||||
assert.equal(calls.length, 2);
|
||||
assert.match(
|
||||
calls[0].text,
|
||||
/create_plugin_package_secret_binding_approval_plan/,
|
||||
);
|
||||
assert.deepEqual(calls[0].parameters, [JSON.stringify(approvalPlan)]);
|
||||
assert.doesNotMatch(calls[0].text, /INSERT INTO/);
|
||||
assert.match(calls[1].text, /WHERE action_ref = \$1/);
|
||||
});
|
||||
|
||||
test('loads one current unbound generation snapshot and rejects provenance drift', async () => {
|
||||
const { lock, proposal, record } = fixture();
|
||||
const pool = {
|
||||
async query(text, parameters) {
|
||||
assert.match(text, /plugin_package_secret_binding_planning_snapshot/);
|
||||
assert.deepEqual(parameters, ['project-1', 'example-monitor']);
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
recordJson: record,
|
||||
lockJson: lock,
|
||||
proposalJson: proposal,
|
||||
observedAtMs: '500',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
const reader = new PostgresPluginPackageSecretBindingApprovalPlanReader(pool);
|
||||
assert.deepEqual(
|
||||
await reader.loadPlanningSnapshot('project-1', 'example-monitor'),
|
||||
{ record, lock, proposal, observedAtMs: 500 },
|
||||
);
|
||||
|
||||
const drifted = new PostgresPluginPackageSecretBindingApprovalPlanReader({
|
||||
async query() {
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
recordJson: record,
|
||||
lockJson: lock,
|
||||
proposalJson: { ...proposal, previewDigest: 'f'.repeat(64) },
|
||||
observedAtMs: 500,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
await assert.rejects(
|
||||
drifted.loadPlanningSnapshot('project-1', 'example-monitor'),
|
||||
PluginPackageSecretBindingApprovalPlanUnavailableError,
|
||||
);
|
||||
});
|
||||
|
||||
test('returns absence and fails closed on replay drift or storage conflict', async () => {
|
||||
const { approvalPlan } = fixture();
|
||||
const reader = new PostgresPluginPackageSecretBindingApprovalPlanReader({
|
||||
async query() {
|
||||
return { rows: [] };
|
||||
},
|
||||
});
|
||||
assert.equal(await reader.findByActionRef(approvalPlan.actionRef), null);
|
||||
|
||||
const otherPlan = createPluginPackageSecretBindingApprovalPlan({
|
||||
actionRef: approvalPlan.actionRef,
|
||||
bindingPlan: approvalPlan.bindingPlan,
|
||||
requestedBy: { type: 'user', id: 'another-owner' },
|
||||
expiresAtMs: approvalPlan.expiresAtMs,
|
||||
});
|
||||
|
||||
const drifted = new PostgresPluginPackageSecretBindingApprovalPlanRepository({
|
||||
async query(text) {
|
||||
return text.includes('create_plugin_package_secret_binding_approval_plan')
|
||||
? { rows: [{ status: 'existing' }] }
|
||||
: { rows: [{ planJson: otherPlan }] };
|
||||
},
|
||||
});
|
||||
await assert.rejects(
|
||||
drifted.create(approvalPlan),
|
||||
PluginPackageSecretBindingApprovalPlanConflictError,
|
||||
);
|
||||
|
||||
const conflict = new PostgresPluginPackageSecretBindingApprovalPlanRepository(
|
||||
{
|
||||
async query() {
|
||||
const error = new Error('unique violation');
|
||||
error.code = '23505';
|
||||
throw error;
|
||||
},
|
||||
},
|
||||
);
|
||||
await assert.rejects(
|
||||
conflict.create(approvalPlan),
|
||||
PluginPackageSecretBindingApprovalPlanConflictError,
|
||||
);
|
||||
});
|
||||
|
||||
test('exports plan creation only to the Package manager and readback to the executor', () => {
|
||||
const manager = require('@qinglong/cluster-postgres/package-manager');
|
||||
const executor = require('@qinglong/cluster-postgres/package-executor');
|
||||
assert.equal(
|
||||
manager.PostgresPluginPackageSecretBindingApprovalPlanRepository,
|
||||
PostgresPluginPackageSecretBindingApprovalPlanRepository,
|
||||
);
|
||||
assert.equal(
|
||||
executor.PostgresPluginPackageSecretBindingApprovalPlanRepository,
|
||||
undefined,
|
||||
);
|
||||
assert.equal(
|
||||
executor.PostgresPluginPackageSecretBindingApprovalPlanReader,
|
||||
PostgresPluginPackageSecretBindingApprovalPlanReader,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user