openship 0.4.6 → 0.4.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/dist/{bare-2HYDXQEH.js → bare-JY5Y5ELU.js} +1 -1
- package/dist/{chunk-FQGJZ2HS.js → chunk-2D3I5G6P.js} +53 -0
- package/dist/{chunk-5FNIWBFI.js → chunk-5NYUCONN.js} +1 -1
- package/dist/{chunk-3JQTL4R4.js → chunk-BBHX7T4R.js} +53 -23
- package/dist/{chunk-C5OHTYEF.js → chunk-GKHUSZCD.js} +1 -1
- package/dist/{chunk-2ZLR7ULL.js → chunk-SJIFLLX4.js} +144 -20
- package/dist/{chunk-SQ77DZOI.js → chunk-SLTYVHU7.js} +63 -24
- package/dist/{chunk-4TJC4JZQ.js → chunk-XPPZ7B2J.js} +21 -2
- package/dist/{cloud-ZSKOS4GY.js → cloud-KYWY3RP4.js} +4 -4
- package/dist/{detect-SH4FHFBI.js → detect-AZLOHUFX.js} +11 -1
- package/dist/{docker-U7AF5PTA.js → docker-LODK5Y2S.js} +1 -1
- package/dist/{edge-import-V5CBA47C.js → edge-import-ABUDKKJI.js} +7 -7
- package/dist/{ensure-container-edge-CMYROUSI.js → ensure-container-edge-SW6HENSG.js} +2 -4
- package/dist/{executor-YPT6RH6O.js → executor-XJ6RP4Q2.js} +1 -1
- package/dist/index.js +75 -19
- package/dist/{nginx-2GTRUIDE.js → nginx-3L4SIBJA.js} +1 -1
- package/dist/server/index.js +274 -66
- package/dist/{setup-HQIZLUOJ.js → setup-NU6T2PDU.js} +3 -3
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import "./chunk-MZ2WEHHY.js";
|
|
10
10
|
import "./chunk-L2VOGB2X.js";
|
|
11
11
|
import "./chunk-4Z5QO7N6.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-XPPZ7B2J.js";
|
|
13
13
|
import "./chunk-OXWDY22G.js";
|
|
14
14
|
import "./chunk-RPHRPFEH.js";
|
|
15
15
|
import "./chunk-TYVME3XD.js";
|
|
@@ -59,6 +59,54 @@ function sq(value) {
|
|
|
59
59
|
function isOurEdgeContainer(name, image) {
|
|
60
60
|
return /openship-edge/i.test(`${name ?? ""} ${image ?? ""}`);
|
|
61
61
|
}
|
|
62
|
+
function vhostLog(message, level = "info") {
|
|
63
|
+
return { timestamp: (/* @__PURE__ */ new Date()).toISOString(), message, level };
|
|
64
|
+
}
|
|
65
|
+
async function sanitizeEdgeVhosts(executor, sitesDir, onLog) {
|
|
66
|
+
const script = [
|
|
67
|
+
`for f in ${sq(sitesDir)}/*.conf; do`,
|
|
68
|
+
` [ -f "$f" ] || continue;`,
|
|
69
|
+
// A real server_name is anything that isn't the `_` wildcard.
|
|
70
|
+
` if ! grep -qE '^[[:space:]]*server_name[[:space:]]+[^_;[:space:]]' "$f"; then`,
|
|
71
|
+
` echo "dropped-catchall $f"; rm -f "$f"; continue;`,
|
|
72
|
+
` fi;`,
|
|
73
|
+
` if grep -qE '[[:space:]]default_server' "$f"; then`,
|
|
74
|
+
` sed -E 's/([[:space:]]listen[^;]*)[[:space:]]+default_server/\\1/g' "$f" > "$f.osh" && mv "$f.osh" "$f" && echo "unset-default $f";`,
|
|
75
|
+
` fi;`,
|
|
76
|
+
`done`
|
|
77
|
+
].join(" ");
|
|
78
|
+
const out = await executor.exec(script).catch(() => "");
|
|
79
|
+
for (const line of out.split("\n").map((l) => l.trim()).filter(Boolean)) {
|
|
80
|
+
const [action, file] = [line.slice(0, line.indexOf(" ")), line.slice(line.indexOf(" ") + 1)];
|
|
81
|
+
if (action === "dropped-catchall") {
|
|
82
|
+
onLog(vhostLog(`Dropped catch-all vhost ${file} \u2014 the edge image provides it.`, "warn"));
|
|
83
|
+
} else if (action === "unset-default") {
|
|
84
|
+
onLog(vhostLog(`Removed default_server from ${file} \u2014 the edge image owns it.`, "warn"));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function edgeIsBroken(executor) {
|
|
89
|
+
const status = await tryExec(
|
|
90
|
+
executor,
|
|
91
|
+
`docker inspect -f '{{.State.Status}}' ${sq(EDGE_CONTAINER_NAME)} 2>/dev/null`
|
|
92
|
+
);
|
|
93
|
+
const state = (status ?? "").trim();
|
|
94
|
+
return state === "restarting" || state === "exited" || state === "dead";
|
|
95
|
+
}
|
|
96
|
+
async function edgeCrashReason(executor) {
|
|
97
|
+
const logs = await tryExec(
|
|
98
|
+
executor,
|
|
99
|
+
`docker logs --tail 40 ${sq(EDGE_CONTAINER_NAME)} 2>&1`
|
|
100
|
+
);
|
|
101
|
+
return logs ? edgeFailureReason(logs) : null;
|
|
102
|
+
}
|
|
103
|
+
function edgeFailureReason(containerLog) {
|
|
104
|
+
const lines = containerLog.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
105
|
+
const emerg = lines.find((l) => l.includes("[emerg]"));
|
|
106
|
+
if (emerg) return emerg.replace(/^.*\[emerg\]\s*\d*#\d*:\s*/, "");
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
var EDGE_CONTAINER_NAME = "openship-edge";
|
|
62
110
|
async function ourEdgeContainerRunning(executor) {
|
|
63
111
|
return Boolean(await resolveOurEdgeContainer(executor));
|
|
64
112
|
}
|
|
@@ -238,6 +286,11 @@ export {
|
|
|
238
286
|
classifyProxy,
|
|
239
287
|
sq,
|
|
240
288
|
isOurEdgeContainer,
|
|
289
|
+
sanitizeEdgeVhosts,
|
|
290
|
+
edgeIsBroken,
|
|
291
|
+
edgeCrashReason,
|
|
292
|
+
edgeFailureReason,
|
|
293
|
+
EDGE_CONTAINER_NAME,
|
|
241
294
|
ourEdgeContainerRunning,
|
|
242
295
|
invalidateEdgeContainer,
|
|
243
296
|
resolveOurEdgeContainer,
|
|
@@ -101,10 +101,10 @@ import {
|
|
|
101
101
|
} from "./chunk-W73X43R7.js";
|
|
102
102
|
import {
|
|
103
103
|
CloudRuntime
|
|
104
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-5NYUCONN.js";
|
|
105
105
|
import {
|
|
106
106
|
systemCatalog
|
|
107
|
-
} from "./chunk-
|
|
107
|
+
} from "./chunk-SLTYVHU7.js";
|
|
108
108
|
import {
|
|
109
109
|
AwsCrc32,
|
|
110
110
|
__awaiter,
|
|
@@ -139,13 +139,19 @@ import {
|
|
|
139
139
|
DockerRuntime,
|
|
140
140
|
isHostPathSource,
|
|
141
141
|
scopedVolumeName
|
|
142
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-SJIFLLX4.js";
|
|
143
143
|
import {
|
|
144
144
|
BareRuntime
|
|
145
145
|
} from "./chunk-VDMQJHPI.js";
|
|
146
146
|
import {
|
|
147
|
-
|
|
148
|
-
} from "./chunk-
|
|
147
|
+
LocalExecutor
|
|
148
|
+
} from "./chunk-TYVME3XD.js";
|
|
149
|
+
import {
|
|
150
|
+
EDGE_CONTAINER_NAME,
|
|
151
|
+
edgeCrashReason,
|
|
152
|
+
invalidateEdgeContainer,
|
|
153
|
+
sanitizeEdgeVhosts
|
|
154
|
+
} from "./chunk-2D3I5G6P.js";
|
|
149
155
|
import {
|
|
150
156
|
EDGE_CONTAINER_MOUNTS,
|
|
151
157
|
EDGE_HOST_STATE_DIR
|
|
@@ -159,13 +165,6 @@ import {
|
|
|
159
165
|
import chalk from "chalk";
|
|
160
166
|
import ora from "ora";
|
|
161
167
|
|
|
162
|
-
// src/lib/compose.ts
|
|
163
|
-
import { spawnSync as spawnSync3 } from "child_process";
|
|
164
|
-
import { randomBytes as randomBytes5 } from "crypto";
|
|
165
|
-
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
166
|
-
import { homedir as homedir2, userInfo } from "os";
|
|
167
|
-
import { join as join6 } from "path";
|
|
168
|
-
|
|
169
168
|
// ../../packages/adapters/src/runtime/image-transfer.ts
|
|
170
169
|
import { Transform } from "stream";
|
|
171
170
|
|
|
@@ -12668,6 +12667,13 @@ var SftpDestinationImpl = class {
|
|
|
12668
12667
|
registerDestination("sftp", (row) => new SftpDestinationImpl(row));
|
|
12669
12668
|
registerDestination("openship_server", (row) => new SftpDestinationImpl(row));
|
|
12670
12669
|
|
|
12670
|
+
// src/lib/compose.ts
|
|
12671
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
12672
|
+
import { randomBytes as randomBytes5 } from "crypto";
|
|
12673
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
12674
|
+
import { homedir as homedir2, userInfo } from "os";
|
|
12675
|
+
import { join as join6 } from "path";
|
|
12676
|
+
|
|
12671
12677
|
// src/lib/paths.ts
|
|
12672
12678
|
import { homedir } from "os";
|
|
12673
12679
|
import { join as join3, resolve as resolve2 } from "path";
|
|
@@ -13229,7 +13235,7 @@ function renderEnv(opts, host, cfg) {
|
|
|
13229
13235
|
// (hides the local row). Written explicitly so the policy is visible in .env.
|
|
13230
13236
|
`OPENSHIP_HOST_CONTROL=${cfg.hostControl ? "true" : "false"}`,
|
|
13231
13237
|
`OPENSHIP_IMAGE_REGISTRY=${cfg.registry}`,
|
|
13232
|
-
`OPENSHIP_VERSION=${opts.version || (true ? "0.4.
|
|
13238
|
+
`OPENSHIP_VERSION=${opts.version || (true ? "0.4.8" : "latest")}`,
|
|
13233
13239
|
`POSTGRES_PASSWORD=${keepSecret(prev, "POSTGRES_PASSWORD")}`,
|
|
13234
13240
|
`BETTER_AUTH_SECRET=${keepSecret(prev, "BETTER_AUTH_SECRET")}`,
|
|
13235
13241
|
`INTERNAL_TOKEN=${keepSecret(prev, "INTERNAL_TOKEN")}`,
|
|
@@ -13302,7 +13308,14 @@ function compose(args, opts) {
|
|
|
13302
13308
|
});
|
|
13303
13309
|
return r2.status ?? 1;
|
|
13304
13310
|
}
|
|
13305
|
-
function
|
|
13311
|
+
function composePrefetch(opts) {
|
|
13312
|
+
const { buildDir } = materialize(opts);
|
|
13313
|
+
if (buildDir) {
|
|
13314
|
+
return compose(["pull", "postgres", "redis"], { withBuildOverride: true }) === 0 && compose(["build"], { withBuildOverride: true }) === 0;
|
|
13315
|
+
}
|
|
13316
|
+
return compose(["pull"]) === 0;
|
|
13317
|
+
}
|
|
13318
|
+
async function composeUp(opts) {
|
|
13306
13319
|
const { buildDir, cfg, envChanged } = materialize(opts);
|
|
13307
13320
|
const apiPort = cfg.apiPort;
|
|
13308
13321
|
const dashPort = cfg.dashPort;
|
|
@@ -13318,12 +13331,20 @@ function composeUp(opts) {
|
|
|
13318
13331
|
if (dbVolumeExists(project)) {
|
|
13319
13332
|
reconcileDbPassword(env.POSTGRES_USER || "openship", env.POSTGRES_PASSWORD ?? "");
|
|
13320
13333
|
}
|
|
13334
|
+
await sanitizeEdgeVhosts(
|
|
13335
|
+
new LocalExecutor(),
|
|
13336
|
+
EDGE_SITES_HOST_DIR,
|
|
13337
|
+
(l2) => console.log(` ${l2.message}`)
|
|
13338
|
+
).catch(() => {
|
|
13339
|
+
});
|
|
13321
13340
|
if (buildDir) {
|
|
13322
|
-
if (
|
|
13323
|
-
|
|
13324
|
-
|
|
13325
|
-
|
|
13326
|
-
|
|
13341
|
+
if (!opts.alreadyFetched) {
|
|
13342
|
+
if (compose(["pull", "postgres", "redis"], { withBuildOverride: true }) !== 0) {
|
|
13343
|
+
return { ok: false, apiPort, dashPort };
|
|
13344
|
+
}
|
|
13345
|
+
if (compose(["build"], { withBuildOverride: true }) !== 0) {
|
|
13346
|
+
return { ok: false, apiPort, dashPort };
|
|
13347
|
+
}
|
|
13327
13348
|
}
|
|
13328
13349
|
if (up({ withBuildOverride: true }) !== 0) {
|
|
13329
13350
|
return { ok: false, apiPort, dashPort };
|
|
@@ -13332,7 +13353,9 @@ function composeUp(opts) {
|
|
|
13332
13353
|
writeInstallMethod("compose");
|
|
13333
13354
|
return { ok: true, apiPort, dashPort };
|
|
13334
13355
|
}
|
|
13335
|
-
if (compose(["pull"]) !== 0)
|
|
13356
|
+
if (!opts.alreadyFetched && compose(["pull"]) !== 0) {
|
|
13357
|
+
return { ok: false, apiPort, dashPort };
|
|
13358
|
+
}
|
|
13336
13359
|
if (up() !== 0) return { ok: false, apiPort, dashPort };
|
|
13337
13360
|
onEdgeContainerChanged();
|
|
13338
13361
|
writeInstallMethod("compose");
|
|
@@ -13361,9 +13384,9 @@ function composeUninstall(opts = {}) {
|
|
|
13361
13384
|
}
|
|
13362
13385
|
return { ok, removedImages };
|
|
13363
13386
|
}
|
|
13364
|
-
function composeUpdate(version) {
|
|
13387
|
+
async function composeUpdate(version) {
|
|
13365
13388
|
if (!existsSync3(COMPOSE_FILE)) return false;
|
|
13366
|
-
return composeUp(version ? { version } : {}).ok;
|
|
13389
|
+
return (await composeUp(version ? { version } : {})).ok;
|
|
13367
13390
|
}
|
|
13368
13391
|
function composePs() {
|
|
13369
13392
|
return compose(["ps"]);
|
|
@@ -13435,11 +13458,17 @@ async function importMigratedSites(apiPort, sites, certPems) {
|
|
|
13435
13458
|
`Migrated NONE of the ${sites.length} site${sites.length === 1 ? "" : "s"} \u2014 Openship holds :80/:443 and ${sites.length === 1 ? "that hostname is" : "those hostnames are"} NOT being served.`
|
|
13436
13459
|
);
|
|
13437
13460
|
for (const w2 of warnings.slice(0, 8)) console.log(chalk.red(` \u2022 ${w2}`));
|
|
13461
|
+
const edgeLog = await edgeCrashReason(new LocalExecutor());
|
|
13462
|
+
if (edgeLog) {
|
|
13463
|
+
console.log(chalk.red(`
|
|
13464
|
+
The edge container is not running. Its log says:`));
|
|
13465
|
+
console.log(chalk.red(` ${edgeLog}`));
|
|
13466
|
+
}
|
|
13438
13467
|
console.log(
|
|
13439
13468
|
chalk.yellow(
|
|
13440
13469
|
`
|
|
13441
13470
|
Retry the import with \`openship up\` once the cause above is fixed, or put your
|
|
13442
|
-
previous proxy back: docker stop
|
|
13471
|
+
previous proxy back: docker stop ${EDGE_CONTAINER_NAME} && sudo systemctl enable --now nginx
|
|
13443
13472
|
`
|
|
13444
13473
|
)
|
|
13445
13474
|
);
|
|
@@ -13477,6 +13506,7 @@ export {
|
|
|
13477
13506
|
composeIsViableDefault,
|
|
13478
13507
|
ensureDocker,
|
|
13479
13508
|
sourceBuildDir,
|
|
13509
|
+
composePrefetch,
|
|
13480
13510
|
composeUp,
|
|
13481
13511
|
composeDown,
|
|
13482
13512
|
composeUninstall,
|
|
@@ -207,6 +207,8 @@ var DEFAULT_REMOTE_DOCKER_SOCKET_PATH = "/var/run/docker.sock";
|
|
|
207
207
|
var resolvedDockerSocketPathCache = /* @__PURE__ */ new WeakMap();
|
|
208
208
|
var DOCKER_DIAL_STDIO_COMMAND = "docker system dial-stdio";
|
|
209
209
|
var STREAMLOCAL_PROBE_TIMEOUT_MS = 8e3;
|
|
210
|
+
var STREAMLOCAL_DATA_VERIFY_TIMEOUT_MS = 3e3;
|
|
211
|
+
var VERIFY_BUFFER_CAP_BYTES = 8 * 1024 * 1024;
|
|
210
212
|
function toSshConfig(opts) {
|
|
211
213
|
return {
|
|
212
214
|
host: opts.host ?? "",
|
|
@@ -304,13 +306,107 @@ async function openStreamlocalUpstream(opts) {
|
|
|
304
306
|
throw error;
|
|
305
307
|
}
|
|
306
308
|
}
|
|
309
|
+
function pipeThrough(client, upstream) {
|
|
310
|
+
const teardown = () => {
|
|
311
|
+
client.destroy();
|
|
312
|
+
upstream.destroy();
|
|
313
|
+
};
|
|
314
|
+
client.on("error", teardown);
|
|
315
|
+
upstream.on("error", teardown);
|
|
316
|
+
client.once("close", () => upstream.destroy());
|
|
317
|
+
upstream.once("close", () => client.destroy());
|
|
318
|
+
client.pipe(upstream);
|
|
319
|
+
upstream.pipe(client);
|
|
320
|
+
}
|
|
321
|
+
function awaitFirstByte(upstream, ms) {
|
|
322
|
+
return new Promise((resolve) => {
|
|
323
|
+
const finish = (chunk) => {
|
|
324
|
+
clearTimeout(timer);
|
|
325
|
+
upstream.removeListener("data", onData);
|
|
326
|
+
upstream.removeListener("error", onFail);
|
|
327
|
+
upstream.removeListener("close", onFail);
|
|
328
|
+
resolve(chunk);
|
|
329
|
+
};
|
|
330
|
+
const onData = (chunk) => finish(chunk);
|
|
331
|
+
const onFail = () => finish(null);
|
|
332
|
+
const timer = setTimeout(() => finish(null), ms);
|
|
333
|
+
upstream.on("data", onData);
|
|
334
|
+
upstream.once("error", onFail);
|
|
335
|
+
upstream.once("close", onFail);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
function captureClient(client) {
|
|
339
|
+
const chunks = [];
|
|
340
|
+
let bytes = 0;
|
|
341
|
+
let overflowed = false;
|
|
342
|
+
let closed = false;
|
|
343
|
+
let live = null;
|
|
344
|
+
const onData = (chunk) => {
|
|
345
|
+
if (live) live.write(chunk);
|
|
346
|
+
if (overflowed) return;
|
|
347
|
+
chunks.push(chunk);
|
|
348
|
+
bytes += chunk.length;
|
|
349
|
+
if (bytes >= VERIFY_BUFFER_CAP_BYTES) {
|
|
350
|
+
overflowed = true;
|
|
351
|
+
client.pause();
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
const onGone = () => {
|
|
355
|
+
closed = true;
|
|
356
|
+
};
|
|
357
|
+
client.on("data", onData);
|
|
358
|
+
client.once("error", onGone);
|
|
359
|
+
client.once("close", onGone);
|
|
360
|
+
const detach = () => {
|
|
361
|
+
client.removeListener("data", onData);
|
|
362
|
+
client.removeListener("error", onGone);
|
|
363
|
+
client.removeListener("close", onGone);
|
|
364
|
+
};
|
|
365
|
+
return {
|
|
366
|
+
get closed() {
|
|
367
|
+
return closed;
|
|
368
|
+
},
|
|
369
|
+
get overflowed() {
|
|
370
|
+
return overflowed;
|
|
371
|
+
},
|
|
372
|
+
/** Replay everything buffered so far onto `upstream` and forward the rest
|
|
373
|
+
* live. Safe to call again on a different upstream (the fallback): the full
|
|
374
|
+
* buffer is retained until `commit`, so the replay is complete each time. */
|
|
375
|
+
feed(upstream) {
|
|
376
|
+
for (const chunk of chunks) upstream.write(chunk);
|
|
377
|
+
live = upstream;
|
|
378
|
+
},
|
|
379
|
+
/** Stop forwarding to the current upstream (keep buffering) — used before
|
|
380
|
+
* destroying a dead channel so late bytes never hit a torn-down stream. */
|
|
381
|
+
stop() {
|
|
382
|
+
live = null;
|
|
383
|
+
},
|
|
384
|
+
/** Terminal: detach and hand the socket to the (already-fed) `upstream` via a
|
|
385
|
+
* bidi pipe, or destroy it if the client already went away. */
|
|
386
|
+
commit(upstream) {
|
|
387
|
+
detach();
|
|
388
|
+
chunks.length = 0;
|
|
389
|
+
if (closed) {
|
|
390
|
+
upstream.destroy();
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
pipeThrough(client, upstream);
|
|
394
|
+
},
|
|
395
|
+
/** Terminal: no usable upstream — surface the error to dockerode. */
|
|
396
|
+
fail(err) {
|
|
397
|
+
detach();
|
|
398
|
+
client.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
307
402
|
function createDockerSshBridge(opts) {
|
|
308
403
|
const clients = /* @__PURE__ */ new Set();
|
|
309
404
|
let upstreamMode = null;
|
|
310
405
|
let modeDecision = null;
|
|
311
406
|
let dialClient = null;
|
|
312
|
-
|
|
313
|
-
|
|
407
|
+
let pooledUnreliable = false;
|
|
408
|
+
const openDialStdioUpstream = async (forceEphemeral = false) => {
|
|
409
|
+
if (!forceEphemeral && opts.executor?.openDockerDialStdio) {
|
|
314
410
|
const stream = await opts.executor.openDockerDialStdio();
|
|
315
411
|
stream.once(
|
|
316
412
|
"error",
|
|
@@ -373,29 +469,57 @@ function createDockerSshBridge(opts) {
|
|
|
373
469
|
modeDecision ??= decideMode().then((mode) => upstreamMode = mode);
|
|
374
470
|
return modeDecision;
|
|
375
471
|
};
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
472
|
+
const downgradeToDialStdio = async (capture, reason) => {
|
|
473
|
+
console.warn(
|
|
474
|
+
`[docker-ssh] streamlocal unusable (${opts.host ?? "?"}): ${reason} \u2014 downgrading this bridge to dial-stdio (fresh connection) and replaying the buffered request.`
|
|
475
|
+
);
|
|
476
|
+
upstreamMode = "dialstdio";
|
|
477
|
+
pooledUnreliable = true;
|
|
478
|
+
const upstream = await openDialStdioUpstream(true);
|
|
479
|
+
capture.feed(upstream);
|
|
480
|
+
capture.commit(upstream);
|
|
481
|
+
};
|
|
482
|
+
const bridgeClient = async (client) => {
|
|
483
|
+
const capture = captureClient(client);
|
|
484
|
+
try {
|
|
485
|
+
const mode = await ensureMode();
|
|
486
|
+
if (mode === "dialstdio") {
|
|
487
|
+
const upstream = await openDialStdioUpstream(pooledUnreliable);
|
|
488
|
+
capture.feed(upstream);
|
|
489
|
+
capture.commit(upstream);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
let channel;
|
|
493
|
+
try {
|
|
494
|
+
channel = await withTimeout(
|
|
495
|
+
openStreamlocalUpstream(opts),
|
|
496
|
+
STREAMLOCAL_DATA_VERIFY_TIMEOUT_MS,
|
|
497
|
+
`channel open timed out after ${STREAMLOCAL_DATA_VERIFY_TIMEOUT_MS / 1e3}s`
|
|
498
|
+
);
|
|
499
|
+
} catch (err) {
|
|
500
|
+
await downgradeToDialStdio(capture, safeErrorMessage(err));
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
capture.feed(channel);
|
|
504
|
+
const firstByte = capture.overflowed ? null : await awaitFirstByte(channel, STREAMLOCAL_DATA_VERIFY_TIMEOUT_MS);
|
|
505
|
+
if (capture.overflowed || firstByte) {
|
|
506
|
+
if (firstByte) client.write(firstByte);
|
|
507
|
+
capture.commit(channel);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
capture.stop();
|
|
511
|
+
channel.destroy();
|
|
512
|
+
await downgradeToDialStdio(capture, "channel opened but no data flowed");
|
|
513
|
+
} catch (err) {
|
|
514
|
+
console.warn(`[docker-ssh] bridge client failed (${opts.host ?? "?"}): ${safeErrorMessage(err)}`);
|
|
515
|
+
capture.fail(err);
|
|
516
|
+
}
|
|
379
517
|
};
|
|
380
518
|
const server = net2.createServer((client) => {
|
|
381
519
|
clients.add(client);
|
|
382
520
|
client.setNoDelay(true);
|
|
383
521
|
client.once("close", () => clients.delete(client));
|
|
384
|
-
|
|
385
|
-
const teardown = () => {
|
|
386
|
-
client.destroy();
|
|
387
|
-
upstream.destroy();
|
|
388
|
-
};
|
|
389
|
-
client.on("error", teardown);
|
|
390
|
-
upstream.on("error", teardown);
|
|
391
|
-
client.once("close", () => upstream.destroy());
|
|
392
|
-
upstream.once("close", () => client.destroy());
|
|
393
|
-
client.pipe(upstream);
|
|
394
|
-
upstream.pipe(client);
|
|
395
|
-
}).catch((error) => {
|
|
396
|
-
console.warn(`[docker-ssh] bridge upstream open failed: ${safeErrorMessage(error)}`);
|
|
397
|
-
client.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
398
|
-
});
|
|
522
|
+
void bridgeClient(client);
|
|
399
523
|
});
|
|
400
524
|
return {
|
|
401
525
|
start: () => new Promise((resolve, reject) => {
|
|
@@ -22,16 +22,19 @@ import {
|
|
|
22
22
|
sq
|
|
23
23
|
} from "./chunk-FPRHYBY2.js";
|
|
24
24
|
import {
|
|
25
|
+
EDGE_CONTAINER_NAME,
|
|
25
26
|
EdgeConflictError,
|
|
26
27
|
EdgeMigrateRequested,
|
|
28
|
+
edgeFailureReason,
|
|
27
29
|
freeEdgeTargets,
|
|
28
30
|
invalidateEdgeContainer,
|
|
29
31
|
ourLuaOnHost,
|
|
30
32
|
probeEdge,
|
|
31
33
|
resolveOurEdgeContainer,
|
|
34
|
+
sanitizeEdgeVhosts,
|
|
32
35
|
sq as sq2,
|
|
33
36
|
stopTargetsForStatus
|
|
34
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-2D3I5G6P.js";
|
|
35
38
|
import {
|
|
36
39
|
waitForPortListening
|
|
37
40
|
} from "./chunk-ILFETZOM.js";
|
|
@@ -976,6 +979,8 @@ async function readJournal(executor) {
|
|
|
976
979
|
}
|
|
977
980
|
async function rollback(executor, journal, onLog) {
|
|
978
981
|
onLog(log("Rolling back \u2014 restoring the previous proxy...", "warn"));
|
|
982
|
+
await tryExec2(executor, `docker update --restart=no ${sq2(EDGE_CONTAINER_NAME)} 2>/dev/null || true`);
|
|
983
|
+
await tryExec2(executor, `docker stop ${sq2(EDGE_CONTAINER_NAME)} 2>/dev/null || true`);
|
|
979
984
|
await tryExec2(
|
|
980
985
|
executor,
|
|
981
986
|
"systemctl disable --now openresty 2>/dev/null || systemctl stop openresty 2>/dev/null || true; systemctl reset-failed openresty 2>/dev/null || true"
|
|
@@ -1000,6 +1005,24 @@ async function rollback(executor, journal, onLog) {
|
|
|
1000
1005
|
onLog(log(`Could not restore process ${p.pid} \u2014 no command captured.`, "warn"));
|
|
1001
1006
|
}
|
|
1002
1007
|
}
|
|
1008
|
+
const restored = await portIsServed(executor, 80);
|
|
1009
|
+
if (!restored) {
|
|
1010
|
+
onLog(
|
|
1011
|
+
log(
|
|
1012
|
+
"Nothing is listening on :80 after the restore \u2014 the box is NOT serving. Start your proxy by hand (e.g. `systemctl start nginx`, or `docker start <name>`).",
|
|
1013
|
+
"error"
|
|
1014
|
+
)
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
return restored;
|
|
1018
|
+
}
|
|
1019
|
+
async function portIsServed(executor, port) {
|
|
1020
|
+
const hex = port.toString(16).toUpperCase().padStart(4, "0");
|
|
1021
|
+
const out = await tryExec2(
|
|
1022
|
+
executor,
|
|
1023
|
+
`(command -v ss >/dev/null 2>&1 && ss -ltn 2>/dev/null | grep -qE ':${port}[[:space:]]' && echo yes) || (grep -qiE '^[[:space:]]*[0-9]+:[[:space:]]*[0-9A-F]{8}:${hex}[[:space:]]+[0-9A-F]{8}:0000[[:space:]]+0A' /proc/net/tcp 2>/dev/null && echo yes) || true`
|
|
1024
|
+
);
|
|
1025
|
+
return (out ?? "").includes("yes");
|
|
1003
1026
|
}
|
|
1004
1027
|
async function beginEdgeTakeover(executor, status, onLog) {
|
|
1005
1028
|
await writeJournal(executor, await buildJournal(executor, status));
|
|
@@ -1008,9 +1031,9 @@ async function beginEdgeTakeover(executor, status, onLog) {
|
|
|
1008
1031
|
async function rollbackEdgeTakeover(executor, onLog) {
|
|
1009
1032
|
const journal = await readJournal(executor);
|
|
1010
1033
|
if (!journal || journal.completed) return false;
|
|
1011
|
-
await rollback(executor, journal, onLog);
|
|
1034
|
+
const restored = await rollback(executor, journal, onLog);
|
|
1012
1035
|
await clearJournal(executor);
|
|
1013
|
-
return
|
|
1036
|
+
return restored;
|
|
1014
1037
|
}
|
|
1015
1038
|
async function completeEdgeTakeover(executor) {
|
|
1016
1039
|
const journal = await readJournal(executor);
|
|
@@ -1802,7 +1825,6 @@ async function ensureEdgeClear(executor, config, onLog) {
|
|
|
1802
1825
|
}
|
|
1803
1826
|
|
|
1804
1827
|
// ../../packages/adapters/src/system/proxy/ensure-container-edge.ts
|
|
1805
|
-
var EDGE_CONTAINER_NAME = "openship-edge";
|
|
1806
1828
|
function log4(message, level = "info") {
|
|
1807
1829
|
return { timestamp: (/* @__PURE__ */ new Date()).toISOString(), message, level };
|
|
1808
1830
|
}
|
|
@@ -1821,7 +1843,7 @@ async function runningImage(executor, container) {
|
|
|
1821
1843
|
return out.trim() || null;
|
|
1822
1844
|
}
|
|
1823
1845
|
async function containerEdgeProvider(executor, container, opts) {
|
|
1824
|
-
const { NginxProvider: Provider } = await import("./nginx-
|
|
1846
|
+
const { NginxProvider: Provider } = await import("./nginx-3L4SIBJA.js");
|
|
1825
1847
|
return new Provider({
|
|
1826
1848
|
...opts,
|
|
1827
1849
|
// Vhosts go to the BIND-MOUNTED HOST dir (so every host-side reader — the
|
|
@@ -1836,7 +1858,7 @@ async function containerEdgeProvider(executor, container, opts) {
|
|
|
1836
1858
|
}
|
|
1837
1859
|
async function localContainerEdgeProvider(container, opts) {
|
|
1838
1860
|
const { DockerEdgeExecutor } = await import("./docker-edge-executor-VHW3JPJS.js");
|
|
1839
|
-
const { NginxProvider: Provider } = await import("./nginx-
|
|
1861
|
+
const { NginxProvider: Provider } = await import("./nginx-3L4SIBJA.js");
|
|
1840
1862
|
return new Provider({
|
|
1841
1863
|
...opts,
|
|
1842
1864
|
paths: OPENRESTY_DEFAULT_PATHS,
|
|
@@ -1846,6 +1868,18 @@ async function localContainerEdgeProvider(container, opts) {
|
|
|
1846
1868
|
pinPaths: true
|
|
1847
1869
|
});
|
|
1848
1870
|
}
|
|
1871
|
+
async function startEdgeContainer(executor, container, image, onLog) {
|
|
1872
|
+
await sanitizeEdgeVhosts(executor, EDGE_HOST_PATHS.sitesDir, onLog).catch(() => {
|
|
1873
|
+
});
|
|
1874
|
+
await executor.exec(`docker rm -f ${sq(container)} 2>/dev/null || true`).catch(() => {
|
|
1875
|
+
});
|
|
1876
|
+
const run = await executor.streamExec(
|
|
1877
|
+
buildEdgeRunCommand(container, image),
|
|
1878
|
+
onLog
|
|
1879
|
+
);
|
|
1880
|
+
invalidateEdgeContainer(executor);
|
|
1881
|
+
return run.code === 0;
|
|
1882
|
+
}
|
|
1849
1883
|
function buildEdgeRunCommand(container, image) {
|
|
1850
1884
|
const mounts = EDGE_CONTAINER_MOUNTS.map(
|
|
1851
1885
|
// `:z` relabels for SELinux-enforcing hosts; a no-op elsewhere.
|
|
@@ -1869,14 +1903,7 @@ async function swapEdgeImage(executor, container, from, to, opts) {
|
|
|
1869
1903
|
return { swapped: false, edgeDown: false };
|
|
1870
1904
|
}
|
|
1871
1905
|
const start = async (image) => {
|
|
1872
|
-
await executor
|
|
1873
|
-
});
|
|
1874
|
-
const run = await executor.streamExec(
|
|
1875
|
-
buildEdgeRunCommand(container, image),
|
|
1876
|
-
onLog
|
|
1877
|
-
);
|
|
1878
|
-
invalidateEdgeContainer(executor);
|
|
1879
|
-
if (run.code !== 0) return false;
|
|
1906
|
+
if (!await startEdgeContainer(executor, container, image, onLog)) return false;
|
|
1880
1907
|
const listening = await waitForPortListening(executor, 80, {
|
|
1881
1908
|
timeoutMs: opts.verifyTimeoutMs ?? 3e4
|
|
1882
1909
|
});
|
|
@@ -1893,6 +1920,12 @@ async function swapEdgeImage(executor, container, from, to, opts) {
|
|
|
1893
1920
|
onLog(log4(`Rollback to ${from} ALSO failed \u2014 the edge is down on this server.`, "error"));
|
|
1894
1921
|
return { swapped: false, edgeDown: true };
|
|
1895
1922
|
}
|
|
1923
|
+
async function listDir(executor, dir) {
|
|
1924
|
+
const out = await executor.exec(`ls -1 ${sq(dir)} 2>/dev/null || true`).catch(() => "");
|
|
1925
|
+
return new Set(
|
|
1926
|
+
out.split("\n").map((l) => l.trim()).filter(Boolean)
|
|
1927
|
+
);
|
|
1928
|
+
}
|
|
1896
1929
|
async function ensureContainerEdge(executor, opts) {
|
|
1897
1930
|
const { onLog } = opts;
|
|
1898
1931
|
const container = opts.container?.trim() || EDGE_CONTAINER_NAME;
|
|
@@ -1931,16 +1964,27 @@ async function ensureContainerEdge(executor, opts) {
|
|
|
1931
1964
|
});
|
|
1932
1965
|
}
|
|
1933
1966
|
const sitesTarget = EDGE_HOST_PATHS.sitesDir;
|
|
1967
|
+
let beforeCarry = null;
|
|
1934
1968
|
if (bareWasOurs) {
|
|
1935
1969
|
const barePaths = await detectOpenRestyPaths(executor).catch(() => OPENRESTY_DEFAULT_PATHS);
|
|
1936
1970
|
if (barePaths.sitesDir !== sitesTarget && await executor.exists(barePaths.sitesDir)) {
|
|
1937
1971
|
onLog(log4(`Carrying vhosts from ${barePaths.sitesDir}...`));
|
|
1972
|
+
beforeCarry = await listDir(executor, sitesTarget);
|
|
1938
1973
|
await executor.exec(`cp -a ${sq(`${barePaths.sitesDir}/.`)} ${sq(`${sitesTarget}/`)} 2>/dev/null || true`).catch(() => {
|
|
1939
1974
|
});
|
|
1940
1975
|
}
|
|
1941
1976
|
}
|
|
1942
1977
|
const restoreBare = async () => {
|
|
1943
1978
|
if (!bareWasOurs) return;
|
|
1979
|
+
if (beforeCarry) {
|
|
1980
|
+
const now = await listDir(executor, sitesTarget);
|
|
1981
|
+
const added = [...now].filter((name) => !beforeCarry.has(name));
|
|
1982
|
+
if (added.length > 0) {
|
|
1983
|
+
await executor.exec(`rm -f ${added.map((n) => sq(`${sitesTarget}/${n}`)).join(" ")}`).catch(() => {
|
|
1984
|
+
});
|
|
1985
|
+
onLog(log4(`Removed ${added.length} carried vhost(s) so the next edge start is clean.`, "warn"));
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1944
1988
|
onLog(log4("Restoring the host OpenResty edge...", "warn"));
|
|
1945
1989
|
await executor.exec("systemctl enable --now openresty 2>/dev/null || true").catch(() => {
|
|
1946
1990
|
});
|
|
@@ -1953,15 +1997,10 @@ async function ensureContainerEdge(executor, opts) {
|
|
|
1953
1997
|
await executor.exec("systemctl reset-failed openresty 2>/dev/null || true").catch(() => {
|
|
1954
1998
|
});
|
|
1955
1999
|
}
|
|
1956
|
-
await executor.exec(`docker rm -f ${sq(container)} 2>/dev/null || true`).catch(() => {
|
|
1957
|
-
});
|
|
1958
2000
|
onLog(log4("Starting the edge container..."));
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
);
|
|
1963
|
-
invalidateEdgeContainer(executor);
|
|
1964
|
-
if (run.code !== 0) throw new Error("the edge container failed to start");
|
|
2001
|
+
if (!await startEdgeContainer(executor, container, image, onLog)) {
|
|
2002
|
+
throw new Error("the edge container failed to start");
|
|
2003
|
+
}
|
|
1965
2004
|
await executor.exec(containerCommand(container, "openresty -t"));
|
|
1966
2005
|
const listening = await waitForPortListening(executor, 80, {
|
|
1967
2006
|
timeoutMs: opts.verifyTimeoutMs ?? 3e4
|
|
@@ -1982,7 +2021,8 @@ ${logs}`, "error"));
|
|
|
1982
2021
|
});
|
|
1983
2022
|
invalidateEdgeContainer(executor);
|
|
1984
2023
|
await restoreBare();
|
|
1985
|
-
|
|
2024
|
+
const reason = edgeFailureReason(logs);
|
|
2025
|
+
throw new Error(`Edge container setup failed: ${reason ? `${reason} (${msg})` : msg}`);
|
|
1986
2026
|
}
|
|
1987
2027
|
}
|
|
1988
2028
|
|
|
@@ -2000,7 +2040,6 @@ export {
|
|
|
2000
2040
|
rollbackEdgeTakeover,
|
|
2001
2041
|
completeEdgeTakeover,
|
|
2002
2042
|
recoverInterruptedTakeover,
|
|
2003
|
-
EDGE_CONTAINER_NAME,
|
|
2004
2043
|
setDefaultEdgeImage,
|
|
2005
2044
|
resolveEdgeImage,
|
|
2006
2045
|
dockerAvailable,
|