@norskvideo/ctl-test-harness 0.1.14 → 0.1.16
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/package.json +1 -1
- package/smoke.d.ts +8 -2
- package/smoke.js +28 -9
package/package.json
CHANGED
package/smoke.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ export interface SmokeSpec {
|
|
|
87
87
|
clearedMs?: number;
|
|
88
88
|
};
|
|
89
89
|
/** Override the banded ports (see smokePorts). */
|
|
90
|
-
ports?: Partial<
|
|
90
|
+
ports?: Partial<SmokePorts>;
|
|
91
91
|
}
|
|
92
92
|
/** What the observation kinds read. Injected so the phase rule is tested
|
|
93
93
|
* without Studio; defaults are studio-state's fetchers. */
|
|
@@ -125,6 +125,8 @@ export interface SmokeDeps {
|
|
|
125
125
|
/** Docker leaves root-owned bind-mount targets under the store; a non-root
|
|
126
126
|
* runner cannot rm them, so they go from a throwaway root container. */
|
|
127
127
|
nukeStoreAsRoot(storeDir: string): void;
|
|
128
|
+
/** After the nuke: is the store still there? (Then the nuke did not work.) */
|
|
129
|
+
storeExists(storeDir: string): boolean;
|
|
128
130
|
ensureNetwork(): ReturnType<typeof ensureRunnerOnNetwork>;
|
|
129
131
|
supportsNoPublish(): Promise<boolean>;
|
|
130
132
|
containerUser(): string | undefined;
|
|
@@ -133,7 +135,11 @@ export interface SmokeDeps {
|
|
|
133
135
|
/** Per-slug port band, above the product integration harnesses' own bands:
|
|
134
136
|
* daemon, dev backend, published Studio port and an instance base, twenty
|
|
135
137
|
* apart per slug. */
|
|
136
|
-
export
|
|
138
|
+
export interface SmokePorts extends BaseHarnessPorts {
|
|
139
|
+
/** The daemon's oauth2 proxy; banded so co-located runs never meet on :9443. */
|
|
140
|
+
proxyPort: number;
|
|
141
|
+
}
|
|
142
|
+
export declare function smokePorts(slug: string, overrides?: Partial<SmokePorts>): SmokePorts;
|
|
137
143
|
export declare const defaultSmokeDeps: SmokeDeps;
|
|
138
144
|
/** Whether an observation depends on a source being pumped. Structural ones
|
|
139
145
|
* run the satisfied phase only. */
|
package/smoke.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// not a dependency of this package (daemon.ts's cleanupDaemon has the same
|
|
19
19
|
// rule); a product wanting typed commands bridges with toArgv in its own spec.
|
|
20
20
|
import { spawnSync } from "node:child_process";
|
|
21
|
-
import { writeFileSync } from "node:fs";
|
|
21
|
+
import { existsSync, writeFileSync } from "node:fs";
|
|
22
22
|
import { basename, dirname, join } from "node:path";
|
|
23
23
|
import { ensureRunnerOnNetwork, netReachMode, STUDIO_INTERNAL_PORT, studioBaseFrom, } from "./container-net.js";
|
|
24
24
|
import { cleanupDaemon, requireLicenseFile, runCli, startDaemon, } from "./daemon.js";
|
|
@@ -33,15 +33,13 @@ const SMOKE_BAND_WIDTH = 20;
|
|
|
33
33
|
const SMOKE_BANDS = 50;
|
|
34
34
|
const STUDIO_HOST_PORT_PARAM = "STUDIO_HOST_PORT";
|
|
35
35
|
const NUKE_IMAGE = "alpine:3";
|
|
36
|
-
/** Per-slug port band, above the product integration harnesses' own bands:
|
|
37
|
-
* daemon, dev backend, published Studio port and an instance base, twenty
|
|
38
|
-
* apart per slug. */
|
|
39
36
|
export function smokePorts(slug, overrides = {}) {
|
|
40
37
|
const daemonPort = SMOKE_PORT_BASE + hashSlot(`smoke:${slug}`, SMOKE_BANDS) * SMOKE_BAND_WIDTH;
|
|
41
38
|
return {
|
|
42
39
|
daemonPort,
|
|
43
40
|
backendPort: daemonPort + 1,
|
|
44
41
|
studioHostPort: daemonPort + 2,
|
|
42
|
+
proxyPort: daemonPort + 3,
|
|
45
43
|
instancePortBase: daemonPort + 10,
|
|
46
44
|
...overrides,
|
|
47
45
|
};
|
|
@@ -79,6 +77,7 @@ export const defaultSmokeDeps = {
|
|
|
79
77
|
`rm -rf /base/${basename(storeDir)}`,
|
|
80
78
|
]);
|
|
81
79
|
},
|
|
80
|
+
storeExists: (storeDir) => existsSync(storeDir),
|
|
82
81
|
ensureNetwork: () => ensureRunnerOnNetwork(),
|
|
83
82
|
supportsNoPublish: ctlSupportsNoPublish,
|
|
84
83
|
containerUser: runnerContainerUser,
|
|
@@ -188,14 +187,20 @@ export async function runProductSmoke(slug, spec, deps = defaultSmokeDeps) {
|
|
|
188
187
|
let daemon = null;
|
|
189
188
|
let launched = false;
|
|
190
189
|
let handles = [];
|
|
190
|
+
let journeyError;
|
|
191
191
|
try {
|
|
192
|
-
// The same zero point as the guides: `init` on a virgin store, not a seeded
|
|
192
|
+
// The same zero point as the guides: `init` on a virgin store, not a seeded
|
|
193
|
+
// config.yaml. No http redirect (port 80 needs root) and a banded proxy port
|
|
194
|
+
// (the default :9443 would meet a co-located integration run).
|
|
193
195
|
await cliOk([
|
|
194
196
|
"init",
|
|
195
197
|
"--network-mode",
|
|
196
198
|
spec.networkMode ?? "docker",
|
|
197
199
|
"--working-directory",
|
|
198
200
|
join(storeDir, "norsk-runtime"),
|
|
201
|
+
"--proxy-port",
|
|
202
|
+
String(ports.proxyPort),
|
|
203
|
+
"--no-http-redirect",
|
|
199
204
|
"--no-start-server",
|
|
200
205
|
]);
|
|
201
206
|
const started = deps.startDaemon(storeDir, { port: ports.daemonPort, seedConfig: false });
|
|
@@ -316,9 +321,15 @@ export async function runProductSmoke(slug, spec, deps = defaultSmokeDeps) {
|
|
|
316
321
|
deps.log(`${slug}: cleared phase passed for ${phased.length} observation(s)`);
|
|
317
322
|
}
|
|
318
323
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
324
|
+
catch (e) {
|
|
325
|
+
journeyError = e;
|
|
326
|
+
}
|
|
327
|
+
if (handles.length)
|
|
328
|
+
await deps.stopSources(handles).catch(() => { });
|
|
329
|
+
// cleanupDaemon ends with rmSync(storeDir); Docker's root-owned bind-mount
|
|
330
|
+
// targets make that EACCES on a non-root runner. Expected: the root nuke
|
|
331
|
+
// below is what removes them. Only a store that survives the nuke fails.
|
|
332
|
+
try {
|
|
322
333
|
await deps.cleanup({
|
|
323
334
|
deleteInstance: (id) => cli(["instance", "delete", id, "--purge"]),
|
|
324
335
|
stopDaemon: () => cli(["shutdown", "--daemon-only"]),
|
|
@@ -327,6 +338,14 @@ export async function runProductSmoke(slug, spec, deps = defaultSmokeDeps) {
|
|
|
327
338
|
storeDir,
|
|
328
339
|
containers: [`norsk-inst-${instanceId}-studio`, `norsk-inst-${instanceId}-media`],
|
|
329
340
|
});
|
|
330
|
-
|
|
341
|
+
}
|
|
342
|
+
catch (e) {
|
|
343
|
+
deps.log(`${slug}: cleanup could not remove the store itself (${e instanceof Error ? e.message : String(e)}); nuking as root`);
|
|
344
|
+
}
|
|
345
|
+
deps.nukeStoreAsRoot(storeDir);
|
|
346
|
+
if (journeyError !== undefined)
|
|
347
|
+
throw journeyError;
|
|
348
|
+
if (deps.storeExists(storeDir)) {
|
|
349
|
+
throw new Error(`${slug}: store ${storeDir} still present after the root-container nuke`);
|
|
331
350
|
}
|
|
332
351
|
}
|