@celilo/e2e 0.20.4 → 0.20.6

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.
@@ -1,4 +1,4 @@
1
- FROM oven/bun:latest@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
1
+ FROM oven/bun:1.4.2@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
2
2
 
3
3
  RUN apt-get update && apt-get install -y \
4
4
  openssh-server \
@@ -1,4 +1,4 @@
1
- FROM oven/bun:latest@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
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
@@ -1,4 +1,4 @@
1
- FROM oven/bun:latest@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
1
+ FROM oven/bun:1.4.2@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
2
2
 
3
3
  # Caddy (for ACME/TLS termination) + tools needed by entrypoint.sh.
4
4
  # iproute2 provides `ip` so the entrypoint can rewrite the default route
@@ -1,4 +1,4 @@
1
- FROM oven/bun:latest@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
1
+ FROM oven/bun:1.4.2@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
2
2
 
3
3
  # This Dockerfile produces a *vanilla* management image — bun + unzip +
4
4
  # bunfig, no celilo. `cele2e build-infra` runs install.sh inside a
@@ -124,6 +124,36 @@ ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
124
124
  # fall through to Bun's normal resolution.
125
125
  RUN printf '[install.scopes]\n"@celilo" = "http://npm-registry.lab"\n' > /root/.bunfig.toml
126
126
 
127
+ # --- The fleet browser runtime (managed-browser-runtime) ----------------------
128
+ #
129
+ # Browser-driven health checks (celilo-web-console is the first) resolve
130
+ # `/var/lib/celilo/browsers/current/chrome` and a descriptor describing what
131
+ # was installed. This image provisions that runtime the way the celilo-mgmt
132
+ # role does on a real management host (`modules/celilo-mgmt/ansible/roles/
133
+ # celilo-mgmt/tasks/debian.yml`), pinned to the SAME playwright version that
134
+ # role declares — the fleet's single browser-version declaration. A module
135
+ # bundling a different client is a pin-drift finding, not a failure: the
136
+ # consumer passes the stable path explicitly.
137
+ #
138
+ # Revision comes from the pinned client itself (`install --dry-run`), never
139
+ # from a hand-maintained version→revision table — same reasoning as the role.
140
+ ARG PLAYWRIGHT_VERSION=1.60.0
141
+ RUN set -eux; \
142
+ REVISION="$(PLAYWRIGHT_BROWSERS_PATH=/var/lib/celilo/browsers bun x "playwright-core@${PLAYWRIGHT_VERSION}" install --dry-run chromium-headless-shell \
143
+ | grep -oE 'chromium_headless_shell-[0-9]+' | head -1 | cut -d- -f2)"; \
144
+ PLAYWRIGHT_BROWSERS_PATH=/var/lib/celilo/browsers bun x "playwright-core@${PLAYWRIGHT_VERSION}" install chromium-headless-shell; \
145
+ PLAYWRIGHT_BROWSERS_PATH=/var/lib/celilo/browsers bun x "playwright-core@${PLAYWRIGHT_VERSION}" install-deps chromium-headless-shell; \
146
+ BIN="$(find /var/lib/celilo/browsers/chromium_headless_shell-"${REVISION}" -type f \
147
+ \( -name chrome-headless-shell -o -name headless_shell \) | head -1)"; \
148
+ test -x "${BIN}"; \
149
+ mkdir -p /var/lib/celilo/browsers/current; \
150
+ ln -sf "${BIN}" /var/lib/celilo/browsers/current/chrome; \
151
+ BROWSER_VERSION="$(/var/lib/celilo/browsers/current/chrome --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1)"; \
152
+ printf '{"schemaVersion":1,"browser":"chromium","flavor":"headless-shell","browserVersion":"%s","playwrightVersion":"%s","revision":"%s","writtenBy":"e2e management image"}' \
153
+ "${BROWSER_VERSION}" "${PLAYWRIGHT_VERSION}" "${REVISION}" \
154
+ > /var/lib/celilo/browsers/current/celilo-browser.json; \
155
+ cat /var/lib/celilo/browsers/current/celilo-browser.json
156
+
127
157
  # Standard modules pre-packaged as .netapp files — built by e2e-build.
128
158
  # Tests import from /netapps/<module>.netapp instead of the monorepo mount.
129
159
  COPY netapps/ /netapps/
@@ -1,4 +1,4 @@
1
- FROM oven/bun:1@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
1
+ FROM oven/bun:1.4.2@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
2
2
 
3
3
  WORKDIR /app
4
4
 
@@ -1,4 +1,4 @@
1
- FROM oven/bun:1@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
1
+ FROM oven/bun:1.4.2@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
2
2
 
3
3
  WORKDIR /app
4
4
 
@@ -1,4 +1,4 @@
1
- FROM oven/bun:1@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
1
+ FROM oven/bun:1.4.2@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
2
2
 
3
3
  WORKDIR /app
4
4
 
@@ -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@sha256:9114c058aeae42162ee16dd5084b95fe9473970bb6bcb5b232ab1630f0546895
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
@@ -18,6 +18,11 @@ RUN apt-get update && apt-get install -y \
18
18
  iproute2 \
19
19
  iputils-ping \
20
20
  dnsutils \
