signalk-container 1.26.0 → 1.28.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 +17 -0
- package/README.md +36 -6
- package/dist/configNormalize.d.ts +39 -0
- package/dist/configNormalize.d.ts.map +1 -1
- package/dist/configNormalize.js +64 -0
- package/dist/configNormalize.js.map +1 -1
- package/dist/containers.js +4 -4
- package/dist/containers.js.map +1 -1
- package/dist/doctor.d.ts +14 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +139 -2
- package/dist/doctor.js.map +1 -1
- package/dist/doctorReport.d.ts +3 -1
- package/dist/doctorReport.d.ts.map +1 -1
- package/dist/doctorReport.js +18 -0
- package/dist/doctorReport.js.map +1 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -10
- package/dist/index.js.map +1 -1
- package/dist/resources.d.ts +8 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +22 -4
- package/dist/resources.js.map +1 -1
- package/dist/runtime.d.ts +27 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +30 -9
- package/dist/runtime.js.map +1 -1
- package/dist/types.d.ts +46 -1
- package/dist/types.d.ts.map +1 -1
- package/doc/plugin-developer-guide.md +13 -11
- package/doc/run-in-container.md +7 -0
- package/package.json +1 -1
- package/public/configpanel-entry.js +10 -10
package/AGENTS.md
CHANGED
|
@@ -72,6 +72,17 @@ Rootless podman below 5.5.0 silently ignores `HostConfig.Ulimits` on the docker-
|
|
|
72
72
|
- When the host outright REJECTS the ask at container start (crun/runc `setrlimit` — classified `ulimit-rejected` in `src/errors.ts`, checked before the `permission` patterns because the raw text contains "operation not permitted"), the `ensureRunning` create path retries ONCE without the nofile entry — only when the rejection text names nofile (`NOFILE_REJECTION_RE`) — so the container starts on the runtime's default limits, then emits the clamp advisory with `granted` = the observed live hard limit (0 when no source is readable, e.g. podman machine on macOS where the ceiling probe returns null and the ask went through unclamped). Never loop the retry — the nofile ask is dropped at most once. The fallback shares one retry loop with the optimistic-device fallback: a create can trip both, in either order, and every retry rebuilds from the union of ALL concessions so far (dropped device binds + `DEVICES_UNRESOLVED_LABEL` stamp + dropped nofile ask) — a retry rebuilt from the pristine config would re-introduce something the runtime already refused and fail the start that both fallbacks are intended to save.
|
|
73
73
|
- The fallback container carries no nofile entry in its inspect echo, so the regrant reads `asked` from the live limit. Where neither source is readable (macOS) the regrant never fires; elsewhere it may attempt one recreate with the original ask, which fails the same way, re-enters the fallback, and is then bounded by `nofileRegrantAttempts` — do not add a separate attempt map for the fallback, and do not weaken that guard.
|
|
74
74
|
|
|
75
|
+
### Platform recognition (HaLOS)
|
|
76
|
+
|
|
77
|
+
`selfDeployment` reports `platform` (`HostPlatform | null`), detected from a platform-specific marker visible to the SK process (`detectPlatform` probe). Recognition only swaps the remediation **text** — it never changes `status`. When a recognised platform has a known, single fix for a failure mode, the doctor renders a platform-specific guide instead of the generic remediation; `remediationHalosDockerSocket` is the first such case (HaLOS + containerized + `permission-denied`), reading the socket's real GID via the `readSocketGid` probe.
|
|
78
|
+
|
|
79
|
+
The concrete package paths, the compose-edit mechanics, and the exact paste block live in `remediationHalosDockerSocket` / `halosFixScript` in `src/doctor.ts` — that function is the single source of truth, so do not restate them here or in operator docs (they drift when HaLOS packaging changes). The invariants that constrain any such guide:
|
|
80
|
+
|
|
81
|
+
- **The executable block must be safe to paste verbatim.** It derives the docker-socket GID itself at run time (`stat -c %g`) rather than baking a value in — so it works even when the `readSocketGid` probe returned null, and it never carries a `<placeholder>` that a verbatim paste would write literally into the compose. The probe value is used only for the human-readable prose; the block never depends on it.
|
|
82
|
+
- **It must edit narrowly.** It rewrites `group_add` only when the compose has exactly one `group_add:` block **and that block belongs to the `signalk-server` service** — a sole `group_add` on a different service must be refused, never mis-edited (the `sed` targets the first block, so the guard has to prove that block is the right one). Any other shape (multiple blocks, none, missing file, unreadable socket) no-ops with a note.
|
|
83
|
+
- **It must be idempotent** (operators re-run it after a platform update re-installs the file); the "already present" check is quoting-agnostic (`- 993` / `- "993"` / `- '993'`).
|
|
84
|
+
- **A non-packaged deployment must fall through.** Bare-metal Signal K on a HaLOS host (not containerized) gets the generic remediation, not this guide.
|
|
85
|
+
|
|
75
86
|
### Podman SELinux flag
|
|
76
87
|
|
|
77
88
|
`volumeArg(hostPath, containerPath, runtime)` adds `:Z` for podman bind mounts (Fedora/RHEL SELinux relabel). Named volumes — host strings without a leading `/` or `.` — MUST NOT receive `:Z`; podman rejects them with `"invalid option z for named volume"`. Always go through `volumeArg`, never build `-v host:container[:flags]` strings inline.
|
|
@@ -168,6 +179,12 @@ Discoverability — `selfDeployment().containerStorage` reports the filesystem b
|
|
|
168
179
|
|
|
169
180
|
The diff has an optional `prior?: ContainerConfig` parameter for detecting "unset" drift (an env key previously set is now absent, a `command` previously set is now `undefined`). The wrapper in `src/index.ts` reads it from `lastConfigs` before overwriting.
|
|
170
181
|
|
|
182
|
+
### Resource-limit precedence and CPU priority tiers
|
|
183
|
+
|
|
184
|
+
The effective limits of a managed container are `tier ⊕ consumer resources ⊕ user override`, merged field-by-field with `null` = unset. `pluginDefaults` in `src/index.ts` is the single capture point of the bottom two layers (the plugin-wide `containerCpuPriority` tier with the consumer's `resources` on top) — `minimizeOverride`, `updateResources` and "Reset to plugin defaults" all read that map, so a new default-shaped input must be folded in there, not merged separately. Jobs get `jobCpuPriority ⊕ caller resources` in the API `runJob` wrapper.
|
|
185
|
+
|
|
186
|
+
Tiers are `--cpu-shares` values from `CPU_PRIORITY_SHARES` (`src/configNormalize.ts`, browser-safe so the panel shares the table). `normal` is deliberately no request: unset is `cpu.weight` 100 on every runtime, whereas the shares → weight translation is the OCI runtime's and differs (crun and runc < 1.3.2: 1024 → 39; runc ≥ 1.3.2: 1024 → 100 — measured, see the README table), and `podman update --cpu-shares 0` is a no-op — hence `cpuShares` is in `FIELDS_THAT_CANNOT_LIVE_UNSET`, `getLiveResources` reports 1024 as a real value, and neither the panel nor the tests assume one formula (the integration test accepts either).
|
|
187
|
+
|
|
171
188
|
### Recursion guard in ensureRunning
|
|
172
189
|
|
|
173
190
|
After auto-recreate, `ensureRunning` recursively re-enters itself with `_postRecreate=true`. The underscore prefix marks this as an internal-use-only parameter — do not document it for consumer plugins, do not move it earlier in the signature. The guard breaks the loop if state somehow stays `running` or `stopped` after the `remove`.
|
package/README.md
CHANGED
|
@@ -464,6 +464,7 @@ The plugin embeds a React config panel in the Signal K Admin UI (via Module Fede
|
|
|
464
464
|
- **Keep N prior managed-image versions** -- on the prune schedule above, also remove superseded versions of images belonging to managed containers, keeping this many prior versions in addition to the running one (default `1`; `0` keeps only the running image). Only touches images of containers this plugin manages — never your other images (e.g. a hand-pulled questdb/grafana), the running image, or any image in use by a container. See [Image version cleanup](#image-version-cleanup).
|
|
465
465
|
- **Update check interval** -- how often to check consumer plugins for new container images (1h to 1 week, default 24h)
|
|
466
466
|
- **Background update checks** -- toggle for metered connections; manual checks still work when off
|
|
467
|
+
- **CPU priority: containers / jobs** -- soft CPU weight every managed container (default `normal`) and every one-shot job (default `lowest`) gets when they compete for CPU. See [CPU priority](#cpu-priority).
|
|
467
468
|
- **Disable user-namespace remap (ZFS escape hatch)** -- off by default. Secondary fix for ZFS / id-map-less hosts; prefer the host-side `fuse-overlayfs` storage driver first ([ZFS host notes](#zfs-and-other-idmap-incompatible-filesystems)). Enable only if container creation fails with `crun: writing file /proc/<pid>/gid_map: Invalid argument` and you cannot switch storage drivers. With the flag on, signalk-container stops emitting `--userns=keep-id` for rootless Podman; bind-mount file ownership still lands on the host caller for root-by-default images (questdb, grafana, mayara), but non-root images lose host-caller ownership in exchange for being able to start at all.
|
|
468
469
|
|
|
469
470
|
### Managed Containers (one card per running or stopped container)
|
|
@@ -480,9 +481,9 @@ The plugin embeds a React config panel in the Signal K Admin UI (via Module Fede
|
|
|
480
481
|
|
|
481
482
|
### Resource Limits Editor (expands inline when you click "Edit Limits" on a running container)
|
|
482
483
|
|
|
483
|
-
-
|
|
484
|
-
- **Advanced** section (collapsed) for CPU
|
|
485
|
-
- **× button** next to each field to explicitly unset (send `null`, removing a plugin-default limit)
|
|
484
|
+
- Five primary fields visible by default: CPU cores, CPU priority (a tier, or a raw shares value), Memory, Memory+swap, Max processes
|
|
485
|
+
- **Advanced** section (collapsed) for CPU pinning, memory reservation, OOM score adjust
|
|
486
|
+
- **× button** next to each field to explicitly unset (send `null`, removing a plugin-default limit); the CPU priority select has none — Normal is the unset
|
|
486
487
|
- **Apply** -- live update where possible, recreate where needed, with a clear result box showing which method was used and any warnings (e.g. "dropped cpusetCpus — not delegated by cgroups")
|
|
487
488
|
- **Revert** -- discard unsaved form edits, re-seed from current effective state
|
|
488
489
|
- **Reset to default** -- clear the user override entirely and restore the consumer plugin's pristine default limits (confirmation dialog warns about possible recreate)
|
|
@@ -505,7 +506,7 @@ Each consumer plugin (signalk-questdb, signalk-grafana, mayara, etc.) declares d
|
|
|
505
506
|
1. Open the Signal K admin UI → Plugin Config → **Container Manager**
|
|
506
507
|
2. Find the container you want to tune in the "Managed Containers" list
|
|
507
508
|
3. Click **Edit Limits ▸** on the row
|
|
508
|
-
4. Edit the CPU cores, Memory, Memory+swap, or Max processes fields. Use the × button next to a field to explicitly unset a limit the plugin set. Click **Advanced** to access
|
|
509
|
+
4. Edit the CPU cores, CPU priority, Memory, Memory+swap, or Max processes fields. Use the × button next to a field to explicitly unset a limit the plugin set. Click **Advanced** to access cpusetCpus, memoryReservation, and oomScoreAdj.
|
|
509
510
|
5. Click **Apply** — live updated where possible (no downtime), recreated where needed. The result box shows which method was used plus any warnings.
|
|
510
511
|
6. To restore the plugin's default: click **Reset to default** (amber button). This clears your override and applies the pristine default to the running container.
|
|
511
512
|
|
|
@@ -516,7 +517,7 @@ The form re-seeds from the server's fresh state after every Apply or Reset, so t
|
|
|
516
517
|
| Field | Example | What it does |
|
|
517
518
|
| ------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
518
519
|
| `cpus` | `1.5` | Hard CPU cap. `1.5` = max 1.5 cores. The most important field for stability. |
|
|
519
|
-
| `cpuShares` | `512` | Soft CPU weight under contention (default
|
|
520
|
+
| `cpuShares` | `512` | Soft CPU weight under contention (`--cpu-shares`). Unset = the runtime default (`cpu.weight` 100); the panel offers named tiers. See [CPU priority](#cpu-priority). |
|
|
520
521
|
| `cpusetCpus` | `"1,2"` | Pin to specific cores. Useful to keep heavy containers off core 0 where Signal K runs. May force a recreate on hosts where the cpuset cgroup controller isn't delegated. |
|
|
521
522
|
| `memory` | `"512m"`, `"2g"` | Hard memory cap. Container is OOM-killed if exceeded. |
|
|
522
523
|
| `memorySwap` | `"512m"` | Memory + swap total. **Set equal to `memory` to disable swap entirely** — recommended on Pi/eMMC where swap is slow. |
|
|
@@ -600,6 +601,33 @@ curl http://localhost:3000/plugins/signalk-container/api/containers/mayara-serve
|
|
|
600
601
|
|
|
601
602
|
Note that `override` contains only the fields that differ from the consumer plugin's default — this minimization is automatic and lets future plugin default bumps flow through without you having to re-edit your override.
|
|
602
603
|
|
|
604
|
+
### CPU priority
|
|
605
|
+
|
|
606
|
+
`cpus` is a hard cap; `cpuShares` is a _soft_ weight that only matters when containers actually compete for CPU. On an idle host it changes nothing; when a chart import saturates every core, it decides who gets the CPU. The plugin exposes it as named tiers:
|
|
607
|
+
|
|
608
|
+
| Tier | `cpuShares` | `cpu.weight` on crun, runc < 1.3.2 | `cpu.weight` on runc ≥ 1.3.2 | Default for |
|
|
609
|
+
| -------- | ----------- | ---------------------------------- | ---------------------------- | ------------------ |
|
|
610
|
+
| `high` | `5120` | 196 | 363 | |
|
|
611
|
+
| `normal` | _unset_ | 100 | 100 | managed containers |
|
|
612
|
+
| `low` | `512` | 20 | 59 | |
|
|
613
|
+
| `lowest` | `128` | 5 | 21 | jobs (`runJob`) |
|
|
614
|
+
|
|
615
|
+
Two plugin-wide settings pick the default tier for managed containers and for jobs. A consumer plugin's own `resources.cpuShares` beats the tier, and a per-container override (panel or `containerOverrides`) beats both — the tier is simply the bottom layer of the same merge. To return one container to `normal`, pick Normal in the panel's limits editor (or `POST …/resources` with `{"cpuShares": null}`): that recreates the container, because neither runtime can un-set shares in place. A `cpuShares: null` written straight into `containerOverrides` is only picked up as far as a live update can go — the next consumer restart logs "cannot live-unset" and the container keeps its old weight until it is recreated.
|
|
616
|
+
|
|
617
|
+
Two things worth knowing:
|
|
618
|
+
|
|
619
|
+
- **`normal` means no request, not 1024.** The kernel schedules by cgroup v2 `cpu.weight`; the OCI runtime translates `--cpu-shares` into it, and the translation is the runtime's, not the kernel's: crun (Podman's default) and runc before 1.3.2 use a linear formula under which an explicit `1024` lands at weight 39 — _below_ an untouched container at 100 — while runc 1.3.2 and later use a quadratic one that maps 1024 to 100 (measured above on crun 1.21 and runc 1.4.3). Unset is 100 on every runtime and the tier order holds on every runtime; only the absolute numbers differ. Don't "reset" a container by typing 1024; use `normal` (the panel) or `null` (JSON).
|
|
620
|
+
- **Weights rank cgroup siblings only.** Managed containers and jobs are siblings of each other, so the tiers rank them against one another. Signal K itself usually lives in a different cgroup branch, so ranking it _above_ the plugin containers is a systemd `CPUWeight=` at the right level, not a tier here:
|
|
621
|
+
- Universal installer (rootless Podman, Signal K as a Quadlet unit in `app.slice`, managed containers in `user.slice`): the installer writes `~/.config/systemd/user/app.slice.d/50-signalk-cpu-priority.conf` with `[Slice] CPUWeight=300`. A hand-rolled rootless setup with Signal K as a user service takes the same drop-in.
|
|
622
|
+
- Bare-metal Signal K as a system service with rootful Podman/Docker: `signalk.service` and the `docker-*.scope` / `libpod-*.scope` containers are siblings under `system.slice`, so `systemctl edit signalk.service` → `[Service] CPUWeight=300` does it.
|
|
623
|
+
- Signal K itself in Docker (compose): its container is a sibling of the managed ones under `system.slice`, so the tiers already rank against it; give the Signal K service `cpu_shares: 5120` to put it on top.
|
|
624
|
+
|
|
625
|
+
The host's own services in `system.slice` are untouched by any of this.
|
|
626
|
+
|
|
627
|
+
Changing a tier _down_ (or between `high`/`low`/`lowest`) is applied live (`podman update` / `docker update`) to running containers on the next consumer-plugin restart. Moving a container _up_ to `normal` needs a recreate: neither runtime can un-set shares in place, so the restart only logs the mismatch — use the panel (Normal → Apply, or Reset to default) to recreate.
|
|
628
|
+
|
|
629
|
+
If the host has not delegated the `cpu` cgroup controller, the tiers are stored but silently have no effect — the panel says so next to the selects, and the [doctor](#quick-check-apidoctordeployment) reports `cgroup-controllers-incomplete`.
|
|
630
|
+
|
|
603
631
|
### Picking the right values
|
|
604
632
|
|
|
605
633
|
1. Run the container without overrides for a typical workload
|
|
@@ -709,7 +737,7 @@ services:
|
|
|
709
737
|
|
|
710
738
|
You'll see "could not detect self container id" in the SignalK log when the cascade has failed; the override resolves it without code changes.
|
|
711
739
|
|
|
712
|
-
For a full walkthrough of the in-container deployment (socket bind-mount, self-container-id override, reference quadlet, troubleshooting table) see **[doc/run-in-container.md](doc/run-in-container.md)**.
|
|
740
|
+
For a full walkthrough of the in-container deployment (socket bind-mount, self-container-id override, reference quadlet, troubleshooting table) see **[doc/run-in-container.md](doc/run-in-container.md)**. On [HaLOS](https://github.com/halos-org/halos) the Doctor recognises the platform and renders a paste-once fix for the docker-socket permission it ships with — see [doc/run-in-container.md#halos](doc/run-in-container.md#halos).
|
|
713
741
|
|
|
714
742
|
## Connecting back to a container service (`signalkAccessiblePorts`)
|
|
715
743
|
|
|
@@ -912,6 +940,8 @@ All mounted at `/plugins/signalk-container/api/`:
|
|
|
912
940
|
| Keep N prior managed-image versions | `1` | On the prune schedule, also remove superseded versions of managed-container images, keeping this many prior versions besides the running one (`0` = running only). Never touches unregistered images, the running image, or in-use images. See [Image version cleanup](#image-version-cleanup). |
|
|
913
941
|
| Max concurrent jobs | `2` | Limit parallel one-shot job executions |
|
|
914
942
|
| Update check interval | `24h` | How often to check for container image updates (e.g. `24h`, `12h`, `1h`). Min 1h. |
|
|
943
|
+
| CPU priority: containers | `normal` | Soft CPU weight tier for every managed container unless the owning plugin or a per-container override sets `cpuShares`. See [CPU priority](#cpu-priority). |
|
|
944
|
+
| CPU priority: jobs | `lowest` | Soft CPU weight tier for one-shot job containers unless the caller sets `cpuShares`. See [CPU priority](#cpu-priority). |
|
|
915
945
|
| Background update checks | `true` | Periodically check for updates in the background. Disable on metered connections — manual checks via the UI button still work. |
|
|
916
946
|
| Disable user-namespace remap | `false` | Suppress rootless-Podman `--userns=keep-id` on filesystems that cannot be id-mapped (ZFS, some encrypted FS). Secondary escape hatch only — the recommended primary fix is host-side `fuse-overlayfs` storage (see [ZFS host notes](#zfs-and-other-idmap-incompatible-filesystems)). |
|
|
917
947
|
| Container overrides | `{}` | Per-container resource limits (CPU, memory, PIDs). Field-level merged on top of consumer plugin defaults. See dev guide. |
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* backend (`index.ts`) and the React config panel. Kept free of any
|
|
4
4
|
* node-only imports so it can be bundled into the configpanel.
|
|
5
5
|
*/
|
|
6
|
+
import type { ContainerResourceLimits, CpuPriority } from "./types.js";
|
|
6
7
|
/**
|
|
7
8
|
* Prior managed-image versions the reaper keeps by default, in addition
|
|
8
9
|
* to the running one. Shared between the config schema's `default`, the
|
|
@@ -31,4 +32,42 @@ export declare function keepImageVersionsSelectValue(value: unknown): string;
|
|
|
31
32
|
* string can never persist a worse policy than the contract allows.
|
|
32
33
|
*/
|
|
33
34
|
export declare function keepImageVersionsFromSelectValue(selectValue: string): number;
|
|
35
|
+
/**
|
|
36
|
+
* CPU priority tiers, expressed as the `--cpu-shares` value each one
|
|
37
|
+
* maps to. Shares are the wire format the runtime takes; the kernel
|
|
38
|
+
* ranks cgroup siblings by cgroup v2 `cpu.weight`, and the OCI runtime
|
|
39
|
+
* translates shares into a weight. That translation is the runtime's:
|
|
40
|
+
* crun and runc before 1.3.2 use a linear formula (1024 → 39), runc
|
|
41
|
+
* 1.3.2+ a quadratic one (1024 → 100). Unset is weight 100 everywhere,
|
|
42
|
+
* so `normal` is deliberately *no request* — the only value that means
|
|
43
|
+
* the same on every runtime — and the tiers are ordered by shares,
|
|
44
|
+
* which both formulas preserve.
|
|
45
|
+
*
|
|
46
|
+
* Weights only arbitrate among cgroup siblings under contention; hard
|
|
47
|
+
* caps are `cpus`.
|
|
48
|
+
*/
|
|
49
|
+
export declare const CPU_PRIORITY_SHARES: Readonly<Record<CpuPriority, number | null>>;
|
|
50
|
+
/** Tier keys in descending priority order, for select widgets. */
|
|
51
|
+
export declare const CPU_PRIORITIES: readonly CpuPriority[];
|
|
52
|
+
export declare const DEFAULT_CONTAINER_CPU_PRIORITY: CpuPriority;
|
|
53
|
+
/**
|
|
54
|
+
* One-shot helpers (chart imports, GDAL, wipe jobs) are the workloads
|
|
55
|
+
* that saturate every core on a small host; they yield to the
|
|
56
|
+
* long-running services by default.
|
|
57
|
+
*/
|
|
58
|
+
export declare const DEFAULT_JOB_CPU_PRIORITY: CpuPriority;
|
|
59
|
+
/**
|
|
60
|
+
* Coerce a stored config value to a tier name. Anything that is not
|
|
61
|
+
* a known tier (hand-edited config, older panel) falls back to the
|
|
62
|
+
* given default so a typo can never select a priority nobody asked for.
|
|
63
|
+
*/
|
|
64
|
+
export declare function normalizeCpuPriority(value: unknown, fallback: CpuPriority): CpuPriority;
|
|
65
|
+
/**
|
|
66
|
+
* The resource-limits fragment a tier contributes. `normal` contributes
|
|
67
|
+
* nothing, so it never shows up as a limit and cannot mask a
|
|
68
|
+
* consumer's or user's own `cpuShares`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function cpuPriorityLimits(tier: CpuPriority): Pick<ContainerResourceLimits, "cpuShares">;
|
|
71
|
+
/** Reverse lookup for display: the tier a shares value stands for, if any. */
|
|
72
|
+
export declare function cpuPriorityForShares(shares: number | null | undefined): CpuPriority | null;
|
|
34
73
|
//# sourceMappingURL=configNormalize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configNormalize.d.ts","sourceRoot":"","sources":["../src/configNormalize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKjE;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK5E"}
|
|
1
|
+
{"version":3,"file":"configNormalize.d.ts","sourceRoot":"","sources":["../src/configNormalize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKjE;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAK5E;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC,CAM1E,CAAC;AAEJ,kEAAkE;AAClE,eAAO,MAAM,cAAc,EAAE,SAAS,WAAW,EAKhD,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,WAAsB,CAAC;AACpE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,WAAsB,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,WAAW,GACpB,WAAW,CAKb;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,GAChB,IAAI,CAAC,uBAAuB,EAAE,WAAW,CAAC,CAG5C;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,WAAW,GAAG,IAAI,CAMpB"}
|
package/dist/configNormalize.js
CHANGED
|
@@ -44,4 +44,68 @@ export function keepImageVersionsFromSelectValue(selectValue) {
|
|
|
44
44
|
return DEFAULT_KEEP_IMAGE_VERSIONS;
|
|
45
45
|
return normalizeKeepImageVersions(Number(selectValue));
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* CPU priority tiers, expressed as the `--cpu-shares` value each one
|
|
49
|
+
* maps to. Shares are the wire format the runtime takes; the kernel
|
|
50
|
+
* ranks cgroup siblings by cgroup v2 `cpu.weight`, and the OCI runtime
|
|
51
|
+
* translates shares into a weight. That translation is the runtime's:
|
|
52
|
+
* crun and runc before 1.3.2 use a linear formula (1024 → 39), runc
|
|
53
|
+
* 1.3.2+ a quadratic one (1024 → 100). Unset is weight 100 everywhere,
|
|
54
|
+
* so `normal` is deliberately *no request* — the only value that means
|
|
55
|
+
* the same on every runtime — and the tiers are ordered by shares,
|
|
56
|
+
* which both formulas preserve.
|
|
57
|
+
*
|
|
58
|
+
* Weights only arbitrate among cgroup siblings under contention; hard
|
|
59
|
+
* caps are `cpus`.
|
|
60
|
+
*/
|
|
61
|
+
export const CPU_PRIORITY_SHARES = {
|
|
62
|
+
high: 5120,
|
|
63
|
+
normal: null,
|
|
64
|
+
low: 512,
|
|
65
|
+
lowest: 128,
|
|
66
|
+
};
|
|
67
|
+
/** Tier keys in descending priority order, for select widgets. */
|
|
68
|
+
export const CPU_PRIORITIES = [
|
|
69
|
+
"high",
|
|
70
|
+
"normal",
|
|
71
|
+
"low",
|
|
72
|
+
"lowest",
|
|
73
|
+
];
|
|
74
|
+
export const DEFAULT_CONTAINER_CPU_PRIORITY = "normal";
|
|
75
|
+
/**
|
|
76
|
+
* One-shot helpers (chart imports, GDAL, wipe jobs) are the workloads
|
|
77
|
+
* that saturate every core on a small host; they yield to the
|
|
78
|
+
* long-running services by default.
|
|
79
|
+
*/
|
|
80
|
+
export const DEFAULT_JOB_CPU_PRIORITY = "lowest";
|
|
81
|
+
/**
|
|
82
|
+
* Coerce a stored config value to a tier name. Anything that is not
|
|
83
|
+
* a known tier (hand-edited config, older panel) falls back to the
|
|
84
|
+
* given default so a typo can never select a priority nobody asked for.
|
|
85
|
+
*/
|
|
86
|
+
export function normalizeCpuPriority(value, fallback) {
|
|
87
|
+
return typeof value === "string" &&
|
|
88
|
+
CPU_PRIORITIES.includes(value)
|
|
89
|
+
? value
|
|
90
|
+
: fallback;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The resource-limits fragment a tier contributes. `normal` contributes
|
|
94
|
+
* nothing, so it never shows up as a limit and cannot mask a
|
|
95
|
+
* consumer's or user's own `cpuShares`.
|
|
96
|
+
*/
|
|
97
|
+
export function cpuPriorityLimits(tier) {
|
|
98
|
+
const shares = CPU_PRIORITY_SHARES[tier];
|
|
99
|
+
return shares === null ? {} : { cpuShares: shares };
|
|
100
|
+
}
|
|
101
|
+
/** Reverse lookup for display: the tier a shares value stands for, if any. */
|
|
102
|
+
export function cpuPriorityForShares(shares) {
|
|
103
|
+
if (shares === undefined || shares === null)
|
|
104
|
+
return "normal";
|
|
105
|
+
for (const tier of CPU_PRIORITIES) {
|
|
106
|
+
if (CPU_PRIORITY_SHARES[tier] === shares)
|
|
107
|
+
return tier;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
47
111
|
//# sourceMappingURL=configNormalize.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configNormalize.js","sourceRoot":"","sources":["../src/configNormalize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"configNormalize.js","sourceRoot":"","sources":["../src/configNormalize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACvD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO,2BAA2B,CAAC;IACrC,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AACxC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAAc;IACzD,OAAO,MAAM,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gCAAgC,CAAC,WAAmB;IAClE,wEAAwE;IACxE,qEAAqE;IACrE,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,2BAA2B,CAAC;IAClE,OAAO,0BAA0B,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAC9B;IACE,IAAI,EAAE,IAAI;IACV,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,GAAG;IACR,MAAM,EAAE,GAAG;CACZ,CAAC;AAEJ,kEAAkE;AAClE,MAAM,CAAC,MAAM,cAAc,GAA2B;IACpD,MAAM;IACN,QAAQ;IACR,KAAK;IACL,QAAQ;CACT,CAAC;AAEF,MAAM,CAAC,MAAM,8BAA8B,GAAgB,QAAQ,CAAC;AACpE;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAgB,QAAQ,CAAC;AAE9D;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAc,EACd,QAAqB;IAErB,OAAO,OAAO,KAAK,KAAK,QAAQ;QAC7B,cAAoC,CAAC,QAAQ,CAAC,KAAK,CAAC;QACrD,CAAC,CAAE,KAAqB;QACxB,CAAC,CAAC,QAAQ,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAiB;IAEjB,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACtD,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,oBAAoB,CAClC,MAAiC;IAEjC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IAC7D,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,IAAI,mBAAmB,CAAC,IAAI,CAAC,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;IACxD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/containers.js
CHANGED
|
@@ -782,10 +782,10 @@ export async function getLiveResources(runtime, name, client = getClient()) {
|
|
|
782
782
|
out.cpus = Math.round((nano / 1_000_000_000) * 1000) / 1000;
|
|
783
783
|
}
|
|
784
784
|
const shares = Number(cpuShares);
|
|
785
|
-
//
|
|
786
|
-
//
|
|
787
|
-
//
|
|
788
|
-
if (Number.isFinite(shares) && shares > 0
|
|
785
|
+
// Both runtimes report 0 when no shares were requested. 1024 is a real
|
|
786
|
+
// request, not a default: on crun it maps to cpu.weight 39 where unset
|
|
787
|
+
// means 100 (see CPU_PRIORITY_SHARES in configNormalize.ts).
|
|
788
|
+
if (Number.isFinite(shares) && shares > 0) {
|
|
789
789
|
out.cpuShares = shares;
|
|
790
790
|
}
|
|
791
791
|
if (cpusetCpus && cpusetCpus.trim() !== "") {
|