@celilo/e2e 0.20.3 → 0.20.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docker/Dockerfile.apt-repo-sim +1 -1
- package/docker/Dockerfile.authoritative-dns +1 -1
- package/docker/Dockerfile.celilo-website-sim +1 -1
- package/docker/Dockerfile.cpanel-host-sim +1 -1
- package/docker/Dockerfile.dhcp-client +1 -1
- package/docker/Dockerfile.firewall +1 -1
- package/docker/Dockerfile.forward-proxy +1 -1
- package/docker/Dockerfile.greenwave-sim +1 -1
- package/docker/Dockerfile.internal-resolver +1 -1
- package/docker/Dockerfile.ip-echo +1 -1
- package/docker/Dockerfile.isitup +1 -1
- package/docker/Dockerfile.management +1 -1
- package/docker/Dockerfile.minio +3 -3
- package/docker/Dockerfile.namecheap-dns +1 -1
- package/docker/Dockerfile.npm-registry-sim +1 -1
- package/docker/Dockerfile.observer +1 -1
- package/docker/Dockerfile.pebble +2 -2
- package/docker/Dockerfile.proxmox-sim +1 -1
- package/docker/Dockerfile.registry +1 -1
- package/docker/Dockerfile.resolver +1 -1
- package/docker/Dockerfile.router +1 -1
- package/docker/Dockerfile.signal-cli +2 -2
- package/docker/Dockerfile.signal-release +2 -2
- package/docker/Dockerfile.signal-sim +1 -1
- package/docker/Dockerfile.socks-proxy +1 -1
- package/docker/Dockerfile.target-machine +1 -1
- package/docker/Dockerfile.target-machine-docker +1 -1
- package/docker/Dockerfile.target-technitium +1 -1
- package/package.json +3 -3
- package/scripts/dockerhub-login.sh +26 -0
- package/src/doctor.test.ts +17 -0
- package/src/doctor.ts +37 -1
- package/src/host-vm.test.ts +36 -0
- package/src/host-vm.ts +10 -0
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
# generated HERE, at docker-build time, because dpkg-scanpackages /
|
|
18
18
|
# apt-ftparchive aren't available on the macOS build host.
|
|
19
19
|
|
|
20
|
-
FROM debian:trixie-slim
|
|
20
|
+
FROM debian:trixie-slim@sha256:a99cfc517144bc59b1978475ec53b46ecabec7e43635402ee5b77cc54cd1b20a
|
|
21
21
|
|
|
22
22
|
RUN apt-get update \
|
|
23
23
|
&& apt-get install -y --no-install-recommends dpkg-dev apt-utils python3 \
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
FROM debian:bookworm-slim
|
|
1
|
+
FROM debian:bookworm-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251
|
|
2
2
|
|
|
3
3
|
# Caddy + tools needed by entrypoint.sh. iproute2 provides `ip` so the
|
|
4
4
|
# entrypoint can rewrite the default route through fw-ext (same pattern
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
#
|
|
14
14
|
# No systemd: sshd + apache are all this host runs, so a two-process entrypoint
|
|
15
15
|
# is enough and boots in a fraction of the time.
|
|
16
|
-
FROM ubuntu:22.04
|
|
16
|
+
FROM ubuntu:22.04@sha256:b8b6ee6aa931ecd9d0d952abc34dc0e5f7c6a30c6bb71b079fe399fde0329c02
|
|
17
17
|
|
|
18
18
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
19
19
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
FROM oven/bun:
|
|
1
|
+
FROM oven/bun:1.4.2@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
|
|
2
2
|
|
|
3
3
|
# iproute2 so the entrypoint can see its own addressing when debugging; the
|
|
4
4
|
# service itself needs no routing changes (it sits on internet-external with
|
package/docker/Dockerfile.isitup
CHANGED
package/docker/Dockerfile.minio
CHANGED
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
# well-known minioadmin/minioadmin; the migration test passes them to
|
|
18
18
|
# `celilo storage add s3 --access-key-id ... --secret-access-key ...`.
|
|
19
19
|
|
|
20
|
-
FROM minio/mc:latest AS mc
|
|
20
|
+
FROM quay.io/minio/mc:latest@sha256:a7fe349ef4bd8521fb8497f55c6042871b2ae640607cf99d9bede5e9bdf11727 AS mc
|
|
21
21
|
|
|
22
22
|
# Static busybox purely for its `ip` applet: the MinIO image has no iproute2
|
|
23
23
|
# and no package manager worth invoking, but it still needs the ISP route
|
|
24
24
|
# every host on internet-external carries (see minio-startup.sh).
|
|
25
|
-
FROM busybox:stable-musl AS busybox
|
|
25
|
+
FROM busybox:stable-musl@sha256:3c6ae8008e2c2eedd141725c30b20d9c36b026eb796688f88205845ef17aa213 AS busybox
|
|
26
26
|
|
|
27
|
-
FROM minio/minio:latest
|
|
27
|
+
FROM quay.io/minio/minio:latest@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
|
|
28
28
|
|
|
29
29
|
COPY --from=busybox /bin/busybox /usr/local/bin/busybox
|
|
30
30
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# Its faithfulness as a vantage comes from the routing profile installed at startup
|
|
7
7
|
# (see config/routing/observer-setup.sh), driven by the OBSERVER_* env the compose
|
|
8
8
|
# generator sets per vantage.
|
|
9
|
-
FROM ubuntu:22.04
|
|
9
|
+
FROM ubuntu:22.04@sha256:b8b6ee6aa931ecd9d0d952abc34dc0e5f7c6a30c6bb71b079fe399fde0329c02
|
|
10
10
|
|
|
11
11
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
12
12
|
|
package/docker/Dockerfile.pebble
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
FROM ghcr.io/letsencrypt/pebble:latest AS pebble
|
|
1
|
+
FROM ghcr.io/letsencrypt/pebble:latest@sha256:ddf230642b1a584f519f32e347de1b05a6e4c1f6c35c1863b33effeab5f78199 AS pebble
|
|
2
2
|
|
|
3
|
-
FROM ubuntu:22.04
|
|
3
|
+
FROM ubuntu:22.04@sha256:b8b6ee6aa931ecd9d0d952abc34dc0e5f7c6a30c6bb71b079fe399fde0329c02
|
|
4
4
|
|
|
5
5
|
RUN apt-get update && apt-get install -y \
|
|
6
6
|
iproute2 \
|
package/docker/Dockerfile.router
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
ARG SIGNAL_CLI_VERSION=0.14.6
|
|
26
26
|
|
|
27
|
-
FROM --platform=$BUILDPLATFORM debian:bookworm-slim AS fetch
|
|
27
|
+
FROM --platform=$BUILDPLATFORM debian:bookworm-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251 AS fetch
|
|
28
28
|
ARG SIGNAL_CLI_VERSION
|
|
29
29
|
RUN apt-get update \
|
|
30
30
|
&& apt-get install -y --no-install-recommends curl ca-certificates \
|
|
@@ -44,7 +44,7 @@ RUN curl -fsSL \
|
|
|
44
44
|
&& mkdir -p /out/schemas \
|
|
45
45
|
&& tar -xzf /tmp/schemas.tar.gz -C /out/schemas
|
|
46
46
|
|
|
47
|
-
FROM --platform=linux/amd64 eclipse-temurin:25-jre
|
|
47
|
+
FROM --platform=linux/amd64 eclipse-temurin:25-jre@sha256:bb036ed6cfdc57e3da7c22634d15f1b840d2caf76183861c80e81ca4b5104abb
|
|
48
48
|
COPY --from=fetch /out/signal-cli /opt/signal-cli
|
|
49
49
|
COPY --from=fetch /out/schemas /opt/signal-cli-schemas
|
|
50
50
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
ARG SIGNAL_CLI_VERSION=0.14.6
|
|
15
15
|
|
|
16
|
-
FROM --platform=$BUILDPLATFORM debian:bookworm-slim AS fetch
|
|
16
|
+
FROM --platform=$BUILDPLATFORM debian:bookworm-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251 AS fetch
|
|
17
17
|
ARG SIGNAL_CLI_VERSION
|
|
18
18
|
RUN apt-get update \
|
|
19
19
|
&& apt-get install -y --no-install-recommends curl ca-certificates \
|
|
@@ -31,7 +31,7 @@ RUN mkdir -p "/out/v${SIGNAL_CLI_VERSION}" \
|
|
|
31
31
|
"https://github.com/AsamK/signal-cli/releases/download/v${SIGNAL_CLI_VERSION}/signal-cli-${SIGNAL_CLI_VERSION}.tar.gz" \
|
|
32
32
|
-o "/out/v${SIGNAL_CLI_VERSION}/signal-cli-${SIGNAL_CLI_VERSION}.tar.gz"
|
|
33
33
|
|
|
34
|
-
FROM nginx:alpine
|
|
34
|
+
FROM nginx:alpine@sha256:62ff2089abf5a9ed33bd232895bef5e22f7bb4b200675cec49a5ebc48e3d4ac8
|
|
35
35
|
COPY --from=fetch /out /usr/share/nginx/html
|
|
36
36
|
|
|
37
37
|
# Route the customer's public prefix via the ISP edge, like every other host on
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# arrives: it can queue an inbound reply, fail a recipient inside a success,
|
|
7
7
|
# and revoke the device link on demand — none of which the real unlinked
|
|
8
8
|
# daemon can be made to do.
|
|
9
|
-
FROM oven/bun:1
|
|
9
|
+
FROM oven/bun:1.4.2@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
|
|
10
10
|
|
|
11
11
|
WORKDIR /app
|
|
12
12
|
COPY simulators/signal-cli/server.ts ./server.ts
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# internet (split-horizon, namecheap publication, etc.)
|
|
12
12
|
# The startup script handles both based on the VANTAGE env var passed
|
|
13
13
|
# at `docker run` time.
|
|
14
|
-
FROM ubuntu:22.04
|
|
14
|
+
FROM ubuntu:22.04@sha256:b8b6ee6aa931ecd9d0d952abc34dc0e5f7c6a30c6bb71b079fe399fde0329c02
|
|
15
15
|
|
|
16
16
|
RUN apt-get update && apt-get install -y \
|
|
17
17
|
dante-server \
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Used for app-zone machines that run Docker Compose workloads (e.g. authentik).
|
|
3
3
|
# Docker itself is baked in; application images are pulled at deploy time by each module.
|
|
4
4
|
|
|
5
|
-
FROM ubuntu:22.04
|
|
5
|
+
FROM ubuntu:22.04@sha256:b8b6ee6aa931ecd9d0d952abc34dc0e5f7c6a30c6bb71b079fe399fde0329c02
|
|
6
6
|
|
|
7
7
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Based on target-machine; Technitium is installed at image build time so the
|
|
3
3
|
# Ansible role finds it already present and skips the download step.
|
|
4
4
|
|
|
5
|
-
FROM ubuntu:22.04
|
|
5
|
+
FROM ubuntu:22.04@sha256:b8b6ee6aa931ecd9d0d952abc34dc0e5f7c6a30c6bb71b079fe399fde0329c02
|
|
6
6
|
|
|
7
7
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
8
8
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@celilo/e2e",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.5",
|
|
4
4
|
"description": "E2E test infrastructure for Celilo-deployed applications. Provides a simulated internet with DNS hierarchy, ACME server, firewalls, and target machines in Docker.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"README.md"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@celilo/capabilities": "^5.
|
|
41
|
+
"@celilo/capabilities": "^5.1.1",
|
|
42
42
|
"@celilo/cli-display": "^0.2.0",
|
|
43
|
-
"@celilo/event-bus": "^0.
|
|
43
|
+
"@celilo/event-bus": "^0.7.0",
|
|
44
44
|
"@celilo/terraform-fake": "^0.3.1",
|
|
45
45
|
"yaml": "^2.8.0",
|
|
46
46
|
"zod": "^3.24.1"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Authenticate the builder to Docker Hub before any e2e image build.
|
|
3
|
+
#
|
|
4
|
+
# celilo#1376: the e2e base images are digest-pinned, which makes a pull
|
|
5
|
+
# REPRODUCIBLE — it does not make it ALLOWED. Docker Hub meters anonymous pulls
|
|
6
|
+
# per source IP, and once the builder's share is spent every pull fails at
|
|
7
|
+
# `load metadata` with:
|
|
8
|
+
#
|
|
9
|
+
# ERROR: pull access denied ... insufficient_scope: authorization failed
|
|
10
|
+
#
|
|
11
|
+
# which reads like a missing image rather than a spent quota. An authenticated
|
|
12
|
+
# pull draws on the account's much larger allowance instead.
|
|
13
|
+
#
|
|
14
|
+
# No credentials configured (a fork, a local run) => stay anonymous and say so,
|
|
15
|
+
# rather than failing the build for a quota that may well not be spent.
|
|
16
|
+
set -eu
|
|
17
|
+
|
|
18
|
+
if [ -z "${DOCKERHUB_USERNAME:-}" ] || [ -z "${DOCKERHUB_TOKEN:-}" ]; then
|
|
19
|
+
echo "DOCKERHUB_USERNAME/DOCKERHUB_TOKEN unset — pulling Docker Hub anonymously."
|
|
20
|
+
echo "Anonymous pulls share this host's IP quota; a spent quota fails as"
|
|
21
|
+
echo "'insufficient_scope: authorization failed' at 'load metadata' (celilo#1376)."
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
printf '%s' "${DOCKERHUB_TOKEN}" |
|
|
26
|
+
docker login --username "${DOCKERHUB_USERNAME}" --password-stdin
|
package/src/doctor.test.ts
CHANGED
|
@@ -47,6 +47,7 @@ function healthyProbe(overrides: Partial<DoctorProbe> = {}): DoctorProbe {
|
|
|
47
47
|
imageExists: () => true,
|
|
48
48
|
imageCmd: () => [EXPECTED_MANAGEMENT_CMD],
|
|
49
49
|
imageLabel: () => null,
|
|
50
|
+
celiloVersion: () => '1.2.3',
|
|
50
51
|
staleContainers: () => [],
|
|
51
52
|
reclaimableImageBytes: () => 0,
|
|
52
53
|
leakedStacks: () => [],
|
|
@@ -103,6 +104,22 @@ describe('management image (problem 1: the bake fails as an SSH error)', () => {
|
|
|
103
104
|
expect(check.remedy).toBe('cele2e build-infra');
|
|
104
105
|
});
|
|
105
106
|
|
|
107
|
+
test('a hollow image — starts fine, no celilo inside — FAILS', () => {
|
|
108
|
+
// celilo#1390: doctor said "2 baked images present, both running
|
|
109
|
+
// /startup.sh" about an image with no celilo binary at all.
|
|
110
|
+
const check = checkManagementImage(healthyProbe({ celiloVersion: () => null }));
|
|
111
|
+
expect(check.status).toBe('fail');
|
|
112
|
+
expect(check.detail).toContain('HOLLOW');
|
|
113
|
+
expect(check.detail).toContain('127');
|
|
114
|
+
expect(check.remedy).toBe('cele2e build-infra');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('a passing check reports the version it found, so a stale bake is visible too', () => {
|
|
118
|
+
const check = checkManagementImage(healthyProbe({ celiloVersion: () => '0.42.1' }));
|
|
119
|
+
expect(check.status).toBe('ok');
|
|
120
|
+
expect(check.detail).toContain('0.42.1');
|
|
121
|
+
});
|
|
122
|
+
|
|
106
123
|
test('both images present and correctly baked passes', () => {
|
|
107
124
|
const check = checkManagementImage(healthyProbe());
|
|
108
125
|
expect(check.status).toBe('ok');
|
package/src/doctor.ts
CHANGED
|
@@ -105,6 +105,12 @@ export interface DoctorProbe {
|
|
|
105
105
|
imageCmd(ref: string): string[] | null;
|
|
106
106
|
/** One label off the image, or null when the image or the label is absent. */
|
|
107
107
|
imageLabel(ref: string, label: string): string | null;
|
|
108
|
+
/**
|
|
109
|
+
* `celilo --version` run INSIDE the image, or null when the binary is absent
|
|
110
|
+
* or does not run. This is the only check that opens the image up; every
|
|
111
|
+
* other one reasons about metadata, which is how a hollow image passed.
|
|
112
|
+
*/
|
|
113
|
+
celiloVersion(ref: string): string | null;
|
|
108
114
|
/** Names of leftover `celilo-e2e-*` containers. */
|
|
109
115
|
staleContainers(): string[];
|
|
110
116
|
/** Bytes docker reports as reclaimable image space. */
|
|
@@ -228,10 +234,24 @@ export function checkManagementImage(probe: DoctorProbe): DoctorCheck {
|
|
|
228
234
|
remedy: 'cele2e build-infra',
|
|
229
235
|
};
|
|
230
236
|
}
|
|
237
|
+
// The contract every stage depends on: a celilo binary that actually runs.
|
|
238
|
+
// A failed bake (celilo#1365, the install.sh fetch timing out) leaves the
|
|
239
|
+
// image present, correctly Cmd'd and completely hollow, and the suite then
|
|
240
|
+
// dies at stage 1 with `celilo --version exited 127` — which reads as a test
|
|
241
|
+
// defect rather than the bake failure it is.
|
|
242
|
+
const version = probe.celiloVersion(MANAGEMENT_LATEST);
|
|
243
|
+
if (!version) {
|
|
244
|
+
return {
|
|
245
|
+
name: 'management-image',
|
|
246
|
+
status: 'fail',
|
|
247
|
+
detail: `${MANAGEMENT_LATEST} is HOLLOW — it starts, but \`celilo\` is not on PATH inside it, so every stage dies with "celilo --version exited 127: command not found". The bake failed and left the image behind`,
|
|
248
|
+
remedy: 'cele2e build-infra',
|
|
249
|
+
};
|
|
250
|
+
}
|
|
231
251
|
return {
|
|
232
252
|
name: 'management-image',
|
|
233
253
|
status: 'ok',
|
|
234
|
-
detail: `${BAKED_MANAGEMENT_IMAGES.length} baked images present, both running ${EXPECTED_MANAGEMENT_CMD}`,
|
|
254
|
+
detail: `${BAKED_MANAGEMENT_IMAGES.length} baked images present, both running ${EXPECTED_MANAGEMENT_CMD}; celilo ${version} inside ${MANAGEMENT_LATEST}`,
|
|
235
255
|
};
|
|
236
256
|
}
|
|
237
257
|
|
|
@@ -565,6 +585,22 @@ export function createDockerProbe(): DoctorProbe {
|
|
|
565
585
|
return null;
|
|
566
586
|
}
|
|
567
587
|
},
|
|
588
|
+
celiloVersion(ref) {
|
|
589
|
+
try {
|
|
590
|
+
const out = docker([
|
|
591
|
+
'run',
|
|
592
|
+
'--rm',
|
|
593
|
+
'--entrypoint',
|
|
594
|
+
'sh',
|
|
595
|
+
ref,
|
|
596
|
+
'-c',
|
|
597
|
+
'command -v celilo >/dev/null 2>&1 && celilo --version',
|
|
598
|
+
]);
|
|
599
|
+
return out === '' ? null : out.split('\n').pop()?.trim() || null;
|
|
600
|
+
} catch {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
},
|
|
568
604
|
staleContainers() {
|
|
569
605
|
try {
|
|
570
606
|
return docker(['ps', '-a', '--filter', 'name=celilo-e2e', '--format', '{{.Names}}'])
|
package/src/host-vm.test.ts
CHANGED
|
@@ -113,6 +113,42 @@ describe('evaluateHostVm', () => {
|
|
|
113
113
|
expect(evaluateHostVm(vm({ cpus: 16 }), M1_PRO, budget).problems[0]).toContain('16 CPUs');
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
+
test('half the policy CPUs is flagged — the measured case that read as a match', () => {
|
|
117
|
+
const verdict = evaluateHostVm(vm({ cpus: 4 }), M1_PRO, budget);
|
|
118
|
+
expect(verdict.problems).toHaveLength(1);
|
|
119
|
+
expect(verdict.problems[0]).toContain('4 CPUs');
|
|
120
|
+
// colima resizes CPUs on a restart, so this must not demand the destructive path.
|
|
121
|
+
expect(verdict.needsRecreate).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('memory below the policy is flagged', () => {
|
|
125
|
+
const verdict = evaluateHostVm(vm({ memoryGiB: 4 }), M1_PRO, budget);
|
|
126
|
+
expect(verdict.problems).toHaveLength(1);
|
|
127
|
+
expect(verdict.problems[0]).toContain('4 GiB');
|
|
128
|
+
expect(verdict.needsRecreate).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The property that actually failed in celilo#1395: `budget.cpus` was
|
|
133
|
+
* computed, printed inside another rule's message, and never compared. A
|
|
134
|
+
* per-rule test cannot express that — it can only break rules that exist.
|
|
135
|
+
* So drive every budget field away from the budget and require a sentence
|
|
136
|
+
* back. A field used only in a template string produces none.
|
|
137
|
+
*/
|
|
138
|
+
test('every field of the budget is compared, not merely printed', () => {
|
|
139
|
+
const diverge: Record<keyof typeof budget, Partial<HostVmFacts>> = {
|
|
140
|
+
cpus: { cpus: budget.cpus - 1 },
|
|
141
|
+
memoryGiB: { memoryGiB: budget.memoryGiB - 1 },
|
|
142
|
+
mountType: { mountType: 'sshfs' },
|
|
143
|
+
};
|
|
144
|
+
for (const [field, override] of Object.entries(diverge)) {
|
|
145
|
+
expect(
|
|
146
|
+
evaluateHostVm(vm(override), M1_PRO, budget).problems,
|
|
147
|
+
`budget.${field} diverged but no rule fired`,
|
|
148
|
+
).not.toEqual([]);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
116
152
|
test('several problems are all reported, not just the first', () => {
|
|
117
153
|
const verdict = evaluateHostVm(vm({ memoryGiB: 24, mountType: 'sshfs', cpus: 16 }), M1_PRO, {
|
|
118
154
|
...budget,
|
package/src/host-vm.ts
CHANGED
|
@@ -221,10 +221,20 @@ export function evaluateHostVm(
|
|
|
221
221
|
// colima discards a mount-type change on an existing VM.
|
|
222
222
|
needsRecreate = true;
|
|
223
223
|
}
|
|
224
|
+
if (facts.memoryGiB < budget.memoryGiB) {
|
|
225
|
+
problems.push(
|
|
226
|
+
`VM memory is ${facts.memoryGiB} GiB, below the ${budget.memoryGiB} GiB this host can afford it — the guest has less page cache than the rig's images need, so reads that should be cached hit the disk.`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
224
229
|
if (facts.cpus > host.cpus) {
|
|
225
230
|
problems.push(
|
|
226
231
|
`VM is configured with ${facts.cpus} CPUs but the host has ${host.cpus}. Recommended: ${budget.cpus}.`,
|
|
227
232
|
);
|
|
233
|
+
} else if (facts.cpus < budget.cpus) {
|
|
234
|
+
// colima resizes CPUs on a restart, so this does NOT need a recreate.
|
|
235
|
+
problems.push(
|
|
236
|
+
`VM has ${facts.cpus} CPUs against a policy of ${budget.cpus} on a ${host.cpus}-core host — a suite runs on the cores the VM was given, not the ones the host has. Recommended: ${budget.cpus}.`,
|
|
237
|
+
);
|
|
228
238
|
}
|
|
229
239
|
return { problems, needsRecreate };
|
|
230
240
|
}
|