mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
|
||||
const {
|
||||
aiFeatureMigrationJob,
|
||||
applyCloudNativePgResources,
|
||||
applyExecutorNetworkPolicy,
|
||||
applyFixturePostgresVolumes,
|
||||
actorJob,
|
||||
canI,
|
||||
deployProvider,
|
||||
executorJob,
|
||||
providerServerSource,
|
||||
terminalJobSnapshot,
|
||||
} = require('../../scripts/ql3-provider-credential-test-kubernetes-live-contract.cjs');
|
||||
|
||||
function template() {
|
||||
return {
|
||||
apiVersion: 'batch/v1',
|
||||
kind: 'Job',
|
||||
metadata: {
|
||||
name: 'ql3-provider-credential-test-executor',
|
||||
namespace: 'qinglong3-system',
|
||||
},
|
||||
spec: {
|
||||
backoffLimit: 0,
|
||||
activeDeadlineSeconds: 60,
|
||||
ttlSecondsAfterFinished: 300,
|
||||
template: {
|
||||
metadata: { labels: {} },
|
||||
spec: {
|
||||
serviceAccountName: 'ql3-provider-credential-test-executor',
|
||||
automountServiceAccountToken: false,
|
||||
containers: [
|
||||
{
|
||||
name: 'executor',
|
||||
image: 'template',
|
||||
env: [],
|
||||
volumeMounts: [],
|
||||
},
|
||||
],
|
||||
volumes: [
|
||||
{
|
||||
name: 'command',
|
||||
projected: {
|
||||
sources: [
|
||||
{ configMap: { name: 'command' } },
|
||||
{ configMap: { name: 'allowlist' } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'provider-secret',
|
||||
secret: { secretName: 'material', items: [] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('renders an isolated one-shot executor without mutating its template', () => {
|
||||
const source = template();
|
||||
const job = executorJob({
|
||||
template: source,
|
||||
adminImage: 'ql3-admin:live',
|
||||
name: 'ql3-provider-test-one',
|
||||
commandConfigMap: 'command-one',
|
||||
allowlistConfigMap: 'allowlist-one',
|
||||
materialFileName: 'a'.repeat(64),
|
||||
});
|
||||
|
||||
assert.equal(source.metadata.name, 'ql3-provider-credential-test-executor');
|
||||
assert.equal(source.spec.template.spec.volumes.length, 2);
|
||||
assert.equal(job.metadata.name, 'ql3-provider-test-one');
|
||||
assert.equal(job.spec.template.spec.containers[0].image, 'ql3-admin:live');
|
||||
assert.equal(
|
||||
job.spec.template.spec.containers[0].terminationMessagePolicy,
|
||||
'FallbackToLogsOnError',
|
||||
);
|
||||
assert.deepEqual(job.spec.template.spec.containers[0].env.at(-1), {
|
||||
name: 'NODE_EXTRA_CA_CERTS',
|
||||
value: '/var/run/secrets/qinglong3/provider-ca/ca.crt',
|
||||
});
|
||||
assert.deepEqual(
|
||||
job.spec.template.spec.volumes.find(
|
||||
(volume) => volume.name === 'provider-secret',
|
||||
).secret.items,
|
||||
[{ key: 'a'.repeat(64), path: 'a'.repeat(64) }],
|
||||
);
|
||||
assert.equal(
|
||||
job.spec.template.spec.volumes.find(
|
||||
(volume) => volume.name === 'provider-ca',
|
||||
).secret.secretName,
|
||||
'ql3-provider-live-tls',
|
||||
);
|
||||
});
|
||||
|
||||
test('actor falls back to bounded logs when the termination file is empty', () => {
|
||||
const job = actorJob(
|
||||
'ql3-admin:live',
|
||||
'ql3-provider-test-bind',
|
||||
'ql3-provider-test-bind-command',
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
job.spec.template.spec.containers[0].terminationMessagePolicy,
|
||||
'FallbackToLogsOnError',
|
||||
);
|
||||
assert.deepEqual(job.spec.template.spec.containers[0].env[0], {
|
||||
name: 'NODE_PATH',
|
||||
value: '/opt/qinglong/node_modules',
|
||||
});
|
||||
});
|
||||
|
||||
test('derives a separate AI feature migration Job from the base stream', () => {
|
||||
const source = template();
|
||||
source.spec.template.metadata.labels = {
|
||||
'app.kubernetes.io/name': 'ql3-cluster-migration',
|
||||
'app.kubernetes.io/component': 'database-migration',
|
||||
};
|
||||
source.spec.template.spec.containers[0].command = [
|
||||
'node',
|
||||
'/opt/qinglong/node_modules/@qinglong/cluster-postgres/dist/migration/migrationCli.js',
|
||||
];
|
||||
|
||||
const job = aiFeatureMigrationJob(source, 'ql3-admin:live');
|
||||
|
||||
assert.equal(source.metadata.name, 'ql3-provider-credential-test-executor');
|
||||
assert.equal(job.metadata.name, 'ql3-ai-feature-migration');
|
||||
assert.equal(
|
||||
job.spec.template.metadata.labels['app.kubernetes.io/component'],
|
||||
'ai-feature-migration',
|
||||
);
|
||||
assert.deepEqual(job.spec.template.spec.containers[0].command, [
|
||||
'node',
|
||||
'/opt/qinglong/node_modules/@qinglong/cluster-admin/dist/modelInvocationMigrationCli.js',
|
||||
]);
|
||||
});
|
||||
|
||||
test('keeps provider egress closed until an exact Pod CIDR is supplied', () => {
|
||||
const applied = [];
|
||||
const fixture = { apply: (resource) => applied.push(resource) };
|
||||
|
||||
applyExecutorNetworkPolicy(fixture, null);
|
||||
applyExecutorNetworkPolicy(fixture, '10.42.7.19');
|
||||
|
||||
assert.equal(applied[0].spec.egress.length, 2);
|
||||
assert.equal(JSON.stringify(applied[0]).includes('ipBlock'), false);
|
||||
assert.deepEqual(applied[1].spec.egress.at(-1), {
|
||||
to: [{ ipBlock: { cidr: '10.42.7.19/32' } }],
|
||||
ports: [{ protocol: 'TCP', port: 8443 }],
|
||||
});
|
||||
});
|
||||
|
||||
test('prebinds data and WAL volumes to all three fixture nodes', () => {
|
||||
const applied = [];
|
||||
const dockerCommands = [];
|
||||
const fixture = {
|
||||
nodes: ['server', 'agent-a', 'agent-b'],
|
||||
apply: (resource) => applied.push(resource),
|
||||
dockerRun: (command) => dockerCommands.push(command),
|
||||
};
|
||||
|
||||
applyFixturePostgresVolumes(fixture);
|
||||
|
||||
assert.equal(applied[0].kind, 'StorageClass');
|
||||
assert.equal(applied[0].provisioner, 'kubernetes.io/no-provisioner');
|
||||
assert.equal(applied.length, 7);
|
||||
assert.deepEqual(
|
||||
applied.slice(1).map((volume) => volume.spec.claimRef.name),
|
||||
[
|
||||
'ql3-postgres-1',
|
||||
'ql3-postgres-1-wal',
|
||||
'ql3-postgres-2',
|
||||
'ql3-postgres-2-wal',
|
||||
'ql3-postgres-3',
|
||||
'ql3-postgres-3-wal',
|
||||
],
|
||||
);
|
||||
assert.deepEqual(
|
||||
applied
|
||||
.slice(1)
|
||||
.map(
|
||||
(volume) =>
|
||||
volume.spec.nodeAffinity.required.nodeSelectorTerms[0]
|
||||
.matchExpressions[0].values[0],
|
||||
),
|
||||
['agent-a', 'agent-a', 'agent-b', 'agent-b', 'server', 'server'],
|
||||
);
|
||||
assert.equal(dockerCommands.length, 18);
|
||||
assert.deepEqual(dockerCommands.slice(0, 3), [
|
||||
[
|
||||
'exec',
|
||||
'agent-a',
|
||||
'mkdir',
|
||||
'-p',
|
||||
'/var/lib/qinglong3-live/postgres-1-data',
|
||||
],
|
||||
[
|
||||
'exec',
|
||||
'agent-a',
|
||||
'chown',
|
||||
'26:26',
|
||||
'/var/lib/qinglong3-live/postgres-1-data',
|
||||
],
|
||||
[
|
||||
'exec',
|
||||
'agent-a',
|
||||
'chmod',
|
||||
'0700',
|
||||
'/var/lib/qinglong3-live/postgres-1-data',
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
test('patches only fixture storage classes in the reviewed CNPG resources', () => {
|
||||
const applied = [];
|
||||
const fixture = {
|
||||
kubectl() {
|
||||
return {
|
||||
stdout: `apiVersion: postgresql.cnpg.io/v1\nkind: Cluster\nmetadata:\n name: ql3-postgres\nspec:\n storage:\n size: 20Gi\n walStorage:\n size: 5Gi\n---\napiVersion: postgresql.cnpg.io/v1\nkind: Database\nmetadata:\n name: ql3-postgres-qinglong\n`,
|
||||
};
|
||||
},
|
||||
apply: (resource) => applied.push(resource),
|
||||
};
|
||||
|
||||
applyCloudNativePgResources(fixture);
|
||||
|
||||
assert.equal(applied.length, 2);
|
||||
assert.equal(
|
||||
applied[0].spec.storage.storageClass,
|
||||
'ql3-provider-test-static',
|
||||
);
|
||||
assert.equal(
|
||||
applied[0].spec.walStorage.storageClass,
|
||||
'ql3-provider-test-static',
|
||||
);
|
||||
assert.equal(applied[1].kind, 'Database');
|
||||
});
|
||||
|
||||
test('treats kubectl auth can-i exit one as an explicit denial', () => {
|
||||
const calls = [];
|
||||
const fixture = {
|
||||
kubectl(args, options) {
|
||||
calls.push({ args, options });
|
||||
return { status: 1, stdout: 'no', stderr: '' };
|
||||
},
|
||||
};
|
||||
|
||||
assert.equal(canI(fixture, 'get', 'secrets'), 'no');
|
||||
assert.equal(calls[0].options.allowFailure, true);
|
||||
assert.throws(
|
||||
() =>
|
||||
canI(
|
||||
{
|
||||
kubectl: () => ({ status: 2, stdout: '', stderr: 'unavailable' }),
|
||||
},
|
||||
'get',
|
||||
'secrets',
|
||||
),
|
||||
/unexpected kubectl auth can-i response/,
|
||||
);
|
||||
});
|
||||
|
||||
test('captures a terminal Pod before a completed Job can recycle it', async () => {
|
||||
let podVisible = true;
|
||||
const fixture = {
|
||||
kubectlJson(args) {
|
||||
if (args.includes('pods')) {
|
||||
assert.equal(podVisible, true);
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
metadata: { name: 'probe-pod' },
|
||||
status: {
|
||||
containerStatuses: [{ state: { terminated: { exitCode: 0 } } }],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
podVisible = false;
|
||||
return {
|
||||
status: {
|
||||
conditions: [{ type: 'Complete', status: 'True' }],
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const snapshot = await terminalJobSnapshot(fixture, 'probe', 1_000);
|
||||
assert.equal(snapshot.pod.metadata.name, 'probe-pod');
|
||||
assert.equal(snapshot.terminal.complete, true);
|
||||
assert.equal(snapshot.terminal.failed, false);
|
||||
});
|
||||
|
||||
test('reloads the local admin image after node recovery before convergence', () => {
|
||||
const source = fs.readFileSync(
|
||||
path.join(
|
||||
__dirname,
|
||||
'../../scripts/ql3-provider-credential-test-kubernetes-live-contract.cjs',
|
||||
),
|
||||
'utf8',
|
||||
);
|
||||
const nodeRecovery = source.indexOf(
|
||||
"'three recovered CloudNativePG instances'",
|
||||
);
|
||||
const imageReload = source.indexOf("'provider-test-admin-post-failover.tar'");
|
||||
const postFailoverConvergence = source.indexOf("label: 'post-failover'");
|
||||
|
||||
assert.ok(nodeRecovery >= 0);
|
||||
assert.ok(imageReload > nodeRecovery);
|
||||
assert.ok(postFailoverConvergence > imageReload);
|
||||
});
|
||||
|
||||
test('provider fixture logs only generation and authorization decision', () => {
|
||||
const source = providerServerSource();
|
||||
assert.match(source, /event:'provider_request',generation,allowed/);
|
||||
assert.doesNotMatch(source, /JSON\.stringify\([^)]*authorization/);
|
||||
assert.doesNotMatch(source, /process\.stdout\.write\([^)]*expected/);
|
||||
});
|
||||
|
||||
test('provider fixture uses headless DNS for exact Pod CIDR policy', async () => {
|
||||
const applied = [];
|
||||
const fixture = {
|
||||
apply: (resource) => applied.push(resource),
|
||||
kubectl() {},
|
||||
kubectlJson() {
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
metadata: { uid: 'provider-uid' },
|
||||
status: {
|
||||
podIP: '10.42.7.19',
|
||||
conditions: [{ type: 'Ready', status: 'True' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
await deployProvider(fixture, 'ql3-admin:live', 1, 'secret', 'initial');
|
||||
|
||||
const service = applied.find((resource) => resource.kind === 'Service');
|
||||
assert.equal(service.spec.clusterIP, 'None');
|
||||
});
|
||||
|
||||
test('actor writes bounded failure diagnostics to its termination message', () => {
|
||||
const source = fs.readFileSync(
|
||||
path.join(
|
||||
__dirname,
|
||||
'../../scripts/ql3-provider-credential-test-kubernetes-live-actor.cjs',
|
||||
),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
assert.match(source, /fs\.writeFileSync\('\/dev\/termination-log', failure/);
|
||||
assert.match(source, /error\.message\.slice\(0, 1_024\)/);
|
||||
assert.doesNotMatch(source, /stack:/);
|
||||
});
|
||||
Reference in New Issue
Block a user