feat(ql3): persist package secret approval plans

This commit is contained in:
whyour
2026-08-13 12:43:43 +08:00
parent e43fd607aa
commit 3a58e1508e
16 changed files with 1335 additions and 94 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
"reviewedDenseDirectories": [
{
"kind": "ordered_ledger",
"maxDirectSourceFiles": 61,
"maxDirectSourceFiles": 62,
"path": "packages/ql3-cluster-postgres/src/migrations",
"rationale": "PostgreSQL migrations are an append-only version ledger whose ordering and discoverability are safer in one reviewed directory."
},
+13 -8
View File
@@ -15,37 +15,37 @@
"require": "./dist/index.js",
"default": "./dist/index.js"
},
"./runtime": {
"./runtime": {
"types": "./dist/entrypoints/runtime.d.ts",
"require": "./dist/entrypoints/runtime.js",
"default": "./dist/entrypoints/runtime.js"
},
"./admin": {
"./admin": {
"types": "./dist/entrypoints/admin.d.ts",
"require": "./dist/entrypoints/admin.js",
"default": "./dist/entrypoints/admin.js"
},
"./package-manager": {
"./package-manager": {
"types": "./dist/entrypoints/packageManager.d.ts",
"require": "./dist/entrypoints/packageManager.js",
"default": "./dist/entrypoints/packageManager.js"
},
"./package-executor": {
"./package-executor": {
"types": "./dist/entrypoints/packageExecutor.d.ts",
"require": "./dist/entrypoints/packageExecutor.js",
"default": "./dist/entrypoints/packageExecutor.js"
},
"./ai-maintenance": {
"./ai-maintenance": {
"types": "./dist/entrypoints/aiMaintenance.d.ts",
"require": "./dist/entrypoints/aiMaintenance.js",
"default": "./dist/entrypoints/aiMaintenance.js"
},
"./ai-credential-manager": {
"./ai-credential-manager": {
"types": "./dist/entrypoints/aiCredentialManager.d.ts",
"require": "./dist/entrypoints/aiCredentialManager.js",
"default": "./dist/entrypoints/aiCredentialManager.js"
},
"./ai-credential-tester": {
"./ai-credential-tester": {
"types": "./dist/entrypoints/aiCredentialTester.d.ts",
"require": "./dist/entrypoints/aiCredentialTester.js",
"default": "./dist/entrypoints/aiCredentialTester.js"
@@ -85,7 +85,7 @@
"require": "./dist/approval-management/index.js",
"default": "./dist/approval-management/index.js"
},
"./worker-ingress": {
"./worker-ingress": {
"types": "./dist/entrypoints/workerIngress.d.ts",
"require": "./dist/entrypoints/workerIngress.js",
"default": "./dist/entrypoints/workerIngress.js"
@@ -145,6 +145,11 @@
"require": "./dist/plugin-package/installation/pluginPackageSecretBindingRepository.js",
"default": "./dist/plugin-package/installation/pluginPackageSecretBindingRepository.js"
},
"./plugin-package-secret-binding-approval-plan": {
"types": "./dist/plugin-package/secret-binding/pluginPackageSecretBindingApprovalPlanRepository.d.ts",
"require": "./dist/plugin-package/secret-binding/pluginPackageSecretBindingApprovalPlanRepository.js",
"default": "./dist/plugin-package/secret-binding/pluginPackageSecretBindingApprovalPlanRepository.js"
},
"./plugin-package-automation-publication": {
"types": "./dist/plugin-package/publication/pluginPackageAutomationPublicationRepository.d.ts",
"require": "./dist/plugin-package/publication/pluginPackageAutomationPublicationRepository.js",
@@ -41,6 +41,7 @@ export {
export { PostgresPluginPackageMaterializedRevisionRepository } from '../plugin-package/installation/pluginPackageMaterializedRevisionRepository';
export { PostgresPluginPackageSecretBindingRepository } from '../plugin-package/installation/pluginPackageSecretBindingRepository';
export { PostgresPluginPackageSecretBindingApprovalPlanReader } from '../plugin-package/secret-binding/pluginPackageSecretBindingApprovalPlanRepository';
export { PostgresPluginPackageAutomationPublicationRepository } from '../plugin-package/publication/pluginPackageAutomationPublicationRepository';
export {
CLUSTER_PLUGIN_PACKAGE_QUARANTINE_TARGET_LIMIT,
@@ -56,6 +56,10 @@ export {
export { PostgresPluginPackagePublisherTrustAuthorityRepository } from '../plugin-package/publisher/pluginPackagePublisherTrustAuthorityRepository';
export { PostgresPluginPackageLifecyclePlanReader } from '../plugin-package/lifecycle/pluginPackageLifecyclePlanRepository';
export {
PostgresPluginPackageSecretBindingApprovalPlanReader,
PostgresPluginPackageSecretBindingApprovalPlanRepository,
} from '../plugin-package/secret-binding/pluginPackageSecretBindingApprovalPlanRepository';
export { PostgresPluginPackageInstallInventoryReader } from '../plugin-package/installation/pluginPackageInstallRepository';
export { PostgresPluginPackagePublisherRevocationProposalRepository } from '../plugin-package/publisher/pluginPackagePublisherRevocationProposalRepository';
export { PostgresPluginPackagePublisherTrustTransitionProposalRepository } from '../plugin-package/publisher/pluginPackagePublisherTrustTransitionProposalRepository';
@@ -308,5 +308,10 @@ export const postgresqlMainMigrationManifest: MigrationStreamManifest =
checksum:
'28284ca860b39ff9de5b2aa1a2a60ef2c463fd6a72798d237040174272b64b1e',
}),
Object.freeze({
id: 'pg-0061-plugin-package-secret-binding-approval-plans',
checksum:
'c995b7846ae8a57d3abb4b5523961e81aeba890e7405a030bcb505dfc6be3d25',
}),
]),
});
@@ -63,6 +63,7 @@ import { pg0057RunManagementStopBoundaryMigration } from '../run-management/pg-0
import { pg0058PluginPackageAutomationDispositionEventsMigration } from './pg-0058-plugin-package-automation-disposition-events';
import { pg0059PluginPackageSecretBindingsMigration } from './pg-0059-plugin-package-secret-bindings';
import { pg0060PluginPackageSecretMaterializationGuardMigration } from './pg-0060-plugin-package-secret-materialization-guard';
import { pg0061PluginPackageSecretBindingApprovalPlansMigration } from './pg-0061-plugin-package-secret-binding-approval-plans';
export const postgresqlMainMigrationStream: MigrationStreamDefinition<PostgresMigrationContext> =
Object.freeze({
@@ -131,5 +132,6 @@ export const postgresqlMainMigrationStream: MigrationStreamDefinition<PostgresMi
pg0058PluginPackageAutomationDispositionEventsMigration,
pg0059PluginPackageSecretBindingsMigration,
pg0060PluginPackageSecretMaterializationGuardMigration,
pg0061PluginPackageSecretBindingApprovalPlansMigration,
]),
});
@@ -0,0 +1,285 @@
import { CAPABILITIES_V59 } from './pg-0060-plugin-package-secret-materialization-guard';
import { definePostgresSqlMigration } from './sqlMigration';
export const CAPABILITIES_V60 = CAPABILITIES_V59.replace(
'"plugin_package_secret_binding":1,',
'"plugin_package_secret_binding":1,"plugin_package_secret_binding_approval_plan":1,',
);
export const pg0061PluginPackageSecretBindingApprovalPlansMigration =
definePostgresSqlMigration({
id: 'pg-0061-plugin-package-secret-binding-approval-plans',
statements: [
`
CREATE TABLE "ql3"."plugin_package_secret_binding_approval_plans" (
action_ref varchar(255) PRIMARY KEY,
approval_plan_digest char(64) NOT NULL,
binding_plan_digest char(64) NOT NULL,
generation_digest char(64) NOT NULL,
project_id varchar(128) NOT NULL,
package_name varchar(63) NOT NULL,
installation_id varchar(128) NOT NULL,
lock_digest char(64) NOT NULL,
generation integer NOT NULL,
manifest_digest char(64) NOT NULL,
requested_by_type varchar(16) NOT NULL,
requested_by_id varchar(255) NOT NULL,
planned_at_ms bigint NOT NULL,
expires_at_ms bigint NOT NULL,
plan_json jsonb NOT NULL,
CONSTRAINT ql3_plugin_package_secret_binding_approval_plan_project_fk
FOREIGN KEY (project_id) REFERENCES "ql3"."projects" (id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_plugin_package_secret_binding_approval_plan_install_fk
FOREIGN KEY (installation_id)
REFERENCES "ql3"."plugin_package_installs" (installation_id)
ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT ql3_plugin_package_secret_binding_approval_plan_identity_check CHECK (
action_ref ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,254}$' AND
project_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
package_name ~ '^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$' AND
installation_id ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' AND
generation BETWEEN 1 AND 2147483647 AND
requested_by_type = 'user' AND
octet_length(requested_by_id) BETWEEN 1 AND 255 AND
requested_by_id !~ '[[:cntrl:]]'
),
CONSTRAINT ql3_plugin_package_secret_binding_approval_plan_digest_check CHECK (
approval_plan_digest ~ '^[0-9a-f]{64}$' AND
binding_plan_digest ~ '^[0-9a-f]{64}$' AND
generation_digest ~ '^[0-9a-f]{64}$' AND
lock_digest ~ '^[0-9a-f]{64}$' AND
manifest_digest ~ '^[0-9a-f]{64}$'
),
CONSTRAINT ql3_plugin_package_secret_binding_approval_plan_time_check CHECK (
planned_at_ms >= 0 AND
expires_at_ms > planned_at_ms AND
expires_at_ms - planned_at_ms <= 900000
),
CONSTRAINT ql3_plugin_package_secret_binding_approval_plan_json_check CHECK (
jsonb_typeof(plan_json) = 'object' AND
octet_length(plan_json::text) BETWEEN 2 AND 98304 AND
plan_json @> jsonb_build_object(
'schema', 'qinglong/plugin-package-secret-binding-approval-plan@v1',
'actionRef', action_ref,
'approvalPlanDigest', approval_plan_digest,
'requestedBy', jsonb_build_object(
'type', requested_by_type,
'id', requested_by_id
),
'expiresAtMs', expires_at_ms,
'bindingPlan', jsonb_build_object(
'schema', 'qinglong/plugin-package-secret-binding-plan@v1',
'planDigest', binding_plan_digest,
'plannedAtMs', planned_at_ms,
'target', jsonb_build_object(
'generationDigest', generation_digest,
'projectId', project_id,
'packageName', package_name,
'installationId', installation_id,
'lockDigest', lock_digest,
'generation', generation,
'manifestDigest', manifest_digest
)
)
) AND
jsonb_typeof(plan_json #> '{bindingPlan,entries}') = 'array' AND
jsonb_array_length(plan_json #> '{bindingPlan,entries}') BETWEEN 1 AND 64
)
)
`.trim(),
`CREATE UNIQUE INDEX ql3_plugin_package_secret_binding_approval_plan_digest_uidx ON "ql3"."plugin_package_secret_binding_approval_plans" (approval_plan_digest)`,
`CREATE INDEX ql3_plugin_package_secret_binding_approval_plan_target_idx ON "ql3"."plugin_package_secret_binding_approval_plans" (project_id, package_name, generation, action_ref)`,
`CREATE INDEX ql3_plugin_package_secret_binding_approval_plan_expiry_idx ON "ql3"."plugin_package_secret_binding_approval_plans" (expires_at_ms, action_ref)`,
`
CREATE FUNCTION "ql3"."plugin_package_secret_binding_planning_snapshot"(
p_project_id varchar,
p_package_name varchar
)
RETURNS TABLE (
record_json jsonb,
lock_json jsonb,
proposal_json jsonb,
observed_at_ms bigint
)
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER
SET search_path = pg_catalog, ql3
AS $ql3$
BEGIN
IF NOT pg_has_role(session_user, 'ql3_package_manager', 'member') THEN
RAISE EXCEPTION 'Package manager authority is required'
USING ERRCODE = 'insufficient_privilege';
END IF;
IF p_project_id !~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' OR
p_package_name !~ '^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$' THEN
RAISE EXCEPTION 'Package planning identity is invalid'
USING ERRCODE = 'invalid_parameter_value';
END IF;
RETURN QUERY
SELECT install.record_json,
install.lock_json,
proposal.proposal_json,
floor(extract(epoch FROM transaction_timestamp()) * 1000)::bigint
FROM "ql3"."plugin_package_install_heads" AS head
JOIN "ql3"."plugin_package_installs" AS install
ON install.installation_id = head.installation_id
JOIN "ql3"."plugin_package_admission_receipts" AS admission
ON admission.installation_id = install.installation_id
JOIN "ql3"."plugin_package_install_proposals" AS proposal
ON proposal.action_ref = admission.action_ref
LEFT JOIN "ql3"."plugin_package_quarantine_events" AS quarantine
ON quarantine.project_id = install.project_id
AND quarantine.package_name = install.package_name
AND quarantine.installation_id = install.installation_id
AND quarantine.lock_digest = install.lock_digest
LEFT JOIN "ql3"."plugin_package_lifecycle_heads" AS lifecycle
ON lifecycle.project_id = install.project_id
AND lifecycle.package_name = install.package_name
AND lifecycle.installation_id = install.installation_id
AND lifecycle.lock_digest = install.lock_digest
WHERE head.project_id = p_project_id
AND head.package_name = p_package_name
AND install.state = 'active'
AND install.active_lock_digest = install.lock_digest
AND quarantine.event_digest IS NULL
AND COALESCE(lifecycle.disposition, 'active') = 'active'
AND NOT EXISTS (
SELECT 1
FROM "ql3"."plugin_package_secret_bindings" AS binding
WHERE binding.project_id = install.project_id
AND binding.package_name = install.package_name
AND binding.generation = install.target_generation
)
FOR SHARE OF head, install;
END
$ql3$
`.trim(),
`
CREATE FUNCTION "ql3"."create_plugin_package_secret_binding_approval_plan"(
p_plan_json jsonb
)
RETURNS varchar
LANGUAGE plpgsql
VOLATILE
SECURITY DEFINER
SET search_path = pg_catalog, ql3
AS $ql3$
DECLARE
existing_plan jsonb;
inserted_action_ref varchar(255);
BEGIN
IF NOT pg_has_role(session_user, 'ql3_package_manager', 'member') THEN
RAISE EXCEPTION 'Package manager authority is required'
USING ERRCODE = 'insufficient_privilege';
END IF;
IF jsonb_typeof(p_plan_json) IS DISTINCT FROM 'object' THEN
RAISE EXCEPTION 'Secret binding approval plan is invalid'
USING ERRCODE = 'invalid_parameter_value';
END IF;
IF COALESCE(p_plan_json ->> 'actionRef', '') !~
'^[A-Za-z0-9][A-Za-z0-9._:/-]{0,254}$' THEN
RAISE EXCEPTION 'Secret binding approval actionRef is invalid'
USING ERRCODE = 'invalid_parameter_value';
END IF;
PERFORM pg_advisory_xact_lock(
hashtextextended(p_plan_json ->> 'actionRef', 70513061)
);
SELECT plan_json INTO existing_plan
FROM "ql3"."plugin_package_secret_binding_approval_plans"
WHERE action_ref = p_plan_json ->> 'actionRef'
FOR SHARE;
IF existing_plan IS NOT NULL THEN
IF existing_plan = p_plan_json THEN
RETURN 'existing';
END IF;
RAISE EXCEPTION 'Secret binding approval actionRef is already bound'
USING ERRCODE = 'unique_violation';
END IF;
INSERT INTO "ql3"."plugin_package_secret_binding_approval_plans" (
action_ref, approval_plan_digest, binding_plan_digest,
generation_digest, project_id, package_name, installation_id,
lock_digest, generation, manifest_digest, requested_by_type,
requested_by_id, planned_at_ms, expires_at_ms, plan_json
)
SELECT
p_plan_json ->> 'actionRef',
p_plan_json ->> 'approvalPlanDigest',
p_plan_json #>> '{bindingPlan,planDigest}',
p_plan_json #>> '{bindingPlan,target,generationDigest}',
p_plan_json #>> '{bindingPlan,target,projectId}',
p_plan_json #>> '{bindingPlan,target,packageName}',
p_plan_json #>> '{bindingPlan,target,installationId}',
p_plan_json #>> '{bindingPlan,target,lockDigest}',
(p_plan_json #>> '{bindingPlan,target,generation}')::integer,
p_plan_json #>> '{bindingPlan,target,manifestDigest}',
p_plan_json #>> '{requestedBy,type}',
p_plan_json #>> '{requestedBy,id}',
(p_plan_json #>> '{bindingPlan,plannedAtMs}')::bigint,
(p_plan_json ->> 'expiresAtMs')::bigint,
p_plan_json
FROM "ql3"."plugin_package_install_heads" AS head
JOIN "ql3"."plugin_package_installs" AS install
ON install.installation_id = head.installation_id
AND install.project_id = head.project_id
AND install.package_name = head.package_name
LEFT JOIN "ql3"."plugin_package_quarantine_events" AS quarantine
ON quarantine.project_id = install.project_id
AND quarantine.package_name = install.package_name
AND quarantine.installation_id = install.installation_id
AND quarantine.lock_digest = install.lock_digest
LEFT JOIN "ql3"."plugin_package_lifecycle_heads" AS lifecycle
ON lifecycle.project_id = install.project_id
AND lifecycle.package_name = install.package_name
AND lifecycle.installation_id = install.installation_id
AND lifecycle.lock_digest = install.lock_digest
WHERE head.project_id = p_plan_json #>> '{bindingPlan,target,projectId}'
AND head.package_name = p_plan_json #>> '{bindingPlan,target,packageName}'
AND install.installation_id =
p_plan_json #>> '{bindingPlan,target,installationId}'
AND install.lock_digest =
p_plan_json #>> '{bindingPlan,target,lockDigest}'
AND install.target_generation =
(p_plan_json #>> '{bindingPlan,target,generation}')::integer
AND install.lock_json ->> 'manifestDigest' =
p_plan_json #>> '{bindingPlan,target,manifestDigest}'
AND install.state = 'active'
AND install.active_lock_digest = install.lock_digest
AND quarantine.event_digest IS NULL
AND COALESCE(lifecycle.disposition, 'active') = 'active'
AND NOT EXISTS (
SELECT 1
FROM "ql3"."plugin_package_secret_bindings" AS binding
WHERE binding.generation_digest =
p_plan_json #>> '{bindingPlan,target,generationDigest}'
OR (
binding.project_id = install.project_id AND
binding.package_name = install.package_name AND
binding.generation = install.target_generation
)
)
RETURNING action_ref INTO inserted_action_ref;
IF inserted_action_ref IS NULL THEN
RAISE EXCEPTION 'Secret binding target is not the current unbound generation'
USING ERRCODE = 'check_violation';
END IF;
RETURN 'created';
END
$ql3$
`.trim(),
`REVOKE ALL ON "ql3"."plugin_package_secret_binding_approval_plans" FROM PUBLIC, ql3_runtime, ql3_admin, ql3_package_manager, ql3_package_executor, ql3_worker_ingress`,
`GRANT SELECT ON "ql3"."plugin_package_secret_binding_approval_plans" TO ql3_package_manager`,
`GRANT SELECT ON "ql3"."plugin_package_secret_binding_approval_plans" TO ql3_package_executor`,
`REVOKE ALL ON FUNCTION "ql3"."plugin_package_secret_binding_planning_snapshot"(varchar, varchar) FROM PUBLIC, ql3_runtime, ql3_admin, ql3_package_manager, ql3_package_executor, ql3_worker_ingress`,
`REVOKE ALL ON FUNCTION "ql3"."create_plugin_package_secret_binding_approval_plan"(jsonb) FROM PUBLIC, ql3_runtime, ql3_admin, ql3_package_manager, ql3_package_executor, ql3_worker_ingress`,
`GRANT EXECUTE ON FUNCTION "ql3"."plugin_package_secret_binding_planning_snapshot"(varchar, varchar) TO ql3_package_manager`,
`GRANT EXECUTE ON FUNCTION "ql3"."create_plugin_package_secret_binding_approval_plan"(jsonb) TO ql3_package_manager`,
`DO $ql3$ BEGIN UPDATE "ql3"."schema_capabilities" SET contract_version = 60, migration_id = 'pg-0061-plugin-package-secret-binding-approval-plans', capabilities = '${CAPABILITIES_V60}'::jsonb, updated_at_ms = floor(extract(epoch FROM transaction_timestamp()) * 1000)::bigint WHERE contract_name = 'control-core' AND contract_version = 59 AND migration_id = 'pg-0060-plugin-package-secret-materialization-guard' AND capabilities = '${CAPABILITIES_V59}'::jsonb; IF NOT FOUND THEN RAISE EXCEPTION 'control-core capability is not at version 59' USING ERRCODE = 'check_violation'; END IF; END $ql3$`,
],
});
@@ -0,0 +1,234 @@
import type { PostgresPool } from '@qinglong/runtime-core';
import {
normalizePluginPackageInstallProposal,
type PluginPackageInstallProposal,
} from '@qinglong/runtime-core/plugin-package-proposal';
import {
normalizePluginPackageInstallRecord,
normalizePluginPackageLock,
type PluginPackageInstallRecord,
type PluginPackageLock,
} from '@qinglong/runtime-core/plugin-package-install';
import {
InvalidPluginPackageSecretBindingApprovalPlanError,
PluginPackageSecretBindingApprovalPlanConflictError,
PluginPackageSecretBindingApprovalPlanUnavailableError,
normalizePluginPackageSecretBindingApprovalPlan,
type CreatePluginPackageSecretBindingApprovalPlanResult,
type PluginPackageSecretBindingApprovalPlan,
type PluginPackageSecretBindingApprovalPlanRepository,
} from '@qinglong/runtime-core/plugin-package-secret-binding-approval-plan';
import {
postgresRequiredInteger,
postgresRequiredJsonObject,
postgresRequiredString,
postgresSqlState,
} from '../../repository/definitionRepositorySupport';
type Row = Record<string, unknown>;
export interface PostgresPluginPackageSecretBindingPlanningSnapshot {
readonly record: Readonly<PluginPackageInstallRecord>;
readonly lock: Readonly<PluginPackageLock>;
readonly proposal: Readonly<PluginPackageInstallProposal>;
readonly observedAtMs: number;
}
const ACTION_REF_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,254}$/;
function unavailable(
cause?: unknown,
): PluginPackageSecretBindingApprovalPlanUnavailableError {
return new PluginPackageSecretBindingApprovalPlanUnavailableError({
cause: cause instanceof Error ? cause : undefined,
});
}
function mapStorageError(error: unknown): Error {
if (
error instanceof InvalidPluginPackageSecretBindingApprovalPlanError ||
error instanceof PluginPackageSecretBindingApprovalPlanConflictError ||
error instanceof PluginPackageSecretBindingApprovalPlanUnavailableError
) {
return error;
}
const state = postgresSqlState(error);
if (state === '23503' || state === '23505' || state === '23514') {
return new PluginPackageSecretBindingApprovalPlanConflictError(
'plan identity or Package generation is already bound',
);
}
return unavailable(error);
}
function validateActionRef(value: string): string {
if (typeof value !== 'string' || !ACTION_REF_PATTERN.test(value)) {
throw new InvalidPluginPackageSecretBindingApprovalPlanError(
'actionRef is invalid',
);
}
return value;
}
function normalizeRow(
row: Row,
): Readonly<PluginPackageSecretBindingApprovalPlan> {
try {
return normalizePluginPackageSecretBindingApprovalPlan(
postgresRequiredJsonObject(
row.planJson,
unavailable,
) as unknown as PluginPackageSecretBindingApprovalPlan,
);
} catch (error) {
throw unavailable(error);
}
}
function same(left: unknown, right: unknown): boolean {
return JSON.stringify(left) === JSON.stringify(right);
}
export class PostgresPluginPackageSecretBindingApprovalPlanReader {
constructor(protected readonly pool: Pick<PostgresPool, 'query'>) {
if (!pool || typeof pool.query !== 'function') {
throw new TypeError(
'PostgreSQL Plugin Package Secret binding approval plan reader is invalid',
);
}
}
async findByActionRef(
actionRefValue: string,
): Promise<Readonly<PluginPackageSecretBindingApprovalPlan> | null> {
const actionRef = validateActionRef(actionRefValue);
try {
const result = await this.pool.query<Row>(
`SELECT plan_json AS "planJson"
FROM "ql3"."plugin_package_secret_binding_approval_plans"
WHERE action_ref = $1
LIMIT 2`,
[actionRef],
);
if (result.rows.length === 0) return null;
if (result.rows.length !== 1) throw unavailable();
const plan = normalizeRow(result.rows[0]!);
if (plan.actionRef !== actionRef) throw unavailable();
return plan;
} catch (error) {
throw mapStorageError(error);
}
}
async loadPlanningSnapshot(
projectIdValue: string,
packageNameValue: string,
): Promise<Readonly<PostgresPluginPackageSecretBindingPlanningSnapshot> | null> {
if (
typeof projectIdValue !== 'string' ||
!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(projectIdValue) ||
typeof packageNameValue !== 'string' ||
!/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(packageNameValue)
) {
throw new InvalidPluginPackageSecretBindingApprovalPlanError(
'planning target is invalid',
);
}
try {
const result = await this.pool.query<Row>(
`SELECT record_json AS "recordJson",
lock_json AS "lockJson",
proposal_json AS "proposalJson",
observed_at_ms AS "observedAtMs"
FROM "ql3"."plugin_package_secret_binding_planning_snapshot"($1, $2)`,
[projectIdValue, packageNameValue],
);
if (result.rows.length === 0) return null;
if (result.rows.length !== 1) throw unavailable();
const row = result.rows[0]!;
const record = normalizePluginPackageInstallRecord(
postgresRequiredJsonObject(
row.recordJson,
unavailable,
) as unknown as PluginPackageInstallRecord,
);
const lock = normalizePluginPackageLock(
postgresRequiredJsonObject(
row.lockJson,
unavailable,
) as unknown as PluginPackageLock,
);
const proposal = normalizePluginPackageInstallProposal(
postgresRequiredJsonObject(
row.proposalJson,
unavailable,
) as unknown as PluginPackageInstallProposal,
);
const observedAtMs = postgresRequiredInteger(
row.observedAtMs,
unavailable,
);
if (
record.projectId !== projectIdValue ||
record.packageName !== packageNameValue ||
record.lockDigest !== lock.lockDigest ||
proposal.actionDigest !== lock.approval.actionDigest ||
proposal.previewDigest !== lock.approval.previewDigest ||
proposal.actionInput.projectId !== projectIdValue ||
proposal.actionInput.manifest.metadata.name !== packageNameValue ||
proposal.actionInput.targetGeneration !== record.targetGeneration ||
proposal.actionInput.source.contentDigest !==
lock.source.contentDigest ||
proposal.actionInput.manifest.metadata.version !==
record.packageVersion ||
proposal.actionInput.manifest.spec.permissions.secrets.length === 0 ||
!proposal.actionInput.manifest.spec.permissions.tools.includes(
'secret.use',
)
) {
throw unavailable();
}
return Object.freeze({ record, lock, proposal, observedAtMs });
} catch (error) {
throw mapStorageError(error);
}
}
}
export class PostgresPluginPackageSecretBindingApprovalPlanRepository
extends PostgresPluginPackageSecretBindingApprovalPlanReader
implements PluginPackageSecretBindingApprovalPlanRepository
{
async create(
planValue: Readonly<PluginPackageSecretBindingApprovalPlan>,
): Promise<Readonly<CreatePluginPackageSecretBindingApprovalPlanResult>> {
const plan = normalizePluginPackageSecretBindingApprovalPlan(planValue);
try {
const inserted = await this.pool.query<Row>(
`SELECT "ql3"."create_plugin_package_secret_binding_approval_plan"(
$1::jsonb
) AS status`,
[JSON.stringify(plan)],
);
if (inserted.rows.length !== 1) throw unavailable();
const status = postgresRequiredString(
inserted.rows[0]?.status,
unavailable,
);
if (status !== 'created' && status !== 'existing') throw unavailable();
const stored = await this.findByActionRef(plan.actionRef);
if (!stored || !same(stored, plan)) {
throw new PluginPackageSecretBindingApprovalPlanConflictError(
'actionRef is already bound to another Secret binding plan',
);
}
return Object.freeze({
status,
plan: stored,
});
} catch (error) {
throw mapStorageError(error);
}
}
}
@@ -386,6 +386,72 @@ export const pluginPackageSecretBindings = ql3Schema.table(
],
);
export const pluginPackageSecretBindingApprovalPlans = ql3Schema.table(
'plugin_package_secret_binding_approval_plans',
{
actionRef: varchar('action_ref', { length: 255 }).primaryKey(),
approvalPlanDigest: char('approval_plan_digest', { length: 64 }).notNull(),
bindingPlanDigest: char('binding_plan_digest', { length: 64 }).notNull(),
generationDigest: char('generation_digest', { length: 64 }).notNull(),
projectId: varchar('project_id', { length: 128 }).notNull(),
packageName: varchar('package_name', { length: 63 }).notNull(),
installationId: varchar('installation_id', { length: 128 }).notNull(),
lockDigest: char('lock_digest', { length: 64 }).notNull(),
generation: integer('generation').notNull(),
manifestDigest: char('manifest_digest', { length: 64 }).notNull(),
requestedByType: varchar('requested_by_type', { length: 16 }).notNull(),
requestedById: varchar('requested_by_id', { length: 255 }).notNull(),
plannedAtMs: bigint('planned_at_ms', { mode: 'number' }).notNull(),
expiresAtMs: bigint('expires_at_ms', { mode: 'number' }).notNull(),
planJson: jsonb('plan_json').$type<Record<string, unknown>>().notNull(),
},
(table) => [
foreignKey({
name: 'ql3_plugin_package_secret_binding_approval_plan_project_fk',
columns: [table.projectId],
foreignColumns: [projects.id],
})
.onDelete('restrict')
.onUpdate('restrict'),
foreignKey({
name: 'ql3_plugin_package_secret_binding_approval_plan_install_fk',
columns: [table.installationId],
foreignColumns: [pluginPackageInstalls.installationId],
})
.onDelete('restrict')
.onUpdate('restrict'),
check(
'ql3_plugin_package_secret_binding_approval_plan_identity_check',
sql`${table.actionRef} ~ '^[A-Za-z0-9][A-Za-z0-9._:/-]{0,254}$' and ${table.projectId} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' and ${table.packageName} ~ '^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$' and ${table.installationId} ~ '^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$' and ${table.generation} between 1 and 2147483647 and ${table.requestedByType} = 'user' and octet_length(${table.requestedById}) between 1 and 255 and ${table.requestedById} !~ '[[:cntrl:]]'`,
),
check(
'ql3_plugin_package_secret_binding_approval_plan_digest_check',
sql`${table.approvalPlanDigest} ~ '^[0-9a-f]{64}$' and ${table.bindingPlanDigest} ~ '^[0-9a-f]{64}$' and ${table.generationDigest} ~ '^[0-9a-f]{64}$' and ${table.lockDigest} ~ '^[0-9a-f]{64}$' and ${table.manifestDigest} ~ '^[0-9a-f]{64}$'`,
),
check(
'ql3_plugin_package_secret_binding_approval_plan_time_check',
sql`${table.plannedAtMs} >= 0 and ${table.expiresAtMs} > ${table.plannedAtMs} and ${table.expiresAtMs} - ${table.plannedAtMs} <= 900000`,
),
check(
'ql3_plugin_package_secret_binding_approval_plan_json_check',
sql`jsonb_typeof(${table.planJson}) = 'object' and octet_length(${table.planJson}::text) between 2 and 98304 and ${table.planJson} @> jsonb_build_object('schema', 'qinglong/plugin-package-secret-binding-approval-plan@v1', 'actionRef', ${table.actionRef}, 'approvalPlanDigest', ${table.approvalPlanDigest}, 'requestedBy', jsonb_build_object('type', ${table.requestedByType}, 'id', ${table.requestedById}), 'expiresAtMs', ${table.expiresAtMs}, 'bindingPlan', jsonb_build_object('schema', 'qinglong/plugin-package-secret-binding-plan@v1', 'planDigest', ${table.bindingPlanDigest}, 'plannedAtMs', ${table.plannedAtMs}, 'target', jsonb_build_object('generationDigest', ${table.generationDigest}, 'projectId', ${table.projectId}, 'packageName', ${table.packageName}, 'installationId', ${table.installationId}, 'lockDigest', ${table.lockDigest}, 'generation', ${table.generation}, 'manifestDigest', ${table.manifestDigest}))) and jsonb_typeof(${table.planJson} #> '{bindingPlan,entries}') = 'array' and jsonb_array_length(${table.planJson} #> '{bindingPlan,entries}') between 1 and 64`,
),
uniqueIndex(
'ql3_plugin_package_secret_binding_approval_plan_digest_uidx',
).on(table.approvalPlanDigest),
index('ql3_plugin_package_secret_binding_approval_plan_target_idx').on(
table.projectId,
table.packageName,
table.generation,
table.actionRef,
),
index('ql3_plugin_package_secret_binding_approval_plan_expiry_idx').on(
table.expiresAtMs,
table.actionRef,
),
],
);
export const projectToolDefinitionSnapshots = ql3Schema.table(
'project_tool_definition_snapshots',
{
@@ -5873,6 +5939,7 @@ export const ql3PostgresTables = [
pluginPackageInstallMutations,
pluginPackageMaterializedRevisions,
pluginPackageSecretBindings,
pluginPackageSecretBindingApprovalPlans,
projectToolDefinitionSnapshots,
projectToolDefinitionSnapshotSources,
pluginPackageQuarantineEvents,
@@ -15,8 +15,8 @@ export interface PostgresSchemaContractFunction {
export interface PostgresSchemaContract {
readonly schema: 'ql3';
readonly contractName: 'control-core';
readonly contractVersion: 59;
readonly migrationId: 'pg-0060-plugin-package-secret-materialization-guard';
readonly contractVersion: 60;
readonly migrationId: 'pg-0061-plugin-package-secret-binding-approval-plans';
readonly minimumServerMajor: 16;
readonly maximumServerMajor: 18;
readonly capabilities: Readonly<{
@@ -56,6 +56,7 @@ export interface PostgresSchemaContract {
plugin_package_management_quota: 1;
plugin_package_materialized_revision: 1;
plugin_package_secret_binding: 1;
plugin_package_secret_binding_approval_plan: 1;
plugin_package_secret_materialization: 1;
plugin_package_proposal: 1;
plugin_package_publisher_provenance: 1;
@@ -106,8 +107,8 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
Object.freeze({
schema: 'ql3',
contractName: 'control-core',
contractVersion: 59,
migrationId: 'pg-0060-plugin-package-secret-materialization-guard',
contractVersion: 60,
migrationId: 'pg-0061-plugin-package-secret-binding-approval-plans',
minimumServerMajor: 16,
maximumServerMajor: 18,
capabilities: Object.freeze({
@@ -140,6 +141,7 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
plugin_package_management_quota: 1,
plugin_package_materialized_revision: 1,
plugin_package_secret_binding: 1,
plugin_package_secret_binding_approval_plan: 1,
plugin_package_secret_materialization: 1,
plugin_package_proposal: 1,
plugin_package_publisher_provenance: 1,
@@ -260,6 +262,23 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'binding_digest',
'binding_json',
]),
table('plugin_package_secret_binding_approval_plans', [
'action_ref',
'approval_plan_digest',
'binding_plan_digest',
'generation_digest',
'project_id',
'package_name',
'installation_id',
'lock_digest',
'generation',
'manifest_digest',
'requested_by_type',
'requested_by_id',
'planned_at_ms',
'expires_at_ms',
'plan_json',
]),
table('project_tool_definition_snapshots', [
'project_id',
'active_vector_digest',
@@ -1439,6 +1458,10 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'ql3_plugin_package_secret_binding_generation_uidx',
'ql3_plugin_package_secret_binding_digest_uidx',
'ql3_plugin_package_secret_binding_install_idx',
'plugin_package_secret_binding_approval_plans_pkey',
'ql3_plugin_package_secret_binding_approval_plan_digest_uidx',
'ql3_plugin_package_secret_binding_approval_plan_target_idx',
'ql3_plugin_package_secret_binding_approval_plan_expiry_idx',
'project_tool_definition_snapshots_pkey',
'ql3_project_tool_snapshot_withdrawal_key',
'ql3_project_tool_definition_snapshot_digest_uidx',
@@ -1733,6 +1756,10 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'ql3_plugin_package_secret_binding_identity_check',
'ql3_plugin_package_secret_binding_digest_check',
'ql3_plugin_package_secret_binding_json_check',
'ql3_plugin_package_secret_binding_approval_plan_identity_check',
'ql3_plugin_package_secret_binding_approval_plan_digest_check',
'ql3_plugin_package_secret_binding_approval_plan_time_check',
'ql3_plugin_package_secret_binding_approval_plan_json_check',
'ql3_plugin_package_quarantine_identity_check',
'ql3_plugin_package_quarantine_state_check',
'ql3_plugin_package_quarantine_subject_check',
@@ -2188,6 +2215,8 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'ql3_plugin_package_materialized_revision_project_fk',
'ql3_plugin_package_secret_binding_project_fk',
'ql3_plugin_package_secret_binding_install_fk',
'ql3_plugin_package_secret_binding_approval_plan_project_fk',
'ql3_plugin_package_secret_binding_approval_plan_install_fk',
'ql3_project_tool_definition_snapshot_project_fk',
'ql3_project_tool_definition_snapshot_source_snapshot_fk',
'ql3_project_tool_definition_snapshot_source_install_fk',
@@ -2338,6 +2367,23 @@ export const postgresqlControlSchemaContract: PostgresSchemaContract =
'ql3_run_retry_policies_run_fk',
]),
functions: Object.freeze([
Object.freeze({
name: 'plugin_package_secret_binding_planning_snapshot',
identityArguments:
'p_project_id character varying, p_package_name character varying',
owner: 'ql3_migration',
securityDefiner: true,
volatility: 'volatile',
configuration: Object.freeze(['search_path=pg_catalog, ql3']),
}),
Object.freeze({
name: 'create_plugin_package_secret_binding_approval_plan',
identityArguments: 'p_plan_json jsonb',
owner: 'ql3_migration',
securityDefiner: true,
volatility: 'volatile',
configuration: Object.freeze(['search_path=pg_catalog, ql3']),
}),
Object.freeze({
name: 'enforce_plugin_package_secret_materialization',
identityArguments: '',
@@ -173,6 +173,12 @@ const REQUIRED_RUNTIME_PRIVILEGES = Object.freeze({
update: false,
delete: false,
}),
plugin_package_secret_binding_approval_plans: Object.freeze({
select: false,
insert: false,
update: false,
delete: false,
}),
project_tool_definition_snapshots: Object.freeze({
select: false,
insert: false,
@@ -692,6 +698,12 @@ const REQUIRED_ADMIN_PRIVILEGES = Object.freeze({
update: false,
delete: false,
}),
plugin_package_secret_binding_approval_plans: Object.freeze({
select: false,
insert: false,
update: false,
delete: false,
}),
project_tool_definition_snapshots: Object.freeze({
select: false,
insert: false,
@@ -1193,6 +1205,7 @@ const REQUIRED_PACKAGE_MANAGER_PRIVILEGES: RequiredPrivileges = Object.freeze(
name === 'projects' ||
name === 'project_role_bindings' ||
name === 'plugin_package_lifecycle_plans' ||
name === 'plugin_package_secret_binding_approval_plans' ||
name === 'plugin_package_automation_publications' ||
name === 'plugin_package_automation_publication_heads' ||
name === 'plugin_package_publisher_trust_transition_receipts'
@@ -1235,6 +1248,7 @@ const REQUIRED_PACKAGE_EXECUTOR_PRIVILEGES: RequiredPrivileges = Object.freeze(
name === 'projects' ||
name === 'project_role_bindings' ||
name === 'plugin_package_install_proposals' ||
name === 'plugin_package_secret_binding_approval_plans' ||
name === 'plugin_package_publisher_revocation_proposals' ||
name === 'plugin_package_publisher_trust_transition_proposals'
? { ...NO_TABLE_PRIVILEGES, select: true }
@@ -1504,6 +1518,7 @@ const REQUIRED_ADMIN_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
const REQUIRED_RUNTIME_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
Object.freeze({
create_plugin_package_secret_binding_approval_plan: false,
commit_plugin_package_lifecycle: false,
commit_plugin_package_quarantine: false,
commit_plugin_package_task_reconciliation: false,
@@ -1517,12 +1532,14 @@ const REQUIRED_RUNTIME_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
plugin_package_workflow_task_attempt_snapshot: true,
plugin_package_lifecycle_blocking_runs: false,
plugin_package_run_start_allowed: true,
plugin_package_secret_binding_planning_snapshot: false,
plugin_package_tool_start_allowed: true,
register_plugin_package_automation_disposition_event: false,
});
const REQUIRED_PACKAGE_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
Object.freeze({
create_plugin_package_secret_binding_approval_plan: true,
commit_plugin_package_lifecycle: false,
commit_plugin_package_quarantine: false,
commit_plugin_package_task_reconciliation: false,
@@ -1536,12 +1553,14 @@ const REQUIRED_PACKAGE_MANAGER_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
plugin_package_workflow_task_attempt_snapshot: false,
plugin_package_lifecycle_blocking_runs: false,
plugin_package_run_start_allowed: false,
plugin_package_secret_binding_planning_snapshot: true,
plugin_package_tool_start_allowed: false,
register_plugin_package_automation_disposition_event: false,
});
const REQUIRED_PACKAGE_EXECUTOR_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges =
Object.freeze({
create_plugin_package_secret_binding_approval_plan: false,
commit_plugin_package_lifecycle: true,
commit_plugin_package_quarantine: true,
commit_plugin_package_task_reconciliation: true,
@@ -1555,6 +1574,7 @@ const REQUIRED_PACKAGE_EXECUTOR_FUNCTION_PRIVILEGES: RequiredFunctionPrivileges
plugin_package_workflow_task_attempt_snapshot: false,
plugin_package_lifecycle_blocking_runs: true,
plugin_package_run_start_allowed: false,
plugin_package_secret_binding_planning_snapshot: false,
plugin_package_tool_start_allowed: false,
register_plugin_package_automation_disposition_event: false,
});
@@ -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,
);
});
@@ -108,7 +108,10 @@ const {
} = require('@qinglong/runtime-core/plugin-package-admission');
const {
pluginPackageInstallActionDigest,
pluginPackageActivationIntentDigest,
pluginPackageInstallCommit,
pluginPackageInstallPlanDigest,
transitionPluginPackageInstall,
} = require('@qinglong/runtime-core/plugin-package-install');
const {
createPluginPackageInstallProposal,
@@ -183,6 +186,17 @@ const {
const {
createPluginPackageResourceGenerationFromReferences,
} = require('@qinglong/runtime-core/plugin-package-resource-generation');
const {
createPluginPackageSecretBindingPlan,
} = require('@qinglong/runtime-core/plugin-package-secret-binding-plan');
const {
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');
const {
createProjectToolDefinitionSnapshot,
projectToolDefinitionRegistry,
@@ -291,8 +305,8 @@ function admissionPackageAction() {
},
permissions: {
network: { allowedHosts: [] },
secrets: [],
tools: [],
secrets: [{ name: 'TOKEN', required: true }],
tools: ['secret.use'],
},
contents: { tasks: [], workflows: [], prompts: [], tools: [] },
},
@@ -882,14 +896,16 @@ if (!migrationConnectionString) {
GROUP BY run.id, attempt.id`,
[accepted.runId],
);
assert.deepEqual(durable.rows, [{
status: 'queued',
version: 2,
eventSequence: 2,
trigger_type: 'task_start',
attemptStatus: 'claimed',
eventCount: 2,
}]);
assert.deepEqual(durable.rows, [
{
status: 'queued',
version: 2,
eventSequence: 2,
trigger_type: 'task_start',
attemptStatus: 'claimed',
eventCount: 2,
},
]);
} finally {
if (runtimeDatabase && runtimeDatabase !== migrationDatabase) {
await runtimeDatabase.close();
@@ -2087,12 +2103,14 @@ if (!migrationConnectionString) {
runtimeDatabase.pool,
);
assert.deepEqual(await taskAttempts.listCandidates({ limit: 8 }), {
candidates: [{
runId: plan.runId,
stepRunId: plan.steps[0].stepRunId,
readyAtMs: plan.plannedAtMs,
planDigest: plan.planDigest,
}],
candidates: [
{
runId: plan.runId,
stepRunId: plan.steps[0].stepRunId,
readyAtMs: plan.plannedAtMs,
planDigest: plan.planDigest,
},
],
truncated: false,
});
const taskAttempt = await taskAttempts.admit(
@@ -2167,29 +2185,34 @@ if (!migrationConnectionString) {
GROUP BY run.id, attempt.id, step.id`,
[plan.runId, taskAttempt.receipt.attemptId],
);
assert.deepEqual(recovered.rows, [{
runStatus: 'running',
runVersion: 6,
eventSequence: 6,
attemptStatus: 'lost',
stepStatus: 'ready',
stepVersion: 2,
stepAttemptCount: 0,
events: [
'workflow.task_attempt_admitted',
'workflow.task_attempt.lost',
'step.ready',
],
}]);
assert.deepEqual(recovered.rows, [
{
runStatus: 'running',
runVersion: 6,
eventSequence: 6,
attemptStatus: 'lost',
stepStatus: 'ready',
stepVersion: 2,
stepAttemptCount: 0,
events: [
'workflow.task_attempt_admitted',
'workflow.task_attempt.lost',
'step.ready',
],
},
]);
const requeued = await taskAttempts.listCandidates({ limit: 8 });
assert.deepEqual(
requeued.candidates.map(
({ runId, stepRunId }) => ({ runId, stepRunId }),
),
[{
runId: plan.runId,
stepRunId: plan.steps[0].stepRunId,
}],
requeued.candidates.map(({ runId, stepRunId }) => ({
runId,
stepRunId,
})),
[
{
runId: plan.runId,
stepRunId: plan.steps[0].stepRunId,
},
],
);
const secondTaskAttempt = await taskAttempts.admit(
plan.runId,
@@ -2242,20 +2265,22 @@ if (!migrationConnectionString) {
WHERE run.id = $1`,
[plan.runId],
);
assert.deepEqual(cancelled.rows, [{
runStatus: 'cancelled',
runVersion: 11,
eventSequence: 11,
attemptStatuses: ['cancelled', 'lost'],
stepStatuses: ['cancelled'],
events: [
'workflow.task_attempt_admitted',
'workflow.task_attempt.cancelled',
'step.cancelled',
'step.cancelled',
'workflow.cancelled',
],
}]);
assert.deepEqual(cancelled.rows, [
{
runStatus: 'cancelled',
runVersion: 11,
eventSequence: 11,
attemptStatuses: ['cancelled', 'lost'],
stepStatuses: ['cancelled'],
events: [
'workflow.task_attempt_admitted',
'workflow.task_attempt.cancelled',
'step.cancelled',
'step.cancelled',
'workflow.cancelled',
],
},
]);
} finally {
await runtimeDatabase.close();
}
@@ -3607,10 +3632,181 @@ if (!migrationConnectionString) {
await repository.findAdmissionReceipt(consumed.dispatch.id),
admitted.receipt,
);
const staged = transitionPluginPackageInstall(lock, admitted.record, {
type: 'stage_completed',
mutationId: 'stage-package-secret-binding-integration-1',
occurredAtMs: admittedAtMs + 1,
stageRef: `stage:${lock.lockDigest}`,
artifactDigest: lock.source.artifactDigest,
manifestDigest: lock.manifestDigest,
contentDigest: lock.source.contentDigest,
evidenceDigest: 'd'.repeat(64),
});
const stageProvenance = createPluginPackagePublisherProvenance({
projectId: staged.projectId,
packageName: staged.packageName,
installationId: staged.installationId,
lockDigest: staged.lockDigest,
artifactDigest: staged.stageReceipt.artifactDigest,
manifestDigest: staged.stageReceipt.manifestDigest,
contentDigest: staged.stageReceipt.contentDigest,
stageEvidenceDigest: staged.stageReceipt.evidenceDigest,
signature: {
publisher: 'integration.qinglong.dev',
keyId: 'integration-key-1',
signatureDigest: 'e'.repeat(64),
keyNotBeforeMs: 0,
keyNotAfterMs: admittedAtMs + 60_000,
verifiedAtMs: admittedAtMs,
},
});
await executorDatabase.pool.query(
`INSERT INTO "ql3"."plugin_package_publisher_provenance" (
installation_id, project_id, package_name, lock_digest,
artifact_digest, manifest_digest, content_digest,
stage_evidence_digest, publisher, key_id, signature_digest,
key_not_before_ms, key_not_after_ms, verified_at_ms,
provenance_digest, provenance_json
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11,
$12, $13, $14, $15, $16::jsonb
)`,
[
stageProvenance.installationId,
stageProvenance.projectId,
stageProvenance.packageName,
stageProvenance.lockDigest,
stageProvenance.artifactDigest,
stageProvenance.manifestDigest,
stageProvenance.contentDigest,
stageProvenance.stageEvidenceDigest,
stageProvenance.publisher,
stageProvenance.keyId,
stageProvenance.signatureDigest,
stageProvenance.keyNotBeforeMs,
stageProvenance.keyNotAfterMs,
stageProvenance.verifiedAtMs,
stageProvenance.provenanceDigest,
JSON.stringify(stageProvenance),
],
);
await repository.commit(
pluginPackageInstallCommit(admitted.record, staged),
);
const activating = transitionPluginPackageInstall(lock, staged, {
type: 'activation_started',
mutationId: 'activate-package-secret-binding-integration-1',
occurredAtMs: admittedAtMs + 2,
});
await repository.commit(pluginPackageInstallCommit(staged, activating));
const active = transitionPluginPackageInstall(lock, activating, {
type: 'activation_committed',
mutationId: 'commit-package-secret-binding-integration-1',
occurredAtMs: admittedAtMs + 3,
activationRef: `active:${lock.lockDigest}`,
intentDigest: pluginPackageActivationIntentDigest(lock, activating),
generation: lock.targetGeneration,
contentDigest: lock.source.contentDigest,
});
await repository.commit(pluginPackageInstallCommit(activating, active));
} finally {
await executorDatabase.close();
}
const bindingManagerDatabase = await open('package-manager');
let approvalPlan;
try {
const plans =
new PostgresPluginPackageSecretBindingApprovalPlanRepository(
bindingManagerDatabase.pool,
);
const snapshot = await plans.loadPlanningSnapshot(
'default',
'postgres-monitor',
);
assert.ok(snapshot);
const generation = createPluginPackageResourceGenerationFromReferences({
installationId: snapshot.record.installationId,
projectId: snapshot.record.projectId,
packageName: snapshot.record.packageName,
lockDigest: snapshot.record.lockDigest,
generation: snapshot.record.targetGeneration,
previousActiveLockDigest: snapshot.record.previousActiveLockDigest,
contentDigest: snapshot.lock.source.contentDigest,
resources: [],
});
const bindingPlan = createPluginPackageSecretBindingPlan({
generation,
manifest: snapshot.proposal.actionInput.manifest,
assignments: [
{
name: 'TOKEN',
secretRef: createSecretRef({
projectId: 'default',
name: 'postgres-monitor-token',
version: 1,
}),
},
],
plannedAtMs: snapshot.observedAtMs,
});
approvalPlan = createPluginPackageSecretBindingApprovalPlan({
actionRef: 'secret-binding:postgres-monitor-v1',
bindingPlan,
requestedBy: requester,
expiresAtMs: snapshot.observedAtMs + 60_000,
});
assert.equal((await plans.create(approvalPlan)).status, 'created');
assert.equal((await plans.create(approvalPlan)).status, 'existing');
await assert.rejects(
bindingManagerDatabase.pool.query(
`INSERT INTO "ql3"."plugin_package_secret_binding_approval_plans" (
action_ref
) VALUES ('forbidden-direct-manager-insert')`,
),
(error) => error?.code === '42501',
);
} finally {
await bindingManagerDatabase.close();
}
const bindingExecutorDatabase = await open('package-executor');
try {
assert.deepEqual(
await new PostgresPluginPackageSecretBindingApprovalPlanReader(
bindingExecutorDatabase.pool,
).findByActionRef(approvalPlan.actionRef),
approvalPlan,
);
await assert.rejects(
bindingExecutorDatabase.pool.query(
`SELECT "ql3"."create_plugin_package_secret_binding_approval_plan"(
$1::jsonb
)`,
[JSON.stringify(approvalPlan)],
),
(error) => error?.code === '42501',
);
} finally {
await bindingExecutorDatabase.close();
}
const runtimeDatabase = await open('runtime');
try {
await assert.rejects(
runtimeDatabase.pool.query(
`SELECT *
FROM "ql3"."plugin_package_secret_binding_planning_snapshot"(
'default', 'postgres-monitor'
)`,
),
(error) => error?.code === '42501',
);
} finally {
await runtimeDatabase.close();
}
const verificationDatabase = await open('migration');
try {
const facts = await verificationDatabase.pool.query(
@@ -3623,13 +3819,17 @@ if (!migrationConnectionString) {
FROM "ql3"."plugin_package_admission_receipts") AS receipts,
(SELECT count(*)::integer
FROM "ql3"."security_audit_events"
WHERE operation_id = 'plugin_package.admit') AS audits`,
WHERE operation_id = 'plugin_package.admit') AS audits,
(SELECT count(*)::integer
FROM "ql3"."plugin_package_secret_binding_approval_plans")
AS "approvalPlans"`,
);
assert.deepEqual(facts.rows[0], {
installs: 1,
mutations: 1,
mutations: 4,
receipts: 1,
audits: 1,
approvalPlans: 1,
});
} finally {
await verificationDatabase.close();
@@ -4718,10 +4918,9 @@ if (!migrationConnectionString) {
new PostgresTaskDefinitionAdministrationRepository(
automationDatabase.pool,
);
const triggerMutations =
new PostgresTriggerAdministrationRepository(
automationDatabase.pool,
);
const triggerMutations = new PostgresTriggerAdministrationRepository(
automationDatabase.pool,
);
const taskBase = {
projectId,
taskId: 'automation-task',
@@ -111,6 +111,7 @@ test('defines the immutable PostgreSQL capability and Run core stream', async ()
'pg-0058-plugin-package-automation-disposition-events',
'pg-0059-plugin-package-secret-bindings',
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
],
);
for (const migration of postgresqlMainMigrationStream.migrations) {
@@ -163,6 +164,7 @@ test('keeps local-only and legacy tables out of the cluster baseline', async ()
'plugin_package_install_mutations',
'plugin_package_materialized_revisions',
'plugin_package_secret_bindings',
'plugin_package_secret_binding_approval_plans',
'plugin_package_lifecycle_events',
'plugin_package_lifecycle_heads',
'plugin_package_lifecycle_receipts',
@@ -547,6 +549,11 @@ test('freezes every published PostgreSQL migration checksum', () => {
checksum:
'28284ca860b39ff9de5b2aa1a2a60ef2c463fd6a72798d237040174272b64b1e',
},
{
id: 'pg-0061-plugin-package-secret-binding-approval-plans',
checksum:
'c995b7846ae8a57d3abb4b5523961e81aeba890e7405a030bcb505dfc6be3d25',
},
];
assert.deepEqual(
postgresqlMainMigrationStream.migrations.map(({ id, checksum }) => ({
@@ -2030,3 +2037,49 @@ test('advances capability v59 with fail-closed Package Secret materialization',
assert.match(sql, /contract_version = 58/);
assert.match(sql, /migration_id = 'pg-0059-plugin-package-secret-bindings'/);
});
test('advances capability v60 with least-privilege Package Secret binding approval plans', async () => {
const migration = migrationById(
'pg-0061-plugin-package-secret-binding-approval-plans',
);
const statements = [];
await migration.up({
async query(statement) {
statements.push(statement);
return { rows: [] };
},
});
const sql = statements.join('\n');
assert.match(
sql,
/CREATE TABLE "ql3"\."plugin_package_secret_binding_approval_plans"/,
);
assert.match(
sql,
/CREATE FUNCTION "ql3"\."plugin_package_secret_binding_planning_snapshot"\([\s\S]+p_project_id varchar,[\s\S]+p_package_name varchar[\s\S]+\)/,
);
assert.match(
sql,
/CREATE FUNCTION "ql3"\."create_plugin_package_secret_binding_approval_plan"\([\s\S]+p_plan_json jsonb[\s\S]+\)/,
);
assert.match(sql, /SECURITY DEFINER/);
assert.match(
sql,
/GRANT SELECT ON "ql3"\."plugin_package_secret_binding_approval_plans" TO ql3_package_manager/,
);
assert.match(
sql,
/GRANT SELECT ON "ql3"\."plugin_package_secret_binding_approval_plans" TO ql3_package_executor/,
);
assert.doesNotMatch(
sql,
/GRANT (?:INSERT|UPDATE|DELETE)[^;]+plugin_package_secret_binding_approval_plans[^;]+ql3_package_manager/,
);
assert.match(sql, /contract_version = 60/);
assert.match(sql, /"plugin_package_secret_binding_approval_plan":1/);
assert.match(sql, /contract_version = 59/);
assert.match(
sql,
/migration_id = 'pg-0060-plugin-package-secret-materialization-guard'/,
);
});
@@ -75,6 +75,7 @@ function validPrivileges() {
plugin_package_install_mutations: [false, false, false, false],
plugin_package_materialized_revisions: [false, false, false, false],
plugin_package_secret_bindings: [false, false, false, false],
plugin_package_secret_binding_approval_plans: [false, false, false, false],
project_tool_definition_snapshots: [false, false, false, false],
project_tool_definition_snapshot_sources: [false, false, false, false],
plugin_package_quarantine_events: [false, false, false, false],
@@ -194,6 +195,7 @@ function validAdminPrivileges() {
plugin_package_install_mutations: [false, false, false, false],
plugin_package_materialized_revisions: [false, false, false, false],
plugin_package_secret_bindings: [false, false, false, false],
plugin_package_secret_binding_approval_plans: [false, false, false, false],
project_tool_definition_snapshots: [false, false, false, false],
project_tool_definition_snapshot_sources: [false, false, false, false],
plugin_package_quarantine_events: [false, false, false, false],
@@ -287,6 +289,7 @@ function packagePrivileges(kind) {
'plugin_package_publisher_trust_transition_proposals',
'plugin_package_publisher_trust_transition_receipts',
'plugin_package_lifecycle_plans',
'plugin_package_secret_binding_approval_plans',
'plugin_package_automation_publications',
'plugin_package_automation_publication_heads',
...(manager
@@ -667,7 +670,13 @@ function queryable(overrides = {}) {
contract.functions.map(({ name: functionName }) => ({
functionName,
executeAllowed:
overrides.functionMode === 'manager'
overrides.functionMode === 'package-manager'
? [
'create_plugin_package_secret_binding_approval_plan',
'lock_approval_policy_fence',
'plugin_package_secret_binding_planning_snapshot',
].includes(functionName)
: overrides.functionMode === 'manager'
? functionName === 'lock_approval_policy_fence'
: overrides.functionMode === 'run-manager'
? functionName === 'lock_run_management_policy_fence'
@@ -754,7 +763,7 @@ test('accepts the exact PostgreSQL control schema and least-privilege runtime ro
serverMajor: 16,
currentUser: 'ql3_runtime',
contractName: 'control-core',
contractVersion: 59,
contractVersion: 60,
migrationIds: [
'pg-0001-schema-capability',
'pg-0002-run-core',
@@ -816,6 +825,7 @@ test('accepts the exact PostgreSQL control schema and least-privilege runtime ro
'pg-0058-plugin-package-automation-disposition-events',
'pg-0059-plugin-package-secret-bindings',
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
],
});
});
@@ -846,10 +856,10 @@ test('accepts the exact schema and isolated least-privilege admin role', async (
}),
);
assert.equal(report.currentUser, 'ql3_admin');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
});
@@ -862,10 +872,10 @@ test('accepts the isolated least-privilege automation manager role', async () =>
}),
);
assert.equal(report.currentUser, 'ql3_automation_manager');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
const widened = automationManagerPrivileges();
@@ -894,10 +904,10 @@ test('accepts the isolated least-privilege human Approval manager role', async (
}),
);
assert.equal(report.currentUser, 'ql3_approval_manager');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
const widened = approvalManagerPrivileges();
@@ -928,10 +938,10 @@ test('accepts the isolated least-privilege Run manager role', async () => {
}),
);
assert.equal(report.currentUser, 'ql3_run_manager');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
const widened = runManagerPrivileges();
@@ -983,7 +993,7 @@ test('accepts isolated Package manager and executor roles', async () => {
queryable({
currentUser: 'ql3_package_manager',
privileges: packagePrivileges('manager'),
functionMode: 'manager',
functionMode: 'package-manager',
}),
);
assert.equal(manager.currentUser, 'ql3_package_manager');
@@ -1063,10 +1073,10 @@ test('accepts the exact schema and isolated Worker ingress role', async () => {
}),
);
assert.equal(report.currentUser, 'ql3_worker_ingress');
assert.equal(report.contractVersion, 59);
assert.equal(report.contractVersion, 60);
assert.equal(
report.migrationIds.at(-1),
'pg-0060-plugin-package-secret-materialization-guard',
'pg-0061-plugin-package-secret-binding-approval-plans',
);
});
+11 -11
View File
@@ -74,7 +74,7 @@ test('current QL3 workspace has exactly eighteen reviewed package boundaries', (
[
{
directory: 'packages/ql3-cluster-postgres/src/migrations',
directSourceFiles: 61,
directSourceFiles: 62,
reviewKind: 'ordered_ledger',
},
{
@@ -97,10 +97,10 @@ test('current QL3 workspace has exactly eighteen reviewed package boundaries', (
rootSourceFileRoles: localAdmin.rootSourceFileRoles,
},
{
sourceFiles: 43,
sourceFiles: 44,
rootSourceFiles: 1,
rootSourceLines: 9,
nestedSourceFiles: 42,
nestedSourceFiles: 43,
rootSourceFileRoles: { 'runtime.ts': 'public_export' },
},
);
@@ -299,10 +299,10 @@ test('current QL3 workspace has exactly eighteen reviewed package boundaries', (
rootSourceFileRoles: runtimeCore.rootSourceFileRoles,
},
{
sourceFiles: 153,
sourceFiles: 156,
rootSourceFiles: 1,
rootSourceLines: 160,
nestedSourceFiles: 152,
nestedSourceFiles: 155,
rootSourceFileRoles: { 'index.ts': 'public_export' },
},
);
@@ -340,10 +340,10 @@ test('current QL3 workspace has exactly eighteen reviewed package boundaries', (
rootSourceFileRoles: clusterAdmin.rootSourceFileRoles,
},
{
sourceFiles: 99,
sourceFiles: 100,
rootSourceFiles: 1,
rootSourceLines: 61,
nestedSourceFiles: 98,
nestedSourceFiles: 99,
rootSourceFileRoles: {
'modelInvocationMigrationCli.ts': 'binary_entry',
},
@@ -421,10 +421,10 @@ test('current QL3 workspace has exactly eighteen reviewed package boundaries', (
rootSourceFileRoles: clusterPostgres.rootSourceFileRoles,
},
{
sourceFiles: 157,
sourceFiles: 159,
rootSourceFiles: 1,
rootSourceLines: 126,
nestedSourceFiles: 156,
nestedSourceFiles: 158,
rootSourceFileRoles: { 'index.ts': 'public_export' },
},
);
@@ -540,10 +540,10 @@ test('current QL3 workspace has exactly eighteen reviewed package boundaries', (
rootSourceFileRoles: localSqlite.rootSourceFileRoles,
},
{
sourceFiles: 187,
sourceFiles: 188,
rootSourceFiles: 1,
rootSourceLines: 31,
nestedSourceFiles: 186,
nestedSourceFiles: 187,
rootSourceFileRoles: { 'index.ts': 'public_export' },
},
);