Files
Genarrative/scripts/check-pingora-direct-enable.mjs
T
kdletters 071faa482c 统一 Rust 与 TypeScript 格式化门禁
纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口

完成项目 TypeScript/Prettier 与 Rust 全量格式化

修复 Pingora expected executable 门禁的空白敏感误报

同步开发运维文档与 AGC skill pack 格式化忽略规则
2026-09-01 16:28:34 +08:00

2501 lines
72 KiB
JavaScript

#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
symlinkSync,
writeFileSync,
} from 'node:fs';
import { tmpdir, userInfo } from 'node:os';
import path from 'node:path';
const ENABLE_SCRIPT = 'scripts/deploy/pingora-direct-enable.sh';
const PREFLIGHT_SCRIPT = 'scripts/check-pingora-direct-preflight.mjs';
const failures = [];
const [DIRECT_TLS_PORT, DIRECT_HTTP_REDIRECT_PORT] = allocateLoopbackPorts(2);
const DIRECT_TLS_LISTEN = `127.0.0.1:${DIRECT_TLS_PORT}`;
const DIRECT_HTTP_REDIRECT_LISTEN = `127.0.0.1:${DIRECT_HTTP_REDIRECT_PORT}`;
const tmpRoot = mkdtempSync(path.join(tmpdir(), 'genarrative-pingora-enable-'));
try {
main();
} finally {
rmSync(tmpRoot, { recursive: true, force: true });
}
if (failures.length > 0) {
console.error('[check:pingora-direct-enable] FAILED');
for (const failure of failures) {
console.error(`- ${failure}`);
}
process.exit(1);
}
console.log('[check:pingora-direct-enable] OK');
function main() {
assertScriptShape();
assertDirectPreflightChecksServiceEnvironmentFile();
assertDirectPreflightRejectsPublicForwardedForTrust();
assertDryRunDoesNotInstallDropin();
assertDryRunRunsPreflightWhenRequested();
assertReleaseLayoutDryRunUsesBundledDirectChecks();
assertMissingTemplateDryRunWarns();
assertRelativePathsRejected();
assertFilesystemRootPathsRejected();
assertApplyRequiresPreflightEnvFile();
assertApplyRequiresCertReadablePreflight();
assertApplyRequiresServiceEnvFilePreflight();
assertApplyRequiresServiceUserCertReadablePreflight();
assertApplyRequiresServiceBinaryExecutablePreflight();
assertApplyRequiresPortsFreePreflight();
assertApplyRequiresDirectLiveArgs();
assertRejectsControlCharacterInputsBeforeApply();
assertApplyFailsWhenCurrentReleaseAuditFailsBeforeInstall();
assertApplyFailsWhenPreflightScriptMissingBeforeInstall();
assertApplyFailsWhenDirectLiveScriptMissingBeforeInstall();
assertApplyRejectsSymlinkDropinDirectoryBeforeInstall();
assertApplyRejectsSymlinkDropinFileBeforeInstall();
assertApplyFailsWhenSystemdEnvFileDiffers();
assertApplyFailsWhenSystemdEnvFileOnlySharesPrefix();
assertApplyFailsWhenSystemdExecStartDiffers();
assertApplyFailsWhenDirectLiveSmokeFails();
assertApplyFailsWhenDirectLiveAccessLogJsonMissing();
}
function assertDirectPreflightChecksServiceEnvironmentFile() {
const preflightRoot = path.join(tmpRoot, 'preflight-env-file');
const envPath = path.join(preflightRoot, 'pingora-gateway.env');
const servicePath = path.join(
preflightRoot,
'genarrative-pingora-gateway.service',
);
const mismatchServicePath = path.join(
preflightRoot,
'genarrative-pingora-gateway-mismatch.service',
);
const dropinPath = path.join(
preflightRoot,
'genarrative-pingora-gateway-direct-entry.conf',
);
const envExamplePath = path.join(
preflightRoot,
'pingora-gateway.env.example',
);
const systemctlBinary = path.join(preflightRoot, 'systemctl');
mkdirSync(preflightRoot, { recursive: true });
writeFileSync(
envPath,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https\n',
'utf8',
);
writeFileSync(
servicePath,
[
'[Unit]',
'Description=Pingora 影子网关只监听本机高端口',
'[Service]',
`EnvironmentFile=-${envPath}`,
'ExecStart=/opt/genarrative/current/pingora-gateway',
'',
].join('\n'),
'utf8',
);
writeFileSync(
mismatchServicePath,
[
'[Unit]',
'Description=Pingora 影子网关只监听本机高端口',
'[Service]',
'EnvironmentFile=/etc/genarrative/other-pingora.env',
'ExecStart=/opt/genarrative/current/pingora-gateway',
'',
].join('\n'),
'utf8',
);
writeFileSync(
dropinPath,
[
'# 人工复制到 /etc/systemd/system/genarrative-pingora-gateway.service.d/direct-entry.conf 后生效',
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envExamplePath,
'# CAP_NET_BIND_SERVICE 只由 direct-entry drop-in 授予\n',
'utf8',
);
const baseArgs = [
'--env-file',
envPath,
'--check-service-env-file',
'--service-unit',
servicePath,
'--dropin-template',
dropinPath,
'--env-example',
envExamplePath,
];
const okResult = runPreflight(baseArgs);
assertStatus(
okResult,
0,
'direct preflight 应接受 optional EnvironmentFile=-<env> 指向本次 env。',
);
const serviceMismatchResult = runPreflight([
'--env-file',
envPath,
'--check-service-env-file',
'--service-unit',
mismatchServicePath,
'--dropin-template',
dropinPath,
'--env-example',
envExamplePath,
]);
if (serviceMismatchResult.status === 0) {
failures.push(
'service 模板 EnvironmentFile 漂移时 direct preflight 必须失败。',
);
}
assertIncludes(
serviceMismatchResult.stderr,
`${mismatchServicePath} EnvironmentFile 未包含本次 --env-file`,
'service 模板 EnvironmentFile 漂移时必须给出明确错误。',
);
writeFileSync(
systemctlBinary,
[
'#!/usr/bin/env bash',
'if [[ "$1" == "cat" ]]; then',
' echo "[Service]"',
' echo "EnvironmentFile=/etc/genarrative/other-pingora.env"',
' echo "AmbientCapabilities=CAP_NET_BIND_SERVICE"',
' echo "CapabilityBoundingSet=CAP_NET_BIND_SERVICE"',
' exit 0',
'fi',
'exit 1',
'',
].join('\n'),
'utf8',
);
chmodExecutable(systemctlBinary);
const systemdMismatchResult = runPreflight([...baseArgs, '--systemd-cat'], {
PATH: `${preflightRoot}:${process.env.PATH || ''}`,
});
if (systemdMismatchResult.status === 0) {
failures.push(
'systemctl cat EnvironmentFile 漂移时 direct preflight 必须失败。',
);
}
assertIncludes(
systemdMismatchResult.stderr,
'systemctl cat genarrative-pingora-gateway.service EnvironmentFile 未包含本次 --env-file',
'systemd 最终配置 EnvironmentFile 漂移时必须给出明确错误。',
);
}
function assertDirectPreflightRejectsPublicForwardedForTrust() {
const preflightRoot = path.join(tmpRoot, 'preflight-forwarded-for');
const envPath = path.join(preflightRoot, 'pingora-gateway.env');
const servicePath = path.join(
preflightRoot,
'genarrative-pingora-gateway.service',
);
const dropinPath = path.join(
preflightRoot,
'genarrative-pingora-gateway-direct-entry.conf',
);
const envExamplePath = path.join(
preflightRoot,
'pingora-gateway.env.example',
);
mkdirSync(preflightRoot, { recursive: true });
writeFileSync(
servicePath,
[
'[Unit]',
'Description=Pingora 影子网关只监听本机高端口',
'[Service]',
`EnvironmentFile=${envPath}`,
'ExecStart=/opt/genarrative/current/pingora-gateway',
'',
].join('\n'),
'utf8',
);
writeFileSync(
dropinPath,
[
'# 人工复制到 /etc/systemd/system/genarrative-pingora-gateway.service.d/direct-entry.conf 后生效',
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envExamplePath,
'# CAP_NET_BIND_SERVICE 只由 direct-entry drop-in 授予\n',
'utf8',
);
const baseArgs = [
'--env-file',
envPath,
'--require-live-env',
'--service-unit',
servicePath,
'--dropin-template',
dropinPath,
'--env-example',
envExamplePath,
];
writeFileSync(
envPath,
[
'GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN=0.0.0.0:443',
'GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN=0.0.0.0:80',
'GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE=/tmp/cert.pem',
'GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE=/tmp/key.pem',
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https',
'GENARRATIVE_PINGORA_GATEWAY_TRUST_X_FORWARDED_FOR=true',
'GENARRATIVE_PINGORA_GATEWAY_TRUSTED_FRONT_PROXY_CONFIRMED=true',
'',
].join('\n'),
'utf8',
);
const publicResult = runPreflight(baseArgs);
if (publicResult.status === 0) {
failures.push(
'公网直连开启 X-Forwarded-For 信任时 direct preflight 必须失败。',
);
}
assertIncludes(
publicResult.stderr,
'公网直连 Pingora 时 GENARRATIVE_PINGORA_GATEWAY_TRUST_X_FORWARDED_FOR 必须保持 false',
'公网直连误信任 X-Forwarded-For 时必须给出明确错误。',
);
writeFileSync(
envPath,
[
`GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN=${DIRECT_TLS_LISTEN}`,
`GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN=${DIRECT_HTTP_REDIRECT_LISTEN}`,
'GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE=/tmp/cert.pem',
'GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE=/tmp/key.pem',
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https',
'GENARRATIVE_PINGORA_GATEWAY_TRUST_X_FORWARDED_FOR=true',
'GENARRATIVE_PINGORA_GATEWAY_TRUSTED_FRONT_PROXY_CONFIRMED=true',
'',
].join('\n'),
'utf8',
);
const loopbackResult = runPreflight(baseArgs);
assertStatus(
loopbackResult,
0,
'loopback 入口在显式确认前置代理清洗 X-Forwarded-For 后应允许 preflight 通过。',
);
writeFileSync(
envPath,
[
`GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN=${DIRECT_TLS_LISTEN}`,
`GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN=${DIRECT_HTTP_REDIRECT_LISTEN}`,
'GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE=/tmp/cert.pem',
'GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE=/tmp/key.pem',
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https',
'GENARRATIVE_PINGORA_GATEWAY_TRUST_X_FORWARDED_FOR=true',
'GENARRATIVE_PINGORA_GATEWAY_TRUSTED_FRONT_PROXY_CONFIRMED=false',
'',
].join('\n'),
'utf8',
);
const missingConfirmationResult = runPreflight(baseArgs);
if (missingConfirmationResult.status === 0) {
failures.push(
'开启 X-Forwarded-For 信任但缺少确认开关时 direct preflight 必须失败。',
);
}
assertIncludes(
missingConfirmationResult.stderr,
'GENARRATIVE_PINGORA_GATEWAY_TRUST_X_FORWARDED_FOR=true 时必须同时设置 GENARRATIVE_PINGORA_GATEWAY_TRUSTED_FRONT_PROXY_CONFIRMED=true',
'X-Forwarded-For 信任缺少确认开关时必须给出明确错误。',
);
}
function assertReleaseLayoutDryRunUsesBundledDirectChecks() {
const currentUser = currentUsername();
const releaseRoot = path.join(tmpRoot, 'current-release');
const releaseScript = path.join(
releaseRoot,
'scripts',
'deploy',
'pingora-direct-enable.sh',
);
const templatePath = path.join(
releaseRoot,
'deploy',
'systemd',
'genarrative-pingora-gateway-direct-entry.conf',
);
const dropinPath = path.join(
tmpRoot,
'release-layout-service.d',
'direct-entry.conf',
);
const envPath = path.join(tmpRoot, 'release-layout-pingora-gateway.env');
const certPath = path.join(tmpRoot, 'release-layout-cert.pem');
const keyPath = path.join(tmpRoot, 'release-layout-key.pem');
const pingoraGatewayPath = path.join(releaseRoot, 'pingora-gateway');
mkdirSync(path.join(releaseRoot, 'scripts', 'deploy'), { recursive: true });
mkdirSync(path.join(releaseRoot, 'deploy', 'systemd'), { recursive: true });
mkdirSync(path.join(releaseRoot, 'deploy', 'pingora'), { recursive: true });
writeFileSync(releaseScript, readFileSync(ENABLE_SCRIPT, 'utf8'), 'utf8');
writeFileSync(
path.join(releaseRoot, 'scripts', 'check-pingora-direct-preflight.mjs'),
readFileSync('scripts/check-pingora-direct-preflight.mjs', 'utf8'),
'utf8',
);
writeFileSync(
path.join(releaseRoot, 'scripts', 'check-pingora-direct-live.mjs'),
readFileSync('scripts/check-pingora-direct-live.mjs', 'utf8'),
'utf8',
);
writeFileSync(
path.join(
releaseRoot,
'deploy',
'systemd',
'genarrative-pingora-gateway.service',
),
`[Unit]\nDescription=Pingora 影子网关只监听本机高端口\n[Service]\nUser=${currentUser}\nEnvironmentFile=${envPath}\nExecStart=${pingoraGatewayPath}\n`,
'utf8',
);
writeFileSync(
templatePath,
[
'# 人工复制到 /etc/systemd/system/genarrative-pingora-gateway.service.d/direct-entry.conf 后生效',
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
path.join(releaseRoot, 'deploy', 'pingora', 'pingora-gateway.env.example'),
[
'# CAP_NET_BIND_SERVICE 只由 direct-entry drop-in 授予',
'GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN=0.0.0.0:443',
'GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN=0.0.0.0:80',
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https',
'',
].join('\n'),
'utf8',
);
writeFileSync(certPath, 'fake-cert\n', 'utf8');
writeFileSync(keyPath, 'fake-key\n', 'utf8');
writeFileSync(
envPath,
[
`GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN=${DIRECT_TLS_LISTEN}`,
`GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN=${DIRECT_HTTP_REDIRECT_LISTEN}`,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https',
`GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE=${certPath}`,
`GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE=${keyPath}`,
'',
].join('\n'),
'utf8',
);
writeFileSync(
pingoraGatewayPath,
'#!/usr/bin/env bash\nprintf "fake pingora gateway\\n"\n',
'utf8',
);
chmodExecutable(releaseScript);
chmodExecutable(pingoraGatewayPath);
const result = spawnSync(
'bash',
[
releaseScript,
'--dropin-path',
dropinPath,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--no-status',
],
{
cwd: tmpRoot,
encoding: 'utf8',
env: process.env,
},
);
assertStatus(
result,
0,
'release current 目录中的 enable dry-run 应能使用随包携带的 direct preflight。',
);
assertIncludes(
result.stdout,
`${releaseRoot}/scripts/check-pingora-direct-preflight.mjs`,
'release current 目录中的 enable dry-run 必须调用随包携带的 direct preflight。',
);
assertIncludes(
result.stdout,
`template=${templatePath}`,
'release current 目录中的 enable dry-run 必须默认读取随包携带的 direct-entry 模板。',
);
assertIncludes(
result.stdout,
`+ install -m 0644 ${templatePath} ${dropinPath}`,
'release current 目录中的 enable dry-run 必须展示安装随包 direct-entry 模板。',
);
assertIncludes(
result.stdout,
'[pingora-direct-preflight] OK',
'release current 目录中的 direct preflight 必须带服务用户证书可读和 service 二进制可执行检查并真实通过。',
);
if (existsSync(dropinPath)) {
failures.push('release current 目录 dry-run 不应安装临时 drop-in 文件。');
}
}
function currentUsername() {
if (process.env.USER || process.env.LOGNAME) {
return process.env.USER || process.env.LOGNAME;
}
try {
return userInfo().username;
} catch {
return 'root';
}
}
function assertScriptShape() {
const content = readFileSync(ENABLE_SCRIPT, 'utf8');
assertIncludes(content, 'APPLY="false"', '启用脚本必须默认 dry-run。');
assertIncludes(
content,
'--apply',
'启用脚本必须显式要求 --apply 才修改系统。',
);
assertIncludes(
content,
'systemctl daemon-reload',
'安装 drop-in 后必须 reload systemd。',
);
assertIncludes(
content,
'restart "${SERVICE_NAME}"',
'启用脚本必须重启 Pingora 服务。',
);
assertIncludes(
content,
'GENARRATIVE_PINGORA_DIRECT_TEMPLATE_PATH',
'启用脚本必须支持覆盖 direct-entry 模板路径。',
);
assertIncludes(
content,
'deploy/systemd/genarrative-pingora-gateway-direct-entry.conf',
'启用脚本默认模板必须来自 current release 随包 deploy/systemd。',
);
assertIncludes(
content,
'direct-entry.conf',
'启用脚本必须默认指向 direct-entry drop-in。',
);
assertIncludes(
content,
'该脚本不写入 TLS env',
'启用脚本必须声明不改 TLS/env/证书边界。',
);
assertIncludes(
content,
'--preflight-env-file',
'启用脚本必须支持直连 env 预检参数。',
);
assertIncludes(
content,
'--apply 必须同时提供 --preflight-env-file',
'启用脚本 apply 时必须强制预检 env 文件。',
);
assertIncludes(
content,
'--apply 必须同时提供 --preflight-check-cert-readable',
'启用脚本 apply 时必须强制证书可读预检。',
);
assertIncludes(
content,
'--apply 必须同时提供 --preflight-check-service-env-file',
'启用脚本 apply 时必须强制 service EnvironmentFile 一致性预检。',
);
assertIncludes(
content,
'--apply 必须同时提供 --preflight-check-service-user-cert-readable',
'启用脚本 apply 时必须强制 systemd 服务用户证书可读预检。',
);
assertIncludes(
content,
'--apply 必须同时提供 --preflight-check-service-binary-executable',
'启用脚本 apply 时必须强制 Pingora service 二进制可执行预检。',
);
assertIncludes(
content,
'--apply 必须同时提供 --preflight-check-ports-free',
'启用脚本 apply 时必须强制端口释放预检。',
);
assertIncludes(
content,
'--apply 必须同时提供 --direct-https-base-url',
'启用脚本 apply 时必须强制 direct HTTPS live smoke 参数。',
);
assertIncludes(
content,
'--apply 必须同时提供 --direct-http-base-url',
'启用脚本 apply 时必须强制 direct HTTP live smoke 参数。',
);
assertIncludes(
content,
'--apply 必须同时提供 --direct-host',
'启用脚本 apply 时必须强制正式域名 Host/SNI 参数。',
);
assertIncludes(
content,
'--direct-redirect-base-url',
'启用脚本必须支持 direct live redirect base URL 覆盖,用于高端口 rehearsal。',
);
assertIncludes(
content,
'--apply 必须同时提供 --direct-redirect-host',
'启用脚本 apply 时必须强制 redirect Location host 参数。',
);
assertIncludes(
content,
'--apply 必须同时提供 --direct-pingora-access-log',
'启用脚本 apply 时必须强制 direct access log 落盘校验参数。',
);
assertIncludes(
content,
'--apply 必须同时提供 --direct-spacetime-database',
'启用脚本 apply 时必须强制 WSS subscribe 数据库名参数。',
);
assertIncludes(
content,
'GENARRATIVE_PINGORA_DIRECT_CURRENT_RELEASE_AUDIT_SCRIPT',
'启用脚本必须支持固定 current release 自审脚本路径。',
);
assertIncludes(
content,
'--current-release-root',
'启用脚本必须支持固定 current release 根目录。',
);
assertIncludes(
content,
'--require-pingora-gateway',
'启用脚本 apply 前 current release 自审必须要求 Pingora 网关产物存在。',
);
assertIncludes(
content,
'--systemd-show',
'启用脚本 apply 前 current release 自审必须核验 systemd ExecStart。',
);
assertIncludes(
content,
'systemctl cat ${SERVICE_NAME}',
'启用脚本必须在 apply 后核验 systemd 最终配置。',
);
assertIncludes(
content,
'systemctl is-active ${SERVICE_NAME}',
'启用脚本必须在 apply 后核验 Pingora service active。',
);
assertIncludes(
content,
'systemctl show ${SERVICE_NAME} --property=ExecStart --value --no-pager',
'启用脚本必须在 apply 后核验 systemd ExecStart 指向 current release。',
);
assertIncludes(
content,
'主 service 模板中的',
'启用脚本 ExecStart 核验必须以随包主 service 模板为真相源。',
);
assertIncludes(
content,
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'启用脚本必须核验 AmbientCapabilities 已生效。',
);
assertIncludes(
content,
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'启用脚本必须核验 CapabilityBoundingSet 已生效。',
);
assertIncludes(
content,
'systemctl cat 未显示 EnvironmentFile=',
'启用脚本必须在 apply 后核验 systemd 最终配置读取的 env 和 preflight env 一致。',
);
assertIncludes(
content,
'--require-wss-upgrade',
'启用脚本必须在 apply 后强制 direct live WSS 101。',
);
assertIncludes(
content,
'--json',
'启用脚本必须用 JSON 模式运行 direct live smoke,便于校验结构化接流证据。',
);
assertIncludes(
content,
'direct live JSON 缺少 direct-access-log 结构化结果',
'启用脚本必须拒绝缺少 direct-access-log 结构化结果的 direct live 输出。',
);
assertIncludes(
content,
'direct-access-log evidence checked=',
'启用脚本必须在 direct-access-log 结构化证据通过后输出确认摘要。',
);
assertIncludes(
content,
'print_redacted_command node "${direct_live_args[@]}"',
'启用脚本展示 direct live smoke 命令时必须隐藏 probe token。',
);
assertIncludes(
content,
'direct_live_args+=(--redirect-base-url "${DIRECT_REDIRECT_BASE_URL}")',
'启用脚本必须把 direct redirect base URL 透传给 direct live smoke。',
);
assertIncludes(
content,
'drop-in 目录不能是符号链接',
'启用脚本 apply 前必须拒绝符号链接 drop-in 目录。',
);
assertIncludes(
content,
'drop-in 目标不能是符号链接',
'启用脚本 apply 前必须拒绝符号链接 drop-in 目标。',
);
assertIncludes(
content,
'reject_control_characters',
'启用脚本必须统一拒绝换行或 NUL 参数。',
);
assertIncludes(
content,
'不能包含换行或 NUL 字符',
'启用脚本拒绝控制字符时必须给出明确错误。',
);
assertIncludes(
content,
'reject_filesystem_root_path',
'启用脚本必须统一拒绝文件系统根目录路径参数。',
);
assertIncludes(
content,
'不能是文件系统根目录',
'启用脚本拒绝文件系统根目录路径时必须给出明确错误。',
);
}
function assertDryRunDoesNotInstallDropin() {
const templatePath = path.join(tmpRoot, 'template.conf');
const dropinPath = path.join(tmpRoot, 'service.d', 'direct-entry.conf');
writeFileSync(
templatePath,
'[Service]\nAmbientCapabilities=CAP_NET_BIND_SERVICE\n',
'utf8',
);
const result = runEnable([
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
'--service',
'genarrative-pingora-gateway.service',
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-probe-token',
'direct-secret-token',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
]);
assertStatus(result, 0, 'dry-run 应成功退出。');
assertIncludes(
result.stdout,
'apply=false',
'dry-run 输出必须明确 apply=false。',
);
assertIncludes(
result.stdout,
`+ install -d -m 0755 ${path.dirname(dropinPath)}`,
'dry-run 必须展示创建 drop-in 目录。',
);
assertIncludes(
result.stdout,
`+ install -m 0644 ${templatePath} ${dropinPath}`,
'dry-run 必须展示安装 drop-in。',
);
assertIncludes(
result.stdout,
'+ systemctl daemon-reload',
'dry-run 必须展示 daemon-reload。',
);
assertIncludes(
result.stdout,
'+ systemctl restart genarrative-pingora-gateway.service',
'dry-run 必须展示重启 Pingora。',
);
assertIncludes(
result.stdout,
'+ systemctl cat genarrative-pingora-gateway.service',
'dry-run 必须展示启用后 systemd drop-in 生效核验。',
);
assertIncludes(
result.stdout,
'+ systemctl is-active genarrative-pingora-gateway.service',
'dry-run 必须展示启用后 service active 核验。',
);
assertIncludes(
result.stdout,
'+ systemctl show genarrative-pingora-gateway.service --property=ExecStart --value --no-pager',
'dry-run 必须展示启用后 ExecStart 指向核验。',
);
assertIncludes(
result.stdout,
'+ node ',
'dry-run 必须展示 current release 自审和启用后 direct live smoke。',
);
assertIncludes(
result.stdout,
'pingora-current-release-audit.mjs',
'dry-run 必须展示 apply 前 current release 自审。',
);
assertIncludes(
result.stdout,
'--require-pingora-gateway --systemd-show',
'dry-run current release 自审必须要求 Pingora 产物和 systemd ExecStart。',
);
assertIncludes(
result.stdout,
'--require-wss-upgrade',
'dry-run 必须展示 direct live smoke 强制 WSS 101。',
);
assertIncludes(
result.stdout,
'--json',
'dry-run 必须展示 direct live smoke 会输出 JSON 结构化证据。',
);
assertIncludes(
result.stdout,
'--pingora-access-log',
'dry-run 必须展示 direct live smoke 会校验 Pingora access log 落盘。',
);
assertIncludes(
result.stdout,
'--probe-token <redacted>',
'dry-run direct live smoke 命令必须隐藏 probe token。',
);
if (result.stdout.includes('direct-secret-token')) {
failures.push('dry-run direct live smoke 命令不能泄露 probe token 原文。');
}
if (existsSync(dropinPath)) {
failures.push('dry-run 不应安装临时 drop-in 文件。');
}
}
function assertDryRunRunsPreflightWhenRequested() {
const templatePath = path.join(tmpRoot, 'preflight-template.conf');
const dropinPath = path.join(
tmpRoot,
'preflight-service.d',
'direct-entry.conf',
);
const envPath = path.join(tmpRoot, 'pingora-gateway.env');
const certPath = path.join(tmpRoot, 'cert.pem');
const keyPath = path.join(tmpRoot, 'key.pem');
writeFileSync(
templatePath,
'[Service]\nAmbientCapabilities=CAP_NET_BIND_SERVICE\n',
'utf8',
);
writeFileSync(certPath, 'fake-cert\n', 'utf8');
writeFileSync(keyPath, 'fake-key\n', 'utf8');
writeFileSync(
envPath,
[
`GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN=${DIRECT_TLS_LISTEN}`,
`GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN=${DIRECT_HTTP_REDIRECT_LISTEN}`,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https',
`GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE=${certPath}`,
`GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE=${keyPath}`,
'',
].join('\n'),
'utf8',
);
const result = runEnable([
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-ports-free',
'--no-status',
]);
assertStatus(result, 0, '带 preflight 的 dry-run 应成功退出。');
assertIncludes(
result.stdout,
'+ node ',
'dry-run 必须展示并执行 direct preflight。',
);
assertIncludes(
result.stdout,
'check-pingora-direct-preflight.mjs',
'dry-run preflight 命令必须调用 direct preflight 脚本。',
);
assertIncludes(
result.stdout,
`--env-file ${envPath} --require-live-env --check-cert-readable --check-ports-free`,
'dry-run preflight 命令必须带 env、证书和端口释放检查参数。',
);
assertIncludes(
result.stdout,
'[pingora-direct-preflight] OK',
'dry-run preflight 必须真实通过。',
);
assertIncludes(
result.stdout,
'--https-base-url <direct-https-base-url>',
'带 preflight 的 dry-run 仍必须提示 apply 后会执行 direct live smoke。',
);
if (existsSync(dropinPath)) {
failures.push('带 preflight 的 dry-run 不应安装临时 drop-in 文件。');
}
}
function assertMissingTemplateDryRunWarns() {
const templatePath = path.join(tmpRoot, 'missing-template.conf');
const dropinPath = path.join(
tmpRoot,
'missing-service.d',
'direct-entry.conf',
);
const result = runEnable([
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
'--no-status',
]);
assertStatus(result, 0, '模板不存在时 dry-run 应只提示不失败。');
assertIncludes(
result.stderr,
'dry-run 提示:当前模板不存在',
'模板不存在的 dry-run 必须提示 --apply 会失败。',
);
}
function assertRelativePathsRejected() {
const templateResult = runEnable([
'--template-path',
'relative/template.conf',
'--dropin-path',
path.join(tmpRoot, 'service.d', 'direct-entry.conf'),
'--no-status',
]);
if (templateResult.status === 0) {
failures.push('相对路径 template 必须被拒绝。');
}
assertIncludes(
templateResult.stderr,
'--template-path 必须是绝对路径',
'相对 template 路径负例必须说明需要绝对路径。',
);
const dropinResult = runEnable([
'--template-path',
path.join(tmpRoot, 'template.conf'),
'--dropin-path',
'relative/direct-entry.conf',
'--no-status',
]);
if (dropinResult.status === 0) {
failures.push('相对路径 drop-in 必须被拒绝。');
}
assertIncludes(
dropinResult.stderr,
'--dropin-path 必须是绝对路径',
'相对 drop-in 路径负例必须说明需要绝对路径。',
);
const preflightResult = runEnable([
'--template-path',
path.join(tmpRoot, 'template.conf'),
'--dropin-path',
path.join(tmpRoot, 'service.d', 'direct-entry.conf'),
'--preflight-env-file',
'relative/pingora-gateway.env',
'--no-status',
]);
if (preflightResult.status === 0) {
failures.push('相对路径 preflight env 必须被拒绝。');
}
assertIncludes(
preflightResult.stderr,
'--preflight-env-file 必须是绝对路径',
'相对 preflight env 路径负例必须说明需要绝对路径。',
);
const auditScriptResult = runEnable([
'--template-path',
path.join(tmpRoot, 'template.conf'),
'--dropin-path',
path.join(tmpRoot, 'service.d', 'direct-entry.conf'),
'--current-release-audit-script',
'relative/pingora-current-release-audit.mjs',
'--no-status',
]);
if (auditScriptResult.status === 0) {
failures.push('相对路径 current release 自审脚本必须被拒绝。');
}
assertIncludes(
auditScriptResult.stderr,
'--current-release-audit-script 必须是绝对路径',
'相对 current release 自审脚本路径负例必须说明需要绝对路径。',
);
const preflightScriptResult = runEnable([
'--template-path',
path.join(tmpRoot, 'template.conf'),
'--dropin-path',
path.join(tmpRoot, 'service.d', 'direct-entry.conf'),
'--preflight-script',
'relative/check-pingora-direct-preflight.mjs',
'--no-status',
]);
if (preflightScriptResult.status === 0) {
failures.push('相对路径 direct preflight 脚本必须被拒绝。');
}
assertIncludes(
preflightScriptResult.stderr,
'--preflight-script 必须是绝对路径',
'相对 direct preflight 脚本路径负例必须说明需要绝对路径。',
);
const auditRootResult = runEnable([
'--template-path',
path.join(tmpRoot, 'template.conf'),
'--dropin-path',
path.join(tmpRoot, 'service.d', 'direct-entry.conf'),
'--current-release-root',
'relative/current',
'--no-status',
]);
if (auditRootResult.status === 0) {
failures.push('相对路径 current release 根目录必须被拒绝。');
}
assertIncludes(
auditRootResult.stderr,
'--current-release-root 必须是绝对路径',
'相对 current release 根目录负例必须说明需要绝对路径。',
);
const directLiveScriptResult = runEnable([
'--template-path',
path.join(tmpRoot, 'template.conf'),
'--dropin-path',
path.join(tmpRoot, 'service.d', 'direct-entry.conf'),
'--direct-live-script',
'relative/check-pingora-direct-live.mjs',
'--no-status',
]);
if (directLiveScriptResult.status === 0) {
failures.push('相对路径 direct live smoke 脚本必须被拒绝。');
}
assertIncludes(
directLiveScriptResult.stderr,
'--direct-live-script 必须是绝对路径',
'相对 direct live smoke 脚本路径负例必须说明需要绝对路径。',
);
}
function assertFilesystemRootPathsRejected() {
const rootDropinResult = runEnable([
'--template-path',
path.join(tmpRoot, 'template.conf'),
'--dropin-path',
'/',
'--no-status',
]);
if (rootDropinResult.status === 0) {
failures.push('文件系统根目录 --dropin-path 必须被拒绝。');
}
assertIncludes(
rootDropinResult.stderr,
'--dropin-path 不能是文件系统根目录',
'文件系统根目录 drop-in 路径负例必须给出明确错误。',
);
const rootReleaseResult = runEnable([
'--template-path',
path.join(tmpRoot, 'template.conf'),
'--dropin-path',
path.join(tmpRoot, 'service.d', 'direct-entry.conf'),
'--current-release-root',
'/',
'--no-status',
]);
if (rootReleaseResult.status === 0) {
failures.push('文件系统根目录 --current-release-root 必须被拒绝。');
}
assertIncludes(
rootReleaseResult.stderr,
'--current-release-root 不能是文件系统根目录',
'文件系统根目录 current release 根目录负例必须给出明确错误。',
);
}
function assertApplyRequiresPreflightEnvFile() {
const result = runEnable(['--apply', '--no-status']);
if (result.status === 0) {
failures.push('--apply 缺少 --preflight-env-file 必须被拒绝。');
}
assertIncludes(
result.stderr,
'--apply 必须同时提供 --preflight-env-file',
'apply 负例必须说明不能跳过 preflight env。',
);
}
function assertApplyRequiresCertReadablePreflight() {
const result = runEnable([
'--apply',
'--preflight-env-file',
path.join(tmpRoot, 'pingora-gateway.env'),
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-ports-free',
'--no-status',
]);
if (result.status === 0) {
failures.push('--apply 缺少 --preflight-check-cert-readable 必须被拒绝。');
}
assertIncludes(
result.stderr,
'--apply 必须同时提供 --preflight-check-cert-readable',
'apply 负例必须说明不能跳过证书可读 preflight。',
);
}
function assertApplyRequiresServiceEnvFilePreflight() {
const result = runEnable([
'--apply',
'--preflight-env-file',
path.join(tmpRoot, 'pingora-gateway.env'),
'--preflight-check-cert-readable',
'--preflight-check-service-user-cert-readable',
'--preflight-check-ports-free',
'--no-status',
]);
if (result.status === 0) {
failures.push(
'--apply 缺少 --preflight-check-service-env-file 必须被拒绝。',
);
}
assertIncludes(
result.stderr,
'--apply 必须同时提供 --preflight-check-service-env-file',
'apply 负例必须说明不能跳过 service EnvironmentFile 一致性 preflight。',
);
}
function assertApplyRequiresServiceUserCertReadablePreflight() {
const result = runEnable([
'--apply',
'--preflight-env-file',
path.join(tmpRoot, 'pingora-gateway.env'),
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-ports-free',
'--no-status',
]);
if (result.status === 0) {
failures.push(
'--apply 缺少 --preflight-check-service-user-cert-readable 必须被拒绝。',
);
}
assertIncludes(
result.stderr,
'--apply 必须同时提供 --preflight-check-service-user-cert-readable',
'apply 负例必须说明不能跳过 systemd 服务用户证书可读 preflight。',
);
}
function assertApplyRequiresServiceBinaryExecutablePreflight() {
const result = runEnable([
'--apply',
'--preflight-env-file',
path.join(tmpRoot, 'pingora-gateway.env'),
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-ports-free',
'--no-status',
]);
if (result.status === 0) {
failures.push(
'--apply 缺少 --preflight-check-service-binary-executable 必须被拒绝。',
);
}
assertIncludes(
result.stderr,
'--apply 必须同时提供 --preflight-check-service-binary-executable',
'apply 负例必须说明不能跳过 service 二进制可执行 preflight。',
);
}
function assertApplyRequiresPortsFreePreflight() {
const result = runEnable([
'--apply',
'--preflight-env-file',
path.join(tmpRoot, 'pingora-gateway.env'),
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--no-status',
]);
if (result.status === 0) {
failures.push('--apply 缺少 --preflight-check-ports-free 必须被拒绝。');
}
assertIncludes(
result.stderr,
'--apply 必须同时提供 --preflight-check-ports-free',
'apply 负例必须说明不能跳过端口释放 preflight。',
);
}
function assertApplyRequiresDirectLiveArgs() {
const auditArgs = createFakeCurrentReleaseAuditArgs(
'direct-live-args-audit-ok',
);
const baseArgs = [
'--apply',
'--preflight-env-file',
path.join(tmpRoot, 'pingora-gateway.env'),
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
...auditArgs,
'--no-status',
];
const httpsResult = runEnable(baseArgs);
assertApplyFailure(
httpsResult,
'--apply 必须同时提供 --direct-https-base-url',
'--apply 缺少 --direct-https-base-url 必须被拒绝。',
);
const httpResult = runEnable([
...baseArgs,
'--direct-https-base-url',
'https://127.0.0.1',
]);
assertApplyFailure(
httpResult,
'--apply 必须同时提供 --direct-http-base-url',
'--apply 缺少 --direct-http-base-url 必须被拒绝。',
);
const hostResult = runEnable([
...baseArgs,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
]);
assertApplyFailure(
hostResult,
'--apply 必须同时提供 --direct-host',
'--apply 缺少 --direct-host 必须被拒绝。',
);
const redirectHostResult = runEnable([
...baseArgs,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
]);
assertApplyFailure(
redirectHostResult,
'--apply 必须同时提供 --direct-redirect-host',
'--apply 缺少 --direct-redirect-host 必须被拒绝。',
);
const accessLogResult = runEnable([
...baseArgs,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
]);
assertApplyFailure(
accessLogResult,
'--apply 必须同时提供 --direct-pingora-access-log',
'--apply 缺少 --direct-pingora-access-log 必须被拒绝。',
);
const databaseResult = runEnable([
...baseArgs,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
]);
assertApplyFailure(
databaseResult,
'--apply 必须同时提供 --direct-spacetime-database',
'--apply 缺少 --direct-spacetime-database 必须被拒绝。',
);
}
function assertRejectsControlCharacterInputsBeforeApply() {
const fixture = createApplyPathSafetyFixture('control-character-input');
const dropinPath = path.join(fixture.root, 'service.d', 'direct-entry.conf');
const result = runEnable([
...fixture.args,
'--dropin-path',
dropinPath,
'--direct-host',
'example.com\ninjected.example.com',
]);
if (result.status === 0) {
failures.push('带换行的 direct host 必须在 apply 修改系统前被拒绝。');
}
assertIncludes(
result.stderr,
'--direct-host 不能包含换行或 NUL 字符',
'direct host 控制字符负例必须给出明确错误。',
);
if (existsSync(dropinPath)) {
failures.push(
'带换行的 direct host 被拒绝后不应安装 direct-entry drop-in。',
);
}
}
function assertApplyFailsWhenCurrentReleaseAuditFailsBeforeInstall() {
const templatePath = path.join(tmpRoot, 'audit-fail-template.conf');
const dropinPath = path.join(
tmpRoot,
'audit-fail-service.d',
'direct-entry.conf',
);
const envPath = path.join(tmpRoot, 'audit-fail-pingora-gateway.env');
const auditScript = path.join(tmpRoot, 'fake-current-release-audit-fail.mjs');
writeFileSync(
templatePath,
[
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envPath,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https\n',
'utf8',
);
writeFileSync(
auditScript,
'#!/usr/bin/env node\nconsole.error("fake current release audit failed");\nprocess.exit(21);\n',
'utf8',
);
chmodExecutable(auditScript);
const result = runEnable([
'--apply',
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
'--current-release-audit-script',
auditScript,
'--current-release-root',
tmpRoot,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
]);
if (result.status === 0) {
failures.push('current release 自审失败时 enable apply 必须失败。');
}
assertIncludes(
result.stderr,
'fake current release audit failed',
'current release 自审失败时必须暴露自审错误。',
);
if (existsSync(dropinPath)) {
failures.push(
'current release 自审失败发生在安装前,不应留下 direct-entry drop-in。',
);
}
}
function assertApplyFailsWhenPreflightScriptMissingBeforeInstall() {
const templatePath = path.join(tmpRoot, 'preflight-missing-template.conf');
const dropinPath = path.join(
tmpRoot,
'preflight-missing-service.d',
'direct-entry.conf',
);
const envPath = path.join(tmpRoot, 'preflight-missing-pingora-gateway.env');
const missingPreflightScript = path.join(
tmpRoot,
'missing-check-pingora-direct-preflight.mjs',
);
const directLiveScript = path.join(
tmpRoot,
'fake-direct-live-preflight-missing.mjs',
);
const auditArgs = createFakeCurrentReleaseAuditArgs(
'preflight-missing-audit-ok',
);
writeFileSync(
templatePath,
[
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envPath,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https\n',
'utf8',
);
writeFileSync(
directLiveScript,
'#!/usr/bin/env node\nconsole.log("[fake-direct-live] OK");\n',
'utf8',
);
chmodExecutable(directLiveScript);
const result = runEnable([
'--apply',
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
...auditArgs,
'--preflight-script',
missingPreflightScript,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--direct-live-script',
directLiveScript,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
]);
if (result.status === 0) {
failures.push('direct preflight 脚本缺失时 enable apply 必须失败。');
}
assertIncludes(
result.stderr,
'direct preflight 脚本不存在',
'direct preflight 脚本缺失时必须给出明确错误。',
);
if (existsSync(dropinPath)) {
failures.push(
'direct preflight 脚本缺失发生在安装前,不应留下 direct-entry drop-in。',
);
}
}
function assertApplyFailsWhenDirectLiveScriptMissingBeforeInstall() {
const templatePath = path.join(tmpRoot, 'direct-live-missing-template.conf');
const dropinPath = path.join(
tmpRoot,
'direct-live-missing-service.d',
'direct-entry.conf',
);
const envPath = path.join(tmpRoot, 'direct-live-missing-pingora-gateway.env');
const preflightScript = path.join(
tmpRoot,
'fake-preflight-direct-live-missing.mjs',
);
const missingDirectLiveScript = path.join(
tmpRoot,
'missing-check-pingora-direct-live.mjs',
);
const auditArgs = createFakeCurrentReleaseAuditArgs(
'direct-live-missing-audit-ok',
);
writeFileSync(
templatePath,
[
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envPath,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https\n',
'utf8',
);
writeFileSync(
preflightScript,
'#!/usr/bin/env node\nconsole.log("[fake-preflight] OK");\n',
'utf8',
);
chmodExecutable(preflightScript);
const result = runEnable([
'--apply',
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
...auditArgs,
'--preflight-script',
preflightScript,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--direct-live-script',
missingDirectLiveScript,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
]);
if (result.status === 0) {
failures.push('direct live smoke 脚本缺失时 enable apply 必须失败。');
}
assertIncludes(
result.stderr,
'direct live smoke 脚本不存在',
'direct live smoke 脚本缺失时必须给出明确错误。',
);
if (existsSync(dropinPath)) {
failures.push(
'direct live smoke 脚本缺失发生在安装前,不应留下 direct-entry drop-in。',
);
}
}
function assertApplyRejectsSymlinkDropinDirectoryBeforeInstall() {
const fixture = createApplyPathSafetyFixture('symlink-dropin-dir');
const realDropinDir = path.join(fixture.root, 'real-service.d');
const symlinkDropinDir = path.join(fixture.root, 'linked-service.d');
mkdirSync(realDropinDir, { recursive: true });
symlinkSync(realDropinDir, symlinkDropinDir);
const dropinPath = path.join(symlinkDropinDir, 'direct-entry.conf');
const result = runEnable([...fixture.args, '--dropin-path', dropinPath]);
if (result.status === 0) {
failures.push('drop-in 目录是符号链接时 enable apply 必须失败。');
}
assertIncludes(
result.stderr,
'drop-in 目录不能是符号链接',
'drop-in 符号链接目录负例必须给出明确错误。',
);
if (existsSync(path.join(realDropinDir, 'direct-entry.conf'))) {
failures.push('drop-in 目录是符号链接时不应写入真实目标目录。');
}
}
function assertApplyRejectsSymlinkDropinFileBeforeInstall() {
const fixture = createApplyPathSafetyFixture('symlink-dropin-file');
const dropinDir = path.join(fixture.root, 'service.d');
const realDropinFile = path.join(fixture.root, 'real-direct-entry.conf');
const symlinkDropinFile = path.join(dropinDir, 'direct-entry.conf');
mkdirSync(dropinDir, { recursive: true });
writeFileSync(realDropinFile, 'original dropin\n', 'utf8');
symlinkSync(realDropinFile, symlinkDropinFile);
const result = runEnable([
...fixture.args,
'--dropin-path',
symlinkDropinFile,
]);
if (result.status === 0) {
failures.push('drop-in 目标是符号链接时 enable apply 必须失败。');
}
assertIncludes(
result.stderr,
'drop-in 目标不能是符号链接',
'drop-in 符号链接目标负例必须给出明确错误。',
);
const realContent = readFileSync(realDropinFile, 'utf8');
if (realContent !== 'original dropin\n') {
failures.push('drop-in 目标是符号链接时不应改写真实目标文件。');
}
}
function assertApplyFailsWhenSystemdEnvFileDiffers() {
const templatePath = path.join(tmpRoot, 'systemd-env-mismatch-template.conf');
const dropinPath = path.join(
tmpRoot,
'systemd-env-mismatch-service.d',
'direct-entry.conf',
);
const envPath = path.join(
tmpRoot,
'systemd-env-mismatch-pingora-gateway.env',
);
const preflightScript = path.join(tmpRoot, 'fake-preflight-env-ok.mjs');
const directLiveScript = path.join(tmpRoot, 'fake-direct-live-env-ok.mjs');
const auditArgs = createFakeCurrentReleaseAuditArgs(
'systemd-env-mismatch-audit-ok',
);
const systemctlBinary = path.join(tmpRoot, 'systemctl');
writeFileSync(
templatePath,
[
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envPath,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https\n',
'utf8',
);
writeFileSync(
preflightScript,
'#!/usr/bin/env node\nconsole.log("[fake-preflight] OK");\n',
'utf8',
);
writeFileSync(
directLiveScript,
'#!/usr/bin/env node\nconsole.log("[fake-direct-live] OK");\n',
'utf8',
);
writeFileSync(
systemctlBinary,
[
'#!/usr/bin/env bash',
'if [[ "$1" == "cat" ]]; then',
' echo "[Service]"',
' echo "EnvironmentFile=/etc/genarrative/other-pingora.env"',
' echo "AmbientCapabilities=CAP_NET_BIND_SERVICE"',
' echo "CapabilityBoundingSet=CAP_NET_BIND_SERVICE"',
' exit 0',
'fi',
'if [[ "$1" == "is-active" ]]; then',
' echo active',
' exit 0',
'fi',
'exit 0',
'',
].join('\n'),
'utf8',
);
chmodExecutable(preflightScript);
chmodExecutable(directLiveScript);
chmodExecutable(systemctlBinary);
const result = runEnable(
[
'--apply',
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
...auditArgs,
'--preflight-script',
preflightScript,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--direct-live-script',
directLiveScript,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
],
{ PATH: `${tmpRoot}:${process.env.PATH || ''}` },
);
if (result.status === 0) {
failures.push(
'systemctl cat 的 EnvironmentFile 漂移时 enable apply 必须失败。',
);
}
assertIncludes(
result.stderr,
`systemctl cat 未显示 EnvironmentFile=${envPath}`,
'systemd 最终配置读取的 env 和 preflight env 不一致时必须给出明确错误。',
);
}
function assertApplyFailsWhenSystemdEnvFileOnlySharesPrefix() {
const templatePath = path.join(tmpRoot, 'systemd-env-prefix-template.conf');
const dropinPath = path.join(
tmpRoot,
'systemd-env-prefix-service.d',
'direct-entry.conf',
);
const envPath = path.join(tmpRoot, 'systemd-env-prefix-pingora-gateway.env');
const preflightScript = path.join(tmpRoot, 'fake-preflight-prefix-ok.mjs');
const directLiveScript = path.join(tmpRoot, 'fake-direct-live-prefix-ok.mjs');
const auditArgs = createFakeCurrentReleaseAuditArgs(
'systemd-env-prefix-audit-ok',
);
const systemctlBinary = path.join(tmpRoot, 'systemctl');
writeFileSync(
templatePath,
[
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envPath,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https\n',
'utf8',
);
writeFileSync(
preflightScript,
'#!/usr/bin/env node\nconsole.log("[fake-preflight] OK");\n',
'utf8',
);
writeFileSync(
directLiveScript,
'#!/usr/bin/env node\nconsole.log("[fake-direct-live] OK");\n',
'utf8',
);
writeFileSync(
systemctlBinary,
[
'#!/usr/bin/env bash',
'if [[ "$1" == "cat" ]]; then',
' echo "[Service]"',
` echo "EnvironmentFile=${envPath}.bak"`,
' echo "AmbientCapabilities=CAP_NET_BIND_SERVICE"',
' echo "CapabilityBoundingSet=CAP_NET_BIND_SERVICE"',
' exit 0',
'fi',
'if [[ "$1" == "is-active" ]]; then',
' echo active',
' exit 0',
'fi',
'exit 0',
'',
].join('\n'),
'utf8',
);
chmodExecutable(preflightScript);
chmodExecutable(directLiveScript);
chmodExecutable(systemctlBinary);
const result = runEnable(
[
'--apply',
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
...auditArgs,
'--preflight-script',
preflightScript,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--direct-live-script',
directLiveScript,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
],
{ PATH: `${tmpRoot}:${process.env.PATH || ''}` },
);
if (result.status === 0) {
failures.push(
'systemctl cat 的 EnvironmentFile 只共享路径前缀时 enable apply 必须失败。',
);
}
assertIncludes(
result.stderr,
`systemctl cat 未显示 EnvironmentFile=${envPath}`,
'systemd 最终配置 EnvironmentFile 不能用路径前缀误判为一致。',
);
}
function assertApplyFailsWhenDirectLiveSmokeFails() {
const templatePath = path.join(tmpRoot, 'direct-live-fail-template.conf');
const serviceUnitPath = path.join(
tmpRoot,
'direct-live-fail-service.service',
);
const dropinPath = path.join(
tmpRoot,
'direct-live-fail-service.d',
'direct-entry.conf',
);
const envPath = path.join(tmpRoot, 'direct-live-fail-pingora-gateway.env');
const certPath = path.join(tmpRoot, 'direct-live-fail-cert.pem');
const keyPath = path.join(tmpRoot, 'direct-live-fail-key.pem');
const preflightScript = path.join(tmpRoot, 'fake-preflight-ok.mjs');
const directLiveScript = path.join(tmpRoot, 'fake-direct-live-fail.mjs');
const auditArgs = createFakeCurrentReleaseAuditArgs(
'direct-live-fail-audit-ok',
);
const systemctlBinary = path.join(tmpRoot, 'systemctl');
writeFileSync(
templatePath,
[
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
serviceUnitPath,
[
'[Unit]',
'Description=Pingora 影子网关只监听本机高端口',
'[Service]',
`EnvironmentFile=${envPath}`,
'ExecStart=/opt/genarrative/current/pingora-gateway',
'',
].join('\n'),
'utf8',
);
writeFileSync(certPath, 'fake-cert\n', 'utf8');
writeFileSync(keyPath, 'fake-key\n', 'utf8');
writeFileSync(
envPath,
[
`GENARRATIVE_PINGORA_GATEWAY_TLS_LISTEN=${DIRECT_TLS_LISTEN}`,
`GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN=${DIRECT_HTTP_REDIRECT_LISTEN}`,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https',
`GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE=${certPath}`,
`GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE=${keyPath}`,
'',
].join('\n'),
'utf8',
);
writeFileSync(
preflightScript,
'#!/usr/bin/env node\nconsole.log("[fake-preflight] OK");\n',
'utf8',
);
writeFileSync(
directLiveScript,
'#!/usr/bin/env node\nconsole.error("fake direct live failed");\nprocess.exit(12);\n',
'utf8',
);
writeFileSync(
systemctlBinary,
[
'#!/usr/bin/env bash',
'if [[ "$1" == "cat" ]]; then',
' echo "[Service]"',
` echo "EnvironmentFile=${envPath}"`,
' echo "AmbientCapabilities=CAP_NET_BIND_SERVICE"',
' echo "CapabilityBoundingSet=CAP_NET_BIND_SERVICE"',
' exit 0',
'fi',
'if [[ "$1" == "show" ]]; then',
' echo "{ path=/opt/genarrative/current/pingora-gateway ; argv[]=/opt/genarrative/current/pingora-gateway ; }"',
' exit 0',
'fi',
'if [[ "$1" == "is-active" ]]; then',
' echo active',
' exit 0',
'fi',
'exit 0',
'',
].join('\n'),
'utf8',
);
chmodExecutable(preflightScript);
chmodExecutable(directLiveScript);
chmodExecutable(systemctlBinary);
const result = runEnable(
[
'--apply',
'--service-unit-path',
serviceUnitPath,
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
...auditArgs,
'--preflight-script',
preflightScript,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--direct-live-script',
directLiveScript,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
],
{ PATH: `${tmpRoot}:${process.env.PATH || ''}` },
);
if (result.status === 0) {
failures.push('direct live smoke 失败时 enable apply 必须失败。');
}
assertIncludes(
result.stderr,
'fake direct live failed',
'direct live smoke 失败时必须暴露检查错误。',
);
if (!existsSync(dropinPath)) {
failures.push(
'direct live smoke 失败发生在安装后,临时 drop-in 应已存在供后续回退脚本处理。',
);
}
}
function assertApplyFailsWhenDirectLiveAccessLogJsonMissing() {
const templatePath = path.join(
tmpRoot,
'direct-live-json-missing-template.conf',
);
const serviceUnitPath = path.join(
tmpRoot,
'direct-live-json-missing-service.service',
);
const dropinPath = path.join(
tmpRoot,
'direct-live-json-missing-service.d',
'direct-entry.conf',
);
const envPath = path.join(
tmpRoot,
'direct-live-json-missing-pingora-gateway.env',
);
const preflightScript = path.join(
tmpRoot,
'fake-preflight-json-missing-ok.mjs',
);
const directLiveScript = path.join(
tmpRoot,
'fake-direct-live-json-missing.mjs',
);
const auditArgs = createFakeCurrentReleaseAuditArgs(
'direct-live-json-missing-audit-ok',
);
const systemctlBinary = path.join(tmpRoot, 'systemctl');
writeFileSync(
templatePath,
[
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
serviceUnitPath,
[
'[Unit]',
'Description=Pingora 影子网关只监听本机高端口',
'[Service]',
`EnvironmentFile=${envPath}`,
'ExecStart=/opt/genarrative/current/pingora-gateway',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envPath,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https\n',
'utf8',
);
writeFileSync(
preflightScript,
'#!/usr/bin/env node\nconsole.log("[fake-preflight] OK");\n',
'utf8',
);
writeFileSync(
directLiveScript,
[
'#!/usr/bin/env node',
'console.log("[fake-direct-live] OK");',
'console.log(JSON.stringify({ ok: true, results: [{ name: "https-root" }] }, null, 2));',
'',
].join('\n'),
'utf8',
);
writeFileSync(
systemctlBinary,
[
'#!/usr/bin/env bash',
'if [[ "$1" == "cat" ]]; then',
' echo "[Service]"',
` echo "EnvironmentFile=${envPath}"`,
' echo "AmbientCapabilities=CAP_NET_BIND_SERVICE"',
' echo "CapabilityBoundingSet=CAP_NET_BIND_SERVICE"',
' exit 0',
'fi',
'if [[ "$1" == "show" ]]; then',
' echo "{ path=/opt/genarrative/current/pingora-gateway ; argv[]=/opt/genarrative/current/pingora-gateway ; }"',
' exit 0',
'fi',
'if [[ "$1" == "is-active" ]]; then',
' echo active',
' exit 0',
'fi',
'exit 0',
'',
].join('\n'),
'utf8',
);
chmodExecutable(preflightScript);
chmodExecutable(directLiveScript);
chmodExecutable(systemctlBinary);
const result = runEnable(
[
'--apply',
'--service-unit-path',
serviceUnitPath,
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
...auditArgs,
'--preflight-script',
preflightScript,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--direct-live-script',
directLiveScript,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
],
{ PATH: `${tmpRoot}:${process.env.PATH || ''}` },
);
if (result.status === 0) {
failures.push(
'direct live JSON 缺少 direct-access-log 时 enable apply 必须失败。',
);
}
assertIncludes(
result.stderr,
'direct live JSON 缺少 direct-access-log 结构化结果',
'direct live JSON 缺少 access log 结构化结果时必须给出明确错误。',
);
if (!existsSync(dropinPath)) {
failures.push(
'direct live JSON 结构化证据失败发生在安装后,临时 drop-in 应已存在供后续回退脚本处理。',
);
}
}
function assertApplyFailsWhenSystemdExecStartDiffers() {
const templatePath = path.join(
tmpRoot,
'systemd-exec-mismatch-template.conf',
);
const serviceUnitPath = path.join(tmpRoot, 'systemd-exec-mismatch.service');
const dropinPath = path.join(
tmpRoot,
'systemd-exec-mismatch-service.d',
'direct-entry.conf',
);
const envPath = path.join(
tmpRoot,
'systemd-exec-mismatch-pingora-gateway.env',
);
const preflightScript = path.join(tmpRoot, 'fake-preflight-exec-ok.mjs');
const directLiveScript = path.join(tmpRoot, 'fake-direct-live-exec-ok.mjs');
const auditArgs = createFakeCurrentReleaseAuditArgs(
'systemd-exec-mismatch-audit-ok',
);
const systemctlBinary = path.join(tmpRoot, 'systemctl');
writeFileSync(
templatePath,
[
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
serviceUnitPath,
[
'[Unit]',
'Description=Pingora 影子网关只监听本机高端口',
'[Service]',
`EnvironmentFile=${envPath}`,
'ExecStart=/opt/genarrative/current/pingora-gateway',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envPath,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https\n',
'utf8',
);
writeFileSync(
preflightScript,
'#!/usr/bin/env node\nconsole.log("[fake-preflight] OK");\n',
'utf8',
);
writeFileSync(
directLiveScript,
'#!/usr/bin/env node\nconsole.log("[fake-direct-live] OK");\n',
'utf8',
);
writeFileSync(
systemctlBinary,
[
'#!/usr/bin/env bash',
'if [[ "$1" == "cat" ]]; then',
' echo "[Service]"',
` echo "EnvironmentFile=${envPath}"`,
' echo "AmbientCapabilities=CAP_NET_BIND_SERVICE"',
' echo "CapabilityBoundingSet=CAP_NET_BIND_SERVICE"',
' exit 0',
'fi',
'if [[ "$1" == "show" ]]; then',
' echo "{ path=/opt/genarrative/old-release/pingora-gateway ; argv[]=/opt/genarrative/old-release/pingora-gateway ; }"',
' exit 0',
'fi',
'if [[ "$1" == "is-active" ]]; then',
' echo active',
' exit 0',
'fi',
'exit 0',
'',
].join('\n'),
'utf8',
);
chmodExecutable(preflightScript);
chmodExecutable(directLiveScript);
chmodExecutable(systemctlBinary);
const result = runEnable(
[
'--apply',
'--service-unit-path',
serviceUnitPath,
'--template-path',
templatePath,
'--dropin-path',
dropinPath,
...auditArgs,
'--preflight-script',
preflightScript,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--direct-live-script',
directLiveScript,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
],
{ PATH: `${tmpRoot}:${process.env.PATH || ''}` },
);
if (result.status === 0) {
failures.push(
'systemctl show ExecStart 指向旧 release 时 enable apply 必须失败。',
);
}
assertIncludes(
result.stderr,
'systemctl show ExecStart 未指向主 service 模板中的 /opt/genarrative/current/pingora-gateway',
'systemd 最终 ExecStart 指向旧 release 时必须给出明确错误。',
);
}
function runEnable(args, env = {}) {
return spawnSync('bash', [ENABLE_SCRIPT, ...args], {
cwd: process.cwd(),
encoding: 'utf8',
env: {
...process.env,
...env,
},
});
}
function runPreflight(args, env = {}) {
return spawnSync(process.execPath, ['--', PREFLIGHT_SCRIPT, ...args], {
cwd: process.cwd(),
encoding: 'utf8',
env: {
...process.env,
...env,
},
});
}
function chmodExecutable(filePath) {
spawnSync('chmod', ['0755', filePath], {
cwd: process.cwd(),
encoding: 'utf8',
});
}
function createFakeCurrentReleaseAuditArgs(name) {
const auditScript = path.join(tmpRoot, `${name}.mjs`);
writeFileSync(
auditScript,
'#!/usr/bin/env node\nconsole.log("[fake-current-release-audit] OK");\n',
'utf8',
);
chmodExecutable(auditScript);
return [
'--current-release-audit-script',
auditScript,
'--current-release-root',
tmpRoot,
];
}
function createApplyPathSafetyFixture(name) {
const root = path.join(tmpRoot, name);
mkdirSync(root, { recursive: true });
const templatePath = path.join(root, 'template.conf');
const envPath = path.join(root, 'pingora-gateway.env');
const preflightScript = path.join(root, 'fake-preflight.mjs');
const directLiveScript = path.join(root, 'fake-direct-live.mjs');
const auditArgs = createFakeCurrentReleaseAuditArgs(`${name}-audit-ok`);
writeFileSync(
templatePath,
[
'[Service]',
'AmbientCapabilities=CAP_NET_BIND_SERVICE',
'CapabilityBoundingSet=CAP_NET_BIND_SERVICE',
'',
].join('\n'),
'utf8',
);
writeFileSync(
envPath,
'GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO=https\n',
'utf8',
);
writeFileSync(
preflightScript,
'#!/usr/bin/env node\nconsole.log("[fake-preflight] OK");\n',
'utf8',
);
writeFileSync(
directLiveScript,
'#!/usr/bin/env node\nconsole.log("[fake-direct-live] OK");\n',
'utf8',
);
chmodExecutable(preflightScript);
chmodExecutable(directLiveScript);
return {
root,
args: [
'--apply',
'--template-path',
templatePath,
...auditArgs,
'--preflight-script',
preflightScript,
'--preflight-env-file',
envPath,
'--preflight-check-cert-readable',
'--preflight-check-service-env-file',
'--preflight-check-service-user-cert-readable',
'--preflight-check-service-binary-executable',
'--preflight-check-ports-free',
'--direct-live-script',
directLiveScript,
'--direct-https-base-url',
'https://127.0.0.1',
'--direct-http-base-url',
'http://127.0.0.1',
'--direct-host',
'example.com',
'--direct-redirect-host',
'example.com',
'--direct-pingora-access-log',
'/var/log/genarrative/pingora-gateway.access.log',
'--direct-spacetime-database',
'genarrative-prod',
'--no-status',
],
};
}
function assertApplyFailure(result, needle, reason) {
if (result.status === 0) {
failures.push(reason);
return;
}
assertIncludes(result.stderr, needle, `${reason} 错误信息应明确。`);
}
function assertStatus(result, expected, reason) {
const actual = result.status ?? 0;
if (actual !== expected) {
failures.push(
`${reason} 预期退出码 ${expected},实际 ${actual}。\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`,
);
}
}
function assertIncludes(content, needle, reason) {
if (!content.includes(needle)) {
failures.push(`${reason} 缺少: ${needle}`);
}
}
function allocateLoopbackPorts(count) {
const ports = [];
for (let offset = 0; ports.length < count && offset < 1000; offset += 1) {
const candidate = 28080 + offset;
const result = spawnSync(
process.execPath,
[
'-e',
[
"const net = require('node:net');",
`const server = net.createServer();`,
`server.once('error', () => process.exit(1));`,
`server.listen(${candidate}, '127.0.0.1', () => server.close(() => process.exit(0)));`,
].join(''),
],
{
cwd: process.cwd(),
encoding: 'utf8',
},
);
if ((result.status ?? 1) === 0) {
ports.push(candidate);
}
}
if (ports.length !== count) {
throw new Error(
'无法为 Pingora direct enable 自测分配空闲 loopback 端口。',
);
}
return ports;
}