@norskvideo/ctl-test-harness 0.1.62 → 0.1.64

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/demo/run.d.ts CHANGED
@@ -242,6 +242,38 @@ export declare function demoUrlHost(opts: {
242
242
  daemon: DemoDaemonPolicy;
243
243
  env?: NodeJS.ProcessEnv;
244
244
  }): string;
245
+ /** Where the daemon's nginx PROXY answers, which is not where the daemon does.
246
+ *
247
+ * The daemon is a process in this very process' container, so `localhost`
248
+ * reaches it. Its proxy is NOT: every `nginx proxy starting on port N` is
249
+ * followed by `docker: Container norsk-proxy Created/Started`, for a private
250
+ * daemon exactly as for a reused one. On a docker-outside-of-docker runner
251
+ * that container is created by the HOST's daemon and publishes there
252
+ * (`0.0.0.0:22263->22263/tcp`, HostIp "" — measured on test2, 2026-10-01), so
253
+ * from inside this container `localhost:<proxyPort>` reaches nothing at all.
254
+ *
255
+ * That is playout's `visualiser unreachable … (180s)`: composeAdvancingGate
256
+ * reads through `{ proxy: "/instance/{id}/visualiser" }`, got the daemon's
257
+ * host, and every poll failed to connect while the compositor was advancing.
258
+ * It is not the 2026-09-03 bug of the same shape — that one was the proxy
259
+ * being pointed AWAY from localhost; this is it being pinned TO localhost
260
+ * after the proxy became a container.
261
+ *
262
+ * `NORSK_TEST_HOST` is the explicit answer when a runner has one. Otherwise a
263
+ * containerised run takes the host-gateway alias, which these runners do
264
+ * resolve (`host.docker.internal -> 172.18.0.1` from test2-runner-one-1).
265
+ * Off a runner the test process is not in a container and the published port
266
+ * really is on localhost, so nothing changes for a dev box. */
267
+ export declare const HOST_GATEWAY_ALIAS = "host.docker.internal";
268
+ export declare function proxyUrlHost(opts: {
269
+ publicHost: string | undefined;
270
+ inContainer: boolean;
271
+ env?: NodeJS.ProcessEnv;
272
+ }): string;
273
+ /** Whether this process is itself in a container — the DooD case, where a
274
+ * sibling container's published port is on the host and not on our localhost.
275
+ * `/.dockerenv` is written by the daemon into every container it creates. */
276
+ export declare function runningInContainer(exists?: (p: string) => boolean): boolean;
245
277
  /** The pieces of a run that `runDemo`, `runExportCheck` and the dev-loop
246
278
  * share: the daemon (private on its band, or the developer's), the dev
247
279
  * backend on the driver's port, the registration, and the template (built
package/demo/run.js CHANGED
@@ -344,6 +344,43 @@ export function demoUrlHost(opts) {
344
344
  return envHost;
345
345
  return "localhost";
346
346
  }
347
+ /** Where the daemon's nginx PROXY answers, which is not where the daemon does.
348
+ *
349
+ * The daemon is a process in this very process' container, so `localhost`
350
+ * reaches it. Its proxy is NOT: every `nginx proxy starting on port N` is
351
+ * followed by `docker: Container norsk-proxy Created/Started`, for a private
352
+ * daemon exactly as for a reused one. On a docker-outside-of-docker runner
353
+ * that container is created by the HOST's daemon and publishes there
354
+ * (`0.0.0.0:22263->22263/tcp`, HostIp "" — measured on test2, 2026-10-01), so
355
+ * from inside this container `localhost:<proxyPort>` reaches nothing at all.
356
+ *
357
+ * That is playout's `visualiser unreachable … (180s)`: composeAdvancingGate
358
+ * reads through `{ proxy: "/instance/{id}/visualiser" }`, got the daemon's
359
+ * host, and every poll failed to connect while the compositor was advancing.
360
+ * It is not the 2026-09-03 bug of the same shape — that one was the proxy
361
+ * being pointed AWAY from localhost; this is it being pinned TO localhost
362
+ * after the proxy became a container.
363
+ *
364
+ * `NORSK_TEST_HOST` is the explicit answer when a runner has one. Otherwise a
365
+ * containerised run takes the host-gateway alias, which these runners do
366
+ * resolve (`host.docker.internal -> 172.18.0.1` from test2-runner-one-1).
367
+ * Off a runner the test process is not in a container and the published port
368
+ * really is on localhost, so nothing changes for a dev box. */
369
+ export const HOST_GATEWAY_ALIAS = "host.docker.internal";
370
+ export function proxyUrlHost(opts) {
371
+ if (opts.publicHost !== undefined)
372
+ return opts.publicHost;
373
+ const envHost = (opts.env ?? process.env).NORSK_TEST_HOST;
374
+ if (envHost)
375
+ return envHost;
376
+ return opts.inContainer ? HOST_GATEWAY_ALIAS : "localhost";
377
+ }
378
+ /** Whether this process is itself in a container — the DooD case, where a
379
+ * sibling container's published port is on the host and not on our localhost.
380
+ * `/.dockerenv` is written by the daemon into every container it creates. */
381
+ export function runningInContainer(exists = existsSync) {
382
+ return exists("/.dockerenv");
383
+ }
347
384
  /** The pieces of a run that `runDemo`, `runExportCheck` and the dev-loop
348
385
  * share: the daemon (private on its band, or the developer's), the dev
349
386
  * backend on the driver's port, the registration, and the template (built
@@ -381,19 +418,21 @@ export class DemoSession {
381
418
  this.publicHost = opts.publicHost;
382
419
  const host = demoUrlHost({ publicHost: opts.publicHost, daemon: opts.daemon });
383
420
  this.host = host;
421
+ // The proxy is a container even when the daemon is ours; see proxyUrlHost.
422
+ const proxyHost = proxyUrlHost({ publicHost: opts.publicHost, inContainer: runningInContainer() });
384
423
  if (opts.daemon === "reuse") {
385
424
  const daemonPort = opts.daemonPort ?? (Number(process.env.NORSK_CTL_PORT) || DEFAULT_DAEMON_PORT);
386
425
  this.storeDir = deps.realStoreDir();
387
426
  const proxy = parseProxyConfig(deps.readFile(join(this.storeDir, "config.yaml")));
388
427
  this.ports = demoPorts(slug, { daemonPort, proxyPort: proxy.port });
389
- this.proxyBase = `${proxy.scheme}://${host}:${proxy.port}`;
428
+ this.proxyBase = `${proxy.scheme}://${proxyHost}:${proxy.port}`;
390
429
  }
391
430
  else {
392
431
  this.ports = demoPorts(slug);
393
432
  this.storeDir = opts.storeDir ?? deps.storeDir(slug);
394
433
  // The private daemon is initialised without a cert source, so its proxy
395
434
  // speaks plain http (seen live: https:// gave 000, http:// served the page).
396
- this.proxyBase = `http://${host}:${this.ports.proxyPort}`;
435
+ this.proxyBase = `http://${proxyHost}:${this.ports.proxyPort}`;
397
436
  }
398
437
  this.proxyAuth = opts.proxy;
399
438
  this.daemonEnv = opts.daemonEnv ?? {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-test-harness",
3
- "version": "0.1.62",
3
+ "version": "0.1.64",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {