openship 0.6.0 → 0.6.5
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 +7 -2
- package/dist/{bare-FSUAM5V5.js → bare-XQIPDO4R.js} +10 -9
- package/dist/{chunk-PSZSKEPA.js → chunk-3ZAGC5NT.js} +64 -17
- package/dist/{chunk-6ODUBKUB.js → chunk-5OUWIPOB.js} +69 -7
- package/dist/chunk-6HOA5JT7.js +124 -0
- package/dist/{chunk-TWJCLD4W.js → chunk-6JELW75A.js} +4 -4
- package/dist/{chunk-GUQ3QCJ7.js → chunk-7334NSJL.js} +399 -369
- package/dist/chunk-7S4USQYF.js +1753 -0
- package/dist/{chunk-3LDWWY7U.js → chunk-A7EWM2KA.js} +41 -7
- package/dist/chunk-ELGRMZOW.js +25 -0
- package/dist/chunk-EZPPTWJQ.js +292 -0
- package/dist/{chunk-IXM5245F.js → chunk-GRTXMUHA.js} +553 -538
- package/dist/chunk-HPXP6LXZ.js +428 -0
- package/dist/{chunk-OTLSJYAC.js → chunk-LEYSL46B.js} +2135 -85
- package/dist/{chunk-EMXI4UCU.js → chunk-LFCSC4IU.js} +29 -10
- package/dist/{chunk-JIMUYEKN.js → chunk-MSZUCNPV.js} +1 -1
- package/dist/{chunk-OGY63DGD.js → chunk-PZRRL5PC.js} +71 -28
- package/dist/{chunk-VE2YOK4I.js → chunk-QLX7NBEA.js} +1218 -588
- package/dist/chunk-RFWJE7ZN.js +68 -0
- package/dist/{chunk-VDDMADOF.js → chunk-TM2IQPYU.js} +16 -2
- package/dist/{chunk-3FYIXVX5.js → chunk-VDESJSG6.js} +451 -106
- package/dist/{chunk-B3CE2OMR.js → chunk-VHMOQL5Y.js} +276 -50
- package/dist/{chunk-QEH4QUBX.js → chunk-VLQ3UIVO.js} +393 -53
- package/dist/{chunk-VZ2ZHPS2.js → chunk-WE7BXW6L.js} +3 -9
- package/dist/chunk-WFRXYWK2.js +444 -0
- package/dist/chunk-XRNRT37W.js +316 -0
- package/dist/{chunk-EP2TNALB.js → chunk-XVOBL7BC.js} +31 -8
- package/dist/cloud-3DODOWTR.js +36 -0
- package/dist/{detect-ALF4GXRX.js → detect-P4CIQBRH.js} +3 -3
- package/dist/{docker-QECQJ5P4.js → docker-3K2NEBD6.js} +15 -8
- package/dist/{docker-edge-executor-62N64MGS.js → docker-edge-executor-YZZ6P7IC.js} +5 -5
- package/dist/{edge-import-CQWHN2Q3.js → edge-import-MCJNVTZ2.js} +26 -23
- package/dist/edge-preflight-ES6FH4JK.js +42 -0
- package/dist/{ensure-container-edge-MJVXJQG5.js → ensure-container-edge-YPITNH45.js} +9 -8
- package/dist/{executor-JELARVKC.js → executor-DZGX55K2.js} +12 -5
- package/dist/host-channel-preflight-MUXE3KZU.js +49 -0
- package/dist/index.js +3668 -4063
- package/dist/{nginx-AXGNWRKZ.js → nginx-NSAHWGJV.js} +11 -5
- package/dist/node-bootstrap.js +241 -0
- package/dist/node-entry.js +52 -1
- package/dist/{openresty-lua-IE4P5YQX.js → openresty-lua-DR4D3EZ6.js} +6 -2
- package/dist/privilege-YTVI4SKF.js +19 -0
- package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +4 -1
- package/dist/server/index.js +21501 -13137
- package/dist/server/lua/geo_country.lua +4 -3
- package/dist/server/lua/pipe_stream.lua +52 -39
- package/dist/server/lua/site_logger.lua +22 -24
- package/dist/server/migrations/0098_instance_product_mode.sql +12 -0
- package/dist/server/migrations/0099_mail_webmail_project.sql +24 -0
- package/dist/server/migrations/0100_server_ssh_private_key.sql +11 -0
- package/dist/server/migrations/0101_mail_resume_step.sql +12 -0
- package/dist/server/migrations/0102_project_workload_axes.sql +19 -0
- package/dist/server/migrations/0103_dns_credential.sql +41 -0
- package/dist/server/migrations/0104_instance_host_control.sql +15 -0
- package/dist/server/migrations/meta/_journal.json +49 -0
- package/dist/{setup-FA4PW4NU.js → setup-WUUZURHS.js} +10 -9
- package/dist/up-XEO3SR6Z.js +58 -0
- package/package.json +4 -2
- package/dist/chunk-IW2UKBVF.js +0 -144
- package/dist/chunk-ZCFVHA25.js +0 -89
- package/dist/cloud-E5M6VMCG.js +0 -31
- package/dist/server/lua/pipe_log.lua +0 -77
- /package/dist/{dist-es-JGBPABJI.js → dist-es-ZP5QDSJ3.js} +0 -0
|
@@ -2,14 +2,6 @@
|
|
|
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
|
-
resolveEnvironment
|
|
7
|
-
} from "./chunk-IW2UKBVF.js";
|
|
8
|
-
import {
|
|
9
|
-
formatDuration,
|
|
10
|
-
isRemoteConnectionError,
|
|
11
|
-
systemDebug
|
|
12
|
-
} from "./chunk-ZCFVHA25.js";
|
|
13
5
|
import {
|
|
14
6
|
containerCommand,
|
|
15
7
|
edgeContainerExecutor,
|
|
@@ -17,7 +9,12 @@ import {
|
|
|
17
9
|
readDeclaredPair,
|
|
18
10
|
readMaybeInContainer,
|
|
19
11
|
validateCertFor
|
|
20
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-VLQ3UIVO.js";
|
|
13
|
+
import {
|
|
14
|
+
privilegedExecutor,
|
|
15
|
+
rootChecked,
|
|
16
|
+
rootOrDegrade
|
|
17
|
+
} from "./chunk-RFWJE7ZN.js";
|
|
21
18
|
import {
|
|
22
19
|
EDGE_CONTAINER_NAME,
|
|
23
20
|
EdgeConflictError,
|
|
@@ -32,28 +29,41 @@ import {
|
|
|
32
29
|
sanitizeEdgeVhosts,
|
|
33
30
|
sq as sq2,
|
|
34
31
|
stopTargetsForStatus
|
|
35
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-WE7BXW6L.js";
|
|
36
33
|
import {
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
probeExec,
|
|
35
|
+
tryExec,
|
|
36
|
+
waitForPortListening,
|
|
37
|
+
withReason
|
|
38
|
+
} from "./chunk-A7EWM2KA.js";
|
|
39
39
|
import {
|
|
40
40
|
EDGE_CHALLENGE_HOST_DIR,
|
|
41
41
|
EDGE_CONTAINER_MOUNTS,
|
|
42
42
|
EDGE_HOST_PATHS,
|
|
43
43
|
OPENRESTY_DEFAULT_PATHS
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-PZRRL5PC.js";
|
|
45
|
+
import {
|
|
46
|
+
envOps,
|
|
47
|
+
opScript
|
|
48
|
+
} from "./chunk-EZPPTWJQ.js";
|
|
49
|
+
import {
|
|
50
|
+
resolveEnvironment
|
|
51
|
+
} from "./chunk-WFRXYWK2.js";
|
|
45
52
|
import {
|
|
46
53
|
sq
|
|
47
54
|
} from "./chunk-ZFJXPCQP.js";
|
|
48
55
|
import {
|
|
49
56
|
AppError,
|
|
50
57
|
PROXY_DIRECTIVES,
|
|
58
|
+
answered,
|
|
51
59
|
buildEdgeImageRef,
|
|
52
60
|
compareSemver,
|
|
61
|
+
formatDuration,
|
|
53
62
|
parseProxyValue,
|
|
54
63
|
safeErrorMessage,
|
|
55
|
-
sanitizeProxySettings
|
|
56
|
-
|
|
64
|
+
sanitizeProxySettings,
|
|
65
|
+
systemDebug
|
|
66
|
+
} from "./chunk-LEYSL46B.js";
|
|
57
67
|
|
|
58
68
|
// ../../packages/adapters/src/system/managed-image.ts
|
|
59
69
|
import { posix } from "path";
|
|
@@ -120,14 +130,7 @@ var JOURNAL_PATH = `${JOURNAL_DIR}/edge-takeover.json`;
|
|
|
120
130
|
function log2(message, level = "info") {
|
|
121
131
|
return { timestamp: (/* @__PURE__ */ new Date()).toISOString(), message, level };
|
|
122
132
|
}
|
|
123
|
-
async function
|
|
124
|
-
try {
|
|
125
|
-
return await executor.exec(cmd);
|
|
126
|
-
} catch {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
async function buildJournal(executor, status) {
|
|
133
|
+
async function buildJournal(executor, status, onLog) {
|
|
131
134
|
const units = /* @__PURE__ */ new Map();
|
|
132
135
|
const containers = /* @__PURE__ */ new Map();
|
|
133
136
|
const processes = /* @__PURE__ */ new Map();
|
|
@@ -137,7 +140,17 @@ async function buildJournal(executor, status) {
|
|
|
137
140
|
executor,
|
|
138
141
|
`docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' ${sq2(o.containerName)} 2>/dev/null`
|
|
139
142
|
);
|
|
140
|
-
|
|
143
|
+
if (r === null) {
|
|
144
|
+
onLog?.(
|
|
145
|
+
log2(
|
|
146
|
+
`Could not read ${o.containerName}'s restart policy. If this takeover is rolled back it will be started again, but its policy left untouched \u2014 check it with \`docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' ${o.containerName}\`.`,
|
|
147
|
+
"warn"
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
containers.set(o.containerName, { name: o.containerName });
|
|
151
|
+
} else {
|
|
152
|
+
containers.set(o.containerName, { name: o.containerName, restart: r.trim() || "no" });
|
|
153
|
+
}
|
|
141
154
|
} else if (o.systemdUnit && !units.has(o.systemdUnit)) {
|
|
142
155
|
const en = await tryExec(executor, `systemctl is-enabled ${sq2(o.systemdUnit)} 2>/dev/null`);
|
|
143
156
|
units.set(o.systemdUnit, { unit: o.systemdUnit, wasEnabled: en?.trim() === "enabled" });
|
|
@@ -152,19 +165,44 @@ async function buildJournal(executor, status) {
|
|
|
152
165
|
processes: [...processes.values()]
|
|
153
166
|
};
|
|
154
167
|
}
|
|
168
|
+
function journalFileExecutor(executor) {
|
|
169
|
+
return rootOrDegrade(executor, {
|
|
170
|
+
purpose: "Writing the edge-takeover rollback journal",
|
|
171
|
+
consequence: "An interrupted takeover won't be repaired automatically on the next boot.",
|
|
172
|
+
report: () => {
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
155
176
|
async function writeJournal(executor, journal) {
|
|
156
177
|
try {
|
|
157
|
-
await executor
|
|
158
|
-
await
|
|
178
|
+
const privileged = await journalFileExecutor(executor);
|
|
179
|
+
await privileged.mkdir(JOURNAL_DIR);
|
|
180
|
+
await privileged.writeFile(JOURNAL_PATH, JSON.stringify(journal, null, 2));
|
|
159
181
|
} catch {
|
|
160
182
|
}
|
|
161
183
|
}
|
|
162
184
|
async function clearJournal(executor) {
|
|
163
|
-
await tryExec(executor, `rm -f ${JOURNAL_PATH}`);
|
|
164
|
-
}
|
|
165
|
-
async function readJournal(executor) {
|
|
166
|
-
const
|
|
167
|
-
|
|
185
|
+
await tryExec(await journalFileExecutor(executor), `rm -f ${JOURNAL_PATH}`);
|
|
186
|
+
}
|
|
187
|
+
async function readJournal(executor, onLog) {
|
|
188
|
+
const privileged = await journalFileExecutor(executor);
|
|
189
|
+
const raw = await tryExec(privileged, `cat ${JOURNAL_PATH} 2>/dev/null`);
|
|
190
|
+
if (raw === null) {
|
|
191
|
+
const listed = await tryExec(privileged, `ls ${JOURNAL_PATH} 2>/dev/null`);
|
|
192
|
+
if (listed?.trim()) {
|
|
193
|
+
onLog?.(
|
|
194
|
+
log2(
|
|
195
|
+
`An unfinished edge takeover is recorded at ${JOURNAL_PATH}, but Openship could not read it as this user \u2014 the previous proxy will NOT be restored automatically. Re-run as root, or restore it by hand.`,
|
|
196
|
+
"error"
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
if (!raw.trim()) {
|
|
203
|
+
await clearJournal(executor);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
168
206
|
try {
|
|
169
207
|
return JSON.parse(raw);
|
|
170
208
|
} catch {
|
|
@@ -174,26 +212,43 @@ async function readJournal(executor) {
|
|
|
174
212
|
}
|
|
175
213
|
async function rollback(executor, journal, onLog) {
|
|
176
214
|
onLog(log2("Rolling back \u2014 restoring the previous proxy...", "warn"));
|
|
177
|
-
await
|
|
178
|
-
|
|
215
|
+
const restore = await rootOrDegrade(executor, {
|
|
216
|
+
purpose: "Restarting the proxy that was holding ports 80/443",
|
|
217
|
+
consequence: "Restarting and re-enabling it will likely be refused \u2014 so if :80 is served after this, it is because nothing was ever stopped, not because Openship put it back.",
|
|
218
|
+
report: (message) => onLog(log2(message, "warn"))
|
|
219
|
+
});
|
|
220
|
+
await tryExec(restore, `docker update --restart=no ${sq2(EDGE_CONTAINER_NAME)} 2>/dev/null || true`);
|
|
221
|
+
await tryExec(restore, `docker stop ${sq2(EDGE_CONTAINER_NAME)} 2>/dev/null || true`);
|
|
179
222
|
await tryExec(
|
|
180
|
-
|
|
223
|
+
restore,
|
|
181
224
|
"systemctl disable --now openresty 2>/dev/null || systemctl stop openresty 2>/dev/null || true; systemctl reset-failed openresty 2>/dev/null || true"
|
|
182
225
|
);
|
|
183
226
|
for (const u of journal.units) {
|
|
184
227
|
await tryExec(
|
|
185
|
-
|
|
228
|
+
restore,
|
|
186
229
|
u.wasEnabled ? `systemctl enable --now ${sq2(u.unit)} 2>/dev/null || true` : `systemctl start ${sq2(u.unit)} 2>/dev/null || true`
|
|
187
230
|
);
|
|
188
231
|
}
|
|
189
232
|
for (const c of journal.containers) {
|
|
190
|
-
|
|
191
|
-
|
|
233
|
+
if (c.restart) {
|
|
234
|
+
await tryExec(
|
|
235
|
+
restore,
|
|
236
|
+
`docker update --restart=${sq2(c.restart)} ${sq2(c.name)} 2>/dev/null || true`
|
|
237
|
+
);
|
|
238
|
+
} else {
|
|
239
|
+
onLog(
|
|
240
|
+
log2(
|
|
241
|
+
`Starting ${c.name} without setting a restart policy \u2014 Openship never managed to read the one it had. Confirm it still comes back on reboot.`,
|
|
242
|
+
"warn"
|
|
243
|
+
)
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
await tryExec(restore, `docker start ${sq2(c.name)} 2>/dev/null || true`);
|
|
192
247
|
}
|
|
193
248
|
for (const p of journal.processes ?? []) {
|
|
194
249
|
if (p.command) {
|
|
195
250
|
await tryExec(
|
|
196
|
-
|
|
251
|
+
restore,
|
|
197
252
|
`setsid -f sh -c ${sq2(p.command)} 2>/dev/null || (nohup sh -c ${sq2(p.command)} >/dev/null 2>&1 &) || true`
|
|
198
253
|
);
|
|
199
254
|
} else {
|
|
@@ -219,16 +274,29 @@ async function portIsServed(executor, port) {
|
|
|
219
274
|
);
|
|
220
275
|
return (out ?? "").includes("yes");
|
|
221
276
|
}
|
|
277
|
+
var EDGE_STOP_ELEVATION = {
|
|
278
|
+
purpose: "Stopping the proxy that holds ports 80/443",
|
|
279
|
+
consequence: "Stopping and disabling it will likely be refused, leaving the ports bound."
|
|
280
|
+
};
|
|
222
281
|
async function beginEdgeTakeover(executor, status, onLog, targets) {
|
|
223
|
-
await writeJournal(executor, await buildJournal(executor, status));
|
|
224
|
-
|
|
225
|
-
|
|
282
|
+
await writeJournal(executor, await buildJournal(executor, status, onLog));
|
|
283
|
+
let privilegeDegraded = false;
|
|
284
|
+
const stopExecutor = await rootOrDegrade(executor, {
|
|
285
|
+
...EDGE_STOP_ELEVATION,
|
|
286
|
+
report: (message) => {
|
|
287
|
+
privilegeDegraded = true;
|
|
288
|
+
onLog(log2(message, "warn"));
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
const freed = await freeEdgeTargets(
|
|
292
|
+
stopExecutor,
|
|
226
293
|
targets?.length ? targets : stopTargetsForStatus(status),
|
|
227
294
|
(m, l) => onLog(log2(m, l))
|
|
228
295
|
);
|
|
296
|
+
return { ...freed, privilegeDegraded };
|
|
229
297
|
}
|
|
230
298
|
async function rollbackEdgeTakeover(executor, onLog) {
|
|
231
|
-
const journal = await readJournal(executor);
|
|
299
|
+
const journal = await readJournal(executor, onLog);
|
|
232
300
|
if (!journal || journal.completed) return false;
|
|
233
301
|
const restored = await rollback(executor, journal, onLog);
|
|
234
302
|
await clearJournal(executor);
|
|
@@ -243,7 +311,7 @@ async function completeEdgeTakeover(executor) {
|
|
|
243
311
|
await clearJournal(executor);
|
|
244
312
|
}
|
|
245
313
|
async function recoverInterruptedTakeover(executor, onLog, isEdgeHealthy) {
|
|
246
|
-
const journal = await readJournal(executor);
|
|
314
|
+
const journal = await readJournal(executor, onLog);
|
|
247
315
|
if (!journal) return;
|
|
248
316
|
if (journal.completed) {
|
|
249
317
|
await clearJournal(executor);
|
|
@@ -260,13 +328,6 @@ async function recoverInterruptedTakeover(executor, onLog, isEdgeHealthy) {
|
|
|
260
328
|
}
|
|
261
329
|
|
|
262
330
|
// ../../packages/adapters/src/system/proxy/import/parse-utils.ts
|
|
263
|
-
async function tryExec2(executor, command) {
|
|
264
|
-
try {
|
|
265
|
-
return await executor.exec(command);
|
|
266
|
-
} catch {
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
331
|
function stripComments(text) {
|
|
271
332
|
return text.split("\n").map((line) => {
|
|
272
333
|
let quote;
|
|
@@ -330,7 +391,7 @@ function sq3(value) {
|
|
|
330
391
|
// ../../packages/adapters/src/system/proxy/import/nginx.ts
|
|
331
392
|
async function dumpResolvedConfig(executor, bins) {
|
|
332
393
|
for (const bin of bins) {
|
|
333
|
-
const dumped = await
|
|
394
|
+
const dumped = await tryExec(executor, `${bin} -T 2>/dev/null`);
|
|
334
395
|
if (dumped && /server\s*\{/.test(dumped)) return dumped;
|
|
335
396
|
}
|
|
336
397
|
return null;
|
|
@@ -338,7 +399,7 @@ async function dumpResolvedConfig(executor, bins) {
|
|
|
338
399
|
async function loadNginxConfig(executor) {
|
|
339
400
|
const dumped = await dumpResolvedConfig(executor, ["nginx", "openresty"]);
|
|
340
401
|
if (dumped) return dumped;
|
|
341
|
-
const cat = await
|
|
402
|
+
const cat = await tryExec(
|
|
342
403
|
executor,
|
|
343
404
|
"cat /etc/nginx/nginx.conf /etc/nginx/sites-enabled/* /etc/nginx/conf.d/*.conf 2>/dev/null"
|
|
344
405
|
);
|
|
@@ -533,11 +594,11 @@ async function scanForeignOpenResty(executor) {
|
|
|
533
594
|
}
|
|
534
595
|
async function scanOpenshipEdge(executor) {
|
|
535
596
|
const cat = `cat ${OUR_EDGE_SITE_GLOBS.join(" ")} 2>/dev/null`;
|
|
536
|
-
const raw = await
|
|
597
|
+
const raw = await tryExec(executor, cat);
|
|
537
598
|
if (raw?.trim()) return parseNginxConfig(raw);
|
|
538
599
|
const container = await resolveOurEdgeContainer(executor);
|
|
539
600
|
if (!container) return parseNginxConfig("");
|
|
540
|
-
const fromContainer = await
|
|
601
|
+
const fromContainer = await tryExec(executor, containerCommand(container, cat));
|
|
541
602
|
return parseNginxConfig(fromContainer ?? "");
|
|
542
603
|
}
|
|
543
604
|
|
|
@@ -545,12 +606,12 @@ async function scanOpenshipEdge(executor) {
|
|
|
545
606
|
var CADDYFILE_PATHS = ["/etc/caddy/Caddyfile", "/etc/Caddyfile"];
|
|
546
607
|
async function loadCaddyAdminConfig(executor) {
|
|
547
608
|
const endpoint = "http://localhost:2019/config/";
|
|
548
|
-
const out = await
|
|
609
|
+
const out = await tryExec(executor, `curl -fsS ${endpoint} 2>/dev/null`) || await tryExec(executor, `wget -qO- ${endpoint} 2>/dev/null`);
|
|
549
610
|
return out && out.trim().startsWith("{") ? out : null;
|
|
550
611
|
}
|
|
551
612
|
async function loadCaddyJson(executor) {
|
|
552
613
|
for (const p of CADDYFILE_PATHS) {
|
|
553
|
-
const out = await
|
|
614
|
+
const out = await tryExec(executor, `caddy adapt --adapter caddyfile --config ${p} 2>/dev/null`);
|
|
554
615
|
if (out && out.trim().startsWith("{")) return out;
|
|
555
616
|
}
|
|
556
617
|
return null;
|
|
@@ -665,7 +726,7 @@ function parseCaddyJson(raw) {
|
|
|
665
726
|
}
|
|
666
727
|
async function loadCaddyfile(executor) {
|
|
667
728
|
for (const p of CADDYFILE_PATHS) {
|
|
668
|
-
const out = await
|
|
729
|
+
const out = await tryExec(executor, `cat ${p} 2>/dev/null`);
|
|
669
730
|
if (out && out.trim()) return out;
|
|
670
731
|
}
|
|
671
732
|
return "";
|
|
@@ -800,7 +861,7 @@ async function scanCaddyText(executor) {
|
|
|
800
861
|
|
|
801
862
|
// ../../packages/adapters/src/system/proxy/import/apache.ts
|
|
802
863
|
async function vhostFilesFromDump(executor) {
|
|
803
|
-
const dump = await
|
|
864
|
+
const dump = await tryExec(
|
|
804
865
|
executor,
|
|
805
866
|
"apachectl -S 2>/dev/null || apache2ctl -S 2>/dev/null || httpd -S 2>/dev/null"
|
|
806
867
|
);
|
|
@@ -815,10 +876,10 @@ async function vhostFilesFromDump(executor) {
|
|
|
815
876
|
async function loadApacheConfig(executor) {
|
|
816
877
|
const files = await vhostFilesFromDump(executor);
|
|
817
878
|
if (files.length > 0) {
|
|
818
|
-
const cat2 = await
|
|
879
|
+
const cat2 = await tryExec(executor, `cat ${files.map(sq3).join(" ")} 2>/dev/null`);
|
|
819
880
|
if (cat2 && cat2.trim()) return cat2;
|
|
820
881
|
}
|
|
821
|
-
const cat = await
|
|
882
|
+
const cat = await tryExec(
|
|
822
883
|
executor,
|
|
823
884
|
"cat /etc/apache2/sites-enabled/*.conf /etc/apache2/apache2.conf /etc/httpd/conf.d/*.conf /etc/httpd/conf/httpd.conf 2>/dev/null"
|
|
824
885
|
);
|
|
@@ -1145,7 +1206,7 @@ function parseTraefikLabels(containers) {
|
|
|
1145
1206
|
};
|
|
1146
1207
|
}
|
|
1147
1208
|
async function scanTraefik(executor) {
|
|
1148
|
-
const out = await
|
|
1209
|
+
const out = await tryExec(
|
|
1149
1210
|
executor,
|
|
1150
1211
|
`docker ps -q 2>/dev/null | xargs -r docker inspect --format '{{.Name}} {{json .Config.Labels}} {{range $n, $v := .NetworkSettings.Networks}}{{$n}}={{$v.IPAddress}} {{end}} {{range $p, $v := .Config.ExposedPorts}}{{$p}} {{end}} {{if .Config.Cmd}}{{join .Config.Cmd " "}}{{end}}' 2>/dev/null`
|
|
1151
1212
|
);
|
|
@@ -1277,10 +1338,10 @@ function candidateNames(host) {
|
|
|
1277
1338
|
async function findPairIn(exec, root, host, container) {
|
|
1278
1339
|
for (const name of candidateNames(host)) {
|
|
1279
1340
|
const glob = `${root}/certificates/*/${name}/${name}.crt`;
|
|
1280
|
-
let certPath = firstCrt(await
|
|
1341
|
+
let certPath = firstCrt(await tryExec(exec, `ls -1 ${glob} 2>/dev/null`));
|
|
1281
1342
|
if (!certPath && container) {
|
|
1282
1343
|
certPath = firstCrt(
|
|
1283
|
-
await
|
|
1344
|
+
await tryExec(exec, containerCommand(container, `ls -1 ${glob} 2>/dev/null`))
|
|
1284
1345
|
);
|
|
1285
1346
|
}
|
|
1286
1347
|
if (certPath) return { certPath, keyPath: certPath.replace(/\.crt$/, ".key") };
|
|
@@ -1326,7 +1387,7 @@ async function candidateStoragePaths(exec, container) {
|
|
|
1326
1387
|
};
|
|
1327
1388
|
if (container) {
|
|
1328
1389
|
collect(
|
|
1329
|
-
await
|
|
1390
|
+
await tryExec(
|
|
1330
1391
|
exec,
|
|
1331
1392
|
`docker inspect ${sq2(container)} --format '{{json .Config.Cmd}} {{json .Args}} {{json .Config.Env}}' 2>/dev/null`
|
|
1332
1393
|
)
|
|
@@ -1558,61 +1619,21 @@ function makeApi(exec, kind, ours, container) {
|
|
|
1558
1619
|
}
|
|
1559
1620
|
|
|
1560
1621
|
// ../../packages/adapters/src/system/catalog.ts
|
|
1622
|
+
var MIN_DOCKER_VERSION = "20.10";
|
|
1561
1623
|
function dockerInstallPlan(profile) {
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
supported: true,
|
|
1570
|
-
installCommand: "curl -fsSL https://get.docker.com | sh",
|
|
1571
|
-
startCommand: profile.serviceManager === "systemd" ? "systemctl enable docker && systemctl start docker" : void 0,
|
|
1624
|
+
const ops = envOps(profile);
|
|
1625
|
+
const install = ops.dockerInstall();
|
|
1626
|
+
if (!install.supported) return install;
|
|
1627
|
+
const start = ops.dockerStart();
|
|
1628
|
+
return answered({
|
|
1629
|
+
installCommand: opScript(install.value),
|
|
1630
|
+
service: start.supported ? answered(opScript(start.value)) : start,
|
|
1572
1631
|
verifyCommand: "docker --version"
|
|
1573
|
-
};
|
|
1574
|
-
}
|
|
1575
|
-
function gitInstallPlan(profile) {
|
|
1576
|
-
const commands = {
|
|
1577
|
-
apt: "apt-get update -qq && apt-get install -y -qq git",
|
|
1578
|
-
dnf: "dnf install -y git",
|
|
1579
|
-
yum: "yum install -y git",
|
|
1580
|
-
apk: "apk add --no-cache git",
|
|
1581
|
-
brew: "brew install git"
|
|
1582
|
-
};
|
|
1583
|
-
const installCommand = commands[profile.packageManager];
|
|
1584
|
-
if (!installCommand) {
|
|
1585
|
-
return {
|
|
1586
|
-
supported: false,
|
|
1587
|
-
unsupportedReason: "No supported package manager found for Git installation"
|
|
1588
|
-
};
|
|
1589
|
-
}
|
|
1590
|
-
return {
|
|
1591
|
-
supported: true,
|
|
1592
|
-
installCommand,
|
|
1593
|
-
verifyCommand: "git --version"
|
|
1594
|
-
};
|
|
1632
|
+
});
|
|
1595
1633
|
}
|
|
1596
|
-
function
|
|
1597
|
-
const
|
|
1598
|
-
|
|
1599
|
-
dnf: "dnf install -y rsync",
|
|
1600
|
-
yum: "yum install -y rsync",
|
|
1601
|
-
apk: "apk add --no-cache rsync",
|
|
1602
|
-
brew: "brew install rsync"
|
|
1603
|
-
};
|
|
1604
|
-
const installCommand = commands[profile.packageManager];
|
|
1605
|
-
if (!installCommand) {
|
|
1606
|
-
return {
|
|
1607
|
-
supported: false,
|
|
1608
|
-
unsupportedReason: "No supported package manager found for rsync installation"
|
|
1609
|
-
};
|
|
1610
|
-
}
|
|
1611
|
-
return {
|
|
1612
|
-
supported: true,
|
|
1613
|
-
installCommand,
|
|
1614
|
-
verifyCommand: "rsync --version | head -n 1"
|
|
1615
|
-
};
|
|
1634
|
+
function packagePlan(profile, pkg, verifyCommand) {
|
|
1635
|
+
const install = envOps(profile).pkgInstall([pkg]);
|
|
1636
|
+
return install.supported ? answered({ installCommand: opScript(install.value), verifyCommand }) : install;
|
|
1616
1637
|
}
|
|
1617
1638
|
var systemCatalog = {
|
|
1618
1639
|
checks: {
|
|
@@ -1626,7 +1647,11 @@ var systemCatalog = {
|
|
|
1626
1647
|
daemonCommand: "docker info --format '{{.ServerVersion}}'",
|
|
1627
1648
|
parseVersion: (output) => output.match(/Docker version ([^\s,]+)/)?.[1] ?? output,
|
|
1628
1649
|
missingMessage: "Docker is not installed",
|
|
1629
|
-
notRunningMessage: "Docker is installed but the daemon is not running"
|
|
1650
|
+
notRunningMessage: "Docker is installed but the daemon is not running",
|
|
1651
|
+
// Deliberately does NOT claim the daemon is up or down: the socket's permission
|
|
1652
|
+
// is checked before the daemon is asked, so this arm proves only that we may not
|
|
1653
|
+
// ask. What it can state is the fix, which is the part "not running" got wrong.
|
|
1654
|
+
deniedMessage: "Docker is installed but this connection is not allowed to use its socket \u2014 add the login user to the `docker` group, or connect as root"
|
|
1630
1655
|
},
|
|
1631
1656
|
openresty: {
|
|
1632
1657
|
versionCommand: "openresty -v 2>&1 || /usr/local/openresty/bin/openresty -v 2>&1",
|
|
@@ -1660,185 +1685,9 @@ var systemCatalog = {
|
|
|
1660
1685
|
// CONTAINER. See installer.ts `installContainerEdge`.
|
|
1661
1686
|
installs: {
|
|
1662
1687
|
docker: dockerInstallPlan,
|
|
1663
|
-
git:
|
|
1664
|
-
rsync:
|
|
1665
|
-
}
|
|
1666
|
-
};
|
|
1667
|
-
|
|
1668
|
-
// ../../packages/adapters/src/system/elevated-executor.ts
|
|
1669
|
-
var INSTALL_ENV = "DEBIAN_FRONTEND=noninteractive DPKG_FORCE=confnew";
|
|
1670
|
-
function elevateCommand(command) {
|
|
1671
|
-
return `sudo -n sh -c ${sq(`export ${INSTALL_ENV}; ${command}`)}`;
|
|
1672
|
-
}
|
|
1673
|
-
function dirOf(path) {
|
|
1674
|
-
const idx = path.lastIndexOf("/");
|
|
1675
|
-
if (idx < 0) return ".";
|
|
1676
|
-
return idx === 0 ? "/" : path.slice(0, idx);
|
|
1677
|
-
}
|
|
1678
|
-
function elevatedExecutor(inner) {
|
|
1679
|
-
const writeFileElevated = async (path, content) => {
|
|
1680
|
-
const tmp = `/tmp/.openship-elev-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
1681
|
-
await inner.writeFile(tmp, content);
|
|
1682
|
-
await inner.exec(
|
|
1683
|
-
elevateCommand(`mkdir -p ${sq(dirOf(path))} && mv -f ${sq(tmp)} ${sq(path)}`)
|
|
1684
|
-
);
|
|
1685
|
-
};
|
|
1686
|
-
const overrides = {
|
|
1687
|
-
exec: (command, opts) => inner.exec(elevateCommand(command), opts),
|
|
1688
|
-
streamExec: (command, onLog, opts) => inner.streamExec(elevateCommand(command), onLog, opts),
|
|
1689
|
-
writeFile: writeFileElevated,
|
|
1690
|
-
// Elevated for the same reason as writeFile: the vhost/conf dirs are root-owned,
|
|
1691
|
-
// and the Proxy would otherwise forward this to the inner executor unelevated —
|
|
1692
|
-
// EACCES immediately after an elevated write succeeded.
|
|
1693
|
-
rename: async (from, to) => {
|
|
1694
|
-
await inner.exec(elevateCommand(`mv -f ${sq(from)} ${sq(to)}`));
|
|
1695
|
-
},
|
|
1696
|
-
mkdir: async (path) => {
|
|
1697
|
-
await inner.exec(elevateCommand(`mkdir -p ${sq(path)}`));
|
|
1698
|
-
},
|
|
1699
|
-
rm: async (path) => {
|
|
1700
|
-
await inner.exec(elevateCommand(`rm -rf ${sq(path)}`));
|
|
1701
|
-
}
|
|
1702
|
-
};
|
|
1703
|
-
return new Proxy(inner, {
|
|
1704
|
-
get(target, prop2) {
|
|
1705
|
-
if (Object.prototype.hasOwnProperty.call(overrides, prop2)) {
|
|
1706
|
-
return overrides[prop2];
|
|
1707
|
-
}
|
|
1708
|
-
const value = Reflect.get(target, prop2, target);
|
|
1709
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
1710
|
-
}
|
|
1711
|
-
});
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
// ../../packages/adapters/src/system/installer.ts
|
|
1715
|
-
function log3(message, level = "info") {
|
|
1716
|
-
return { timestamp: (/* @__PURE__ */ new Date()).toISOString(), message, level };
|
|
1717
|
-
}
|
|
1718
|
-
async function prepareExecutor(executor, component) {
|
|
1719
|
-
const profile = await resolveEnvironment(executor);
|
|
1720
|
-
if (profile.isRoot) return { ok: true, executor, profile };
|
|
1721
|
-
if (profile.canSudo) return { ok: true, executor: elevatedExecutor(executor), profile };
|
|
1722
|
-
return {
|
|
1723
|
-
ok: false,
|
|
1724
|
-
result: {
|
|
1725
|
-
component,
|
|
1726
|
-
success: false,
|
|
1727
|
-
error: `Installing ${component} needs root. Connect this server as root, or as a user with passwordless sudo.`
|
|
1728
|
-
}
|
|
1729
|
-
};
|
|
1730
|
-
}
|
|
1731
|
-
async function installDocker(executor, onLog) {
|
|
1732
|
-
const prep = await prepareExecutor(executor, "docker");
|
|
1733
|
-
if (!prep.ok) return prep.result;
|
|
1734
|
-
executor = prep.executor;
|
|
1735
|
-
const profile = prep.profile;
|
|
1736
|
-
const plan = systemCatalog.installs.docker(profile);
|
|
1737
|
-
if (!plan.supported || !plan.installCommand || !plan.verifyCommand) {
|
|
1738
|
-
return { component: "docker", success: false, error: plan.unsupportedReason ?? "Docker install not supported" };
|
|
1739
|
-
}
|
|
1740
|
-
onLog(log3("Installing Docker Engine..."));
|
|
1741
|
-
try {
|
|
1742
|
-
const { code } = await executor.streamExec(plan.installCommand, onLog);
|
|
1743
|
-
if (code !== 0) return { component: "docker", success: false, error: "Docker install failed" };
|
|
1744
|
-
if (plan.startCommand) {
|
|
1745
|
-
onLog(log3("Starting Docker service..."));
|
|
1746
|
-
await executor.streamExec(plan.startCommand, onLog);
|
|
1747
|
-
}
|
|
1748
|
-
const version = await executor.exec(plan.verifyCommand);
|
|
1749
|
-
const parsed = systemCatalog.checks.docker.parseVersion(version);
|
|
1750
|
-
onLog(log3(`Docker ${parsed} installed`));
|
|
1751
|
-
return { component: "docker", success: true, version: parsed };
|
|
1752
|
-
} catch (err) {
|
|
1753
|
-
const msg = safeErrorMessage(err);
|
|
1754
|
-
onLog(log3(`Docker installation failed: ${msg}`, "error"));
|
|
1755
|
-
return { component: "docker", success: false, error: msg };
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
async function installGit(executor, onLog, opts) {
|
|
1759
|
-
const prep = await prepareExecutor(executor, "git");
|
|
1760
|
-
if (!prep.ok) return prep.result;
|
|
1761
|
-
executor = prep.executor;
|
|
1762
|
-
const profile = prep.profile;
|
|
1763
|
-
const plan = systemCatalog.installs.git(profile);
|
|
1764
|
-
if (!plan.supported || !plan.installCommand || !plan.verifyCommand) {
|
|
1765
|
-
return { component: "git", success: false, error: plan.unsupportedReason ?? "Git install not supported" };
|
|
1766
|
-
}
|
|
1767
|
-
onLog(log3(opts?.label ? `Installing Git in ${opts.label}...` : "Installing Git..."));
|
|
1768
|
-
try {
|
|
1769
|
-
const { code } = await executor.streamExec(plan.installCommand, onLog);
|
|
1770
|
-
if (code !== 0) return { component: "git", success: false, error: "Git installation failed" };
|
|
1771
|
-
const version = await executor.exec(plan.verifyCommand);
|
|
1772
|
-
const parsed = systemCatalog.checks.git.parseVersion(version);
|
|
1773
|
-
onLog(log3(opts?.label ? `Git ${parsed} installed in ${opts.label}` : `Git ${parsed} installed`));
|
|
1774
|
-
return { component: "git", success: true, version: parsed };
|
|
1775
|
-
} catch (err) {
|
|
1776
|
-
const msg = safeErrorMessage(err);
|
|
1777
|
-
onLog(log3(`Git installation failed: ${msg}`, "error"));
|
|
1778
|
-
return { component: "git", success: false, error: msg };
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
async function installRsync(executor, onLog) {
|
|
1782
|
-
const prep = await prepareExecutor(executor, "rsync");
|
|
1783
|
-
if (!prep.ok) return prep.result;
|
|
1784
|
-
executor = prep.executor;
|
|
1785
|
-
const profile = prep.profile;
|
|
1786
|
-
const plan = systemCatalog.installs.rsync(profile);
|
|
1787
|
-
if (!plan.supported || !plan.installCommand || !plan.verifyCommand) {
|
|
1788
|
-
return { component: "rsync", success: false, error: plan.unsupportedReason ?? "rsync install not supported" };
|
|
1789
|
-
}
|
|
1790
|
-
onLog(log3("Installing rsync..."));
|
|
1791
|
-
try {
|
|
1792
|
-
const { code } = await executor.streamExec(plan.installCommand, onLog);
|
|
1793
|
-
if (code !== 0) return { component: "rsync", success: false, error: "rsync installation failed" };
|
|
1794
|
-
const version = await executor.exec(plan.verifyCommand);
|
|
1795
|
-
const parsed = systemCatalog.checks.rsync.parseVersion(version);
|
|
1796
|
-
onLog(log3(`rsync ${parsed} installed`));
|
|
1797
|
-
return { component: "rsync", success: true, version: parsed };
|
|
1798
|
-
} catch (err) {
|
|
1799
|
-
const msg = safeErrorMessage(err);
|
|
1800
|
-
onLog(log3(`rsync installation failed: ${msg}`, "error"));
|
|
1801
|
-
return { component: "rsync", success: false, error: msg };
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
async function installContainerEdge(executor, onLog, config) {
|
|
1805
|
-
const prep = await prepareExecutor(executor, "edge");
|
|
1806
|
-
if (!prep.ok) return prep.result;
|
|
1807
|
-
executor = prep.executor;
|
|
1808
|
-
if (!await dockerAvailable(executor)) {
|
|
1809
|
-
const error = "The edge is a container image and needs Docker on this server. Install the Docker component first, then install the edge.";
|
|
1810
|
-
onLog(log3(error, "error"));
|
|
1811
|
-
return { component: "edge", success: false, error };
|
|
1812
|
-
}
|
|
1813
|
-
try {
|
|
1814
|
-
const result = await ensureContainerEdge(executor, {
|
|
1815
|
-
onLog,
|
|
1816
|
-
config,
|
|
1817
|
-
image: config?.edgeImage
|
|
1818
|
-
});
|
|
1819
|
-
if (result.converted) {
|
|
1820
|
-
onLog(log3("Edge moved from host OpenResty to the container edge", "warn"));
|
|
1821
|
-
}
|
|
1822
|
-
const version = await executor.exec(containerCommand(result.container, "openresty -v 2>&1")).catch(() => "");
|
|
1823
|
-
return {
|
|
1824
|
-
component: "edge",
|
|
1825
|
-
success: true,
|
|
1826
|
-
version: version ? systemCatalog.checks.openresty.parseVersion(version) : void 0
|
|
1827
|
-
};
|
|
1828
|
-
} catch (err) {
|
|
1829
|
-
if (err instanceof EdgeMigrateRequested) throw err;
|
|
1830
|
-
const msg = safeErrorMessage(err);
|
|
1831
|
-
onLog(log3(`Edge setup failed: ${msg}`, "error"));
|
|
1832
|
-
return { component: "edge", success: false, error: msg };
|
|
1688
|
+
git: (profile) => packagePlan(profile, "git", "git --version"),
|
|
1689
|
+
rsync: (profile) => packagePlan(profile, "rsync", "rsync --version | head -n 1")
|
|
1833
1690
|
}
|
|
1834
|
-
}
|
|
1835
|
-
var COMPONENT_INSTALLERS = {
|
|
1836
|
-
docker: (exec, log6) => installDocker(exec, log6),
|
|
1837
|
-
// The edge is ONE component and it is a container image — there is no
|
|
1838
|
-
// host-OpenResty or host-certbot installer to reach for any more.
|
|
1839
|
-
edge: (exec, log6, config) => installContainerEdge(exec, log6, config),
|
|
1840
|
-
git: (exec, log6) => installGit(exec, log6),
|
|
1841
|
-
rsync: (exec, log6) => installRsync(exec, log6)
|
|
1842
1691
|
};
|
|
1843
1692
|
|
|
1844
1693
|
// ../../packages/adapters/src/system/available-version.ts
|
|
@@ -1858,32 +1707,30 @@ function normalizePkgVersion(raw) {
|
|
|
1858
1707
|
if (dash !== -1) v = v.slice(0, dash);
|
|
1859
1708
|
return v.trim();
|
|
1860
1709
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
const
|
|
1872
|
-
return out?.match(/Candidate:\s*(\S+)/)?.[1]?.trim() ?? null;
|
|
1873
|
-
}
|
|
1874
|
-
if (pm === "dnf" || pm === "yum") {
|
|
1875
|
-
const out = await tryExec3(
|
|
1876
|
-
executor,
|
|
1877
|
-
`${pm} -q --cacheonly list available ${q} 2>/dev/null | tail -n1`
|
|
1878
|
-
);
|
|
1879
|
-
return out?.trim().split(/\s+/)[1]?.trim() ?? null;
|
|
1880
|
-
}
|
|
1881
|
-
if (pm === "apk") {
|
|
1882
|
-
const out = await tryExec3(executor, `apk policy ${q} 2>/dev/null`);
|
|
1883
|
-
const versions = [...out?.matchAll(/^\s+([\w.+~-]+):\s*$/gm) ?? []].map((m) => m[1]);
|
|
1710
|
+
var READ_CANDIDATE = {
|
|
1711
|
+
// `apt-cache policy` prints " Candidate: <version>".
|
|
1712
|
+
apt: (out) => out.match(/Candidate:\s*(\S+)/)?.[1]?.trim() ?? null,
|
|
1713
|
+
// Cached list; the last "pkg.arch version repo" row carries the newest avail.
|
|
1714
|
+
dnf: (out) => out.trim().split(/\s+/)[1]?.trim() ?? null,
|
|
1715
|
+
yum: (out) => out.trim().split(/\s+/)[1]?.trim() ?? null,
|
|
1716
|
+
// `apk policy` prints one ` <version>:` line per available version (colon at end
|
|
1717
|
+
// of line), each followed by indented source lines (installed marker, repo URLs);
|
|
1718
|
+
// the last version line is newest.
|
|
1719
|
+
apk: (out) => {
|
|
1720
|
+
const versions = [...out.matchAll(/^\s+([\w.+~-]+):\s*$/gm)].map((m) => m[1]);
|
|
1884
1721
|
return versions.length ? versions[versions.length - 1] : null;
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1722
|
+
},
|
|
1723
|
+
// Both refuse the probe itself, so there is no output to read. Stated rather than
|
|
1724
|
+
// omitted — `null` here is paired with a refusal, not with a missing branch.
|
|
1725
|
+
brew: null,
|
|
1726
|
+
none: null
|
|
1727
|
+
};
|
|
1728
|
+
async function probeCandidate(executor, profile, pkg) {
|
|
1729
|
+
const read = READ_CANDIDATE[profile.packageManager];
|
|
1730
|
+
const probe = envOps(profile).pkgAvailableVersion(pkg);
|
|
1731
|
+
if (!read || !probe.supported) return null;
|
|
1732
|
+
const out = await tryExec(executor, opScript(probe.value));
|
|
1733
|
+
return out ? read(out) : null;
|
|
1887
1734
|
}
|
|
1888
1735
|
async function enrichAvailableVersions(executor, profile, statuses) {
|
|
1889
1736
|
const targets = statuses.filter(
|
|
@@ -1893,7 +1740,7 @@ async function enrichAvailableVersions(executor, profile, statuses) {
|
|
|
1893
1740
|
targets.map(async (s) => {
|
|
1894
1741
|
try {
|
|
1895
1742
|
const pkg = PACKAGE_NAMES[s.name];
|
|
1896
|
-
const candidate = await probeCandidate(executor, profile
|
|
1743
|
+
const candidate = await probeCandidate(executor, profile, pkg);
|
|
1897
1744
|
if (!candidate) return;
|
|
1898
1745
|
const avail = normalizePkgVersion(candidate);
|
|
1899
1746
|
const installed = normalizePkgVersion(s.version);
|
|
@@ -1957,37 +1804,8 @@ function getSystemComponentDefinition(name) {
|
|
|
1957
1804
|
}
|
|
1958
1805
|
|
|
1959
1806
|
// ../../packages/adapters/src/system/checks.ts
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
systemDebug("checks", `exec:start ${command}`);
|
|
1963
|
-
try {
|
|
1964
|
-
const result = await executor.exec(command, { timeout: 1e4 });
|
|
1965
|
-
systemDebug(
|
|
1966
|
-
"checks",
|
|
1967
|
-
`exec:ok ${command} (${formatDuration(startedAt)})`
|
|
1968
|
-
);
|
|
1969
|
-
return { output: result.trim(), error: null };
|
|
1970
|
-
} catch (err) {
|
|
1971
|
-
if (isRemoteConnectionError(err)) {
|
|
1972
|
-
systemDebug(
|
|
1973
|
-
"checks",
|
|
1974
|
-
`exec:abort ${command} (${formatDuration(startedAt)}) ${safeErrorMessage(err)}`
|
|
1975
|
-
);
|
|
1976
|
-
throw err;
|
|
1977
|
-
}
|
|
1978
|
-
const msg = safeErrorMessage(err);
|
|
1979
|
-
systemDebug(
|
|
1980
|
-
"checks",
|
|
1981
|
-
`exec:fail ${command} (${formatDuration(startedAt)}) ${msg}`
|
|
1982
|
-
);
|
|
1983
|
-
return { output: "", error: msg || `\`${command}\` failed` };
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
function withReason(message, error) {
|
|
1987
|
-
if (!error) return message;
|
|
1988
|
-
const first = error.split("\n").map((line) => line.trim()).find(Boolean);
|
|
1989
|
-
if (!first) return message;
|
|
1990
|
-
return `${message} \u2014 ${first.length > 200 ? `${first.slice(0, 199)}\u2026` : first}`;
|
|
1807
|
+
function isSocketDenied(error) {
|
|
1808
|
+
return error != null && /permission denied|eacces/i.test(error);
|
|
1991
1809
|
}
|
|
1992
1810
|
function healthy(name, version, running) {
|
|
1993
1811
|
const component = getSystemComponentDefinition(name);
|
|
@@ -2020,22 +1838,31 @@ function unhealthy(name, message, opts) {
|
|
|
2020
1838
|
async function checkDocker(executor) {
|
|
2021
1839
|
const startedAt = Date.now();
|
|
2022
1840
|
const recipe = systemCatalog.checks.docker;
|
|
2023
|
-
const version = await
|
|
1841
|
+
const version = await probeExec(executor, recipe.versionCommand, "checks");
|
|
2024
1842
|
if (!version.output) {
|
|
2025
1843
|
systemDebug("checks", `docker:missing (${formatDuration(startedAt)})`);
|
|
2026
1844
|
return unhealthy("docker", withReason(recipe.missingMessage, version.error));
|
|
2027
1845
|
}
|
|
2028
1846
|
const parsed = recipe.parseVersion(version.output);
|
|
2029
|
-
const info = await
|
|
1847
|
+
const info = await probeExec(executor, recipe.daemonCommand, "checks");
|
|
2030
1848
|
if (!info.output) {
|
|
1849
|
+
const denied = isSocketDenied(info.error);
|
|
2031
1850
|
systemDebug(
|
|
2032
1851
|
"checks",
|
|
2033
|
-
`docker:not-running (${formatDuration(startedAt)}) ${info.error ?? "probe exited 0 with no server version"}`
|
|
1852
|
+
`docker:${denied ? "denied" : "not-running"} (${formatDuration(startedAt)}) ${info.error ?? "probe exited 0 with no server version"}`
|
|
1853
|
+
);
|
|
1854
|
+
return unhealthy(
|
|
1855
|
+
"docker",
|
|
1856
|
+
withReason(denied && recipe.deniedMessage || recipe.notRunningMessage, info.error),
|
|
1857
|
+
{
|
|
1858
|
+
version: parsed,
|
|
1859
|
+
// Left UNSET when denied: `running: false` would be a fact we don't have. The
|
|
1860
|
+
// permission check happens before the daemon is consulted, so a refusal is
|
|
1861
|
+
// equally consistent with a healthy daemon, and asserting it is stopped is the
|
|
1862
|
+
// same class of guess the message above stopped making.
|
|
1863
|
+
...denied ? {} : { running: false }
|
|
1864
|
+
}
|
|
2034
1865
|
);
|
|
2035
|
-
return unhealthy("docker", withReason(recipe.notRunningMessage, info.error), {
|
|
2036
|
-
version: parsed,
|
|
2037
|
-
running: false
|
|
2038
|
-
});
|
|
2039
1866
|
}
|
|
2040
1867
|
systemDebug("checks", `docker:healthy (${formatDuration(startedAt)})`);
|
|
2041
1868
|
return healthy("docker", parsed, true);
|
|
@@ -2043,7 +1870,7 @@ async function checkDocker(executor) {
|
|
|
2043
1870
|
async function checkGit(executor) {
|
|
2044
1871
|
const startedAt = Date.now();
|
|
2045
1872
|
const recipe = systemCatalog.checks.git;
|
|
2046
|
-
const version = await
|
|
1873
|
+
const version = await probeExec(executor, recipe.versionCommand, "checks");
|
|
2047
1874
|
if (!version.output) {
|
|
2048
1875
|
systemDebug("checks", `git:missing (${formatDuration(startedAt)})`);
|
|
2049
1876
|
return unhealthy("git", withReason(recipe.missingMessage, version.error));
|
|
@@ -2055,7 +1882,7 @@ async function checkGit(executor) {
|
|
|
2055
1882
|
async function checkRsync(executor) {
|
|
2056
1883
|
const startedAt = Date.now();
|
|
2057
1884
|
const recipe = systemCatalog.checks.rsync;
|
|
2058
|
-
const version = await
|
|
1885
|
+
const version = await probeExec(executor, recipe.versionCommand, "checks");
|
|
2059
1886
|
if (!version.output) {
|
|
2060
1887
|
systemDebug("checks", `rsync:missing (${formatDuration(startedAt)})`);
|
|
2061
1888
|
return unhealthy("rsync", withReason(recipe.missingMessage, version.error));
|
|
@@ -2083,7 +1910,7 @@ async function checkEdge(executor) {
|
|
|
2083
1910
|
{ running: false }
|
|
2084
1911
|
);
|
|
2085
1912
|
}
|
|
2086
|
-
const version = await
|
|
1913
|
+
const version = await probeExec(executor, containerCommand(container, "openresty -v 2>&1"), "checks");
|
|
2087
1914
|
if (!version.output) {
|
|
2088
1915
|
systemDebug("checks", `edge:container-unresponsive (${formatDuration(startedAt)})`);
|
|
2089
1916
|
return unhealthy(
|
|
@@ -2159,6 +1986,182 @@ async function checkComponents(executor, names) {
|
|
|
2159
1986
|
return results;
|
|
2160
1987
|
}
|
|
2161
1988
|
|
|
1989
|
+
// ../../packages/adapters/src/system/installer.ts
|
|
1990
|
+
function log3(message, level = "info") {
|
|
1991
|
+
return { timestamp: (/* @__PURE__ */ new Date()).toISOString(), message, level };
|
|
1992
|
+
}
|
|
1993
|
+
async function prepareExecutor(executor, component) {
|
|
1994
|
+
const grant = await privilegedExecutor(executor, `Installing ${component}`);
|
|
1995
|
+
if (!grant.supported) {
|
|
1996
|
+
return { ok: false, result: { component, success: false, error: grant.reason } };
|
|
1997
|
+
}
|
|
1998
|
+
return { ok: true, executor: grant.value.executor, profile: grant.value.profile };
|
|
1999
|
+
}
|
|
2000
|
+
function describeInstallFailure(label, code, output) {
|
|
2001
|
+
const lines = output.split("\n").map((line) => line.replace(/\r/g, "").trim()).filter(Boolean);
|
|
2002
|
+
const flagged = lines.filter((line) => /^(e:|err:|error:|dpkg:|fatal:)/i.test(line));
|
|
2003
|
+
const picked = (flagged.length ? flagged : lines).slice(-3).join(" \xB7 ");
|
|
2004
|
+
const detail = picked.length > 400 ? `${picked.slice(0, 399)}\u2026` : picked;
|
|
2005
|
+
const held = /held packages?|--allow-change-held-packages/i.test(output) ? " Packages are held on this host (`apt-mark hold`) and Openship does not override a hold \u2014 unhold them first if you do want this install." : "";
|
|
2006
|
+
return `${label} install failed (exit ${code})${detail ? `: ${detail}` : ""}.${held}`;
|
|
2007
|
+
}
|
|
2008
|
+
async function probeDocker(executor) {
|
|
2009
|
+
const status = await checkDocker(executor);
|
|
2010
|
+
const version = status.version;
|
|
2011
|
+
if (!status.installed) return { verdict: "missing", message: status.message };
|
|
2012
|
+
if (!status.healthy) return { verdict: "stopped", version, message: status.message };
|
|
2013
|
+
if (version && compareSemver(version, MIN_DOCKER_VERSION) < 0) {
|
|
2014
|
+
return { verdict: "outdated", version, message: status.message };
|
|
2015
|
+
}
|
|
2016
|
+
return { verdict: "ok", version, message: status.message };
|
|
2017
|
+
}
|
|
2018
|
+
function dockerAlreadyThere(present, onLog) {
|
|
2019
|
+
const named = present.version ? `Docker ${present.version}` : "Docker";
|
|
2020
|
+
onLog(log3(`${named} is installed and its daemon is answering \u2014 nothing to install`));
|
|
2021
|
+
return { component: "docker", success: true, version: present.version };
|
|
2022
|
+
}
|
|
2023
|
+
async function installDocker(executor, onLog, config) {
|
|
2024
|
+
const forced = config?.reinstall === true;
|
|
2025
|
+
let present = forced ? null : await probeDocker(executor);
|
|
2026
|
+
if (present?.verdict === "ok") return dockerAlreadyThere(present, onLog);
|
|
2027
|
+
const prep = await prepareExecutor(executor, "docker");
|
|
2028
|
+
if (!prep.ok) return prep.result;
|
|
2029
|
+
executor = prep.executor;
|
|
2030
|
+
const profile = prep.profile;
|
|
2031
|
+
if (present && present.verdict !== "missing" && !profile.isRoot) {
|
|
2032
|
+
present = await probeDocker(executor);
|
|
2033
|
+
if (present.verdict === "ok") return dockerAlreadyThere(present, onLog);
|
|
2034
|
+
}
|
|
2035
|
+
const plan = systemCatalog.installs.docker(profile);
|
|
2036
|
+
const service = plan.supported ? plan.value.service : plan;
|
|
2037
|
+
if (present?.verdict === "stopped" && service?.supported) {
|
|
2038
|
+
onLog(log3(`${present.message} \u2014 starting the Docker service`, "warn"));
|
|
2039
|
+
await executor.streamExec(service.value, onLog).catch(() => void 0);
|
|
2040
|
+
present = await probeDocker(executor);
|
|
2041
|
+
if (present.verdict === "ok") {
|
|
2042
|
+
onLog(log3(`Docker ${present.version} is running`));
|
|
2043
|
+
return { component: "docker", success: true, version: present.version };
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
if (present && present.verdict !== "missing") {
|
|
2047
|
+
const cannotStart = present.verdict === "stopped" && service && !service.supported ? ` Openship could not start it for you: ${service.reason}` : "";
|
|
2048
|
+
const error = (present.verdict === "outdated" ? `Docker ${present.version} is installed but Openship needs ${MIN_DOCKER_VERSION} or newer. Upgrading Docker restarts the daemon and every container on this server, so it is never done as part of another step \u2014 upgrade it on the host, or use Reinstall on the Docker component to run the official installer.` : `${present.message}. Start Docker on this server and retry. Openship will not reinstall Docker over a daemon that is merely unreachable: the installer would also upgrade the engine and restart every container on this host. Use Reinstall on the Docker component if that is what you want.`) + cannotStart;
|
|
2049
|
+
onLog(log3(error, "error"));
|
|
2050
|
+
return { component: "docker", success: false, version: present.version, error };
|
|
2051
|
+
}
|
|
2052
|
+
if (!plan.supported) {
|
|
2053
|
+
onLog(log3(plan.reason, "error"));
|
|
2054
|
+
return { component: "docker", success: false, error: plan.reason };
|
|
2055
|
+
}
|
|
2056
|
+
onLog(
|
|
2057
|
+
forced ? log3(
|
|
2058
|
+
"Running the Docker installer \u2014 it may upgrade the engine, which restarts the daemon and every container on this host...",
|
|
2059
|
+
"warn"
|
|
2060
|
+
) : log3("Installing Docker Engine...")
|
|
2061
|
+
);
|
|
2062
|
+
try {
|
|
2063
|
+
const { code, output } = await executor.streamExec(
|
|
2064
|
+
plan.value.installCommand,
|
|
2065
|
+
onLog
|
|
2066
|
+
);
|
|
2067
|
+
if (code !== 0) {
|
|
2068
|
+
const error = describeInstallFailure("Docker", code, output);
|
|
2069
|
+
onLog(log3(error, "error"));
|
|
2070
|
+
return { component: "docker", success: false, error };
|
|
2071
|
+
}
|
|
2072
|
+
if (service?.supported) {
|
|
2073
|
+
onLog(log3("Starting Docker service..."));
|
|
2074
|
+
await executor.streamExec(service.value, onLog);
|
|
2075
|
+
} else if (service) {
|
|
2076
|
+
onLog(log3(`Docker is installed but Openship cannot start it here: ${service.reason}`, "warn"));
|
|
2077
|
+
}
|
|
2078
|
+
const version = await executor.exec(plan.value.verifyCommand);
|
|
2079
|
+
const parsed = systemCatalog.checks.docker.parseVersion(version);
|
|
2080
|
+
onLog(log3(`Docker ${parsed} installed`));
|
|
2081
|
+
return { component: "docker", success: true, version: parsed };
|
|
2082
|
+
} catch (err) {
|
|
2083
|
+
const msg = safeErrorMessage(err);
|
|
2084
|
+
onLog(log3(`Docker installation failed: ${msg}`, "error"));
|
|
2085
|
+
return { component: "docker", success: false, error: msg };
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
async function installPackaged(executor, onLog, component, label, where) {
|
|
2089
|
+
const prep = await prepareExecutor(executor, component);
|
|
2090
|
+
if (!prep.ok) return prep.result;
|
|
2091
|
+
executor = prep.executor;
|
|
2092
|
+
const plan = systemCatalog.installs[component](prep.profile);
|
|
2093
|
+
if (!plan.supported) {
|
|
2094
|
+
onLog(log3(plan.reason, "error"));
|
|
2095
|
+
return { component, success: false, error: plan.reason };
|
|
2096
|
+
}
|
|
2097
|
+
const inside = where ? ` in ${where}` : "";
|
|
2098
|
+
onLog(log3(`Installing ${label}${inside}...`));
|
|
2099
|
+
try {
|
|
2100
|
+
const { code, output } = await executor.streamExec(
|
|
2101
|
+
plan.value.installCommand,
|
|
2102
|
+
onLog
|
|
2103
|
+
);
|
|
2104
|
+
if (code !== 0) {
|
|
2105
|
+
const error = describeInstallFailure(label, code, output);
|
|
2106
|
+
onLog(log3(error, "error"));
|
|
2107
|
+
return { component, success: false, error };
|
|
2108
|
+
}
|
|
2109
|
+
const version = await executor.exec(plan.value.verifyCommand);
|
|
2110
|
+
const parsed = systemCatalog.checks[component].parseVersion(version);
|
|
2111
|
+
onLog(log3(`${label} ${parsed} installed${inside}`));
|
|
2112
|
+
return { component, success: true, version: parsed };
|
|
2113
|
+
} catch (err) {
|
|
2114
|
+
const msg = safeErrorMessage(err);
|
|
2115
|
+
onLog(log3(`${label} installation failed: ${msg}`, "error"));
|
|
2116
|
+
return { component, success: false, error: msg };
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
function installGit(executor, onLog, opts) {
|
|
2120
|
+
return installPackaged(executor, onLog, "git", "Git", opts?.label);
|
|
2121
|
+
}
|
|
2122
|
+
function installRsync(executor, onLog) {
|
|
2123
|
+
return installPackaged(executor, onLog, "rsync", "rsync");
|
|
2124
|
+
}
|
|
2125
|
+
async function installContainerEdge(executor, onLog, config) {
|
|
2126
|
+
const prep = await prepareExecutor(executor, "edge");
|
|
2127
|
+
if (!prep.ok) return prep.result;
|
|
2128
|
+
executor = prep.executor;
|
|
2129
|
+
if (!await dockerAvailable(executor)) {
|
|
2130
|
+
const error = "The edge is a container image and needs Docker on this server. Install the Docker component first, then install the edge.";
|
|
2131
|
+
onLog(log3(error, "error"));
|
|
2132
|
+
return { component: "edge", success: false, error };
|
|
2133
|
+
}
|
|
2134
|
+
try {
|
|
2135
|
+
const result = await ensureContainerEdge(executor, {
|
|
2136
|
+
onLog,
|
|
2137
|
+
config,
|
|
2138
|
+
image: config?.edgeImage
|
|
2139
|
+
});
|
|
2140
|
+
if (result.converted) {
|
|
2141
|
+
onLog(log3("Edge moved from host OpenResty to the container edge", "warn"));
|
|
2142
|
+
}
|
|
2143
|
+
const version = await executor.exec(containerCommand(result.container, "openresty -v 2>&1")).catch(() => "");
|
|
2144
|
+
return {
|
|
2145
|
+
component: "edge",
|
|
2146
|
+
success: true,
|
|
2147
|
+
version: version ? systemCatalog.checks.openresty.parseVersion(version) : void 0
|
|
2148
|
+
};
|
|
2149
|
+
} catch (err) {
|
|
2150
|
+
if (err instanceof EdgeMigrateRequested) throw err;
|
|
2151
|
+
const msg = safeErrorMessage(err);
|
|
2152
|
+
onLog(log3(`Edge setup failed: ${msg}`, "error"));
|
|
2153
|
+
return { component: "edge", success: false, error: msg };
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
var COMPONENT_INSTALLERS = {
|
|
2157
|
+
docker: (exec, log6, config) => installDocker(exec, log6, config),
|
|
2158
|
+
// The edge is ONE component and it is a container image — there is no
|
|
2159
|
+
// host-OpenResty or host-certbot installer to reach for any more.
|
|
2160
|
+
edge: (exec, log6, config) => installContainerEdge(exec, log6, config),
|
|
2161
|
+
git: (exec, log6) => installGit(exec, log6),
|
|
2162
|
+
rsync: (exec, log6) => installRsync(exec, log6)
|
|
2163
|
+
};
|
|
2164
|
+
|
|
2162
2165
|
// ../../packages/adapters/src/system/proxy/index.ts
|
|
2163
2166
|
async function importSites(executor, status) {
|
|
2164
2167
|
const proxy = status.occupants.find((o) => o.proxy)?.proxy;
|
|
@@ -2206,8 +2209,12 @@ async function ensureEdgeClear(executor, config, onLog) {
|
|
|
2206
2209
|
if (!freed.freed) {
|
|
2207
2210
|
await rollbackEdgeTakeover(executor, onLog);
|
|
2208
2211
|
const plural = freed.stillBound.length > 1;
|
|
2212
|
+
const ports = `port${plural ? "s" : ""} ${freed.stillBound.join(" and ")} ${plural ? "are" : "is"} still in use, so the edge can't bind. Nothing was installed`;
|
|
2209
2213
|
throw new AppError(
|
|
2210
|
-
|
|
2214
|
+
// The advice has to match the actual cause. Told to hunt for another holder, an
|
|
2215
|
+
// operator retries the same unprivileged takeover forever — and "stopped the
|
|
2216
|
+
// existing proxy" is untrue on this arm, since the stop is what was refused.
|
|
2217
|
+
freed.privilegeDegraded ? `The ${ports} \u2014 and Openship could not run the stop as root on this host (see the privilege warning above), so stopping the existing proxy was almost certainly refused. Retrying as this user will fail the same way: reconnect as root, or as a user with passwordless sudo.` : `Stopped the existing proxy, but ${ports} and the previous proxy has been restored \u2014 find what else is holding the port and retry.`,
|
|
2211
2218
|
409,
|
|
2212
2219
|
"EDGE_PORTS_STILL_BOUND"
|
|
2213
2220
|
);
|
|
@@ -2262,15 +2269,22 @@ function setDefaultEdgeImage(image) {
|
|
|
2262
2269
|
function resolveEdgeImage(explicit) {
|
|
2263
2270
|
return buildEdgeImageRef({ explicit, injectedDefault: injectedDefaultImage });
|
|
2264
2271
|
}
|
|
2272
|
+
function edgeHostExecutor(executor) {
|
|
2273
|
+
return rootOrDegrade(executor, {
|
|
2274
|
+
purpose: "Configuring routing and TLS",
|
|
2275
|
+
consequence: "Vhosts and certificates may not be writable, and routing will be incomplete \u2014 the deploy continues and the app still runs on its port.",
|
|
2276
|
+
report: (message) => console.error(`[edge] ${message}`)
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2265
2279
|
async function containerEdgeProvider(executor, container, opts) {
|
|
2266
|
-
const { NginxProvider: Provider } = await import("./nginx-
|
|
2280
|
+
const { NginxProvider: Provider } = await import("./nginx-NSAHWGJV.js");
|
|
2267
2281
|
return new Provider({
|
|
2268
2282
|
...opts,
|
|
2269
2283
|
// Vhosts go to the BIND-MOUNTED HOST dir (so every host-side reader — the
|
|
2270
2284
|
// migrate proxy scan, cert reuse, the mail cert symlinks — keeps working),
|
|
2271
2285
|
// while reload/certbot run inside the container.
|
|
2272
2286
|
paths: EDGE_HOST_PATHS,
|
|
2273
|
-
executor: edgeContainerExecutor(executor, container),
|
|
2287
|
+
executor: edgeContainerExecutor(await edgeHostExecutor(executor), container),
|
|
2274
2288
|
// Challenge tokens go to the HOST side of the ACME mount, for the same reason
|
|
2275
2289
|
// vhosts do. This is the ONE mount whose two sides differ
|
|
2276
2290
|
// (/var/lib/openship/edge/acme → /var/www/acme), so the write path and the
|
|
@@ -2287,12 +2301,21 @@ async function containerEdgeProvider(executor, container, opts) {
|
|
|
2287
2301
|
});
|
|
2288
2302
|
}
|
|
2289
2303
|
async function localContainerEdgeProvider(container, opts) {
|
|
2290
|
-
const { DockerEdgeExecutor } = await import("./docker-edge-executor-
|
|
2291
|
-
const { NginxProvider: Provider } = await import("./nginx-
|
|
2304
|
+
const { DockerEdgeExecutor } = await import("./docker-edge-executor-YZZ6P7IC.js");
|
|
2305
|
+
const { NginxProvider: Provider } = await import("./nginx-NSAHWGJV.js");
|
|
2292
2306
|
return new Provider({
|
|
2293
2307
|
...opts,
|
|
2294
2308
|
paths: OPENRESTY_DEFAULT_PATHS,
|
|
2295
|
-
|
|
2309
|
+
// The one place the privilege gate is the WRONG instrument, so the reason is stated
|
|
2310
|
+
// instead: this executor's commands run inside the EDGE container, so probing
|
|
2311
|
+
// through it would measure that container's interior and brand the result with a
|
|
2312
|
+
// verdict about the wrong machine. Privilege here comes from the deployment shape —
|
|
2313
|
+
// the api process is root in its own container, the vhost dir is a bind mount it
|
|
2314
|
+
// owns, and the daemon socket is mounted in. There is no login to elevate.
|
|
2315
|
+
executor: rootChecked(
|
|
2316
|
+
new DockerEdgeExecutor({ containerName: container }),
|
|
2317
|
+
"compose: api is root in its own container, writing shared mounts over a mounted socket"
|
|
2318
|
+
),
|
|
2296
2319
|
// nginx.conf + the Lua are BAKED into the image — nothing to detect, install
|
|
2297
2320
|
// or patch, and detection would answer from inside the container anyway.
|
|
2298
2321
|
pinPaths: true,
|
|
@@ -2412,7 +2435,13 @@ async function ensureContainerEdge(executor, opts) {
|
|
|
2412
2435
|
}
|
|
2413
2436
|
}
|
|
2414
2437
|
for (const mount of EDGE_CONTAINER_MOUNTS) {
|
|
2415
|
-
await executor.exec(`mkdir -p ${sq(mount.host)}`).catch(() => {
|
|
2438
|
+
await executor.exec(`mkdir -p ${sq(mount.host)}`).catch((err) => {
|
|
2439
|
+
onLog(
|
|
2440
|
+
log5(
|
|
2441
|
+
`Could not create the edge state directory ${mount.host}: ${safeErrorMessage(err)}. Docker will create it empty, so vhosts and certificates may not persist.`,
|
|
2442
|
+
"warn"
|
|
2443
|
+
)
|
|
2444
|
+
);
|
|
2416
2445
|
});
|
|
2417
2446
|
}
|
|
2418
2447
|
const clear = await ensureEdgeClear(executor, opts.config, onLog);
|
|
@@ -2472,6 +2501,7 @@ export {
|
|
|
2472
2501
|
verifyEdgeServing,
|
|
2473
2502
|
ensureContainerEdge,
|
|
2474
2503
|
checkGit,
|
|
2504
|
+
COMPONENT_CHECKS,
|
|
2475
2505
|
checkAll,
|
|
2476
2506
|
checkComponents
|
|
2477
2507
|
};
|