@norskvideo/ctl-dev-kit 0.2.38 → 0.2.39

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.
@@ -37,6 +37,14 @@
37
37
  mounts don't forward host->guest inotify events, so file watchers never fire —
38
38
  a failure that looks exactly like a code bug. Unit tests that don't bind-mount
39
39
  may keep using `os.tmpdir()`.
40
+ - **Address another container by its unique `<id>-<service>-1`, never a bare
41
+ service name, unless the caller is on the instance's own network only.** A
42
+ bare name (`studio`, `media`) resolves on every network the caller is on, and
43
+ on `norsk-net` it matches every instance's container: a call silently lands on
44
+ another instance. `media` sits on `<id>_default` only (Docker Desktop UDP
45
+ replies break on a dual-homed container), so never add `norsk-net` to it or to
46
+ a sidecar. Full rules: norsk-ctl `docs/product-template-format.md`,
47
+ "Networking: how services address each other".
40
48
  - **Don't pipe test runs to `tail`** — you lose the failure context. Write output
41
49
  to a temp file, then tail _that_ file for the results.
42
50
  - **Evolve the ctl<->product contract additively.** An older ctl must launch a
@@ -19,11 +19,11 @@ socket), with their ports **published on the host**.
19
19
 
20
20
  Consequence — two address classes the harness must not confuse:
21
21
 
22
- | Reaching… | Address |
23
- | ------------------------------------ | ----------------------------------------- |
24
- | the daemon, the product backend | `localhost:<port>` (in-process) |
25
- | studio / media host-published ports | `${NORSK_TEST_HOST}:<port>` |
26
- | a launched container by compose name | `<instance>-<service>-1` **on norsk-net** |
22
+ | Reaching… | Address |
23
+ | ------------------------------------ | ----------------------------------------------------------------------------------------------------- |
24
+ | the daemon, the product backend | `localhost:<port>` (in-process) |
25
+ | studio / media host-published ports | `${NORSK_TEST_HOST}:<port>` |
26
+ | a launched container by compose name | `<instance>-<service>-1` on its **instance network** (`<instance>_default`); studio also on norsk-net |
27
27
 
28
28
  `NORSK_TEST_HOST` is `host.docker.internal` in CI and unset (→ `localhost`)
29
29
  locally. Set it in the workflow env. The shared `studio-state` fetchers
@@ -43,8 +43,13 @@ runner, one of these must hold:
43
43
 
44
44
  - `NORSK_TEST_NET=direct` — the preferred answer, and what the canonical
45
45
  `integration.yml` / `smoke.yml` / `build-docs.yml` topology step already sets.
46
- Instances are reached by compose name on `norsk-net` at the **container** port,
47
- so no host publish is involved at all.
46
+ Instances are reached by compose name at the **container** port, so no host
47
+ publish is involved at all. Studio is on `norsk-net`; **media is on its
48
+ instance network `<instance>_default` only** (Docker Desktop delivers published
49
+ UDP to either IP of a dual-homed container and breaks SRT replies, see
50
+ norsk-ctl `docs/product-template-format.md`, Networking). The harness's media helpers
51
+ (`mediaHttpBase`, `srtEgressUrl`, `mediaDirectUrl`) join the runner to that
52
+ network for you, and `cleanupDaemon` leaves it before deleting the instance.
48
53
  - the launch passes `--publish-debug-ports`, restoring the old binding. The
49
54
  shared `runProductSmoke` and demo drivers do this for you when the host they
50
55
  fetch from is not loopback, and only after probing that the resolved ctl
@@ -147,24 +152,39 @@ reach. Two cases:
147
152
  for `NORSK_TEST_HOST`). `assertMultivariantHasRenditions` does this by default.
148
153
  - **Proxy-less harness** — the advertised `/instance/<id>/media/...` route is
149
154
  served **only by the daemon's nginx proxy**; with no proxy, nothing answers on
150
- `:443`. Reach the media container **directly on norsk-net** instead:
151
- `mediaDirectUrl(url)` rewrites it to `http://<id>-media-1:8080/<native-path>`
152
- (the same bypass `whip-driver` uses; the media route is unauthenticated). Pass
153
- it via `assertMultivariantHasRenditions`'s `resolveFetchUrl` hook, and **join
154
- the runner to norsk-net** in setup so the compose service name resolves:
155
-
156
- ```ts
157
- spawnSync("docker", ["network", "create", "norsk-net"]); // idempotent
158
- spawnSync("docker", ["network", "connect", "norsk-net", hostname()]);
159
- ```
155
+ `:443`. Reach the media container **directly** instead: `mediaDirectUrl(url)`
156
+ rewrites it to `http://<id>-media-1:8080/<native-path>` (the same bypass
157
+ `whip-driver` uses; the media route is unauthenticated) and joins the runner to
158
+ the instance network `<id>_default`, where that name resolves. Pass it via
159
+ `assertMultivariantHasRenditions`'s `resolveFetchUrl` hook. Anything else that
160
+ addresses `<id>-media-1` by hand (a sibling container, a browser in the runner)
161
+ must be on `<id>_default` too: `currentInstanceNetworks().join(id)` for the
162
+ runner, `docker network connect <id>_default <container>` for a sibling, and
163
+ leave or remove it before the instance is deleted, or `compose down` cannot
164
+ remove the network.
160
165
 
161
166
  ## Capture servers (studio pushes → the runner)
162
167
 
163
- When a test stands up an HLS/SCTE-35 capture server that **studio pushes to**,
164
- studio (a host sibling on norsk-net) can't reach the runner via the host LAN IP.
165
- Join norsk-net and hand studio the runner's **norsk-net IP** (`docker inspect`
166
- self), falling back to the host LAN IP only when `NORSK_TEST_HOST` is unset. See
167
- `manifest-capture.ts`'s `captureHost()`.
168
+ When a test stands up an HLS/SCTE-35 capture server (or an SRT sink) that
169
+ **media pushes to**, media sits on its instance network only and can't reach a
170
+ containerised runner via the host LAN IP. Hand it the runner's address **on the
171
+ instance network**: `currentInstanceNetworks().addressOn(instanceId)` joins that
172
+ network and returns the runner's IP on it, or `null` when the runner is not in a
173
+ container (or the join failed). The harness ships the primitive, not the policy:
174
+ each product picks its own fallback for the `null` case, usually the address
175
+ media already reaches the host by.
176
+
177
+ ```ts
178
+ import { currentInstanceNetworks } from "@norskvideo/ctl-test-harness/container-net";
179
+
180
+ function captureHost(instanceId: string): string {
181
+ // hostLanIp is the product's own off-container fallback.
182
+ return currentInstanceNetworks().addressOn(instanceId) ?? hostLanIp();
183
+ }
184
+ ```
185
+
186
+ The runner leaves every network it joined during `cleanupDaemon`, before the
187
+ instance is deleted.
168
188
 
169
189
  ## Build the product image in the integration job
170
190
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-dev-kit",
3
- "version": "0.2.38",
3
+ "version": "0.2.39",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./create-product": "./create-product/create-product.ts",