@studiometa/trafic-agent 0.1.46 → 0.1.48
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/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/{server-EPU5dtPx.js → server-DZAo8Clg.js} +39 -14
- package/dist/server-DZAo8Clg.js.map +1 -0
- package/dist/server.js +1 -1
- package/dist/setup/ddev.d.ts +25 -2
- package/dist/setup/ddev.d.ts.map +1 -1
- package/dist/setup/docker.d.ts +43 -0
- package/dist/setup/docker.d.ts.map +1 -1
- package/dist/setup/migrations/0014__unattended_upgrades_docker_origin.d.ts +3 -3
- package/dist/setup/migrations/0017__ddev_apt_cloudsmith.d.ts +28 -0
- package/dist/setup/migrations/0017__ddev_apt_cloudsmith.d.ts.map +1 -0
- package/dist/setup/migrations/0018__docker_address_pools.d.ts +36 -0
- package/dist/setup/migrations/0018__docker_address_pools.d.ts.map +1 -0
- package/dist/setup/migrations/0019__recover_orphan_containers.d.ts +36 -0
- package/dist/setup/migrations/0019__recover_orphan_containers.d.ts.map +1 -0
- package/dist/setup/migrations/index.d.ts.map +1 -1
- package/dist/{setup-B02lDDZU.js → setup-DWgb6dAD.js} +246 -9
- package/dist/setup-DWgb6dAD.js.map +1 -0
- package/package.json +1 -1
- package/dist/server-EPU5dtPx.js.map +0 -1
- package/dist/setup-B02lDDZU.js.map +0 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type SetupIo } from "../io.js";
|
|
2
|
+
import type { Migration } from "../types.js";
|
|
3
|
+
/** How long a container gets to stop after SIGTERM, in seconds. */
|
|
4
|
+
export declare const STOP_TIMEOUT = 600;
|
|
5
|
+
/**
|
|
6
|
+
* Migration 0019: stop the containers Docker lost track of in migration 0018.
|
|
7
|
+
*
|
|
8
|
+
* 0018 restarted Docker to apply the address pools. On a server set up before
|
|
9
|
+
* 0.1.47, Docker ran the containerd image store (`overlayfs`) although
|
|
10
|
+
* daemon.json asked for `overlay2`, and the restart switched the driver.
|
|
11
|
+
* dockerd skips containers created with another driver, so it forgot every
|
|
12
|
+
* running container — but `live-restore` had left their processes running.
|
|
13
|
+
* Those orphans keep the locks on the project volumes: each new database
|
|
14
|
+
* container exits on "Can't lock aria control file", each Elasticsearch on
|
|
15
|
+
* "failed to obtain node locks", and the project never starts again. Seen
|
|
16
|
+
* on a live server: 66 orphans, 27 of them databases.
|
|
17
|
+
*
|
|
18
|
+
* containerd still knows the orphans: they are the tasks in the `moby`
|
|
19
|
+
* namespace with no matching Docker container. Each one gets SIGTERM, which
|
|
20
|
+
* MariaDB, MySQL and Elasticsearch handle as a clean shutdown, and as long as
|
|
21
|
+
* it needs. No SIGKILL: a database killed mid-write is the one outcome worse
|
|
22
|
+
* than the outage, so a process that does not stop is reported and left
|
|
23
|
+
* alone. The stopped tasks are then deleted, which ends their shims.
|
|
24
|
+
*
|
|
25
|
+
* Nothing is restarted here: the volumes are free again, and the agent starts
|
|
26
|
+
* each project on its next request, on the new driver.
|
|
27
|
+
*
|
|
28
|
+
* Idempotent: does nothing when every containerd task belongs to a container
|
|
29
|
+
* Docker knows.
|
|
30
|
+
*/
|
|
31
|
+
export declare const migration0019RecoverOrphanContainers: Migration;
|
|
32
|
+
/**
|
|
33
|
+
* The migration body, with its effects injected so tests can drive it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function recoverOrphanContainers(io?: SetupIo): void;
|
|
36
|
+
//# sourceMappingURL=0019__recover_orphan_containers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"0019__recover_orphan_containers.d.ts","sourceRoot":"","sources":["../../../src/setup/migrations/0019__recover_orphan_containers.ts"],"names":[],"mappings":"AACA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,mEAAmE;AACnE,eAAO,MAAM,YAAY,MAAM,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,oCAAoC,EAAE,SAOlD,CAAC;AASF;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,GAAE,OAAgB,GAAG,IAAI,CA8BlE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/setup/migrations/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/setup/migrations/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAqBjF,iDAAiD;AACjD,eAAO,MAAM,qBAAqB,iCAAiC,CAAC;AAEpE,+CAA+C;AAC/C,eAAO,MAAM,cAAc,EAAE,SAAS,EAoBrC,CAAC;AAMF;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,cAAc,CAqBnD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAG9D;AAMD;;GAEG;AACH,wBAAgB,cAAc,IAAI,kBAAkB,EAAE,CAMrD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,UAAQ,GAAG,IAAI,CAyBzD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAU/C"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as
|
|
1
|
+
import { $ as stopProject, A as getIdleProjects, B as info, C as getIdleTimeoutMs, G as success, H as resetSteps, I as nodeIo, K as warn, L as error, P as setProjectStatus, R as exec, S as readToolPorts, T as shouldNeverStop, U as setDryRun, V as isRoot, W as step, Y as getProjectInfo, Z as loadProjectList, _ as findRunningProjects, b as installSystemDeps, d as STATIC_CONFIG, f as TLS_STORE_CONFIG, g as findRunningProject, h as configureTraefik, m as configureDdev, ot as loadConfig, p as addDdevAptRepo, st as parseDuration, u as ROUTER_COMPOSE_OVERRIDE, v as getDockerGatewayIp, w as loadProjectConfig, x as purgeTraefikAcmeStorage, y as installDdev, z as execSilent } from "./server-DZAo8Clg.js";
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
//#region src/setup/docker-firewall.ts
|
|
4
4
|
/** Where the rule script lives, so systemd and a human can both run it. */
|
|
@@ -226,6 +226,44 @@ function installDocker(io = nodeIo) {
|
|
|
226
226
|
io.exec("usermod -aG docker ddev", { silent: true });
|
|
227
227
|
success("Added ddev user to docker group");
|
|
228
228
|
}
|
|
229
|
+
/** Docker daemon config file */
|
|
230
|
+
var DAEMON_CONFIG = "/etc/docker/daemon.json";
|
|
231
|
+
/**
|
|
232
|
+
* Address pools for the networks Docker creates.
|
|
233
|
+
*
|
|
234
|
+
* Every DDEV project gets its own network. Docker's built-in pools hold only
|
|
235
|
+
* about 31 of them (15 × /16 in 172.17–31.x, 16 × /20 in 192.168.x), so a
|
|
236
|
+
* server with ~30 projects fails with "all predefined address pools have
|
|
237
|
+
* been fully subnetted". /24 subnets in 172.16.0.0/12 give 4096 networks,
|
|
238
|
+
* and keep every network inside the range the firewall rules already allow
|
|
239
|
+
* (hardening.ts, docker-firewall.ts) — the built-in 192.168.x pools are not.
|
|
240
|
+
*/
|
|
241
|
+
var DOCKER_ADDRESS_POOLS = [{
|
|
242
|
+
base: "172.16.0.0/12",
|
|
243
|
+
size: 24
|
|
244
|
+
}];
|
|
245
|
+
/**
|
|
246
|
+
* Add the address pools to an existing daemon config.
|
|
247
|
+
*
|
|
248
|
+
* Returns true when the config was changed. Leaves a config that already
|
|
249
|
+
* sets pools alone (an operator choice), and one it cannot parse.
|
|
250
|
+
*/
|
|
251
|
+
function addDockerAddressPools(io = nodeIo) {
|
|
252
|
+
let config = {};
|
|
253
|
+
if (io.fileExists("/etc/docker/daemon.json")) try {
|
|
254
|
+
config = JSON.parse(io.readFile(DAEMON_CONFIG));
|
|
255
|
+
} catch {
|
|
256
|
+
warn("Could not parse existing Docker config, skipping");
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
if ("default-address-pools" in config) return false;
|
|
260
|
+
io.exec("mkdir -p /etc/docker");
|
|
261
|
+
io.writeFile(DAEMON_CONFIG, JSON.stringify({
|
|
262
|
+
...config,
|
|
263
|
+
"default-address-pools": DOCKER_ADDRESS_POOLS
|
|
264
|
+
}, null, 2));
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
229
267
|
/**
|
|
230
268
|
* Configure Docker for production use
|
|
231
269
|
*/
|
|
@@ -238,10 +276,11 @@ function configureDocker(io = nodeIo) {
|
|
|
238
276
|
"max-file": "3"
|
|
239
277
|
},
|
|
240
278
|
"storage-driver": "overlay2",
|
|
241
|
-
"live-restore": true
|
|
279
|
+
"live-restore": true,
|
|
280
|
+
"default-address-pools": DOCKER_ADDRESS_POOLS
|
|
242
281
|
};
|
|
243
282
|
io.exec("mkdir -p /etc/docker");
|
|
244
|
-
const configPath =
|
|
283
|
+
const configPath = DAEMON_CONFIG;
|
|
245
284
|
if (io.fileExists(configPath)) try {
|
|
246
285
|
const merged = {
|
|
247
286
|
...JSON.parse(io.readFile(configPath)),
|
|
@@ -254,10 +293,59 @@ function configureDocker(io = nodeIo) {
|
|
|
254
293
|
return;
|
|
255
294
|
}
|
|
256
295
|
else io.writeFile(configPath, JSON.stringify(daemonConfig, null, 2));
|
|
257
|
-
io
|
|
296
|
+
restartDocker(io);
|
|
258
297
|
success("Docker configured with production settings");
|
|
259
298
|
}
|
|
260
299
|
/**
|
|
300
|
+
* Restart Docker to apply daemon.json, without orphaning running containers.
|
|
301
|
+
*
|
|
302
|
+
* `live-restore` keeps containers running across a restart, but only when
|
|
303
|
+
* the storage driver stays the same. Before 0.1.47, setup applied daemon.json
|
|
304
|
+
* with a reload, and a reload does not apply `storage-driver`. Docker 29
|
|
305
|
+
* defaults a fresh install to the containerd image store (`overlayfs`), so
|
|
306
|
+
* such a server runs `overlayfs` while daemon.json says `overlay2`. The first
|
|
307
|
+
* full restart switches the driver, and dockerd silently skips every
|
|
308
|
+
* container created with the other one. Their processes keep running and
|
|
309
|
+
* keep the locks on the database volumes. DDEV then creates new containers
|
|
310
|
+
* that exit at once on "Can't lock aria control file". Seen on a live server
|
|
311
|
+
* after migration 0018: 66 containers lost, 27 of them databases.
|
|
312
|
+
*
|
|
313
|
+
* So when the driver is about to change, the DDEV projects are stopped first,
|
|
314
|
+
* which shuts their databases down cleanly, and the projects that were running
|
|
315
|
+
* are started again on the new driver. Volumes do not depend on the storage
|
|
316
|
+
* driver, so no data moves. Images are pulled again for the new driver.
|
|
317
|
+
*/
|
|
318
|
+
function restartDocker(io = nodeIo) {
|
|
319
|
+
if (!switchesStorageDriver(io)) {
|
|
320
|
+
io.exec("systemctl restart docker");
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const running = findRunningProjects(io);
|
|
324
|
+
warn("Docker changes its storage driver on restart, stopping DDEV projects first");
|
|
325
|
+
if (io.commandExists("ddev")) io.exec("su - ddev -c 'DDEV_NONINTERACTIVE=true ddev poweroff'", { silent: true });
|
|
326
|
+
io.exec("systemctl restart docker");
|
|
327
|
+
if (running.length > 0) {
|
|
328
|
+
info(`Starting ${running.length} project(s) again: ${running.join(", ")}`);
|
|
329
|
+
io.exec(`su - ddev -c 'DDEV_NONINTERACTIVE=true ddev start ${running.join(" ")}'`, { silent: true });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Whether a restart makes Docker use another storage driver.
|
|
334
|
+
*
|
|
335
|
+
* Compares the driver the daemon runs now with the one daemon.json asks for.
|
|
336
|
+
* A config without `storage-driver` keeps the current one.
|
|
337
|
+
*/
|
|
338
|
+
function switchesStorageDriver(io) {
|
|
339
|
+
const active = io.execSilent("docker info --format '{{.Driver}}'");
|
|
340
|
+
if (!active || !io.fileExists("/etc/docker/daemon.json")) return false;
|
|
341
|
+
try {
|
|
342
|
+
const configured = JSON.parse(io.readFile(DAEMON_CONFIG))["storage-driver"];
|
|
343
|
+
return typeof configured === "string" && configured !== active;
|
|
344
|
+
} catch {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
261
349
|
* Setup Docker system prune cron job
|
|
262
350
|
*/
|
|
263
351
|
function setupDockerPrune(io = nodeIo) {
|
|
@@ -1391,9 +1479,9 @@ var migration0013SshKexDefaults = {
|
|
|
1391
1479
|
* and its dist is the Ubuntu codename, so
|
|
1392
1480
|
* `"origin=Docker,codename=${distro_codename}"` is the entry that matches it.
|
|
1393
1481
|
*
|
|
1394
|
-
* DDEV, installed from
|
|
1395
|
-
* landing unattended can break running previews. DDEV is updated
|
|
1396
|
-
* `trafic-agent upgrade` instead, when an operator is there to see it.
|
|
1482
|
+
* DDEV, installed from its own apt repository, is deliberately not added: a
|
|
1483
|
+
* DDEV major landing unattended can break running previews. DDEV is updated
|
|
1484
|
+
* by `trafic-agent upgrade` instead, when an operator is there to see it.
|
|
1397
1485
|
*
|
|
1398
1486
|
* `configureUnattendedUpgrades` now writes the Docker entry, which covers
|
|
1399
1487
|
* fresh installs. Servers set up by an earlier release still carry the old
|
|
@@ -1548,6 +1636,152 @@ function findRouterContainer(io = nodeIo) {
|
|
|
1548
1636
|
return io.execSilent("docker ps -aq --filter label=com.docker.compose.service=ddev-router").trim().split("\n")[0] || void 0;
|
|
1549
1637
|
}
|
|
1550
1638
|
//#endregion
|
|
1639
|
+
//#region src/setup/migrations/0017__ddev_apt_cloudsmith.ts
|
|
1640
|
+
/** The Gemfury source file setup wrote before this release. */
|
|
1641
|
+
var OLD_SOURCES = "/etc/apt/sources.list.d/ddev.list";
|
|
1642
|
+
/** The dearmoured keyring that source was signed by. */
|
|
1643
|
+
var OLD_KEYRING = "/etc/apt/keyrings/ddev.gpg";
|
|
1644
|
+
/**
|
|
1645
|
+
* Migration 0017: move the DDEV apt repository from Gemfury to Cloudsmith.
|
|
1646
|
+
*
|
|
1647
|
+
* DDEV 1.25.4 moved its Debian and RPM packages to Cloudsmith
|
|
1648
|
+
* (ddev/ddev#8698) and its install docs now name
|
|
1649
|
+
* `https://packages.ddev.com/public/deb/ubuntu` with the key at
|
|
1650
|
+
* `https://packages.ddev.com/public/gpg.key`. The former Gemfury host,
|
|
1651
|
+
* `pkg.ddev.com`, still receives packages and DDEV has announced no
|
|
1652
|
+
* retirement date, so nothing is broken today — but a server left on the old
|
|
1653
|
+
* repository would stop seeing new DDEV releases the day it is switched off,
|
|
1654
|
+
* and `trafic-agent upgrade` runs `apt-get install --only-upgrade ddev`
|
|
1655
|
+
* against whatever repository is configured.
|
|
1656
|
+
*
|
|
1657
|
+
* The old files are removed rather than left alongside the new ones, as
|
|
1658
|
+
* DDEV's own instructions do: two sources offering the same package make apt
|
|
1659
|
+
* pick by version and would leave the Gemfury host in the picture for as
|
|
1660
|
+
* long as it answers. `setup` writes only the Cloudsmith source, so a fresh
|
|
1661
|
+
* server and an upgraded one end up identical.
|
|
1662
|
+
*
|
|
1663
|
+
* Idempotent: skipped once the Cloudsmith source file is in place, and each
|
|
1664
|
+
* removal is guarded by the file's existence.
|
|
1665
|
+
*/
|
|
1666
|
+
var migration0017DdevAptCloudsmith = {
|
|
1667
|
+
id: "0017__ddev_apt_cloudsmith",
|
|
1668
|
+
description: "Move the DDEV apt repository from Gemfury to Cloudsmith",
|
|
1669
|
+
run() {
|
|
1670
|
+
runDdevAptCloudsmithMigration();
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
/** The migration body, with its effects injected so tests can drive it. */
|
|
1674
|
+
function runDdevAptCloudsmithMigration(io = nodeIo) {
|
|
1675
|
+
if (io.fileExists("/etc/apt/sources.list.d/ddev.sources")) return;
|
|
1676
|
+
if (io.fileExists(OLD_SOURCES)) io.exec(`rm -f ${OLD_SOURCES}`, { silent: true });
|
|
1677
|
+
if (io.fileExists(OLD_KEYRING)) io.exec(`rm -f ${OLD_KEYRING}`, { silent: true });
|
|
1678
|
+
addDdevAptRepo(io);
|
|
1679
|
+
io.exec("apt-get update -qq", { silent: true });
|
|
1680
|
+
}
|
|
1681
|
+
//#endregion
|
|
1682
|
+
//#region src/setup/migrations/0018__docker_address_pools.ts
|
|
1683
|
+
/**
|
|
1684
|
+
* Migration 0018: give Docker enough address space for one network per
|
|
1685
|
+
* project.
|
|
1686
|
+
*
|
|
1687
|
+
* `setup` wrote a daemon.json without `default-address-pools`, so Docker
|
|
1688
|
+
* used its built-in pools: about 31 networks. Each DDEV project creates its
|
|
1689
|
+
* own `ddev-<name>_default` network, and a server with ~30 projects failed
|
|
1690
|
+
* to start the next one with "all predefined address pools have been fully
|
|
1691
|
+
* subnetted". The last 16 of those built-in networks are in 192.168.x,
|
|
1692
|
+
* outside the 172.16.0.0/12 range the firewall allows to reach the agent.
|
|
1693
|
+
*
|
|
1694
|
+
* `configureDocker` now writes /24 pools in 172.16.0.0/12, which covers
|
|
1695
|
+
* fresh installs. This migration adds them to existing servers.
|
|
1696
|
+
*
|
|
1697
|
+
* Docker reads the pools only at start, so the daemon is restarted.
|
|
1698
|
+
* `live-restore`, set by setup, keeps the running containers up. Existing
|
|
1699
|
+
* networks keep their subnets; only new networks get a /24, and Docker skips
|
|
1700
|
+
* subnets an existing network already uses.
|
|
1701
|
+
*
|
|
1702
|
+
* Changed after release: the first version ran `systemctl restart docker`
|
|
1703
|
+
* directly. On servers set up before 0.1.47 that restart also switched the
|
|
1704
|
+
* storage driver, which `live-restore` cannot survive, and orphaned every
|
|
1705
|
+
* running container (see restartDocker). The result on a server is the same
|
|
1706
|
+
* either way; only servers that have not run it yet get the safe restart.
|
|
1707
|
+
* Servers that already ran it are repaired by migration 0019.
|
|
1708
|
+
*
|
|
1709
|
+
* Idempotent: does nothing when the config already sets pools.
|
|
1710
|
+
*/
|
|
1711
|
+
var migration0018DockerAddressPools = {
|
|
1712
|
+
id: "0018__docker_address_pools",
|
|
1713
|
+
description: "Give Docker address pools for one network per project",
|
|
1714
|
+
run() {
|
|
1715
|
+
runDockerAddressPoolsMigration();
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1718
|
+
/**
|
|
1719
|
+
* The migration body, with its effects injected so tests can drive it.
|
|
1720
|
+
*/
|
|
1721
|
+
function runDockerAddressPoolsMigration(io = nodeIo) {
|
|
1722
|
+
if (addDockerAddressPools(io)) restartDocker(io);
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Migration 0019: stop the containers Docker lost track of in migration 0018.
|
|
1726
|
+
*
|
|
1727
|
+
* 0018 restarted Docker to apply the address pools. On a server set up before
|
|
1728
|
+
* 0.1.47, Docker ran the containerd image store (`overlayfs`) although
|
|
1729
|
+
* daemon.json asked for `overlay2`, and the restart switched the driver.
|
|
1730
|
+
* dockerd skips containers created with another driver, so it forgot every
|
|
1731
|
+
* running container — but `live-restore` had left their processes running.
|
|
1732
|
+
* Those orphans keep the locks on the project volumes: each new database
|
|
1733
|
+
* container exits on "Can't lock aria control file", each Elasticsearch on
|
|
1734
|
+
* "failed to obtain node locks", and the project never starts again. Seen
|
|
1735
|
+
* on a live server: 66 orphans, 27 of them databases.
|
|
1736
|
+
*
|
|
1737
|
+
* containerd still knows the orphans: they are the tasks in the `moby`
|
|
1738
|
+
* namespace with no matching Docker container. Each one gets SIGTERM, which
|
|
1739
|
+
* MariaDB, MySQL and Elasticsearch handle as a clean shutdown, and as long as
|
|
1740
|
+
* it needs. No SIGKILL: a database killed mid-write is the one outcome worse
|
|
1741
|
+
* than the outage, so a process that does not stop is reported and left
|
|
1742
|
+
* alone. The stopped tasks are then deleted, which ends their shims.
|
|
1743
|
+
*
|
|
1744
|
+
* Nothing is restarted here: the volumes are free again, and the agent starts
|
|
1745
|
+
* each project on its next request, on the new driver.
|
|
1746
|
+
*
|
|
1747
|
+
* Idempotent: does nothing when every containerd task belongs to a container
|
|
1748
|
+
* Docker knows.
|
|
1749
|
+
*/
|
|
1750
|
+
var migration0019RecoverOrphanContainers = {
|
|
1751
|
+
id: "0019__recover_orphan_containers",
|
|
1752
|
+
description: "Stop the containers Docker lost track of when it changed storage driver",
|
|
1753
|
+
run() {
|
|
1754
|
+
recoverOrphanContainers();
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
/**
|
|
1758
|
+
* The migration body, with its effects injected so tests can drive it.
|
|
1759
|
+
*/
|
|
1760
|
+
function recoverOrphanContainers(io = nodeIo) {
|
|
1761
|
+
const known = new Set(io.execSilent("docker ps -aq --no-trunc").split("\n").filter(Boolean));
|
|
1762
|
+
const orphans = parseTasks(io.execSilent("ctr -n moby tasks ls")).filter((task) => !known.has(task.id));
|
|
1763
|
+
if (orphans.length === 0) return;
|
|
1764
|
+
info(`Stopping ${orphans.length} container(s) Docker no longer tracks`);
|
|
1765
|
+
const running = orphans.filter((task) => task.status === "RUNNING");
|
|
1766
|
+
for (const task of running) io.execSilent(`kill -TERM ${task.pid}`);
|
|
1767
|
+
const stuck = running.filter((task) => {
|
|
1768
|
+
io.execSilent(`timeout 600 tail --pid=${task.pid} -f /dev/null`);
|
|
1769
|
+
return io.fileExists(`/proc/${task.pid}`);
|
|
1770
|
+
});
|
|
1771
|
+
for (const task of orphans.filter((task) => !stuck.includes(task))) io.execSilent(`ctr -n moby tasks delete ${task.id}`);
|
|
1772
|
+
for (const task of stuck) warn(`Container ${task.id.slice(0, 12)} (PID ${task.pid}) did not stop, left running`);
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Parse `ctr tasks ls`: a header, then one `TASK PID STATUS` row per task.
|
|
1776
|
+
*/
|
|
1777
|
+
function parseTasks(output) {
|
|
1778
|
+
return output.split("\n").slice(1).map((line) => line.trim().split(/\s+/)).filter((columns) => columns.length >= 3).map(([id, pid, status]) => ({
|
|
1779
|
+
id,
|
|
1780
|
+
pid,
|
|
1781
|
+
status
|
|
1782
|
+
}));
|
|
1783
|
+
}
|
|
1784
|
+
//#endregion
|
|
1551
1785
|
//#region src/setup/migrations/index.ts
|
|
1552
1786
|
/** Path to the persisted migration state file */
|
|
1553
1787
|
var MIGRATIONS_STATE_FILE = "/etc/trafic/.migrations.json";
|
|
@@ -1568,7 +1802,10 @@ var ALL_MIGRATIONS = [
|
|
|
1568
1802
|
migration0013SshKexDefaults,
|
|
1569
1803
|
migration0014UnattendedUpgradesDockerOrigin,
|
|
1570
1804
|
migration0015CatchallPriority,
|
|
1571
|
-
migration0016WildcardDnsChallenge
|
|
1805
|
+
migration0016WildcardDnsChallenge,
|
|
1806
|
+
migration0018DockerAddressPools,
|
|
1807
|
+
migration0017DdevAptCloudsmith,
|
|
1808
|
+
migration0019RecoverOrphanContainers
|
|
1572
1809
|
];
|
|
1573
1810
|
/**
|
|
1574
1811
|
* Load the current migration state from disk.
|
|
@@ -1726,4 +1963,4 @@ async function audit() {
|
|
|
1726
1963
|
//#endregion
|
|
1727
1964
|
export { printAuditResults as a, stopIdleProjects as c, buildDockerFirewallScript as d, buildDockerFirewallUnit as f, runPendingMigrations as i, DOCKER_FIREWALL_SCRIPT as l, setup as n, runAudit as o, configureDockerFirewall as p, listMigrations as r, startIdleScheduler as s, audit as t, DOCKER_FIREWALL_UNIT as u };
|
|
1728
1965
|
|
|
1729
|
-
//# sourceMappingURL=setup-
|
|
1966
|
+
//# sourceMappingURL=setup-DWgb6dAD.js.map
|