signalk-container 1.30.0 → 1.32.0
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/AGENTS.md +22 -0
- package/README.md +51 -24
- package/dist/client.d.ts +24 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +78 -4
- package/dist/client.js.map +1 -1
- package/dist/containers.d.ts +119 -16
- package/dist/containers.d.ts.map +1 -1
- package/dist/containers.js +308 -37
- package/dist/containers.js.map +1 -1
- package/dist/devices.d.ts +55 -3
- package/dist/devices.d.ts.map +1 -1
- package/dist/devices.js +100 -15
- package/dist/devices.js.map +1 -1
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +76 -7
- package/dist/index.js.map +1 -1
- package/dist/namespace.d.ts +9 -0
- package/dist/namespace.d.ts.map +1 -1
- package/dist/namespace.js +11 -0
- package/dist/namespace.js.map +1 -1
- package/dist/runtime.d.ts +1 -13
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +38 -2
- package/dist/runtime.js.map +1 -1
- package/dist/types.d.ts +116 -23
- package/dist/types.d.ts.map +1 -1
- package/doc/plugin-developer-guide.md +99 -28
- package/doc/run-in-container.md +12 -4
- package/package.json +5 -4
- package/public/chunks/_virtual_mf-localSharedImportMap___mfe_internal__signalk_container__mf_owner__1-COEdWfIZ.js +1 -0
- package/public/chunks/dist-S2EeZB5F.js +4 -0
- package/public/chunks/{hostInit-B9SMrbCX.js → hostInit-D-1ZoiV2.js} +1 -1
- package/public/remoteEntry.js +1 -1
- package/public/chunks/_virtual_mf-localSharedImportMap___mfe_internal__signalk_container__mf_owner__1-CfjL8DWB.js +0 -1
- package/public/chunks/dist-CMZwYQxm.js +0 -4
package/AGENTS.md
CHANGED
|
@@ -42,6 +42,14 @@ Do not add error handling, fallbacks, or validation for scenarios that cannot ha
|
|
|
42
42
|
- `npm test` — unit only (`dist/test/*.test.js`, no recursion). Safe to run anywhere.
|
|
43
43
|
- `npm run test:integration` — integration only (`dist/test/integration/*.test.js`). Requires podman or docker; tests still self-skip on Windows and when no runtime is found.
|
|
44
44
|
- `npm run test:all` — both. The pre-PR full sweep on a dev box.
|
|
45
|
+
- **Known Windows CI flake.** `node --test` intermittently marks a whole test
|
|
46
|
+
file failed with a bare `'test failed'` at `:1:1` while every assertion
|
|
47
|
+
inside it passed — the file's child process exits non-zero after its work
|
|
48
|
+
completes. It is not tied to any particular file or to the change under
|
|
49
|
+
test, and `--test-concurrency=1` does not prevent it. Diagnose it by
|
|
50
|
+
checking whether the reported file's own tests all show ✔; if they do,
|
|
51
|
+
re-run the job. `npm test` writes `test-results/junit.xml`, which plugin-CI
|
|
52
|
+
uploads as an artifact — attach that to any upstream report.
|
|
45
53
|
- All new code requires tests. Test behavior at the function boundary, not internal control flow.
|
|
46
54
|
- Inject `client: ContainerClient = getClient()` rather than calling dockerode directly. Tests stub via `makeMockClient(spec)` from `src/test/helpers/mockClient.ts`. See `src/test/getLiveResources.test.ts` for the canonical pattern: a mock whose `getContainer().inspect()` returns the JSON object under test, no real podman/docker invocations.
|
|
47
55
|
- Container-integration tests (those that actually pull `alpine:3.19`) live under `src/test/integration/` and gate on `hasContainerRuntime()` which returns `null` on Windows. Do not add new tests that pull real Linux images without putting them under `src/test/integration/` AND gating on the helper.
|
|
@@ -130,6 +138,20 @@ See `src/client.ts`, `src/containers.ts`, `src/log-stream-broker.ts`, and the te
|
|
|
130
138
|
|
|
131
139
|
`qualifyImage("foo/bar:tag", podmanRuntime)` prefixes `docker.io/` when needed (podman requires fully qualified names unless `unqualified-search-registries` is set; this holds over the API too). Docker passes through. Use this everywhere we feed an image string to a dockerode call.
|
|
132
140
|
|
|
141
|
+
### The compat endpoint silently drops what the CLI honours
|
|
142
|
+
|
|
143
|
+
Everything except `/libpod/info` goes through Podman's **Docker-compat** API via dockerode. That endpoint accepts several fields the CLI supports, returns 201, and then ignores them. Three are known and each cost real debugging time:
|
|
144
|
+
|
|
145
|
+
| Field | CLI | Compat endpoint |
|
|
146
|
+
| -------------------------------- | ------------------------ | --------------------------------------------------------------------------------- |
|
|
147
|
+
| `HostConfig.Ulimits` (nofile) | honoured | dropped below podman 5.5.0 (containers/podman#25881) |
|
|
148
|
+
| `HostConfig.UsernsMode` | honoured | accepted, stored as `private` |
|
|
149
|
+
| `Mounts[].VolumeOptions.Subpath` | `--mount subpath=` works | accepted, ignored (measured on 5.4.2; Docker Engine honours it — 29.7.2/API 1.55) |
|
|
150
|
+
|
|
151
|
+
The lesson is procedural: **a runtime's changelog or man page describes its CLI, not this endpoint.** "Podman 5.4 supports volume subpaths" is true and irrelevant — the plugin cannot use it. Before writing a capability claim into a comment, an error message, or the docs, measure it through dockerode against the socket. `src/test/integration/` is where such a probe belongs.
|
|
152
|
+
|
|
153
|
+
This also means an operator-facing error must name the mechanism precisely. Telling someone "named volumes cannot be subpath-mounted" sends them to the podman docs, which say otherwise, and they conclude the plugin is broken.
|
|
154
|
+
|
|
133
155
|
### Inspect-format diff pattern
|
|
134
156
|
|
|
135
157
|
When we need to read live container state, call `getContainer(name).inspect()` once (through `safeInspect`, which returns `null` on a 404 instead of throwing) and read the JSON fields directly. dockerode returns the same field shapes on podman and docker (verified live: `HostConfig.NanoCpus`, `HostConfig.Memory`, `NetworkSettings.Ports`, `Mounts[].{Type,Source,Destination}`, `Config.{Image,Cmd,Env,Healthcheck}`, etc.), so there's no Go-template parsing and no podman-vs-docker text divergence to guard against. `getLiveResources` and `getLiveContainerConfig` are the canonical examples. `diffContainerConfig` is a pure function over those inspect-derived values — keep new live-state probes reading inspect JSON directly so the diff stays uniform across runtimes.
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Instead of each plugin implementing its own container orchestration, they delega
|
|
|
18
18
|
- **CPU caps that fit the host** -- a `cpus` limit above the daemon's core count is lowered to it instead of failing the container create (Docker rejects `--cpus 1.5` on a 1-vCPU host outright); the consumer plugin can surface it via the optional `onResourceClamped` callback (otherwise it is a debug-log line). See the [developer guide](doc/plugin-developer-guide.md#resource-limits).
|
|
19
19
|
- **Per-process ulimits** -- `ContainerConfig.ulimits` pins per-process limits (`nofile`, `nproc`, …) on a container. A containerized process inherits these from the runtime, not the host sysctl, so raising the host `fs.file-max` alone does not lift a database's open-files limit; setting `ulimits` does. A `nofile` request over the host ceiling is clamped (not rejected) so the container still starts. See the [developer guide](doc/plugin-developer-guide.md#per-process-ulimits-nofile-) and, for raising the host limit, [Raising the open-files limit](#raising-the-open-files-limit-nofile).
|
|
20
20
|
- **Image management** -- scheduled pruning of dangling images (weekly/monthly), plus optional cleanup of superseded versions of managed-container images
|
|
21
|
-
- **Zero-config
|
|
21
|
+
- **Zero-config persistent storage** -- `signalkDataMount` mounts signalk-container's own data directory into any managed container automatically, whether Signal K runs on bare metal, in Docker (named volume), or in Podman (named volume or bind mount). No host paths to configure; namespace a subdirectory, since every managed container shares it.
|
|
22
22
|
- **Zero-config config root sharing** -- `signalkConfigRootMount` mounts the entire SignalK installation config (`~/.signalk/`) — for backup, audit, or config-sync tools that need the whole tree, not the per-plugin subdirectory.
|
|
23
23
|
- **Zero-config container service connectivity** -- `signalkAccessiblePorts` lets the SignalK process connect back to a service running inside a managed container (e.g. an HTTP or TCP server). signalk-container picks the right networking strategy automatically — port binding on the host loopback for bare-metal deployments, or a shared Docker network with DNS for containerised ones. No host ports are exposed unnecessarily.
|
|
24
24
|
- **Host timezone propagation** -- managed containers and one-shot jobs get `TZ=<host zone>` injected automatically, so time-based logic inside them (cron-style Node-RED flows, Grafana/QuestDB time rendering, log timestamps) agrees with the host clock instead of defaulting to UTC. Consumer plugins that set `env.TZ` themselves keep full control; shell-level tools inside minimal images may additionally need the image's `tzdata` package to interpret the zone name. See the [developer guide](doc/plugin-developer-guide.md#host-timezone).
|
|
@@ -644,9 +644,28 @@ The plugin developer guide has a detailed walk-through in [doc/plugin-developer-
|
|
|
644
644
|
> appears to be ignored, the host's cgroup controller delegation is
|
|
645
645
|
> almost certainly the cause — see [Cgroup controller delegation](#cgroup-controller-delegation) above.
|
|
646
646
|
|
|
647
|
-
##
|
|
647
|
+
## Persistent storage for managed containers (`signalkDataMount`)
|
|
648
648
|
|
|
649
|
-
When a managed container needs to read
|
|
649
|
+
When a managed container needs somewhere durable to read and write (e.g. HLS segments, exports, caches), use `signalkDataMount` instead of computing and hardcoding a host path or volume name.
|
|
650
|
+
|
|
651
|
+
It mounts **signalk-container's own plugin data directory** —
|
|
652
|
+
`<configRoot>/plugin-config-data/signalk-container/`. Signal K rewrites
|
|
653
|
+
`getDataDirPath()` per plugin and this resolves it against signalk-container's
|
|
654
|
+
app, so the source is the same for every managed container whichever plugin
|
|
655
|
+
asked — never the caller's own directory, since a cross-plugin API reached
|
|
656
|
+
through `globalThis` never learns who called it.
|
|
657
|
+
|
|
658
|
+
The container never sees more than that directory. A bind mount is narrowed
|
|
659
|
+
to the exact host path; a **named volume** is accepted only when it is
|
|
660
|
+
attached to the directory itself, because signalk-container mounts a volume
|
|
661
|
+
whole, and one covering a parent would expose that volume's entire contents —
|
|
662
|
+
sibling plugins' data, or the whole SignalK config tree when the volume sits
|
|
663
|
+
on the config root. That case fails with an error rather than over-sharing —
|
|
664
|
+
see the note below.
|
|
665
|
+
|
|
666
|
+
**Namespace a subdirectory** (as the example below does) so two consumers
|
|
667
|
+
cannot collide. For the SignalK config root see `signalkConfigRootMount`; to
|
|
668
|
+
mount your own plugin's data dir, translate it with `resolveHostPath`.
|
|
650
669
|
|
|
651
670
|
```typescript
|
|
652
671
|
const SK_MOUNT = "/signalk-data";
|
|
@@ -654,7 +673,7 @@ const SK_MOUNT = "/signalk-data";
|
|
|
654
673
|
await containers.ensureRunning("my-worker", {
|
|
655
674
|
image: "myorg/myworker",
|
|
656
675
|
tag: "latest",
|
|
657
|
-
signalkDataMount: SK_MOUNT, // ←
|
|
676
|
+
signalkDataMount: SK_MOUNT, // ← shared managed-container storage
|
|
658
677
|
command: ["--output", path.join(SK_MOUNT, "my-plugin/output/result.bin")],
|
|
659
678
|
});
|
|
660
679
|
```
|
|
@@ -668,22 +687,30 @@ signalk-container resolves the correct source automatically:
|
|
|
668
687
|
| Docker, bind-backed data dir | the exact host path, even when a parent directory is bind-mounted |
|
|
669
688
|
| Podman (rootless or root) | same logic; named volumes receive no `:Z` flag |
|
|
670
689
|
|
|
671
|
-
|
|
690
|
+
On bare metal or a bind mount, `SK_MOUNT` corresponds to the root of the
|
|
691
|
+
resolved directory, so paths compose with `path.join`:
|
|
672
692
|
|
|
673
693
|
```typescript
|
|
674
|
-
// Path inside managed container
|
|
675
|
-
const containerPath = path.join(
|
|
676
|
-
SK_MOUNT,
|
|
677
|
-
path.relative(app.getDataDirPath(), absSignalkPath),
|
|
678
|
-
);
|
|
694
|
+
// Path inside the managed container, for a path under the resolved dir:
|
|
695
|
+
const containerPath = path.join(SK_MOUNT, "my-plugin", "output", "result.bin");
|
|
679
696
|
```
|
|
680
697
|
|
|
698
|
+
A **named volume** backing the directory works the same way, since it is
|
|
699
|
+
only accepted when attached to that directory. Call
|
|
700
|
+
`containers.resolveSignalkDataMount()` if you need the resolved source (a
|
|
701
|
+
host path or a volume name) at runtime.
|
|
702
|
+
|
|
681
703
|
> [!note]
|
|
682
|
-
>
|
|
683
|
-
>
|
|
684
|
-
>
|
|
685
|
-
>
|
|
686
|
-
>
|
|
704
|
+
> signalk-container mounts a named volume whole: it sends no subpath, and podman's
|
|
705
|
+
> Docker-compat endpoint ignores one anyway (measured on 5.4.2 — Docker Engine would
|
|
706
|
+
> honour it, so narrowing cannot work uniformly). A volume attached to the
|
|
707
|
+
> resolved directory is mounted whole, which is exactly that directory — fine. A volume
|
|
708
|
+
> attached to a **parent** (the SignalK config root, say) would expose far more than was
|
|
709
|
+
> asked for, so `ensureRunning` rejects it with an error naming the volume and the fix:
|
|
710
|
+
> attach the volume to the data directory, or use `resolveHostPath()` and handle the
|
|
711
|
+
> returned `subPath` deliberately. Also avoid writing to paths inside `SK_MOUNT` that are
|
|
712
|
+
> separately bind-mounted in the Signal K container — those are not visible from inside
|
|
713
|
+
> the managed container.
|
|
687
714
|
|
|
688
715
|
You can also call `containers.resolveSignalkDataMount()` if you need to inspect the resolved source at runtime (e.g. for logging).
|
|
689
716
|
|
|
@@ -712,7 +739,7 @@ The deployment-mode resolution is identical to `signalkDataMount`: bare-metal re
|
|
|
712
739
|
`app.config.configPath` is provided by the SignalK server runtime. If the caller's `app` object lacks it (a non-standard host), `ensureRunning()` throws.
|
|
713
740
|
|
|
714
741
|
> [!note]
|
|
715
|
-
> The same named-volume
|
|
742
|
+
> The same named-volume caveat applies — the volume is mounted whole. If `app.config.configPath` happens to live under a parent-directory bind, signalk-container computes the exact host path so the container sees the right tree.
|
|
716
743
|
|
|
717
744
|
### When SignalK runs in a container: self-container detection
|
|
718
745
|
|
|
@@ -902,7 +929,7 @@ See [doc/plugin-developer-guide.md](doc/plugin-developer-guide.md) for the full
|
|
|
902
929
|
| `updateResources(name, limits)` | Apply new resource limits live, fall back to recreate |
|
|
903
930
|
| `getResources(name)` | Currently effective limits (plugin defaults ⊕ user override) |
|
|
904
931
|
| `resolveSignalkDataMount()` | Resolve the volume name or host path that backs `app.getDataDirPath()` in the current deployment; returns `null` if the runtime is not yet initialised |
|
|
905
|
-
| `probeHostDevice(path)` | Ask whether a device path exists **on the host** and which groups own its nodes — a plugin cannot `stat()` this itself once SignalK is containerized. Returns `null` for "unknown", which is not the same as `{exists:false}`. 1.30.0+
|
|
932
|
+
| `probeHostDevice(path)` | Ask whether a device path exists **on the host** and which groups own its nodes — a plugin cannot `stat()` this itself once SignalK is containerized. Returns `null` for "unknown", which is not the same as `{exists:false}`. 1.30.0+ |
|
|
906
933
|
| `resolveHostPath(absPath)` | Translate an arbitrary absolute path into the `{ source, subPath }` pair the runtime needs to mount it; handles bare-metal, bind, and named-volume topologies |
|
|
907
934
|
| `resolveContainerAddress(name, port)` | Return the `host:port` string to reach `port` on a managed container from the SignalK process; call after `ensureRunning()` with `signalkAccessiblePorts` set |
|
|
908
935
|
| `doctor.imageRunsAsUser(image, user?)` | Probe whether `image` runs cleanly under the host-UID mapping signalk-container will emit (1.8.0+). Never throws — returns `{ ok, output, error? }` |
|
|
@@ -955,10 +982,10 @@ All mounted at `/plugins/signalk-container/api/`:
|
|
|
955
982
|
device you might pass through — from a plugin that cannot see the host:
|
|
956
983
|
|
|
957
984
|
```js
|
|
958
|
-
const gpu = await containers.probeHostDevice?.(
|
|
985
|
+
const gpu = await containers.probeHostDevice?.("/dev/dri");
|
|
959
986
|
if (gpu?.exists) {
|
|
960
|
-
config.devices = [
|
|
961
|
-
config.groupAdd = gpu.groups
|
|
987
|
+
config.devices = ["/dev/dri"];
|
|
988
|
+
config.groupAdd = gpu.groups; // names, resolved per host
|
|
962
989
|
}
|
|
963
990
|
```
|
|
964
991
|
|
|
@@ -973,10 +1000,10 @@ number loses access on someone else's machine.
|
|
|
973
1000
|
|
|
974
1001
|
Three results, and the difference matters:
|
|
975
1002
|
|
|
976
|
-
| Result | Meaning
|
|
977
|
-
| ------------------------------- |
|
|
978
|
-
| `{exists: true, nodes, groups}` | Device is there; pass `groups` to `groupAdd`
|
|
979
|
-
| `{exists: false, …}` | Definitely absent
|
|
1003
|
+
| Result | Meaning |
|
|
1004
|
+
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1005
|
+
| `{exists: true, nodes, groups}` | Device is there; pass `groups` to `groupAdd` |
|
|
1006
|
+
| `{exists: false, …}` | Definitely absent |
|
|
980
1007
|
| `null` | **Unknown** — no runtime, the path could not be read (a permission error, say), or the nodes are there but their owning group could not be determined |
|
|
981
1008
|
|
|
982
1009
|
Treat `null` as "assume no device, but do not report it as absent".
|
package/dist/client.d.ts
CHANGED
|
@@ -62,15 +62,36 @@ export interface LibpodNetworkBackendInfo {
|
|
|
62
62
|
path?: string;
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
+
/** One `container_id`/`host_id`/`size` triple from podman's `/info`. */
|
|
66
|
+
export interface LibpodIdMapEntry {
|
|
67
|
+
container_id?: number;
|
|
68
|
+
host_id?: number;
|
|
69
|
+
size?: number;
|
|
70
|
+
}
|
|
65
71
|
/**
|
|
66
72
|
* Podman's native `/libpod/info` — the docker-compat `/info` the rest of the
|
|
67
73
|
* plugin uses does NOT expose `networkBackendInfo`, so host-side network
|
|
68
74
|
* facts (which DNS helper netavark resolved, if any) are only visible here.
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* on any request failure, and when the modem can't dial (test mocks).
|
|
75
|
+
* Returns `null` on Docker, on any request failure, and when the modem
|
|
76
|
+
* can't dial (test mocks).
|
|
72
77
|
*/
|
|
73
78
|
export declare function libpodNetworkBackendInfo(client: ContainerClient): Promise<LibpodNetworkBackendInfo | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Subordinate ID range podman has for the calling user, from its own
|
|
81
|
+
* `/info` — the sum of every mapping entry beyond the identity one.
|
|
82
|
+
*
|
|
83
|
+
* `--userns=keep-id:uid=N` asks for a 65536-wide subordinate block. Where
|
|
84
|
+
* the account's `/etc/subuid` allocation is narrower, podman clamps the
|
|
85
|
+
* length, and at the limit clamps it to zero — which the kernel rejects
|
|
86
|
+
* with `writing file /proc/<pid>/gid_map: Invalid argument`. Reading the
|
|
87
|
+
* real width lets the caller bound the request to what exists.
|
|
88
|
+
*
|
|
89
|
+
* Asked of podman rather than read from `/etc/subuid` because the plugin
|
|
90
|
+
* usually runs inside a container, where that file is absent while the
|
|
91
|
+
* socket still answers. Returns `null` on Docker, on any failure, and when
|
|
92
|
+
* the modem cannot dial.
|
|
93
|
+
*/
|
|
94
|
+
export declare function libpodSubordinateUidCount(client: Pick<ContainerClient, "modem">): Promise<number | null>;
|
|
74
95
|
/** Runtime the caller prefers when more than one socket is reachable. */
|
|
75
96
|
export type SocketPreference = "auto" | "podman" | "docker";
|
|
76
97
|
export interface ResolvedClient {
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,MAAM,MAAM,WAAW,CAAC;AAG/B,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC;IACrC,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;IACvC,eAAe,CACb,IAAI,EAAE,MAAM,CAAC,sBAAsB,GAClC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7B,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1E,cAAc,CACZ,IAAI,CAAC,EAAE,MAAM,CAAC,oBAAoB,GACjC,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;IACnC,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IACzE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IACrE,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAC5D,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IACzC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IACzB,iFAAiF;IACjF,KAAK,EAAE;QACL,cAAc,CACZ,MAAM,EAAE,MAAM,CAAC,cAAc,EAC7B,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,EAC5D,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAClC,IAAI,CAAC;QACR,WAAW,CACT,MAAM,EAAE,MAAM,CAAC,cAAc,EAC7B,MAAM,EAAE,MAAM,CAAC,cAAc,EAC7B,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,IAAI,CAAC;QACR;;;WAGG;QACH,IAAI,CAAC,CACH,OAAO,EAAE;YACP,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;SAC/C,EACD,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,GACnD,IAAI,CAAC;KACT,CAAC;CACH;AAED,0EAA0E;AAC1E,MAAM,WAAW,wBAAwB;IACvC,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,GAAG,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -19,13 +19,18 @@ import Docker from "dockerode";
|
|
|
19
19
|
import { stat } from "node:fs/promises";
|
|
20
20
|
import { PassThrough } from "node:stream";
|
|
21
21
|
import { categorizeError, messageWithRaw, } from "./errors.js";
|
|
22
|
+
/**
|
|
23
|
+
* Podman's native info endpoint. The version prefix is required —
|
|
24
|
+
* unversioned libpod paths 404 — and podman accepts any client version
|
|
25
|
+
* here. Shared by both probes so the two cannot drift apart.
|
|
26
|
+
*/
|
|
27
|
+
const LIBPOD_INFO_PATH = "/v4.0.0/libpod/info";
|
|
22
28
|
/**
|
|
23
29
|
* Podman's native `/libpod/info` — the docker-compat `/info` the rest of the
|
|
24
30
|
* plugin uses does NOT expose `networkBackendInfo`, so host-side network
|
|
25
31
|
* facts (which DNS helper netavark resolved, if any) are only visible here.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* on any request failure, and when the modem can't dial (test mocks).
|
|
32
|
+
* Returns `null` on Docker, on any request failure, and when the modem
|
|
33
|
+
* can't dial (test mocks).
|
|
29
34
|
*/
|
|
30
35
|
export function libpodNetworkBackendInfo(client) {
|
|
31
36
|
return new Promise((resolve) => {
|
|
@@ -36,7 +41,7 @@ export function libpodNetworkBackendInfo(client) {
|
|
|
36
41
|
}
|
|
37
42
|
try {
|
|
38
43
|
dial({
|
|
39
|
-
path:
|
|
44
|
+
path: LIBPOD_INFO_PATH,
|
|
40
45
|
method: "GET",
|
|
41
46
|
statusCodes: { 200: true, 500: "server error" },
|
|
42
47
|
}, (err, data) => {
|
|
@@ -55,6 +60,75 @@ export function libpodNetworkBackendInfo(client) {
|
|
|
55
60
|
}
|
|
56
61
|
});
|
|
57
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Total width of the subordinate entries in one mapping table, or `null`
|
|
65
|
+
* when the table is absent or contributes nothing usable.
|
|
66
|
+
*
|
|
67
|
+
* The identity entry maps the caller's own id and is not part of the
|
|
68
|
+
* subordinate block. Sizes must be positive safe integers: podman parses
|
|
69
|
+
* `size` as a uint and rejects `size=1.5` outright, and a negative would
|
|
70
|
+
* shrink the total below what the account actually has.
|
|
71
|
+
*/
|
|
72
|
+
function subordinateWidth(map) {
|
|
73
|
+
if (!Array.isArray(map))
|
|
74
|
+
return null;
|
|
75
|
+
const total = map
|
|
76
|
+
.filter((e) => (e?.container_id ?? 0) !== 0)
|
|
77
|
+
.reduce((n, e) => {
|
|
78
|
+
const size = e?.size;
|
|
79
|
+
return typeof size === "number" && Number.isSafeInteger(size) && size > 0
|
|
80
|
+
? n + size
|
|
81
|
+
: n;
|
|
82
|
+
}, 0);
|
|
83
|
+
return total > 0 ? total : null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Subordinate ID range podman has for the calling user, from its own
|
|
87
|
+
* `/info` — the sum of every mapping entry beyond the identity one.
|
|
88
|
+
*
|
|
89
|
+
* `--userns=keep-id:uid=N` asks for a 65536-wide subordinate block. Where
|
|
90
|
+
* the account's `/etc/subuid` allocation is narrower, podman clamps the
|
|
91
|
+
* length, and at the limit clamps it to zero — which the kernel rejects
|
|
92
|
+
* with `writing file /proc/<pid>/gid_map: Invalid argument`. Reading the
|
|
93
|
+
* real width lets the caller bound the request to what exists.
|
|
94
|
+
*
|
|
95
|
+
* Asked of podman rather than read from `/etc/subuid` because the plugin
|
|
96
|
+
* usually runs inside a container, where that file is absent while the
|
|
97
|
+
* socket still answers. Returns `null` on Docker, on any failure, and when
|
|
98
|
+
* the modem cannot dial.
|
|
99
|
+
*/
|
|
100
|
+
export function libpodSubordinateUidCount(client) {
|
|
101
|
+
return new Promise((resolve) => {
|
|
102
|
+
const dial = client.modem.dial?.bind(client.modem);
|
|
103
|
+
if (!dial) {
|
|
104
|
+
resolve(null);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
dial({
|
|
109
|
+
path: LIBPOD_INFO_PATH,
|
|
110
|
+
method: "GET",
|
|
111
|
+
statusCodes: { 200: true, 500: "server error" },
|
|
112
|
+
}, (err, data) => {
|
|
113
|
+
if (err || typeof data !== "object" || data === null) {
|
|
114
|
+
resolve(null);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const maps = data.host?.idMappings;
|
|
118
|
+
const uid = subordinateWidth(maps?.uidmap);
|
|
119
|
+
const gid = subordinateWidth(maps?.gidmap);
|
|
120
|
+
// One `size` governs both the uid and gid mappings, so the usable
|
|
121
|
+
// width is the smaller of the two — asking for the uid width when
|
|
122
|
+
// fewer gids exist reproduces the very failure this bounds.
|
|
123
|
+
// Unknown on either side means no safe bound at all.
|
|
124
|
+
resolve(uid === null || gid === null ? null : Math.min(uid, gid));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
resolve(null);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
58
132
|
/**
|
|
59
133
|
* Socket candidates, in priority order, plus whether the operator pinned an
|
|
60
134
|
* explicit endpoint. When `DOCKER_HOST`/`CONTAINER_HOST` is set we honour ONLY
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,eAAe,EACf,cAAc,GAEf,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/containers.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type Docker from "dockerode";
|
|
2
|
-
import { ContainerConfig, ContainerInfo, ContainerRuntimeInfo, ContainerState, DeviceIssue, EnsureRunningOptions, LocalImageSummary, ManagedImageRef, NofileLimits, PruneResult, ContainerWedged, ResourceClamp, UlimitClamp, VolumeIssue, VolumeSpec } from "./types.js";
|
|
2
|
+
import { ContainerConfig, ContainerInfo, ContainerRuntimeInfo, ContainerState, DeviceIssue, EnsureRunningOptions, LocalImageSummary, ManagedImageRef, NofileLimits, PruneResult, ContainerStateDetail, RequestedConfigProvenance, ContainerWedged, ResourceClamp, UlimitClamp, VolumeIssue, VolumeSpec } from "./types.js";
|
|
3
3
|
import { StreamingProcessHandle } from "./runtime.js";
|
|
4
4
|
import { type ContainerClient } from "./client.js";
|
|
5
5
|
import { type ErrorKind } from "./errors.js";
|
|
@@ -84,7 +84,17 @@ export declare function resolveHostTimezone(systemZone?: () => string): string |
|
|
|
84
84
|
* `defaultHomeForConfigRoot`.
|
|
85
85
|
*/
|
|
86
86
|
export declare function defaultTimezoneEnv(env: Record<string, string> | undefined, zone: string | undefined): Record<string, string> | undefined;
|
|
87
|
-
|
|
87
|
+
/**
|
|
88
|
+
* What a host-source probe could establish about a path.
|
|
89
|
+
*
|
|
90
|
+
* `"unknown"` is NOT a synonym for absent. When the manager runs inside a
|
|
91
|
+
* container its filesystem is not the one the runtime resolves bind sources
|
|
92
|
+
* against, so `existsSync` on a host path answers a different question than
|
|
93
|
+
* the one asked. Reporting that as absent drops a volume whose source is
|
|
94
|
+
* perfectly real — the bug this type exists to prevent.
|
|
95
|
+
*/
|
|
96
|
+
export type VolumeSourceState = boolean | "unknown";
|
|
97
|
+
export declare function classifyVolumeSources(volumes: Record<string, string | VolumeSpec> | undefined, probe?: (path: string) => VolumeSourceState): {
|
|
88
98
|
kept: Record<string, string>;
|
|
89
99
|
skipped: Array<{
|
|
90
100
|
containerPath: string;
|
|
@@ -94,6 +104,11 @@ export declare function classifyVolumeSources(volumes: Record<string, string | V
|
|
|
94
104
|
containerPath: string;
|
|
95
105
|
source: string;
|
|
96
106
|
}>;
|
|
107
|
+
/** Host paths whose existence could not be established. Kept, not dropped. */
|
|
108
|
+
unverified: Array<{
|
|
109
|
+
containerPath: string;
|
|
110
|
+
source: string;
|
|
111
|
+
}>;
|
|
97
112
|
};
|
|
98
113
|
/**
|
|
99
114
|
* Given the volume issues from the last `ensureRunning` call (both
|
|
@@ -325,6 +340,19 @@ export declare function getLiveContainerDigest(runtime: ContainerRuntimeInfo, co
|
|
|
325
340
|
*/
|
|
326
341
|
export declare function pullImage(runtime: ContainerRuntimeInfo, image: string, onProgress?: (msg: string) => void, client?: ContainerClient): Promise<void>;
|
|
327
342
|
export declare function getContainerState(runtime: ContainerRuntimeInfo, name: string, client?: ContainerClient): Promise<ContainerState>;
|
|
343
|
+
/**
|
|
344
|
+
* Coarse state plus the detail that explains it — exit code, OOM kill,
|
|
345
|
+
* restart count and the container's own healthcheck verdict.
|
|
346
|
+
*
|
|
347
|
+
* `getContainerState` answers "can I talk to it"; this answers "why is
|
|
348
|
+
* it like that". Both read the same `State` block from one inspect, and
|
|
349
|
+
* share `coarseStateFrom` so the two can never disagree about running.
|
|
350
|
+
*
|
|
351
|
+
* Returns `state: "missing"` with no detail when the container is gone.
|
|
352
|
+
* Individual fields stay undefined when the runtime omits them rather
|
|
353
|
+
* than defaulting to a zero that would read as real data.
|
|
354
|
+
*/
|
|
355
|
+
export declare function getContainerStateDetail(name: string, client?: ContainerClient): Promise<ContainerStateDetail>;
|
|
328
356
|
/**
|
|
329
357
|
* Upper bound on the `.State.Error` text surfaced to callers. Some OCI
|
|
330
358
|
* runtime rejections embed multi-kilobyte detail (spec dumps, nested
|
|
@@ -377,6 +405,19 @@ export declare function getLiveResources(runtime: ContainerRuntimeInfo, name: st
|
|
|
377
405
|
* current-vs-target check minus runtime-injected fields.
|
|
378
406
|
*/
|
|
379
407
|
export declare function getRequestedResources(name: string, client?: ContainerClient): Promise<import("./types.js").ContainerResourceLimits | undefined>;
|
|
408
|
+
/**
|
|
409
|
+
* Parse the requested-config provenance label written at create time.
|
|
410
|
+
*
|
|
411
|
+
* Returns `undefined` — meaning "no provenance", so the caller keeps
|
|
412
|
+
* today's positive-only drift behaviour — when the label is absent,
|
|
413
|
+
* unparseable, or not an object. It must never return `{}` for a
|
|
414
|
+
* missing label: an empty provenance reads as "the container was
|
|
415
|
+
* created with no env at all", which would mask a genuine unset.
|
|
416
|
+
*
|
|
417
|
+
* Never throws. A hand-edited or truncated label degrades to
|
|
418
|
+
* `undefined` rather than breaking `ensureRunning`.
|
|
419
|
+
*/
|
|
420
|
+
export declare function parseRequestedConfigLabel(raw: string | undefined): RequestedConfigProvenance | undefined;
|
|
380
421
|
/**
|
|
381
422
|
* One host-side endpoint for a single container port. Mirror of the
|
|
382
423
|
* `{ HostIp, HostPort }` shape that podman/docker emit under
|
|
@@ -503,18 +544,20 @@ export declare function getLiveContainerConfig(runtime: ContainerRuntimeInfo, na
|
|
|
503
544
|
* for floating tags (`:latest` digest drift) is the update service's job.
|
|
504
545
|
* - command: explicit drift when `requested.command` is set and differs
|
|
505
546
|
* from `live.command`. When `requested.command` is undefined, drift is
|
|
506
|
-
* reported only if a `
|
|
507
|
-
* unsetting it).
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
547
|
+
* reported only if a prior `command` was set (i.e. the user is now
|
|
548
|
+
* unsetting it). The prior comes from the wrapper's `lastConfigs`
|
|
549
|
+
* cache when warm, and otherwise from the create-time provenance
|
|
550
|
+
* label; with neither, an undefined `requested.command` can't be
|
|
551
|
+
* told apart from "image's baked CMD" so we skip.
|
|
511
552
|
* - networkMode: runtime defaults (`bridge`, `slirp4netns`, etc.) are
|
|
512
553
|
* normalized to `""` and compared as equivalent to requested undefined.
|
|
513
554
|
* - env: requested keys must match live values. Additionally, any key
|
|
514
|
-
* present in
|
|
515
|
-
* as drift (the user is unsetting it). Image-baked env keys
|
|
516
|
-
*
|
|
517
|
-
*
|
|
555
|
+
* present in the prior env but absent from `requested.env` is treated
|
|
556
|
+
* as drift (the user is unsetting it). Image-baked env keys in neither
|
|
557
|
+
* `requested.env` nor the prior are ignored — they were never ours.
|
|
558
|
+
* The prior is the warm `lastConfigs` entry, falling back to the env
|
|
559
|
+
* KEY NAMES recorded in the create-time provenance label; values are
|
|
560
|
+
* never recorded there because this check reads only key presence.
|
|
518
561
|
* - volumes: trailing slashes stripped on both sides; `(host, container)`
|
|
519
562
|
* tuples compared as a Map keyed by container path. Live binds have
|
|
520
563
|
* their `:Z`/`:ro` flags already stripped by `getLiveContainerConfig`.
|
|
@@ -612,8 +655,10 @@ export declare function ensureRunning(runtime: ContainerRuntimeInfo, name: strin
|
|
|
612
655
|
* this signalk-container lifetime, if any. Used to detect "unset" drift
|
|
613
656
|
* — env keys removed, `command` previously set and now undefined. The
|
|
614
657
|
* wrapper in `index.ts` reads from its `lastConfigs` cache before
|
|
615
|
-
* overwriting it
|
|
616
|
-
*
|
|
658
|
+
* overwriting it. On the first call (or after a Signal K restart) it
|
|
659
|
+
* is undefined and the diff falls back to the requested-config
|
|
660
|
+
* provenance label stamped at create time, so an unset is still
|
|
661
|
+
* detected across a restart.
|
|
617
662
|
*/
|
|
618
663
|
prior?: ContainerConfig, _postRecreate?: boolean, _pull?: PullFn): Promise<void>;
|
|
619
664
|
export declare function startContainer(runtime: ContainerRuntimeInfo, name: string, client?: ContainerClient): Promise<void>;
|
|
@@ -877,6 +922,24 @@ export declare function readSelfContainerIdsFromMountinfo(): string[];
|
|
|
877
922
|
* return null to the caller (depends on the consumer).
|
|
878
923
|
*/
|
|
879
924
|
export declare function findSelfContainerId(runtime: ContainerRuntimeInfo, debug?: (msg: string) => void, client?: ContainerClient): Promise<string | null>;
|
|
925
|
+
/**
|
|
926
|
+
* Refuse a named volume that covers more than the requested directory.
|
|
927
|
+
*
|
|
928
|
+
* Neither runtime can subpath-mount a volume, so a volume attached to a
|
|
929
|
+
* PARENT of the requested directory would hand the managed container
|
|
930
|
+
* everything in it — for `signalkDataMount`, the whole SignalK config tree
|
|
931
|
+
* with `security.json` in it, when the container asked only for scratch
|
|
932
|
+
* space. Throwing gives the caller a clear failure instead of silent access
|
|
933
|
+
* to files it never requested.
|
|
934
|
+
*
|
|
935
|
+
* A volume attached to the requested directory itself is exactly what was
|
|
936
|
+
* asked for and passes. Callers who genuinely want a parent-backed volume
|
|
937
|
+
* have `resolveHostPath`, which reports `subPath` so the choice is explicit.
|
|
938
|
+
*
|
|
939
|
+
* `field` names the API the consumer actually used, since one resolver
|
|
940
|
+
* serves both `signalkDataMount` and `signalkConfigRootMount`.
|
|
941
|
+
*/
|
|
942
|
+
export declare function assertVolumeIsNotBroaderThanRequested(volumeName: string, volumeDest: string, requestedDir: string, field: string): void;
|
|
880
943
|
/**
|
|
881
944
|
* Resolve what to mount in a managed container to give it access to
|
|
882
945
|
* the SignalK data directory, regardless of how SignalK itself is deployed.
|
|
@@ -890,10 +953,13 @@ export declare function findSelfContainerId(runtime: ContainerRuntimeInfo, debug
|
|
|
890
953
|
* fail gracefully at container-create time with a clear Docker error.
|
|
891
954
|
*
|
|
892
955
|
* The result can be used directly as `volumes: { [mountPoint]: source }` in
|
|
893
|
-
* a ContainerConfig. The content visible at mountPoint
|
|
894
|
-
*
|
|
956
|
+
* a ContainerConfig. The content visible at mountPoint corresponds to the
|
|
957
|
+
* root of dataDir: a bind is narrowed to the exact host path, and a named
|
|
958
|
+
* volume is only accepted when it is mounted on dataDir itself. A volume
|
|
959
|
+
* covering a parent throws rather than over-sharing: the plugin sends no
|
|
960
|
+
* subpath, and podman's compat endpoint would ignore one.
|
|
895
961
|
*/
|
|
896
|
-
export declare function resolveSignalkDataSource(dataDir: string, runtime: ContainerRuntimeInfo, debug?: (msg: string) => void, client?: ContainerClient): Promise<string>;
|
|
962
|
+
export declare function resolveSignalkDataSource(dataDir: string, runtime: ContainerRuntimeInfo, debug?: (msg: string) => void, client?: ContainerClient, field?: string): Promise<string>;
|
|
897
963
|
/**
|
|
898
964
|
* Mount entry as parsed from `podman inspect --format '{{range .Mounts}}...'`.
|
|
899
965
|
* Exposed so tests can drive `resolveHostPathFromMounts` directly without
|
|
@@ -959,6 +1025,43 @@ export interface ContainerMountResolution {
|
|
|
959
1025
|
* `resolveSignalkDataSource` does for backwards-compat).
|
|
960
1026
|
*/
|
|
961
1027
|
export declare function resolveHostPath(absPath: string, runtime: ContainerRuntimeInfo, debug?: (msg: string) => void, client?: ContainerClient): Promise<ContainerMountResolution | null>;
|
|
1028
|
+
/**
|
|
1029
|
+
* Can `existsSync(hostPath)` be trusted inside this container?
|
|
1030
|
+
*
|
|
1031
|
+
* The caller holds a HOST path and stats that same string locally, so the
|
|
1032
|
+
* mount has to make those two the same thing. Three conditions, and all are
|
|
1033
|
+
* load-bearing:
|
|
1034
|
+
*
|
|
1035
|
+
* - **Bind mounts only.** A named volume's contents are not the host
|
|
1036
|
+
* filesystem at that path, so a file seen inside one proves nothing.
|
|
1037
|
+
* - **Path-preserving only** (`source === dest`). A bind of `/host/data` to
|
|
1038
|
+
* `/data` puts the host's `/host/data` at `/data`; the string `/data` inside
|
|
1039
|
+
* the container names `/host/data` on the host, and the host's own `/data`
|
|
1040
|
+
* is not visible at all. Verified against a real runtime. Trusting such a
|
|
1041
|
+
* mount would let `existsSync("/data/certs")` answer about
|
|
1042
|
+
* `/host/data/certs` and report a nonexistent required source as present --
|
|
1043
|
+
* exactly the `ifMissing: "abort"` failure this guards.
|
|
1044
|
+
* - **Exact-or-child.** A mount at `/data` covers `/data` and `/data/sub`,
|
|
1045
|
+
* never `/database`.
|
|
1046
|
+
*
|
|
1047
|
+
* Factored out of `ownBindMountCoverage` so the rule is testable without a
|
|
1048
|
+
* runtime.
|
|
1049
|
+
*/
|
|
1050
|
+
export declare function isPathUnderBindMount(absPath: string, mounts: readonly InspectedMount[]): boolean;
|
|
1051
|
+
/**
|
|
1052
|
+
* Host paths this container can see truthfully, as a predicate.
|
|
1053
|
+
*
|
|
1054
|
+
* A bind mount makes this container's view of a path the HOST's view at a
|
|
1055
|
+
* known offset, so `existsSync` under one is authoritative in both directions.
|
|
1056
|
+
* Anywhere else a containerized process is looking at a different filesystem
|
|
1057
|
+
* entirely, and neither answer means anything about the host.
|
|
1058
|
+
*
|
|
1059
|
+
* Returns a predicate rather than resolving one path so the inspect happens
|
|
1060
|
+
* ONCE per reconcile instead of once per volume. On bare metal, or when the
|
|
1061
|
+
* self-inspect fails, every path is covered / not covered respectively — the
|
|
1062
|
+
* caller decides what that means.
|
|
1063
|
+
*/
|
|
1064
|
+
export declare function ownBindMountCoverage(runtime: ContainerRuntimeInfo, debug?: (msg: string) => void, client?: ContainerClient): Promise<(absPath: string) => boolean>;
|
|
962
1065
|
/**
|
|
963
1066
|
* Release a port that was reserved by `findAvailablePort()`.
|
|
964
1067
|
* Must be called after the container runtime has successfully bound the port
|