openship 0.6.6 → 0.6.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-FA66N7GC.js +29 -0
- package/dist/{chunk-77QDF6UD.js → chunk-3MP6ACKM.js} +587 -156
- package/dist/{chunk-YIMOCZBZ.js → chunk-5DJOB7IX.js} +3 -3
- package/dist/{chunk-WVAML4H6.js → chunk-5I3COAFH.js} +274 -65
- package/dist/{chunk-5DMWSTOM.js → chunk-5UXHT6ED.js} +940 -224
- package/dist/{chunk-LUKYPVJ4.js → chunk-6GU6JSK6.js} +329 -466
- package/dist/{chunk-RSR7PFIQ.js → chunk-7PEXM2UI.js} +213 -45
- package/dist/{chunk-VTGPEC6B.js → chunk-7YBWXEM6.js} +6 -4
- package/dist/chunk-AHJYP7DG.js +938 -0
- package/dist/chunk-BO6CY4V2.js +265 -0
- package/dist/{chunk-FE7ZZQSE.js → chunk-D3OEMXYP.js} +3 -3
- package/dist/chunk-D6HU4VUZ.js +249 -0
- package/dist/{chunk-LFE5KQ7Y.js → chunk-ENKJ7XIF.js} +152 -57
- package/dist/{chunk-YMMQRE5J.js → chunk-GNOMEMNI.js} +1 -1
- package/dist/{chunk-QVW76WDL.js → chunk-IAMOUDHY.js} +190 -64
- package/dist/{chunk-OBPKYXQL.js → chunk-MUWVC5R4.js} +219 -58
- package/dist/chunk-N5YZMO33.js +111 -0
- package/dist/{chunk-A4GXYE4L.js → chunk-NIYD7MD2.js} +206 -52
- package/dist/{chunk-UVNMQM7W.js → chunk-NPSRGIHV.js} +8 -6
- package/dist/{chunk-2GWYYHOR.js → chunk-NRU2EBYZ.js} +2 -2
- package/dist/{chunk-CBLD2HS5.js → chunk-NUT6HKK6.js} +8 -5
- package/dist/{chunk-ZFJXPCQP.js → chunk-P7RCYELG.js} +4 -3
- package/dist/chunk-R4VDPFLS.js +106 -0
- package/dist/{chunk-HKRQXPTI.js → chunk-R5DC5PEF.js} +1 -1
- package/dist/{chunk-NK5N5LXN.js → chunk-RCMERVBL.js} +24 -9
- package/dist/{chunk-RXQBD4ZY.js → chunk-RHLNVZMS.js} +2 -2
- package/dist/chunk-SK4FFCFR.js +70 -0
- package/dist/{chunk-4I2BM2DT.js → chunk-T46BQOLY.js} +3 -3
- package/dist/{chunk-6FH4AXVY.js → chunk-T6DVUX4F.js} +1 -1
- package/dist/{chunk-PCPVTH6M.js → chunk-TAZZZKYO.js} +6 -4
- package/dist/{chunk-MLYSEZQB.js → chunk-UAWY6X7V.js} +43 -72
- package/dist/{chunk-Q7WGTQQ5.js → chunk-VAI5VOOI.js} +1 -1
- package/dist/{chunk-UJ6JZM65.js → chunk-WEZFTL4V.js} +1325 -513
- package/dist/{chunk-2YM3NI2E.js → chunk-X53JXRS4.js} +38 -44
- package/dist/{cloud-KRTZ2IL5.js → cloud-2CRDKOWQ.js} +1 -1
- package/dist/cloud-REV3DPJH.js +42 -0
- package/dist/{detect-OIE5V3VZ.js → detect-BBFT5GEU.js} +9 -6
- package/dist/{docker-VTR2GK5Y.js → docker-EG3QSE6I.js} +11 -8
- package/dist/{docker-edge-executor-22IBXNYJ.js → docker-edge-executor-E5TOTLMR.js} +8 -6
- package/dist/edge-import-YJVJUGYC.js +56 -0
- package/dist/{edge-preflight-GBNCPNVU.js → edge-preflight-QUW4BM7N.js} +15 -12
- package/dist/{ensure-container-edge-KPI2CVXJ.js → ensure-container-edge-FOEFQ3PI.js} +15 -10
- package/dist/{executor-7LXXZPBR.js → executor-NTWDJ3DB.js} +8 -6
- package/dist/host-channel-preflight-OMCZMMKR.js +54 -0
- package/dist/index.js +198 -76
- package/dist/{local-executor-WOFZD72X.js → local-executor-OQ2GRN7K.js} +3 -2
- package/dist/{nginx-KUFMLMAC.js → nginx-4U4C2XUS.js} +11 -8
- package/dist/{noop-NJ72F46T.js → noop-CLLJHCPL.js} +1 -1
- package/dist/{openresty-lua-ATTH35BE.js → openresty-lua-LLWT5XQW.js} +8 -5
- package/dist/{privilege-I4W3ZNZK.js → privilege-MNDVMDST.js} +6 -5
- package/dist/server/index.js +40049 -30378
- package/dist/server/migrations/0109_service_build_args.sql +9 -0
- package/dist/server/migrations/0110_service_deployment_host_ports.sql +18 -0
- package/dist/server/migrations/0111_host_port_claim.sql +381 -0
- package/dist/server/migrations/0112_backup_run_execution_lease.sql +12 -0
- package/dist/server/migrations/0113_docker_migration_execution_lease.sql +11 -0
- package/dist/server/migrations/0114_build_session_execution_lease_index.sql +6 -0
- package/dist/server/migrations/0115_orphan_target_key.sql +3 -0
- package/dist/server/migrations/0116_domain_ssl_challenge.sql +2 -0
- package/dist/server/migrations/meta/_journal.json +56 -0
- package/dist/setup-IFIMPQZJ.js +25 -0
- package/dist/{ssh-client-JBMA7SN4.js → ssh-client-2XWZCMGI.js} +3 -2
- package/dist/up-HQS7IR7H.js +61 -0
- package/package.json +1 -1
- package/dist/bare-OXJVW3AK.js +0 -25
- package/dist/chunk-LH2PLRQO.js +0 -434
- package/dist/chunk-SM26HDS2.js +0 -185
- package/dist/cloud-75VIT4KV.js +0 -37
- package/dist/edge-import-WJUB4M7F.js +0 -52
- package/dist/host-channel-preflight-KE6OONYK.js +0 -50
- package/dist/setup-XGLGUUFW.js +0 -22
- package/dist/up-K4BV7466.js +0 -57
|
@@ -9,32 +9,32 @@ import {
|
|
|
9
9
|
readDeclaredPair,
|
|
10
10
|
readMaybeInContainer,
|
|
11
11
|
validateCertFor
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-IAMOUDHY.js";
|
|
13
13
|
import {
|
|
14
14
|
EDGE_CHALLENGE_HOST_DIR,
|
|
15
15
|
EDGE_CONTAINER_MOUNTS,
|
|
16
16
|
EDGE_HOST_PATHS,
|
|
17
17
|
OPENRESTY_DEFAULT_PATHS
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-7PEXM2UI.js";
|
|
19
19
|
import {
|
|
20
20
|
privilegedExecutor,
|
|
21
21
|
rootChecked,
|
|
22
22
|
rootOrDegrade
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-T46BQOLY.js";
|
|
24
24
|
import {
|
|
25
25
|
envOps,
|
|
26
26
|
opScript
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-GNOMEMNI.js";
|
|
28
28
|
import {
|
|
29
29
|
resolveEnvironment
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-NPSRGIHV.js";
|
|
31
31
|
import {
|
|
32
32
|
sq
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-P7RCYELG.js";
|
|
34
34
|
import {
|
|
35
|
-
EDGE_CONTAINER_NAME,
|
|
36
35
|
EdgeConflictError,
|
|
37
36
|
EdgeMigrateRequested,
|
|
37
|
+
detectEdgeContainer,
|
|
38
38
|
edgeCrashReason,
|
|
39
39
|
edgeFailureReason,
|
|
40
40
|
edgeIsBroken,
|
|
@@ -45,25 +45,32 @@ import {
|
|
|
45
45
|
sanitizeEdgeVhosts,
|
|
46
46
|
sq as sq2,
|
|
47
47
|
stopTargetsForStatus
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-X53JXRS4.js";
|
|
49
|
+
import {
|
|
50
|
+
waitForPortListening
|
|
51
|
+
} from "./chunk-BO6CY4V2.js";
|
|
49
52
|
import {
|
|
53
|
+
EDGE_CONTAINER_NAME,
|
|
50
54
|
probeExec,
|
|
51
55
|
tryExec,
|
|
52
|
-
waitForPortListening,
|
|
53
56
|
withReason
|
|
54
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-D6HU4VUZ.js";
|
|
58
|
+
import {
|
|
59
|
+
formatDuration,
|
|
60
|
+
systemDebug
|
|
61
|
+
} from "./chunk-N5YZMO33.js";
|
|
55
62
|
import {
|
|
56
63
|
AppError,
|
|
57
64
|
PROXY_DIRECTIVES,
|
|
58
65
|
answered,
|
|
59
66
|
buildEdgeImageRef,
|
|
60
67
|
compareSemver,
|
|
61
|
-
|
|
68
|
+
isLoopbackHost,
|
|
62
69
|
parseProxyValue,
|
|
63
70
|
safeErrorMessage,
|
|
64
71
|
sanitizeProxySettings,
|
|
65
|
-
|
|
66
|
-
} from "./chunk-
|
|
72
|
+
shellQuote
|
|
73
|
+
} from "./chunk-WEZFTL4V.js";
|
|
67
74
|
|
|
68
75
|
// ../../packages/adapters/src/system/managed-image.ts
|
|
69
76
|
import { posix } from "path";
|
|
@@ -384,9 +391,6 @@ function collapseByHost(items, hostsOf, score) {
|
|
|
384
391
|
}
|
|
385
392
|
return { kept, dropped };
|
|
386
393
|
}
|
|
387
|
-
function sq3(value) {
|
|
388
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
389
|
-
}
|
|
390
394
|
|
|
391
395
|
// ../../packages/adapters/src/system/proxy/import/nginx.ts
|
|
392
396
|
async function dumpResolvedConfig(executor, bins) {
|
|
@@ -423,8 +427,9 @@ function parseUpstreams(config) {
|
|
|
423
427
|
let m;
|
|
424
428
|
while ((m = re.exec(config)) !== null) {
|
|
425
429
|
const name = m[1];
|
|
426
|
-
const
|
|
427
|
-
|
|
430
|
+
const body = m[2] ?? "";
|
|
431
|
+
const targets = [...body.matchAll(/(?:^|[\s;{])server\s+([^;\s]+)/g)].map((server) => server[1]?.trim()).filter((target) => Boolean(target));
|
|
432
|
+
if (name && targets.length > 0) map.set(name, targets);
|
|
428
433
|
}
|
|
429
434
|
return map;
|
|
430
435
|
}
|
|
@@ -437,14 +442,55 @@ function resolveProxyTarget(proxyPass, upstreams) {
|
|
|
437
442
|
const scheme = m[1];
|
|
438
443
|
const authority = m[2];
|
|
439
444
|
const host = authority.replace(/:\d+$/, "");
|
|
440
|
-
|
|
445
|
+
const declaredUpstream = upstreams.get(authority);
|
|
446
|
+
if (declaredUpstream?.[0]) return { url: `${scheme}${declaredUpstream[0]}` };
|
|
441
447
|
const isIp = /^\d{1,3}(\.\d{1,3}){3}$/.test(host);
|
|
442
448
|
if (host === "localhost" || host === "ip6-localhost") {
|
|
443
449
|
return { url: raw.replace(/^(https?:\/\/)[^/:]+/i, "$1127.0.0.1") };
|
|
444
450
|
}
|
|
445
|
-
if (isIp || host.includes(".")
|
|
451
|
+
if (isIp || host.includes(".") || host.startsWith("[") && host.endsWith("]")) {
|
|
452
|
+
return { url: raw };
|
|
453
|
+
}
|
|
446
454
|
return { reason: `proxy_pass host "${host}" is an undeclared upstream \u2014 not migratable` };
|
|
447
455
|
}
|
|
456
|
+
function strictLoopbackUpstreamPorts(config) {
|
|
457
|
+
const normalized = stripComments(config);
|
|
458
|
+
const upstreams = parseUpstreams(normalized);
|
|
459
|
+
const ports = /* @__PURE__ */ new Set();
|
|
460
|
+
for (const match of normalized.matchAll(/(?:^|[;{}\s])proxy_pass\s+([^;]+);/g)) {
|
|
461
|
+
const raw = match[1]?.trim();
|
|
462
|
+
if (!raw) continue;
|
|
463
|
+
const resolved = resolveProxyTarget(raw, upstreams);
|
|
464
|
+
if ("reason" in resolved) {
|
|
465
|
+
if (/\/\/unix:/i.test(raw)) continue;
|
|
466
|
+
throw new Error(`Cannot inventory Openship edge routes: ${resolved.reason}`);
|
|
467
|
+
}
|
|
468
|
+
const authority = raw.match(/^https?:\/\/([^/]+)/i)?.[1];
|
|
469
|
+
const scheme = raw.match(/^(https?:\/\/)/i)?.[1];
|
|
470
|
+
const targets = authority && scheme && upstreams.has(authority) ? upstreams.get(authority).map((target) => `${scheme}${target}`) : [resolved.url];
|
|
471
|
+
for (const target of targets) {
|
|
472
|
+
let url;
|
|
473
|
+
try {
|
|
474
|
+
url = new URL(target);
|
|
475
|
+
} catch {
|
|
476
|
+
throw new Error(`Cannot inventory Openship edge routes: invalid proxy_pass "${raw}"`);
|
|
477
|
+
}
|
|
478
|
+
if (!isLoopbackHost(url.hostname)) continue;
|
|
479
|
+
const port = url.port ? Number(url.port) : url.protocol === "https:" ? 443 : 80;
|
|
480
|
+
if (!Number.isSafeInteger(port) || port < 1 || port > 65535) {
|
|
481
|
+
throw new Error(`Cannot inventory Openship edge routes: invalid loopback port in "${raw}"`);
|
|
482
|
+
}
|
|
483
|
+
ports.add(port);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return ports;
|
|
487
|
+
}
|
|
488
|
+
function parseStrictEdgeConfig(raw) {
|
|
489
|
+
return {
|
|
490
|
+
...parseNginxConfig(raw),
|
|
491
|
+
loopbackUpstreamPorts: strictLoopbackUpstreamPorts(raw)
|
|
492
|
+
};
|
|
493
|
+
}
|
|
448
494
|
function locationBlocks(serverBody) {
|
|
449
495
|
const out = [];
|
|
450
496
|
const re = /(?:^|[\s;}])location\s+([^{]+?)\s*\{/g;
|
|
@@ -536,7 +582,9 @@ function resolveStaticRoot(root, prefix) {
|
|
|
536
582
|
}
|
|
537
583
|
function parseServer(body, source, upstreams, prefix) {
|
|
538
584
|
const warnings = [];
|
|
539
|
-
const names = firstDirective(body, "server_name")?.split(/\s+/).filter(
|
|
585
|
+
const names = firstDirective(body, "server_name")?.split(/\s+/).filter(
|
|
586
|
+
(n) => n && n !== "_" && !n.startsWith("~") && !LOOPBACK_SERVER_NAMES.has(n.toLowerCase())
|
|
587
|
+
) ?? [];
|
|
540
588
|
const listens = [...body.matchAll(/(?:^|[;{\s])listen\s+([^;]+);/g)].map((m) => m[1]);
|
|
541
589
|
const ssl = listens.some((l) => /\bssl\b/.test(l) || /\b443\b/.test(l));
|
|
542
590
|
const root = firstDirective(body, "root");
|
|
@@ -632,6 +680,35 @@ async function scanOpenshipEdge(executor) {
|
|
|
632
680
|
const fromContainer = await tryExec(executor, containerCommand(container, cat));
|
|
633
681
|
return parseNginxConfig(fromContainer ?? "");
|
|
634
682
|
}
|
|
683
|
+
async function scanOpenshipEdgeStrict(executor, knownContainer) {
|
|
684
|
+
const detected = await detectEdgeContainer(executor);
|
|
685
|
+
if (!detected) {
|
|
686
|
+
const bareConfig = await dumpResolvedConfig(executor, ["openresty"]);
|
|
687
|
+
if (bareConfig) return parseStrictEdgeConfig(bareConfig);
|
|
688
|
+
throw new Error(
|
|
689
|
+
"Cannot read Openship edge routes: neither Docker nor bare OpenResty inventory is available"
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
if (!detected.exists) {
|
|
693
|
+
const bareConfig = await dumpResolvedConfig(executor, ["openresty"]);
|
|
694
|
+
if (bareConfig) return parseStrictEdgeConfig(bareConfig);
|
|
695
|
+
throw new Error(
|
|
696
|
+
"Cannot read Openship edge routes: no running Openship edge or bare OpenResty inventory is available"
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
if (!detected.running) {
|
|
700
|
+
throw new Error("Cannot read Openship edge routes: the edge container is not running");
|
|
701
|
+
}
|
|
702
|
+
const container = knownContainer ?? detected.name;
|
|
703
|
+
if (!container) throw new Error("Cannot read Openship edge routes: container identity missing");
|
|
704
|
+
const fromContainer = await executor.exec(
|
|
705
|
+
containerCommand(container, "openresty -T 2>/dev/null")
|
|
706
|
+
);
|
|
707
|
+
if (!fromContainer.trim() || !/server\s*\{/.test(fromContainer)) {
|
|
708
|
+
throw new Error("Cannot read Openship edge routes: active OpenResty config dump was empty");
|
|
709
|
+
}
|
|
710
|
+
return parseStrictEdgeConfig(fromContainer);
|
|
711
|
+
}
|
|
635
712
|
|
|
636
713
|
// ../../packages/adapters/src/system/proxy/import/caddy.ts
|
|
637
714
|
var CADDYFILE_PATHS = ["/etc/caddy/Caddyfile", "/etc/Caddyfile"];
|
|
@@ -907,7 +984,7 @@ async function vhostFilesFromDump(executor) {
|
|
|
907
984
|
async function loadApacheConfig(executor) {
|
|
908
985
|
const files = await vhostFilesFromDump(executor);
|
|
909
986
|
if (files.length > 0) {
|
|
910
|
-
const cat2 = await tryExec(executor, `cat ${files.map(
|
|
987
|
+
const cat2 = await tryExec(executor, `cat ${files.map(shellQuote).join(" ")} 2>/dev/null`);
|
|
911
988
|
if (cat2 && cat2.trim()) return cat2;
|
|
912
989
|
}
|
|
913
990
|
const cat = await tryExec(
|
|
@@ -1611,6 +1688,24 @@ function makeApi(exec, kind, ours, container) {
|
|
|
1611
1688
|
}));
|
|
1612
1689
|
return scan;
|
|
1613
1690
|
};
|
|
1691
|
+
let strictLoopbackPorts = null;
|
|
1692
|
+
const listLoopbackUpstreamPortsStrict = (opts = {}) => {
|
|
1693
|
+
if (!ours) {
|
|
1694
|
+
return Promise.reject(
|
|
1695
|
+
new Error("Strict loopback upstream inventory is only available for the Openship edge")
|
|
1696
|
+
);
|
|
1697
|
+
}
|
|
1698
|
+
if (opts.refresh || !strictLoopbackPorts) {
|
|
1699
|
+
const nextScan = scanOpenshipEdgeStrict(exec, container).then(
|
|
1700
|
+
({ loopbackUpstreamPorts }) => loopbackUpstreamPorts
|
|
1701
|
+
);
|
|
1702
|
+
strictLoopbackPorts = nextScan;
|
|
1703
|
+
void nextScan.catch(() => {
|
|
1704
|
+
if (strictLoopbackPorts === nextScan) strictLoopbackPorts = null;
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
return strictLoopbackPorts;
|
|
1708
|
+
};
|
|
1614
1709
|
const siteFor = async (host) => {
|
|
1615
1710
|
const target = host.toLowerCase();
|
|
1616
1711
|
const { sites } = await listSites();
|
|
@@ -1642,6 +1737,7 @@ function makeApi(exec, kind, ours, container) {
|
|
|
1642
1737
|
ours,
|
|
1643
1738
|
container,
|
|
1644
1739
|
listSites,
|
|
1740
|
+
listLoopbackUpstreamPortsStrict,
|
|
1645
1741
|
sitesByPort: async () => buildProxyRouteIndex((await listSites()).sites),
|
|
1646
1742
|
siteFor,
|
|
1647
1743
|
certFor: async (host) => (await certCandidateFor(host)).cert,
|
|
@@ -2308,7 +2404,7 @@ function edgeHostExecutor(executor) {
|
|
|
2308
2404
|
});
|
|
2309
2405
|
}
|
|
2310
2406
|
async function containerEdgeProvider(executor, container, opts) {
|
|
2311
|
-
const { NginxProvider: Provider } = await import("./nginx-
|
|
2407
|
+
const { NginxProvider: Provider } = await import("./nginx-4U4C2XUS.js");
|
|
2312
2408
|
return new Provider({
|
|
2313
2409
|
...opts,
|
|
2314
2410
|
// Vhosts go to the BIND-MOUNTED HOST dir (so every host-side reader — the
|
|
@@ -2332,8 +2428,8 @@ async function containerEdgeProvider(executor, container, opts) {
|
|
|
2332
2428
|
});
|
|
2333
2429
|
}
|
|
2334
2430
|
async function localContainerEdgeProvider(container, opts) {
|
|
2335
|
-
const { DockerEdgeExecutor } = await import("./docker-edge-executor-
|
|
2336
|
-
const { NginxProvider: Provider } = await import("./nginx-
|
|
2431
|
+
const { DockerEdgeExecutor } = await import("./docker-edge-executor-E5TOTLMR.js");
|
|
2432
|
+
const { NginxProvider: Provider } = await import("./nginx-4U4C2XUS.js");
|
|
2337
2433
|
return new Provider({
|
|
2338
2434
|
...opts,
|
|
2339
2435
|
paths: OPENRESTY_DEFAULT_PATHS,
|
|
@@ -2354,20 +2450,40 @@ async function localContainerEdgeProvider(container, opts) {
|
|
|
2354
2450
|
containerEdge: true
|
|
2355
2451
|
});
|
|
2356
2452
|
}
|
|
2357
|
-
async function startEdgeContainer(executor, container, image, onLog) {
|
|
2453
|
+
async function startEdgeContainer(executor, container, image, onLog, mounts) {
|
|
2358
2454
|
await sanitizeEdgeVhosts(executor, EDGE_HOST_PATHS.sitesDir, onLog).catch(() => {
|
|
2359
2455
|
});
|
|
2360
2456
|
await executor.exec(`docker rm -f ${sq(container)} 2>/dev/null || true`).catch(() => {
|
|
2361
2457
|
});
|
|
2362
2458
|
const run = await executor.streamExec(
|
|
2363
|
-
buildEdgeRunCommand(container, image),
|
|
2459
|
+
buildEdgeRunCommand(container, image, mounts),
|
|
2364
2460
|
onLog
|
|
2365
2461
|
);
|
|
2366
2462
|
invalidateEdgeContainer(executor);
|
|
2367
2463
|
return run.code === 0;
|
|
2368
2464
|
}
|
|
2369
|
-
function
|
|
2370
|
-
|
|
2465
|
+
async function resolveEdgeContainerMounts(executor) {
|
|
2466
|
+
return Promise.all(
|
|
2467
|
+
EDGE_CONTAINER_MOUNTS.map(async (mount) => {
|
|
2468
|
+
let host;
|
|
2469
|
+
try {
|
|
2470
|
+
host = (await executor.exec(`cd ${sq(mount.host)} && pwd -P`)).trim();
|
|
2471
|
+
} catch (err) {
|
|
2472
|
+
throw new Error(
|
|
2473
|
+
`Could not resolve edge bind-mount source ${mount.host} on the target host: ${safeErrorMessage(err)}`
|
|
2474
|
+
);
|
|
2475
|
+
}
|
|
2476
|
+
if (!host.startsWith("/") || host.includes("\n")) {
|
|
2477
|
+
throw new Error(
|
|
2478
|
+
`Could not resolve edge bind-mount source ${mount.host} on the target host: expected one absolute path, received ${JSON.stringify(host)}.`
|
|
2479
|
+
);
|
|
2480
|
+
}
|
|
2481
|
+
return { host, container: mount.container };
|
|
2482
|
+
})
|
|
2483
|
+
);
|
|
2484
|
+
}
|
|
2485
|
+
function buildEdgeRunCommand(container, image, edgeMounts = EDGE_CONTAINER_MOUNTS) {
|
|
2486
|
+
const mounts = edgeMounts.map(
|
|
2371
2487
|
// `:z` relabels for SELinux-enforcing hosts; a no-op elsewhere.
|
|
2372
2488
|
(m) => `-v ${sq(`${m.host}:${m.container}:z`)}`
|
|
2373
2489
|
).join(" ");
|
|
@@ -2380,10 +2496,27 @@ function buildEdgeRunCommand(container, image) {
|
|
|
2380
2496
|
sq(image)
|
|
2381
2497
|
].join(" ");
|
|
2382
2498
|
}
|
|
2383
|
-
function
|
|
2499
|
+
async function edgeContainerMountsMatch(executor, container, expected) {
|
|
2500
|
+
let raw;
|
|
2501
|
+
try {
|
|
2502
|
+
raw = await executor.exec(
|
|
2503
|
+
`docker inspect -f '{{range .HostConfig.Binds}}{{println .}}{{end}}' ${sq(container)}`
|
|
2504
|
+
);
|
|
2505
|
+
} catch {
|
|
2506
|
+
return null;
|
|
2507
|
+
}
|
|
2508
|
+
const actual = /* @__PURE__ */ new Map();
|
|
2509
|
+
for (const line of raw.split("\n")) {
|
|
2510
|
+
const [source, destination] = line.trim().split(":");
|
|
2511
|
+
if (!source || !destination) continue;
|
|
2512
|
+
actual.set(destination, source);
|
|
2513
|
+
}
|
|
2514
|
+
return expected.every((mount) => actual.get(mount.container) === mount.host);
|
|
2515
|
+
}
|
|
2516
|
+
function makeEdgeStart(executor, container, opts, mounts) {
|
|
2384
2517
|
const { onLog } = opts;
|
|
2385
2518
|
return async (image) => {
|
|
2386
|
-
if (!await startEdgeContainer(executor, container, image, onLog)) return false;
|
|
2519
|
+
if (!await startEdgeContainer(executor, container, image, onLog, mounts)) return false;
|
|
2387
2520
|
const verdict = await verifyEdgeServing(executor, container, {
|
|
2388
2521
|
timeoutMs: opts.verifyTimeoutMs ?? 3e4
|
|
2389
2522
|
});
|
|
@@ -2418,6 +2551,17 @@ async function ensureContainerEdge(executor, opts) {
|
|
|
2418
2551
|
const { onLog } = opts;
|
|
2419
2552
|
const container = opts.container?.trim() || EDGE_CONTAINER_NAME;
|
|
2420
2553
|
const image = resolveEdgeImage(opts.image);
|
|
2554
|
+
let resolvedMounts = null;
|
|
2555
|
+
const targetMounts = async (checked) => {
|
|
2556
|
+
if (resolvedMounts) return resolvedMounts;
|
|
2557
|
+
const host = checked ?? await rootOrDegrade(executor, {
|
|
2558
|
+
purpose: "Resolving the edge's bind-mount sources",
|
|
2559
|
+
consequence: "Docker may mount a different host directory than Openship writes.",
|
|
2560
|
+
report: (message) => onLog(log5(message, "warn"))
|
|
2561
|
+
});
|
|
2562
|
+
resolvedMounts = await resolveEdgeContainerMounts(host);
|
|
2563
|
+
return resolvedMounts;
|
|
2564
|
+
};
|
|
2421
2565
|
const existing = await resolveOurEdgeContainer(executor, { fresh: true });
|
|
2422
2566
|
if (existing) {
|
|
2423
2567
|
const verdict = await verifyEdgeServing(executor, existing);
|
|
@@ -2425,27 +2569,35 @@ async function ensureContainerEdge(executor, opts) {
|
|
|
2425
2569
|
onLog(log5(`The edge container exists but isn't serving: ${verdict.reason}`, "warn"));
|
|
2426
2570
|
onLog(log5("Recreating it...", "warn"));
|
|
2427
2571
|
} else {
|
|
2428
|
-
const
|
|
2429
|
-
const
|
|
2430
|
-
if (
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
start
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2572
|
+
const mounts2 = await targetMounts();
|
|
2573
|
+
const mountsMatch = await edgeContainerMountsMatch(executor, existing, mounts2);
|
|
2574
|
+
if (mountsMatch === false) {
|
|
2575
|
+
onLog(
|
|
2576
|
+
log5("The edge container uses stale or non-canonical bind mounts; recreating it...", "warn")
|
|
2577
|
+
);
|
|
2578
|
+
} else {
|
|
2579
|
+
const current = await containerImageRef(executor, existing);
|
|
2580
|
+
const stale = current != null && current !== image;
|
|
2581
|
+
if (stale) {
|
|
2582
|
+
const start = makeEdgeStart(executor, existing, opts, mounts2);
|
|
2583
|
+
const swap = await swapManagedImage(executor, {
|
|
2584
|
+
kind: "edge",
|
|
2585
|
+
from: current ?? image,
|
|
2586
|
+
to: image,
|
|
2587
|
+
label: "edge",
|
|
2588
|
+
onLog,
|
|
2589
|
+
start
|
|
2590
|
+
});
|
|
2591
|
+
return {
|
|
2592
|
+
container: existing,
|
|
2593
|
+
image,
|
|
2594
|
+
converted: false,
|
|
2595
|
+
updated: swap.swapped,
|
|
2596
|
+
edgeDown: swap.down
|
|
2597
|
+
};
|
|
2598
|
+
}
|
|
2599
|
+
return { container: existing, image, converted: false };
|
|
2447
2600
|
}
|
|
2448
|
-
return { container: existing, image, converted: false };
|
|
2449
2601
|
}
|
|
2450
2602
|
}
|
|
2451
2603
|
if (!await dockerAvailable(executor)) {
|
|
@@ -2480,10 +2632,11 @@ async function ensureContainerEdge(executor, opts) {
|
|
|
2480
2632
|
);
|
|
2481
2633
|
});
|
|
2482
2634
|
}
|
|
2635
|
+
const mounts = await targetMounts(hostState);
|
|
2483
2636
|
const clear = await ensureEdgeClear(executor, opts.config, onLog);
|
|
2484
2637
|
try {
|
|
2485
2638
|
onLog(log5("Starting the edge container..."));
|
|
2486
|
-
if (!await startEdgeContainer(executor, container, image, onLog)) {
|
|
2639
|
+
if (!await startEdgeContainer(executor, container, image, onLog, mounts)) {
|
|
2487
2640
|
throw new Error("the edge container failed to start");
|
|
2488
2641
|
}
|
|
2489
2642
|
const verdict = await verifyEdgeServing(executor, container, {
|
|
@@ -2533,6 +2686,7 @@ export {
|
|
|
2533
2686
|
resolveEdgeImage,
|
|
2534
2687
|
containerEdgeProvider,
|
|
2535
2688
|
localContainerEdgeProvider,
|
|
2689
|
+
resolveEdgeContainerMounts,
|
|
2536
2690
|
buildEdgeRunCommand,
|
|
2537
2691
|
verifyEdgeServing,
|
|
2538
2692
|
ensureContainerEdge,
|
|
@@ -4,18 +4,20 @@ import { createRequire as __ospCreateRequire } from "node:module";
|
|
|
4
4
|
const require = __ospCreateRequire(import.meta.url);
|
|
5
5
|
import {
|
|
6
6
|
sq
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-P7RCYELG.js";
|
|
8
8
|
import {
|
|
9
|
-
assessHostSupport,
|
|
10
|
-
classifyDistro,
|
|
11
9
|
formatDuration,
|
|
12
10
|
isRemoteConnectionError,
|
|
11
|
+
systemDebug
|
|
12
|
+
} from "./chunk-N5YZMO33.js";
|
|
13
|
+
import {
|
|
14
|
+
assessHostSupport,
|
|
15
|
+
classifyDistro,
|
|
13
16
|
parseOsRelease,
|
|
14
17
|
parseSystemArch,
|
|
15
18
|
parseSystemOs,
|
|
16
|
-
safeErrorMessage
|
|
17
|
-
|
|
18
|
-
} from "./chunk-UJ6JZM65.js";
|
|
19
|
+
safeErrorMessage
|
|
20
|
+
} from "./chunk-WEZFTL4V.js";
|
|
19
21
|
|
|
20
22
|
// ../../packages/adapters/src/system/elevated-executor.ts
|
|
21
23
|
import { randomBytes } from "crypto";
|
|
@@ -13,11 +13,11 @@ var CloudInfraProvider = class {
|
|
|
13
13
|
async registerRoute(route) {
|
|
14
14
|
void route;
|
|
15
15
|
}
|
|
16
|
-
async removeRoute(domain) {
|
|
16
|
+
async removeRoute(domain, _opts) {
|
|
17
17
|
void domain;
|
|
18
18
|
}
|
|
19
19
|
// ── SSL ──────────────────────────────────────────────────────────────
|
|
20
|
-
async provisionCert(domain) {
|
|
20
|
+
async provisionCert(domain, _opts) {
|
|
21
21
|
return { domain, expiresAt: "", issuer: "oblien", verified: false };
|
|
22
22
|
}
|
|
23
23
|
async renewCert(domain) {
|
|
@@ -19,13 +19,16 @@ import {
|
|
|
19
19
|
rollbackEdgeTakeover,
|
|
20
20
|
scanImportableSites,
|
|
21
21
|
unreachableStaticRoots
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-NIYD7MD2.js";
|
|
23
23
|
import {
|
|
24
24
|
LocalExecutor
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-T6DVUX4F.js";
|
|
26
26
|
import {
|
|
27
27
|
ourEdgeContainerRunning
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-X53JXRS4.js";
|
|
29
|
+
import {
|
|
30
|
+
isOurEdgeContainer
|
|
31
|
+
} from "./chunk-D6HU4VUZ.js";
|
|
29
32
|
|
|
30
33
|
// src/lib/edge-preflight.ts
|
|
31
34
|
import { spawnSync } from "child_process";
|
|
@@ -195,7 +198,7 @@ async function diagnoseEdge() {
|
|
|
195
198
|
} catch {
|
|
196
199
|
return { ...empty, containerExists: exists, containerRunning: running };
|
|
197
200
|
}
|
|
198
|
-
const foreignOccupants = status.occupants.filter((o) => o.containerName
|
|
201
|
+
const foreignOccupants = status.occupants.filter((o) => !isOurEdgeContainer(o.containerName));
|
|
199
202
|
const hostProxySquatting = !running && foreignOccupants.some((o) => !o.containerName);
|
|
200
203
|
let sites = [];
|
|
201
204
|
if (status.occupants.length > 0) {
|
|
@@ -240,7 +243,7 @@ async function repairEdgeConflict(mode, apiPort, onLog) {
|
|
|
240
243
|
}
|
|
241
244
|
spawnSync("docker", ["restart", "openship-edge"], { stdio: "ignore" });
|
|
242
245
|
if (mode === "migrate" && scan.sites.length > 0) {
|
|
243
|
-
const { importMigratedSites } = await import("./edge-import-
|
|
246
|
+
const { importMigratedSites } = await import("./edge-import-YJVJUGYC.js");
|
|
244
247
|
const outcome = await importMigratedSites(apiPort, scan.sites, certPems);
|
|
245
248
|
if (outcome.registered.length === 0) {
|
|
246
249
|
return {
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
|
|
3
3
|
import { createRequire as __ospCreateRequire } from "node:module";
|
|
4
4
|
const require = __ospCreateRequire(import.meta.url);
|
|
5
|
+
import {
|
|
6
|
+
shellQuote
|
|
7
|
+
} from "./chunk-WEZFTL4V.js";
|
|
5
8
|
|
|
6
9
|
// ../../packages/adapters/src/system/local-shell.ts
|
|
7
10
|
import { execFile } from "child_process";
|
|
@@ -28,9 +31,7 @@ var LOCAL_BUILD_ENV_KEYS = [
|
|
|
28
31
|
"XDG_CACHE_HOME",
|
|
29
32
|
"XDG_DATA_HOME"
|
|
30
33
|
];
|
|
31
|
-
|
|
32
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
33
|
-
}
|
|
34
|
+
var sq = shellQuote;
|
|
34
35
|
function logEntry(message, level = "info", rawData) {
|
|
35
36
|
return { timestamp: (/* @__PURE__ */ new Date()).toISOString(), message, level, ...rawData ? { rawData } : {} };
|
|
36
37
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
|
|
3
|
+
import { createRequire as __ospCreateRequire } from "node:module";
|
|
4
|
+
const require = __ospCreateRequire(import.meta.url);
|
|
5
|
+
|
|
6
|
+
// ../../packages/adapters/src/system/reachability.ts
|
|
7
|
+
import { connect } from "net";
|
|
8
|
+
import { request } from "http";
|
|
9
|
+
function classifyDialError(code) {
|
|
10
|
+
switch (code) {
|
|
11
|
+
case "ECONNREFUSED":
|
|
12
|
+
case "ECONNRESET":
|
|
13
|
+
return "refused";
|
|
14
|
+
case "ENOTFOUND":
|
|
15
|
+
case "EAI_AGAIN":
|
|
16
|
+
return "unresolved";
|
|
17
|
+
case "EHOSTUNREACH":
|
|
18
|
+
case "ENETUNREACH":
|
|
19
|
+
case "ENETDOWN":
|
|
20
|
+
return "no_route";
|
|
21
|
+
// The kernel's own connect timeout, i.e. our `timeout` reported for us.
|
|
22
|
+
case "ETIMEDOUT":
|
|
23
|
+
return "timeout";
|
|
24
|
+
default:
|
|
25
|
+
return "error";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function probeTcpDetailed(host, port, timeoutMs = 2500) {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
let settled = false;
|
|
31
|
+
const done = (result, socket2) => {
|
|
32
|
+
if (settled) return;
|
|
33
|
+
settled = true;
|
|
34
|
+
try {
|
|
35
|
+
socket2?.destroy();
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
resolve(result);
|
|
39
|
+
};
|
|
40
|
+
const socket = connect({ host, port });
|
|
41
|
+
socket.setTimeout(timeoutMs);
|
|
42
|
+
socket.once("connect", () => done({ ok: true }, socket));
|
|
43
|
+
socket.once(
|
|
44
|
+
"timeout",
|
|
45
|
+
() => done({ ok: false, reason: "timeout", message: `no response within ${timeoutMs}ms` }, socket)
|
|
46
|
+
);
|
|
47
|
+
socket.once("error", (err) => {
|
|
48
|
+
const errno = err;
|
|
49
|
+
done(
|
|
50
|
+
{
|
|
51
|
+
ok: false,
|
|
52
|
+
reason: classifyDialError(errno.code),
|
|
53
|
+
...errno.code ? { code: errno.code } : {},
|
|
54
|
+
...errno.message ? { message: errno.message } : {}
|
|
55
|
+
},
|
|
56
|
+
socket
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function probeHostedHttp(opts) {
|
|
62
|
+
const { hostname, path = "/", port = 80, timeoutMs = 3e3 } = opts;
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
let settled = false;
|
|
65
|
+
const done = (value) => {
|
|
66
|
+
if (settled) return;
|
|
67
|
+
settled = true;
|
|
68
|
+
resolve(value);
|
|
69
|
+
};
|
|
70
|
+
const req = request(
|
|
71
|
+
{
|
|
72
|
+
host: "127.0.0.1",
|
|
73
|
+
port,
|
|
74
|
+
path,
|
|
75
|
+
method: "GET",
|
|
76
|
+
timeout: timeoutMs,
|
|
77
|
+
// Claim https so the :80 block SERVES instead of answering our own
|
|
78
|
+
// HTTP→HTTPS redirect before the doc root is consulted — see the curl twin in
|
|
79
|
+
// output-exists.ts for why a 301 here read as a healthy site.
|
|
80
|
+
headers: { Host: hostname, "X-Forwarded-Proto": "https" }
|
|
81
|
+
},
|
|
82
|
+
(res) => {
|
|
83
|
+
const status = res.statusCode ?? 0;
|
|
84
|
+
res.resume();
|
|
85
|
+
done(
|
|
86
|
+
status > 0 ? { status, served: status >= 200 && status < 400 || status === 401 || status === 403 } : null
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
const fail = () => {
|
|
91
|
+
try {
|
|
92
|
+
req.destroy();
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
done(null);
|
|
96
|
+
};
|
|
97
|
+
req.once("timeout", fail);
|
|
98
|
+
req.once("error", fail);
|
|
99
|
+
req.end();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export {
|
|
104
|
+
probeTcpDetailed,
|
|
105
|
+
probeHostedHttp
|
|
106
|
+
};
|