a7711d2dc3
新增 pingora-gateway 独立二进制 crate,覆盖路由、静态资源、压缩、接流保护、TLS 直连和访问日志能力。 新增 Nginx canary、realpath canary、direct preflight、direct live、direct enable 和 rollback 脚本。 新增 Pingora 切流证据包、命令证据、manifest 验真、根目录总审计和 release readiness 聚合门禁。 完善 API release、Jenkins、systemd、health patrol、生产部署和发布包自包含校验。 更新 Pingora 试点文档、Nginx README 与 Hermes 共享记忆。
135 lines
3.6 KiB
JavaScript
135 lines
3.6 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { spawnSync } from 'node:child_process';
|
|
import {
|
|
chmodSync,
|
|
mkdirSync,
|
|
mkdtempSync,
|
|
readFileSync,
|
|
rmSync,
|
|
writeFileSync,
|
|
} from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
const PREFLIGHT_SCRIPT = 'scripts/check-pingora-direct-preflight.mjs';
|
|
const failures = [];
|
|
const tmpRoot = mkdtempSync(
|
|
path.join(tmpdir(), 'genarrative-pingora-direct-preflight-guard-'),
|
|
);
|
|
|
|
try {
|
|
main();
|
|
} finally {
|
|
rmSync(tmpRoot, { recursive: true, force: true });
|
|
}
|
|
|
|
if (failures.length > 0) {
|
|
console.error('[check:pingora-direct-preflight-guard] FAILED');
|
|
for (const failure of failures) {
|
|
console.error(`- ${failure}`);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log('[check:pingora-direct-preflight-guard] OK');
|
|
|
|
function main() {
|
|
assertRejectsEnvFileWithControlCharacters();
|
|
assertRejectsSystemdServiceWithControlCharactersBeforeSystemctl();
|
|
}
|
|
|
|
function assertRejectsEnvFileWithControlCharacters() {
|
|
const fixture = prepareFixture('env-file-control-character');
|
|
const result = runPreflight(fixture, [
|
|
'--env-file',
|
|
`${path.join(fixture.root, 'pingora-gateway.env')}\n--fake-flag`,
|
|
'--require-live-env',
|
|
]);
|
|
|
|
assertNonZero(result, 'direct preflight 必须拒绝带换行的 --env-file。');
|
|
assertIncludes(
|
|
`${result.stdout}\n${result.stderr}`,
|
|
'--env-file 不能包含换行或 NUL 字符',
|
|
'带换行的 --env-file 必须给出明确错误。',
|
|
);
|
|
}
|
|
|
|
function assertRejectsSystemdServiceWithControlCharactersBeforeSystemctl() {
|
|
const fixture = prepareFixture('systemd-service-control-character');
|
|
const result = runPreflight(fixture, [
|
|
'--systemd-cat',
|
|
'--systemd-service',
|
|
'genarrative-pingora-gateway.service\n--fake-flag',
|
|
]);
|
|
|
|
assertNonZero(
|
|
result,
|
|
'direct preflight 必须拒绝带换行的 systemd service 参数。',
|
|
);
|
|
assertIncludes(
|
|
`${result.stdout}\n${result.stderr}`,
|
|
'子命令参数 不能包含换行或 NUL 字符',
|
|
'带换行的 systemd service 参数必须在执行 systemctl 前失败。',
|
|
);
|
|
|
|
const commandsLog = readFileSync(fixture.commandsLog, 'utf8');
|
|
if (commandsLog.includes('systemctl')) {
|
|
failures.push('systemd service 含控制字符时必须在执行 systemctl 前失败。');
|
|
}
|
|
}
|
|
|
|
function prepareFixture(name) {
|
|
const root = path.join(tmpRoot, name);
|
|
const fakeBin = path.join(root, 'bin');
|
|
const commandsLog = path.join(root, 'commands.log');
|
|
mkdirSync(fakeBin, { recursive: true });
|
|
writeFileSync(commandsLog, '', 'utf8');
|
|
writeFileSync(
|
|
path.join(fakeBin, 'systemctl'),
|
|
[
|
|
'#!/usr/bin/env bash',
|
|
`printf 'systemctl %s\\n' "$*" >> ${shellQuote(commandsLog)}`,
|
|
'cat <<SYSTEMD',
|
|
'# /etc/systemd/system/genarrative-pingora-gateway.service',
|
|
'[Service]',
|
|
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
|
|
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
|
|
'SYSTEMD',
|
|
'',
|
|
].join('\n'),
|
|
'utf8',
|
|
);
|
|
chmodSync(path.join(fakeBin, 'systemctl'), 0o755);
|
|
return { root, fakeBin, commandsLog };
|
|
}
|
|
|
|
function runPreflight(fixture, args) {
|
|
return spawnSync('node', ['--', PREFLIGHT_SCRIPT, ...args], {
|
|
cwd: process.cwd(),
|
|
encoding: 'utf8',
|
|
env: {
|
|
...process.env,
|
|
PATH: `${fixture.fakeBin}:${process.env.PATH || ''}`,
|
|
},
|
|
});
|
|
}
|
|
|
|
function assertNonZero(result, reason) {
|
|
if ((result.status ?? 0) === 0) {
|
|
failures.push(
|
|
`${reason}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
function assertIncludes(value, expected, reason) {
|
|
if (!String(value).includes(expected)) {
|
|
failures.push(`${reason} 缺少: ${expected}`);
|
|
}
|
|
}
|
|
|
|
function shellQuote(value) {
|
|
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
}
|