@mzzsfy/dsh-maintain 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/package.json +2 -3
- package/src/client.js +18 -14
- package/src/index.js +87 -80
- package/test/parity.test.mjs +12 -19
- package/test/restart-ready.test.mjs +7 -8
- package/test/route.test.mjs +125 -88
- package/test/upgrade.test.mjs +10 -20
- package/src/runtime.mjs +0 -69
- package/test/runtime.test.mjs +0 -67
package/src/runtime.mjs
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// 运行环境检测:判定宿主进程是否处于托管启动形态,决定升级后可否自动重启。
|
|
2
|
-
// 纯函数,探测点经参数注入;顺序:env 显式覆盖 > 进程环境表 > POSIX 容器探测 > 双 TTY > unknown。
|
|
3
|
-
import { stat, readFile } from 'node:fs/promises'
|
|
4
|
-
|
|
5
|
-
export const RUNTIME_KINDS = Object.freeze({
|
|
6
|
-
DECLARED_MANAGED: 'declared-managed',
|
|
7
|
-
PM2: 'pm2',
|
|
8
|
-
SYSTEMD: 'systemd',
|
|
9
|
-
SUPERVISORD: 'supervisord',
|
|
10
|
-
KUBERNETES: 'kubernetes',
|
|
11
|
-
DOCKER: 'docker',
|
|
12
|
-
CONTAINER: 'container',
|
|
13
|
-
MANUAL_START: 'manual-start-likely',
|
|
14
|
-
UNKNOWN: 'unknown',
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
// 环境覆盖键:managed|manual,测试与用户显式纠偏用
|
|
18
|
-
const RUNTIME_ENV_OVERRIDE = 'DSH_MAINTAIN_RUNTIME_ENV'
|
|
19
|
-
const OVERRIDE_MANAGED = 'managed'
|
|
20
|
-
const OVERRIDE_MANUAL = 'manual'
|
|
21
|
-
|
|
22
|
-
// 各托管形态的特征环境变量表:命中任一即声明
|
|
23
|
-
const MANAGED_ENV_KEYS = Object.freeze([
|
|
24
|
-
[RUNTIME_KINDS.PM2, Object.freeze(['pm_id', 'PM2_HOME', 'pm_uptime'])],
|
|
25
|
-
[RUNTIME_KINDS.SYSTEMD, Object.freeze(['INVOCATION_ID', 'JOURNAL_STREAM', 'NOTIFY_SOCKET'])],
|
|
26
|
-
[RUNTIME_KINDS.SUPERVISORD, Object.freeze(['SUPERVISOR_ENABLED', 'SUPERVISOR_PROCESS_NAME'])],
|
|
27
|
-
[RUNTIME_KINDS.KUBERNETES, Object.freeze(['KUBERNETES_SERVICE_HOST'])],
|
|
28
|
-
])
|
|
29
|
-
|
|
30
|
-
const DOCKERENV_PATH = '/.dockerenv'
|
|
31
|
-
const CGROUP_PATH = '/proc/1/cgroup'
|
|
32
|
-
// cgroup 归类:/docker/ 与 dockerenv 同为 docker;kubepods 即 k8s;其余容器运行时归 container
|
|
33
|
-
const CGROUP_DOCKER_PATTERN = /\/docker\//
|
|
34
|
-
const CGROUP_K8S_PATTERN = /kubepods/
|
|
35
|
-
const CGROUP_CONTAINER_PATTERN = /containerd|lxc|podman/
|
|
36
|
-
|
|
37
|
-
const fallbackExists = async (path) => {
|
|
38
|
-
try {
|
|
39
|
-
await stat(path)
|
|
40
|
-
return true
|
|
41
|
-
} catch {
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const fallbackRead = async (path) => readFile(path, 'utf8')
|
|
47
|
-
|
|
48
|
-
export async function detectRuntimeEnv({ env, platform, isTTY, existsImpl = fallbackExists, readFileImpl = fallbackRead }) {
|
|
49
|
-
const override = env[RUNTIME_ENV_OVERRIDE]
|
|
50
|
-
if (override === OVERRIDE_MANAGED) return { kind: RUNTIME_KINDS.DECLARED_MANAGED, declared: true }
|
|
51
|
-
if (override === OVERRIDE_MANUAL) return { kind: RUNTIME_KINDS.MANUAL_START, declared: false }
|
|
52
|
-
|
|
53
|
-
for (const [kind, keys] of MANAGED_ENV_KEYS) {
|
|
54
|
-
if (keys.some((key) => env[key])) return { kind, declared: true }
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (platform !== 'win32') {
|
|
58
|
-
try {
|
|
59
|
-
if (await existsImpl(DOCKERENV_PATH)) return { kind: RUNTIME_KINDS.DOCKER, declared: true }
|
|
60
|
-
const cgroup = await readFileImpl(CGROUP_PATH)
|
|
61
|
-
if (CGROUP_DOCKER_PATTERN.test(cgroup)) return { kind: RUNTIME_KINDS.DOCKER, declared: true }
|
|
62
|
-
if (CGROUP_K8S_PATTERN.test(cgroup)) return { kind: RUNTIME_KINDS.KUBERNETES, declared: true }
|
|
63
|
-
if (CGROUP_CONTAINER_PATTERN.test(cgroup)) return { kind: RUNTIME_KINDS.CONTAINER, declared: true }
|
|
64
|
-
} catch { /* 探测失败按未声明继续 */ }
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (isTTY.stdin === true && isTTY.stdout === true) return { kind: RUNTIME_KINDS.MANUAL_START, declared: false }
|
|
68
|
-
return { kind: RUNTIME_KINDS.UNKNOWN, declared: false }
|
|
69
|
-
}
|
package/test/runtime.test.mjs
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict'
|
|
2
|
-
import { test } from 'node:test'
|
|
3
|
-
import { detectRuntimeEnv, RUNTIME_KINDS } from '../src/runtime.mjs'
|
|
4
|
-
|
|
5
|
-
const POSIX = 'linux'
|
|
6
|
-
const WIN32 = 'win32'
|
|
7
|
-
const BOTH_TTY = { stdin: true, stdout: true }
|
|
8
|
-
const NO_TTY = { stdin: false, stdout: false }
|
|
9
|
-
const noProbes = { existsImpl: async () => false, readFileImpl: async () => { throw new Error('ENOENT') } }
|
|
10
|
-
|
|
11
|
-
test('环境检测:env 覆盖最高优先', async () => {
|
|
12
|
-
const managed = await detectRuntimeEnv({ env: { DSH_MAINTAIN_RUNTIME_ENV: 'managed', pm_id: '0' }, platform: POSIX, isTTY: BOTH_TTY, ...noProbes })
|
|
13
|
-
assert.deepEqual(managed, { kind: RUNTIME_KINDS.DECLARED_MANAGED, declared: true })
|
|
14
|
-
const manual = await detectRuntimeEnv({ env: { DSH_MAINTAIN_RUNTIME_ENV: 'manual', pm_id: '0' }, platform: POSIX, isTTY: NO_TTY, ...noProbes })
|
|
15
|
-
assert.deepEqual(manual, { kind: RUNTIME_KINDS.MANUAL_START, declared: false })
|
|
16
|
-
// 非法覆盖值不生效,继续走常规判定
|
|
17
|
-
const bogus = await detectRuntimeEnv({ env: { DSH_MAINTAIN_RUNTIME_ENV: 'sideways' }, platform: POSIX, isTTY: NO_TTY, ...noProbes })
|
|
18
|
-
assert.deepEqual(bogus, { kind: RUNTIME_KINDS.UNKNOWN, declared: false })
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
test('环境检测:env 表命中各托管形态', async () => {
|
|
22
|
-
const table = [
|
|
23
|
-
[{ pm_id: '0' }, RUNTIME_KINDS.PM2],
|
|
24
|
-
[{ PM2_HOME: 'C:/pm2' }, RUNTIME_KINDS.PM2],
|
|
25
|
-
[{ pm_uptime: '123' }, RUNTIME_KINDS.PM2],
|
|
26
|
-
[{ INVOCATION_ID: 'x' }, RUNTIME_KINDS.SYSTEMD],
|
|
27
|
-
[{ JOURNAL_STREAM: '9' }, RUNTIME_KINDS.SYSTEMD],
|
|
28
|
-
[{ NOTIFY_SOCKET: '/run/notify' }, RUNTIME_KINDS.SYSTEMD],
|
|
29
|
-
[{ SUPERVISOR_ENABLED: '1' }, RUNTIME_KINDS.SUPERVISORD],
|
|
30
|
-
[{ SUPERVISOR_PROCESS_NAME: 'dsh' }, RUNTIME_KINDS.SUPERVISORD],
|
|
31
|
-
[{ KUBERNETES_SERVICE_HOST: '10.0.0.1' }, RUNTIME_KINDS.KUBERNETES],
|
|
32
|
-
]
|
|
33
|
-
for (const [env, kind] of table) {
|
|
34
|
-
const result = await detectRuntimeEnv({ env, platform: POSIX, isTTY: NO_TTY, ...noProbes })
|
|
35
|
-
assert.deepEqual(result, { kind, declared: true }, JSON.stringify(env))
|
|
36
|
-
}
|
|
37
|
-
// 空串环境值不构成声明
|
|
38
|
-
const empty = await detectRuntimeEnv({ env: { pm_id: '' }, platform: POSIX, isTTY: NO_TTY, ...noProbes })
|
|
39
|
-
assert.deepEqual(empty, { kind: RUNTIME_KINDS.UNKNOWN, declared: false })
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
test('环境检测:POSIX 容器探测', async () => {
|
|
43
|
-
const docker = await detectRuntimeEnv({ env: {}, platform: POSIX, isTTY: NO_TTY, existsImpl: async (p) => p === '/.dockerenv', readFileImpl: async () => { throw new Error('ENOENT') } })
|
|
44
|
-
assert.deepEqual(docker, { kind: RUNTIME_KINDS.DOCKER, declared: true })
|
|
45
|
-
for (const [marker, kind] of [['/docker/', RUNTIME_KINDS.DOCKER], ['kubepods', RUNTIME_KINDS.KUBERNETES], ['containerd', RUNTIME_KINDS.CONTAINER], ['lxc', RUNTIME_KINDS.CONTAINER], ['podman', RUNTIME_KINDS.CONTAINER]]) {
|
|
46
|
-
const result = await detectRuntimeEnv({ env: {}, platform: POSIX, isTTY: NO_TTY, existsImpl: async () => false, readFileImpl: async () => '12:pids:/sys/fs/cgroup/' + marker + 'abc' })
|
|
47
|
-
assert.deepEqual(result, { kind, declared: true }, marker)
|
|
48
|
-
}
|
|
49
|
-
// 探测器异常不致命,继续后续判定
|
|
50
|
-
const broken = await detectRuntimeEnv({ env: {}, platform: POSIX, isTTY: NO_TTY, existsImpl: async () => { throw new Error('EACCES') }, readFileImpl: async () => { throw new Error('EACCES') } })
|
|
51
|
-
assert.deepEqual(broken, { kind: RUNTIME_KINDS.UNKNOWN, declared: false })
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test('环境检测:win32 跳过 POSIX 探测', async () => {
|
|
55
|
-
const win = await detectRuntimeEnv({ env: {}, platform: WIN32, isTTY: NO_TTY, existsImpl: async () => true, readFileImpl: async () => '/docker/xyz' })
|
|
56
|
-
assert.deepEqual(win, { kind: RUNTIME_KINDS.UNKNOWN, declared: false })
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
test('环境检测:双 TTY 判手动直跑,单 TTY 不构成', async () => {
|
|
60
|
-
const both = await detectRuntimeEnv({ env: {}, platform: POSIX, isTTY: BOTH_TTY, ...noProbes })
|
|
61
|
-
assert.deepEqual(both, { kind: RUNTIME_KINDS.MANUAL_START, declared: false })
|
|
62
|
-
const stdinOnly = await detectRuntimeEnv({ env: {}, platform: POSIX, isTTY: { stdin: true, stdout: false }, ...noProbes })
|
|
63
|
-
assert.deepEqual(stdinOnly, { kind: RUNTIME_KINDS.UNKNOWN, declared: false })
|
|
64
|
-
// 托管声明优先于 TTY
|
|
65
|
-
const declaredWithTty = await detectRuntimeEnv({ env: { pm_id: '0' }, platform: POSIX, isTTY: BOTH_TTY, ...noProbes })
|
|
66
|
-
assert.deepEqual(declaredWithTty, { kind: RUNTIME_KINDS.PM2, declared: true })
|
|
67
|
-
})
|