21
+ # wireguard-tools: the control-plane-vpn vantage. A browser that reaches
22
+ # celilo-web-console (secure-mgmt, VPN-only per D7) must enter as a real
23
+ # VPN peer — the proxy brings up a wg tunnel, it does not attach directly
24
+ # to the zone. Needs /dev/net/tun at `docker run` time (socks-proxy.ts).
25
+ wireguard-tools \
21
26
  && rm -rf /var/lib/apt/lists/*
22
27
 
23
28
  COPY config/socks/danted.conf /etc/danted.conf
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celilo/e2e",
3
- "version": "0.20.4",
3
+ "version": "0.20.6",
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.1.0",
41
+ "@celilo/capabilities": "^6.0.0",
42
42
  "@celilo/cli-display": "^0.2.0",
43
- "@celilo/event-bus": "^0.6.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
@@ -3,6 +3,7 @@ import { existsSync } from 'node:fs';
3
3
  import { createConnection } from 'node:net';
4
4
  import { join } from 'node:path';
5
5
  import { progress } from './progress';
6
+ import type { ExecResult, SocksProxyHandle } from './types';
6
7
 
7
8
  /** Package root — `docker/` and `config/` live here. */
8
9
  const PACKAGE_ROOT = join(__dirname, '..');
@@ -37,17 +38,6 @@ export interface SocksProxyOptions {
37
38
  nameserver?: string;
38
39
  }
39
40
 
40
- export interface SocksProxyHandle {
41
- /** URL the host can use to reach the proxy, e.g. 'socks5://127.0.0.1:54321' */
42
- hostUrl: string;
43
- /** The published host port (the second half of hostUrl) */
44
- hostPort: number;
45
- /** Container name (deterministic per project + vantage) */
46
- containerName: string;
47
- /** Tear down the proxy. Idempotent. */
48
- stop(): Promise<void>;
49
- }
50
-
51
41
  /**
52
42
  * Custom image with dante-server + an entrypoint that fixes routing
53
43
  * and DNS for the chosen vantage. Built from
@@ -139,6 +129,11 @@ export async function startSocksProxy(
139
129
  `--network ${networkName}`,
140
130
  `--ip ${cfg.ip}`,
141
131
  '--cap-add NET_ADMIN',
132
+ // wireguard-tools in the image plus the TUN device let the proxy bring
133
+ // up a real WireGuard tunnel, which is how a browser enters the
134
+ // topology as a control-plane-vpn peer (celilo-web-console's only
135
+ // client path, D7). Harmless for the plain vantages.
136
+ '--device /dev/net/tun',
142
137
  `-e VANTAGE=${vantage}`,
143
138
  ...(options.nameserver ? [`-e NAMESERVER_OVERRIDE=${options.nameserver}`] : []),
144
139
  '-p 127.0.0.1::1080',
@@ -224,6 +219,32 @@ function makeHandle(containerName: string, port: number): SocksProxyHandle {
224
219
  hostUrl: `socks5://127.0.0.1:${port}`,
225
220
  hostPort: port,
226
221
  containerName,
222
+ /**
223
+ * Run a command inside the proxy container.
224
+ *
225
+ * The proxy is started with `docker run`, not compose, so
226
+ * `net.exec` (a `docker compose exec`) cannot reach it. The
227
+ * control-plane-vpn test uses this to bring the proxy's WireGuard
228
+ * tunnel up after the tunnel has deployed — the peer is the proxy,
229
+ * so the browser behind it enters the topology the way a real
230
+ * admin laptop does.
231
+ */
232
+ async exec(cmd, timeoutMs = 60_000): Promise<ExecResult> {
233
+ try {
234
+ const stdout = execSync(`docker exec ${containerName} bash -c ${JSON.stringify(cmd)}`, {
235
+ timeout: timeoutMs,
236
+ stdio: 'pipe',
237
+ }).toString();
238
+ return { stdout, stderr: '', exitCode: 0 };
239
+ } catch (err: unknown) {
240
+ const e = err as { stdout?: Buffer; stderr?: Buffer; status?: number };
241
+ return {
242
+ stdout: e.stdout?.toString() ?? '',
243
+ stderr: e.stderr?.toString() ?? String(err),
244
+ exitCode: e.status ?? 1,
245
+ };
246
+ }
247
+ },
227
248
  async stop(): Promise<void> {
228
249
  if (stopped) return;
229
250
  stopped = true;
package/src/types.ts CHANGED
@@ -220,6 +220,14 @@ export interface SocksProxyHandle {
220
220
  hostPort: number;
221
221
  /** Container name (deterministic per project + vantage) */
222
222
  containerName: string;
223
+ /**
224
+ * Run a command inside the proxy container (raw `docker exec`).
225
+ *
226
+ * The proxy is started with `docker run`, not compose, so `net.exec`
227
+ * cannot reach it. The control-plane-vpn browser test uses this to bring
228
+ * the proxy's WireGuard tunnel up after the tunnel has deployed.
229
+ */
230
+ exec(cmd: string, timeoutMs?: number): Promise<ExecResult>;
223
231
  /** Tear down the proxy. Idempotent. */
224
232
  stop(): Promise<void>;
225
233
  }