feat(ql3): materialize auditable local trial kits

This commit is contained in:
whyour
2026-08-26 09:58:29 +08:00
parent 2253b99066
commit 2620be0587
11 changed files with 1026 additions and 81 deletions
@@ -0,0 +1,209 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const test = require('node:test');
const {
auditLocalAlphaTrialKit,
createLocalAlphaTrialKit,
parseArguments,
} = require('../../scripts/ql3-local-alpha-trial-kit-bundle.cjs');
const {
createClusterImageSbom,
} = require('../../scripts/ql3-cluster-image-sbom.cjs');
const {
readReleaseIdentity,
} = require('../../scripts/lib/ql3-release-identity.cjs');
const root = path.resolve(__dirname, '../..');
const version = readReleaseIdentity(root).version;
const revision = 'a'.repeat(40);
function imageInspection(
role,
idCharacter = role === 'application' ? '1' : '2',
) {
return {
Id: `sha256:${idCharacter.repeat(64)}`,
Os: 'linux',
Architecture: 'arm64',
Config: {
User: '65532:65532',
Labels: {
'org.opencontainers.image.title':
role === 'application'
? 'QingLong 3.0 Local Application'
: 'QingLong 3.0 Local Operator',
'org.opencontainers.image.source': 'https://github.com/whyour/qinglong',
'org.opencontainers.image.revision': revision,
'org.opencontainers.image.version': version,
...(role === 'application'
? {
'io.qinglong.profile': 'edge,standalone',
'io.qinglong.ai': 'excluded',
}
: {
'io.qinglong.lifecycle': 'short-lived',
'io.qinglong.authority': 'local-owner-management',
'io.qinglong.network': 'none-by-default',
}),
},
},
};
}
function fixture(t) {
const fixtureRoot = fs.realpathSync(
fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-local-alpha-bundle-')),
);
t.after(() => fs.rmSync(fixtureRoot, { recursive: true, force: true }));
const applicationSbom = path.join(fixtureRoot, 'application.json');
const operatorSbom = path.join(fixtureRoot, 'operator.json');
const readme = path.join(fixtureRoot, 'README-source.md');
fs.writeFileSync(
applicationSbom,
`${JSON.stringify(createClusterImageSbom({ root, image: 'local' }))}\n`,
);
fs.writeFileSync(
operatorSbom,
`${JSON.stringify(
createClusterImageSbom({ root, image: 'local-operator' }),
)}\n`,
);
fs.writeFileSync(readme, '# Local Alpha Trial Kit\n');
return {
fixtureRoot,
applicationSbom,
operatorSbom,
readme,
outputRoot: path.join(fixtureRoot, 'bundle'),
};
}
function createOptions(paths) {
return {
root,
outputRoot: paths.outputRoot,
architecture: 'arm64',
sourceRevision: revision,
applicationImage: 'qinglong3-local-application:test-arm64',
operatorImage: 'qinglong3-local-operator:test-arm64',
applicationSbom: paths.applicationSbom,
operatorSbom: paths.operatorSbom,
readme: paths.readme,
};
}
function adapters(overrides = {}) {
return {
inspectImage(image) {
return image.includes('operator')
? imageInspection('operator')
: imageInspection('application');
},
saveImages(images, archivePath) {
assert.deepEqual(images, [
'qinglong3-local-application:test-arm64',
'qinglong3-local-operator:test-arm64',
]);
fs.writeFileSync(archivePath, Buffer.alloc(2048, 7), { flag: 'wx' });
},
...overrides,
};
}
test('materializes and offline-audits one closed two-image trial kit', (t) => {
const paths = fixture(t);
const manifest = createLocalAlphaTrialKit(createOptions(paths), adapters());
assert.equal(manifest.schema, 'qinglong/alpha-local-trial-kit@v1');
assert.equal(manifest.sourceRevision, revision);
assert.equal(manifest.architecture, 'arm64');
assert.equal(manifest.images.application.architecture, 'arm64');
assert.equal(manifest.images.operator.architecture, 'arm64');
assert.notEqual(manifest.images.application.id, manifest.images.operator.id);
const report = auditLocalAlphaTrialKit({ bundleRoot: paths.outputRoot });
assert.equal(report.compatible, true);
assert.equal(report.sourceRevision, revision);
assert.deepEqual(fs.readdirSync(paths.outputRoot).sort(), [
'README.md',
'SHA256SUMS',
'manifest.json',
'qinglong3-local-application.cdx.json',
'qinglong3-local-operator.cdx.json',
'qinglong3-local-trial-kit-arm64.docker.tar',
]);
});
test('fails closed and removes a partial output on incompatible image identity', (t) => {
const paths = fixture(t);
const options = createOptions(paths);
assert.throws(
() =>
createLocalAlphaTrialKit(
options,
adapters({
inspectImage(image) {
const inspection = image.includes('operator')
? imageInspection('operator')
: imageInspection('application');
inspection.Config.Labels['org.opencontainers.image.revision'] =
'b'.repeat(40);
return inspection;
},
}),
),
/image identity is incompatible/,
);
assert.equal(fs.existsSync(paths.outputRoot), false);
});
test('offline audit rejects archive mutation, extra files and SBOM substitution', (t) => {
for (const mutation of ['archive', 'extra', 'sbom']) {
const paths = fixture(t);
paths.outputRoot = path.join(paths.fixtureRoot, `bundle-${mutation}`);
createLocalAlphaTrialKit(createOptions(paths), adapters());
if (mutation === 'archive') {
fs.appendFileSync(
path.join(
paths.outputRoot,
'qinglong3-local-trial-kit-arm64.docker.tar',
),
'tamper',
);
} else if (mutation === 'extra') {
fs.writeFileSync(path.join(paths.outputRoot, 'credential.txt'), 'secret');
} else {
fs.copyFileSync(
path.join(paths.outputRoot, 'qinglong3-local-application.cdx.json'),
path.join(paths.outputRoot, 'qinglong3-local-operator.cdx.json'),
);
}
assert.throws(
() => auditLocalAlphaTrialKit({ bundleRoot: paths.outputRoot }),
/differs|not closed|incompatible/,
mutation,
);
}
});
test('CLI grammar is exact and separates create from offline audit', () => {
assert.deepEqual(
parseArguments(['--mode=audit', '--bundle=/tmp/ql3-bundle']),
{ mode: 'audit', bundleRoot: '/tmp/ql3-bundle' },
);
assert.throws(
() =>
parseArguments([
'--mode=audit',
'--bundle=/tmp/ql3-bundle',
'--allow-extra=true',
]),
/audit arguments are invalid/,
);
assert.throws(
() => parseArguments(['--mode=create', '--output=/tmp/output']),
/create arguments are invalid/,
);
});