mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 01:32:44 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { readFile, writeFile, mkdtemp, rm } = require('node:fs/promises');
|
||||
const { tmpdir } = require('node:os');
|
||||
const { join, resolve } = require('node:path');
|
||||
const { test } = require('node:test');
|
||||
|
||||
const {
|
||||
ClusterModelProviderCredentialManagementProcessConfigError,
|
||||
loadClusterModelProviderCredentialManagementProcessConfig,
|
||||
startClusterModelProviderCredentialManagementProcess,
|
||||
} = require('@qinglong/cluster-admin/model-provider-credential-management-process');
|
||||
const {
|
||||
createModelProviderCredentialTestAllowlist,
|
||||
} = require('@qinglong/ai/model-provider-credential-test-connection');
|
||||
|
||||
const FIXTURES = resolve(
|
||||
__dirname,
|
||||
'../../ql3-cluster-control/test/fixtures/mtls',
|
||||
);
|
||||
const NOW_MS = Date.UTC(2030, 0, 1);
|
||||
|
||||
function enabledEnvironment(paths, overrides = {}) {
|
||||
return {
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_ENABLED: 'true',
|
||||
QL3_PROFILE: 'cluster-admin',
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_HOST: '127.0.0.1',
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_PORT: '8446',
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_TLS_CERT_FILE:
|
||||
paths.certificateFile,
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_TLS_KEY_FILE: paths.privateKeyFile,
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_CLIENT_CA_FILE:
|
||||
paths.clientCertificateAuthorityFile,
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_CLIENT_CRL_FILE:
|
||||
paths.clientCertificateRevocationListFile,
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_IDENTITY_KEYSET_FILE:
|
||||
paths.identityKeysetFile,
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_TEST_ALLOWLIST_FILE: paths.testAllowlistFile,
|
||||
QL3_POSTGRES_AI_CREDENTIAL_MANAGER_URL:
|
||||
'postgresql://ql3_ai_credential_manager:secret@postgres.example.test/ql3',
|
||||
QL3_POSTGRES_AI_CREDENTIAL_MANAGER_TLS_MODE: 'disable',
|
||||
QL3_POSTGRES_AI_CREDENTIAL_MANAGER_ALLOW_INSECURE: 'true',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
async function tlsFixture(run) {
|
||||
const directory = await mkdtemp(join(tmpdir(), 'ql3-provider-manager-'));
|
||||
const paths = {
|
||||
certificateFile: join(directory, 'tls.crt'),
|
||||
privateKeyFile: join(directory, 'tls.key'),
|
||||
clientCertificateAuthorityFile: join(directory, 'client-ca.crt'),
|
||||
clientCertificateRevocationListFile: join(directory, 'client.crl'),
|
||||
identityKeysetFile: join(directory, 'keyset.json'),
|
||||
testAllowlistFile: join(directory, 'test-allowlist.json'),
|
||||
};
|
||||
try {
|
||||
await writeFile(
|
||||
paths.certificateFile,
|
||||
await readFile(join(FIXTURES, 'server-cert.pem')),
|
||||
{ mode: 0o644 },
|
||||
);
|
||||
await writeFile(
|
||||
paths.privateKeyFile,
|
||||
await readFile(join(FIXTURES, 'server-key.pem')),
|
||||
{ mode: 0o640 },
|
||||
);
|
||||
await writeFile(
|
||||
paths.clientCertificateAuthorityFile,
|
||||
await readFile(join(FIXTURES, 'ca-cert.pem')),
|
||||
{ mode: 0o644 },
|
||||
);
|
||||
await writeFile(
|
||||
paths.clientCertificateRevocationListFile,
|
||||
await readFile(join(FIXTURES, 'empty-crl.pem')),
|
||||
{ mode: 0o644 },
|
||||
);
|
||||
await writeFile(paths.identityKeysetFile, '{}\n', { mode: 0o644 });
|
||||
await writeFile(
|
||||
paths.testAllowlistFile,
|
||||
`${JSON.stringify(
|
||||
createModelProviderCredentialTestAllowlist({
|
||||
revision: 'catalog-v1',
|
||||
providers: [
|
||||
{
|
||||
provider: 'openai-compatible',
|
||||
adapter: 'openai-compatible',
|
||||
baseUrl: 'https://provider.example.test/v1/',
|
||||
revision: 'endpoint-v1',
|
||||
deadlineMs: 5_000,
|
||||
maxResponseBytes: 64 * 1_024,
|
||||
maxModels: 64,
|
||||
maxCostMicrousd: 0,
|
||||
retryLimit: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
)}\n`,
|
||||
{ mode: 0o644 },
|
||||
);
|
||||
return await run(paths);
|
||||
} finally {
|
||||
await rm(directory, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
test('disabled provider credential manager opens no database or files', async () => {
|
||||
let opened = 0;
|
||||
const runtime = await startClusterModelProviderCredentialManagementProcess({
|
||||
environment: {
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_ENABLED: 'false',
|
||||
},
|
||||
async openDatabase() {
|
||||
opened += 1;
|
||||
throw new Error('must not open');
|
||||
},
|
||||
});
|
||||
assert.equal(runtime.status, 'disabled');
|
||||
assert.equal(opened, 0);
|
||||
await runtime.close();
|
||||
});
|
||||
|
||||
test('loads bounded provider-only network and PostgreSQL configuration', async () => {
|
||||
await tlsFixture(async (paths) => {
|
||||
const config = loadClusterModelProviderCredentialManagementProcessConfig(
|
||||
enabledEnvironment(paths),
|
||||
);
|
||||
assert.equal(config.enabled, true);
|
||||
assert.equal(config.port, 8446);
|
||||
assert.equal(config.database.pool.maxConnections, 2);
|
||||
assert.equal(
|
||||
config.database.pool.applicationName,
|
||||
'qinglong3-ai-credential-manager',
|
||||
);
|
||||
assert.equal(config.http.maxConnections, 32);
|
||||
assert.equal(config.http.maxConcurrentRequests, 8);
|
||||
assert.equal(config.http.maxBodyBytes, 32 * 1024);
|
||||
assert.equal(config.testConnection.planLifetimeMs, 60_000);
|
||||
assert.equal(config.testConnection.quotaWindowMs, 60_000);
|
||||
assert.equal(config.testConnection.quotaLimit, 5);
|
||||
assert.equal(config.testConnection.allowlistFile, paths.testAllowlistFile);
|
||||
assert.match(
|
||||
config.database.connection.connectionString,
|
||||
/^postgresql:\/\/ql3_ai_credential_manager:/,
|
||||
);
|
||||
});
|
||||
assert.throws(
|
||||
() =>
|
||||
loadClusterModelProviderCredentialManagementProcessConfig({
|
||||
QL3_MODEL_PROVIDER_CREDENTIAL_MANAGEMENT_ENABLED: 'true',
|
||||
QL3_PROFILE: 'edge',
|
||||
}),
|
||||
ClusterModelProviderCredentialManagementProcessConfigError,
|
||||
);
|
||||
});
|
||||
|
||||
test('starts after exact readiness and identity, wipes key and closes in order', async () => {
|
||||
await tlsFixture(async (paths) => {
|
||||
const order = [];
|
||||
let privateKey;
|
||||
let transport;
|
||||
let httpClosed = 0;
|
||||
let databaseClosed = 0;
|
||||
const pool = {
|
||||
async query() {
|
||||
throw new Error('repositories remain lazy during composition');
|
||||
},
|
||||
async connect() {
|
||||
throw new Error('repositories remain lazy during composition');
|
||||
},
|
||||
};
|
||||
const runtime = await startClusterModelProviderCredentialManagementProcess({
|
||||
environment: enabledEnvironment(paths),
|
||||
now: () => NOW_MS,
|
||||
async openDatabase() {
|
||||
order.push('open');
|
||||
return {
|
||||
pool,
|
||||
async close() {
|
||||
order.push('database-close');
|
||||
databaseClosed += 1;
|
||||
},
|
||||
};
|
||||
},
|
||||
async assertReady(candidate) {
|
||||
order.push('ready');
|
||||
assert.equal(candidate, pool);
|
||||
return {
|
||||
ready: true,
|
||||
currentUser: 'ql3_ai_credential_manager',
|
||||
migrationIds: [
|
||||
'pg-9014-ai-model-provider-credential-management-identity-ledger',
|
||||
],
|
||||
writablePrimary: true,
|
||||
managerAuthority: true,
|
||||
leastPrivilege: true,
|
||||
};
|
||||
},
|
||||
identities: {
|
||||
async reload() {
|
||||
order.push('identity');
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
generation: 3,
|
||||
digest: 'identity-digest',
|
||||
issuer: 'https://identity.example.test/',
|
||||
audience: 'qinglong3-model-provider-credential-management',
|
||||
activeKeyIds: ['key-3'],
|
||||
revokedKeyIds: ['key-2'],
|
||||
};
|
||||
},
|
||||
bind() {
|
||||
throw new Error('HTTP stub does not authenticate');
|
||||
},
|
||||
},
|
||||
async startHttp(options) {
|
||||
order.push('http');
|
||||
privateKey = options.tls.privateKey;
|
||||
transport = options.transport;
|
||||
assert.equal(options.limits.maxConcurrentRequests, 8);
|
||||
assert.ok(options.tls.clientCertificateAuthority);
|
||||
assert.ok(options.tls.clientCertificateRevocationList);
|
||||
return {
|
||||
status: 'active',
|
||||
address: { host: options.host, port: options.port },
|
||||
availabilityStatus: () => 'ready',
|
||||
withdraw() {},
|
||||
async close() {
|
||||
order.push('http-close');
|
||||
httpClosed += 1;
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
assert.equal(runtime.status, 'active');
|
||||
assert.deepEqual(order, ['open', 'ready', 'identity', 'http']);
|
||||
assert.equal(typeof transport.execute, 'function');
|
||||
assert.equal(
|
||||
privateKey.every((byte) => byte === 0),
|
||||
true,
|
||||
);
|
||||
await Promise.all([runtime.close(), runtime.close()]);
|
||||
assert.equal(httpClosed, 1);
|
||||
assert.equal(databaseClosed, 1);
|
||||
assert.deepEqual(order.slice(-2), ['http-close', 'database-close']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user