rollbridge 0.1.7 → 0.1.8
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/package.json +1 -1
- package/src/daemon.js +6 -4
- package/src/doctor.js +14 -5
- package/test/doctor.test.js +26 -4
package/package.json
CHANGED
package/src/daemon.js
CHANGED
|
@@ -821,7 +821,7 @@ export function releasesToPrune(releases, policy, now) {
|
|
|
821
821
|
}
|
|
822
822
|
|
|
823
823
|
/**
|
|
824
|
-
* @typedef {{alive: boolean, application?: string, activeReleaseId?: string | null}} ControlSocketInspection
|
|
824
|
+
* @typedef {{alive: boolean, application?: string, activeReleaseId?: string | null, proxy?: {host: string, port: number}}} ControlSocketInspection
|
|
825
825
|
*/
|
|
826
826
|
|
|
827
827
|
/**
|
|
@@ -874,7 +874,7 @@ export async function inspectControlSocket(socketPath, timeoutMs = 1000) {
|
|
|
874
874
|
|
|
875
875
|
const status = parseControlStatus(buffer.slice(0, newlineIndex))
|
|
876
876
|
|
|
877
|
-
finish(status ? {activeReleaseId: status.activeReleaseId, alive: true, application: status.application} : {alive: true})
|
|
877
|
+
finish(status ? {activeReleaseId: status.activeReleaseId, alive: true, application: status.application, proxy: status.proxy} : {alive: true})
|
|
878
878
|
})
|
|
879
879
|
socket.once("error", (error) => {
|
|
880
880
|
if (settled) return
|
|
@@ -896,7 +896,7 @@ export async function inspectControlSocket(socketPath, timeoutMs = 1000) {
|
|
|
896
896
|
/**
|
|
897
897
|
* Parses a control status response line into a Rollbridge identity, if it is one.
|
|
898
898
|
* @param {string} line - JSON response line.
|
|
899
|
-
* @returns {{application: string, activeReleaseId: string | null} | undefined} Identity, or undefined when unrecognized.
|
|
899
|
+
* @returns {{application: string, activeReleaseId: string | null, proxy: {host: string, port: number} | undefined} | undefined} Identity, or undefined when unrecognized.
|
|
900
900
|
*/
|
|
901
901
|
function parseControlStatus(line) {
|
|
902
902
|
/** @type {JsonValue} */
|
|
@@ -911,5 +911,7 @@ function parseControlStatus(line) {
|
|
|
911
911
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined
|
|
912
912
|
if (typeof parsed.application !== "string") return undefined
|
|
913
913
|
|
|
914
|
-
|
|
914
|
+
const proxy = "proxy" in parsed && parsed.proxy && typeof parsed.proxy === "object" && !Array.isArray(parsed.proxy) && typeof parsed.proxy.host === "string" && typeof parsed.proxy.port === "number" ? {host: parsed.proxy.host, port: parsed.proxy.port} : undefined
|
|
915
|
+
|
|
916
|
+
return {activeReleaseId: typeof parsed.activeReleaseId === "string" ? parsed.activeReleaseId : null, application: parsed.application, proxy}
|
|
915
917
|
}
|
package/src/doctor.js
CHANGED
|
@@ -26,7 +26,7 @@ export async function runEnvironmentChecks(config) {
|
|
|
26
26
|
|
|
27
27
|
checks.push(controlSocketCheck(config, socketInspection))
|
|
28
28
|
checks.push(await controlSocketDirectoryCheck(config))
|
|
29
|
-
checks.push(await proxyPortCheck(config))
|
|
29
|
+
checks.push(await proxyPortCheck(config, socketInspection))
|
|
30
30
|
|
|
31
31
|
if (config.statePath !== undefined) {
|
|
32
32
|
// A live daemon persists its own (live) pids into the state file, so they are not orphans.
|
|
@@ -79,7 +79,7 @@ async function orphanCheck(statePath, daemonRunning) {
|
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* @param {string} socketPath - Control socket path.
|
|
82
|
-
* @returns {Promise<
|
|
82
|
+
* @returns {Promise<import("./daemon.js").ControlSocketInspection | {error: string}>} Probe result, or the probe error.
|
|
83
83
|
*/
|
|
84
84
|
async function inspectControlSocketSafely(socketPath) {
|
|
85
85
|
try {
|
|
@@ -91,7 +91,7 @@ async function inspectControlSocketSafely(socketPath) {
|
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* @param {import("./config.js").RollbridgeConfig} config - Normalized config.
|
|
94
|
-
* @param {
|
|
94
|
+
* @param {import("./daemon.js").ControlSocketInspection | {error: string}} inspection - Control socket probe result.
|
|
95
95
|
* @returns {DoctorCheck} Control socket reachability check.
|
|
96
96
|
*/
|
|
97
97
|
function controlSocketCheck(config, inspection) {
|
|
@@ -109,6 +109,10 @@ function controlSocketCheck(config, inspection) {
|
|
|
109
109
|
return {detail: `another process is already listening on ${socketPath}; the daemon would fail to bind it`, name: "control socket", ok: false}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
if (inspection.application === config.application) {
|
|
113
|
+
return {detail: `Rollbridge daemon for "${inspection.application}" is running on ${socketPath}`, name: "control socket", ok: true}
|
|
114
|
+
}
|
|
115
|
+
|
|
112
116
|
return {detail: `a Rollbridge daemon for "${inspection.application}" is already running on ${socketPath}; stop it before starting another`, name: "control socket", ok: false}
|
|
113
117
|
}
|
|
114
118
|
|
|
@@ -131,9 +135,10 @@ async function controlSocketDirectoryCheck(config) {
|
|
|
131
135
|
|
|
132
136
|
/**
|
|
133
137
|
* @param {import("./config.js").RollbridgeConfig} config - Normalized config.
|
|
134
|
-
* @
|
|
138
|
+
* @param {import("./daemon.js").ControlSocketInspection | {error: string}} inspection - Control socket probe result.
|
|
139
|
+
* @returns {Promise<DoctorCheck>} Whether the proxy port can be bound or is owned by the running daemon.
|
|
135
140
|
*/
|
|
136
|
-
async function proxyPortCheck(config) {
|
|
141
|
+
async function proxyPortCheck(config, inspection) {
|
|
137
142
|
const address = `${config.proxy.host}:${config.proxy.port}`
|
|
138
143
|
const bind = await canBindPort(config.proxy.host, config.proxy.port)
|
|
139
144
|
|
|
@@ -141,6 +146,10 @@ async function proxyPortCheck(config) {
|
|
|
141
146
|
return {detail: `${address} is available`, name: "proxy port", ok: true}
|
|
142
147
|
}
|
|
143
148
|
|
|
149
|
+
if (!("error" in inspection) && inspection.application === config.application && inspection.proxy?.host === config.proxy.host && inspection.proxy.port === config.proxy.port) {
|
|
150
|
+
return {detail: `${address} is already held by the running Rollbridge daemon`, name: "proxy port", ok: true}
|
|
151
|
+
}
|
|
152
|
+
|
|
144
153
|
return {detail: `${address} is unavailable (${bind.code})`, name: "proxy port", ok: false}
|
|
145
154
|
}
|
|
146
155
|
|
package/test/doctor.test.js
CHANGED
|
@@ -188,7 +188,7 @@ test("runEnvironmentChecks reports a missing control socket directory", async ()
|
|
|
188
188
|
assert.match(directoryCheck.detail, /missing or not writable/)
|
|
189
189
|
})
|
|
190
190
|
|
|
191
|
-
test("runEnvironmentChecks
|
|
191
|
+
test("runEnvironmentChecks passes when the running Rollbridge daemon holds the socket and port", async () => {
|
|
192
192
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), "rollbridge-doctor-"))
|
|
193
193
|
const config = buildConfig({controlPath: path.join(root, "rollbridge.sock"), proxyPort: await freePort()})
|
|
194
194
|
const daemon = new RollbridgeDaemon({config, logger: () => {}})
|
|
@@ -199,11 +199,33 @@ test("runEnvironmentChecks fails when a Rollbridge daemon already holds the sock
|
|
|
199
199
|
const checks = await runEnvironmentChecks(config)
|
|
200
200
|
const socketCheck = checkNamed(checks, "control socket")
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
assert.
|
|
204
|
-
assert.
|
|
202
|
+
assert.equal(socketCheck.ok, true)
|
|
203
|
+
assert.match(socketCheck.detail, /Rollbridge daemon for "doctor-test" is running/)
|
|
204
|
+
assert.equal(checkNamed(checks, "proxy port").ok, true)
|
|
205
|
+
} finally {
|
|
206
|
+
await daemon.shutdown()
|
|
207
|
+
await fs.rm(root, {force: true, recursive: true})
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test("runEnvironmentChecks fails when the running daemon does not own the configured proxy port", async () => {
|
|
212
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), "rollbridge-doctor-"))
|
|
213
|
+
const config = buildConfig({controlPath: path.join(root, "rollbridge.sock"), proxyPort: await freePort()})
|
|
214
|
+
const changedConfigPort = await freePort()
|
|
215
|
+
const changedConfig = buildConfig({controlPath: config.control.path, proxyPort: changedConfigPort})
|
|
216
|
+
const server = net.createServer()
|
|
217
|
+
const daemon = new RollbridgeDaemon({config, logger: () => {}})
|
|
218
|
+
|
|
219
|
+
await daemon.start()
|
|
220
|
+
await new Promise((resolve) => server.listen(changedConfigPort, "127.0.0.1", () => resolve(undefined)))
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const checks = await runEnvironmentChecks(changedConfig)
|
|
224
|
+
|
|
225
|
+
assert.equal(checkNamed(checks, "control socket").ok, true)
|
|
205
226
|
assert.equal(checkNamed(checks, "proxy port").ok, false)
|
|
206
227
|
} finally {
|
|
228
|
+
await new Promise((resolve) => server.close(() => resolve(undefined)))
|
|
207
229
|
await daemon.shutdown()
|
|
208
230
|
await fs.rm(root, {force: true, recursive: true})
|
|
209
231
|
}
|