@norskvideo/ctl-sdk 0.1.22 → 0.1.24
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-runner.d.ts +9 -2
- package/docker-runner.js +4 -3
- package/manifest-fetch.d.ts +12 -1
- package/manifest-fetch.js +14 -4
- package/package.json +2 -2
- package/product-service.js +1 -1
package/docker-runner.d.ts
CHANGED
|
@@ -4,10 +4,17 @@ export declare const CONTAINER_INTERNAL_PORT = 4321;
|
|
|
4
4
|
* `norsk-ctl.role` label (proxy, proxy-oauth2, cpu-monitor, product); the
|
|
5
5
|
* value doubles as the card's component name. */
|
|
6
6
|
export declare const PRODUCT_ROLE_LABEL = "norsk-ctl.role=product";
|
|
7
|
+
/** How to place a product container. The network is the host's call: ctl puts
|
|
8
|
+
* the control plane on the same bridge as nginx and every launched instance,
|
|
9
|
+
* which is what makes it reachable from a daemon that is itself a container
|
|
10
|
+
* (see product-reach.ts). Omitted leaves it on docker's default bridge. */
|
|
11
|
+
export interface ProductRunOpts {
|
|
12
|
+
network?: string;
|
|
13
|
+
}
|
|
7
14
|
/** argv (sans leading "docker") that launches a product container: detached,
|
|
8
15
|
* auto-removed, loopback-published to its internal 4321, and role-labelled so
|
|
9
16
|
* it shows up in the Infrastructure tab. */
|
|
10
|
-
export declare function productRunArgs(image: string, hostPort: number): string[];
|
|
17
|
+
export declare function productRunArgs(image: string, hostPort: number, opts?: ProductRunOpts): string[];
|
|
11
18
|
/** Stable container name for a product, derived from its manifest name, so
|
|
12
19
|
* `docker ps` shows `norsk-product-studio` instead of a random docker alias.
|
|
13
20
|
* Mirrors the singleton naming of norsk-proxy / norsk-ctl-cpu-monitor. */
|
|
@@ -16,7 +23,7 @@ export declare function productContainerName(productName: string): string;
|
|
|
16
23
|
* so there is nothing to refresh; a tag can move under a registration. */
|
|
17
24
|
export declare function isDigestRef(image: string): boolean;
|
|
18
25
|
export declare function dockerPull(image: string): Promise<void>;
|
|
19
|
-
export declare function dockerRun(image: string, hostPort: number): Promise<string>;
|
|
26
|
+
export declare function dockerRun(image: string, hostPort: number, opts?: ProductRunOpts): Promise<string>;
|
|
20
27
|
/** The container's address on its first network (the default bridge for a
|
|
21
28
|
* product container), or undefined when docker cannot say. */
|
|
22
29
|
export declare function dockerAddress(containerId: string): Promise<string | undefined>;
|
package/docker-runner.js
CHANGED
|
@@ -8,13 +8,14 @@ export const PRODUCT_ROLE_LABEL = "norsk-ctl.role=product";
|
|
|
8
8
|
/** argv (sans leading "docker") that launches a product container: detached,
|
|
9
9
|
* auto-removed, loopback-published to its internal 4321, and role-labelled so
|
|
10
10
|
* it shows up in the Infrastructure tab. */
|
|
11
|
-
export function productRunArgs(image, hostPort) {
|
|
11
|
+
export function productRunArgs(image, hostPort, opts = {}) {
|
|
12
12
|
return [
|
|
13
13
|
"run",
|
|
14
14
|
"-d",
|
|
15
15
|
"--rm",
|
|
16
16
|
"--label",
|
|
17
17
|
PRODUCT_ROLE_LABEL,
|
|
18
|
+
...(opts.network ? ["--network", opts.network] : []),
|
|
18
19
|
"-p",
|
|
19
20
|
`127.0.0.1:${hostPort}:${CONTAINER_INTERNAL_PORT}`,
|
|
20
21
|
image,
|
|
@@ -43,8 +44,8 @@ export async function dockerPull(image) {
|
|
|
43
44
|
throw new ProductError("DOCKER_PULL_FAILED", `docker pull failed (exit ${exitCode}): ${stderr || "no stderr"}`);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
|
-
export async function dockerRun(image, hostPort) {
|
|
47
|
-
const proc = Bun.spawn(["docker", ...productRunArgs(image, hostPort)], { stdout: "pipe", stderr: "pipe" });
|
|
47
|
+
export async function dockerRun(image, hostPort, opts = {}) {
|
|
48
|
+
const proc = Bun.spawn(["docker", ...productRunArgs(image, hostPort, opts)], { stdout: "pipe", stderr: "pipe" });
|
|
48
49
|
const exitCode = await proc.exited;
|
|
49
50
|
const stdout = (await new Response(proc.stdout).text()).trim();
|
|
50
51
|
const stderr = (await new Response(proc.stderr).text()).trim();
|
package/manifest-fetch.d.ts
CHANGED
|
@@ -17,7 +17,18 @@ export declare function specBaseUrl(spec: ProductSpec, port?: number, reachHost?
|
|
|
17
17
|
* so a recorded reachHost is never forgotten. */
|
|
18
18
|
export declare function productBaseUrl(reg: Pick<ProductRegistration, "spec" | "port" | "reachHost">): string;
|
|
19
19
|
export declare function fetchManifest(baseUrl: string): Promise<Manifest>;
|
|
20
|
-
|
|
20
|
+
/** Poll a product's manifest until it answers.
|
|
21
|
+
*
|
|
22
|
+
* The timeout names `baseUrl`. That is the whole point of the message: the
|
|
23
|
+
* daemon dials a container product on loopback, and where that loopback is not
|
|
24
|
+
* the daemon's own (a docker-outside-of-docker runner, where it belongs to the
|
|
25
|
+
* docker host — see product-reach.ts) the product is perfectly healthy and
|
|
26
|
+
* simply unreachable. Without the address, that reads identically to a product
|
|
27
|
+
* that genuinely failed to start. */
|
|
28
|
+
export declare function waitForReady(baseUrl: string, opts?: {
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
intervalMs?: number;
|
|
31
|
+
}): Promise<void>;
|
|
21
32
|
/**
|
|
22
33
|
* Probe the product's configScreenUrl after registration. Two hard
|
|
23
34
|
* rejections:
|
package/manifest-fetch.js
CHANGED
|
@@ -56,8 +56,18 @@ export async function fetchManifest(baseUrl) {
|
|
|
56
56
|
}
|
|
57
57
|
return parsed.data;
|
|
58
58
|
}
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/** Poll a product's manifest until it answers.
|
|
60
|
+
*
|
|
61
|
+
* The timeout names `baseUrl`. That is the whole point of the message: the
|
|
62
|
+
* daemon dials a container product on loopback, and where that loopback is not
|
|
63
|
+
* the daemon's own (a docker-outside-of-docker runner, where it belongs to the
|
|
64
|
+
* docker host — see product-reach.ts) the product is perfectly healthy and
|
|
65
|
+
* simply unreachable. Without the address, that reads identically to a product
|
|
66
|
+
* that genuinely failed to start. */
|
|
67
|
+
export async function waitForReady(baseUrl, opts = {}) {
|
|
68
|
+
const timeoutMs = opts.timeoutMs ?? READINESS_TIMEOUT_MS;
|
|
69
|
+
const intervalMs = opts.intervalMs ?? READINESS_INTERVAL_MS;
|
|
70
|
+
const deadline = Date.now() + timeoutMs;
|
|
61
71
|
while (Date.now() < deadline) {
|
|
62
72
|
try {
|
|
63
73
|
const r = await fetch(`${baseUrl}/manifest.json`);
|
|
@@ -67,9 +77,9 @@ export async function waitForReady(baseUrl) {
|
|
|
67
77
|
catch {
|
|
68
78
|
// not ready yet
|
|
69
79
|
}
|
|
70
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
80
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
71
81
|
}
|
|
72
|
-
throw new ProductError("READINESS_TIMEOUT", `product did not become ready within ${
|
|
82
|
+
throw new ProductError("READINESS_TIMEOUT", `product did not become ready within ${timeoutMs}ms — nothing answered ${baseUrl}/manifest.json`);
|
|
73
83
|
}
|
|
74
84
|
/**
|
|
75
85
|
* Probe the product's configScreenUrl after registration. Two hard
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@norskvideo/ctl-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"types": "./index.d.ts",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@norskvideo/ctl-foundation": "^0.1.0",
|
|
40
|
-
"@norskvideo/ctl-product-template-schema": "^0.1.
|
|
40
|
+
"@norskvideo/ctl-product-template-schema": "^0.1.12",
|
|
41
41
|
"express": "5",
|
|
42
42
|
"lucide-react": "^0.483.0",
|
|
43
43
|
"react": "^19.0.0",
|
package/product-service.js
CHANGED
|
@@ -127,7 +127,7 @@ export class ProductService {
|
|
|
127
127
|
}
|
|
128
128
|
port = allocated;
|
|
129
129
|
await this.refreshImage(spec.image);
|
|
130
|
-
logger.info(`Starting product container: ${spec.image} on host port ${port}`);
|
|
130
|
+
logger.info(`Starting product container: ${spec.image} on host port ${port} (reach: ${this.reach})`);
|
|
131
131
|
containerId = await this.containerOps.run(spec.image, port);
|
|
132
132
|
reachHost = await this.reachHostFor(containerId);
|
|
133
133
|
baseUrl = specBaseUrl(spec, port, reachHost);
|