@norskvideo/ctl-sdk 0.1.31 → 0.1.32
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 +4 -0
- package/docker-runner.js +1 -0
- package/manifest-schema.d.ts +17 -0
- package/package.json +1 -1
- package/parsing.js +18 -1
- package/product-service.d.ts +4 -2
- package/product-service.js +5 -5
- package/product-types.d.ts +7 -1
package/docker-runner.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export declare const PRODUCT_ROLE_LABEL = "norsk-ctl.role=product";
|
|
|
10
10
|
* (see product-reach.ts). Omitted leaves it on docker's default bridge. */
|
|
11
11
|
export interface ProductRunOpts {
|
|
12
12
|
network?: string;
|
|
13
|
+
/** Deploy-time environment for the control-plane container, passed as
|
|
14
|
+
* `-e KEY=VALUE` (e.g. `FUNKE_HARDWARE=software`). The map is the caller's;
|
|
15
|
+
* what a product reads from it is the product's own contract. */
|
|
16
|
+
env?: Record<string, string>;
|
|
13
17
|
}
|
|
14
18
|
/** argv (sans leading "docker") that launches a product container: detached,
|
|
15
19
|
* auto-removed, loopback-published to its internal 4321, and role-labelled so
|
package/docker-runner.js
CHANGED
|
@@ -27,6 +27,7 @@ export function productRunArgs(image, opts = {}) {
|
|
|
27
27
|
"--label",
|
|
28
28
|
PRODUCT_ROLE_LABEL,
|
|
29
29
|
...(opts.network ? ["--network", opts.network] : []),
|
|
30
|
+
...Object.entries(opts.env ?? {}).flatMap(([k, v]) => ["-e", `${k}=${v}`]),
|
|
30
31
|
"-p",
|
|
31
32
|
`127.0.0.1::${CONTAINER_INTERNAL_PORT}`,
|
|
32
33
|
image,
|
package/manifest-schema.d.ts
CHANGED
|
@@ -130,6 +130,23 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
130
130
|
model: z.ZodOptional<z.ZodString>;
|
|
131
131
|
}, z.core.$strip>>;
|
|
132
132
|
}, z.core.$strip>>;
|
|
133
|
+
accelerator: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
134
|
+
mode: z.ZodLiteral<"fixed">;
|
|
135
|
+
value: z.ZodEnum<{
|
|
136
|
+
none: "none";
|
|
137
|
+
nvidia: "nvidia";
|
|
138
|
+
quadra: "quadra";
|
|
139
|
+
}>;
|
|
140
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
141
|
+
mode: z.ZodLiteral<"default">;
|
|
142
|
+
value: z.ZodEnum<{
|
|
143
|
+
none: "none";
|
|
144
|
+
nvidia: "nvidia";
|
|
145
|
+
quadra: "quadra";
|
|
146
|
+
}>;
|
|
147
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
148
|
+
mode: z.ZodLiteral<"required">;
|
|
149
|
+
}, z.core.$strip>], "mode">>;
|
|
133
150
|
capabilities: z.ZodOptional<z.ZodObject<{
|
|
134
151
|
mode: z.ZodEnum<{
|
|
135
152
|
default: "default";
|
package/package.json
CHANGED
package/parsing.js
CHANGED
|
@@ -15,7 +15,8 @@ export function parseSpec(raw, where, path) {
|
|
|
15
15
|
if (typeof raw.image !== "string") {
|
|
16
16
|
throw new ProductsParseError(path, `${where}.spec.image must be a string`);
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
const env = parseEnv(raw.env, where, path);
|
|
19
|
+
return { kind: "container", image: raw.image, ...(env !== undefined ? { env } : {}) };
|
|
19
20
|
}
|
|
20
21
|
if (raw.kind === "dev") {
|
|
21
22
|
if (typeof raw.url !== "string") {
|
|
@@ -25,6 +26,22 @@ export function parseSpec(raw, where, path) {
|
|
|
25
26
|
}
|
|
26
27
|
throw new ProductsParseError(path, `${where}.spec.kind must be 'container' or 'dev'`);
|
|
27
28
|
}
|
|
29
|
+
/** The container spec's deploy-time env, round-tripped from the store as a
|
|
30
|
+
* `{ KEY: VALUE }` map of strings. Absent stays absent; a present value must
|
|
31
|
+
* be an object of string→string. */
|
|
32
|
+
function parseEnv(raw, where, path) {
|
|
33
|
+
if (raw === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (!isRecord(raw))
|
|
36
|
+
throw new ProductsParseError(path, `${where}.spec.env must be an object`);
|
|
37
|
+
const env = {};
|
|
38
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
39
|
+
if (typeof v !== "string")
|
|
40
|
+
throw new ProductsParseError(path, `${where}.spec.env.${k} must be a string`);
|
|
41
|
+
env[k] = v;
|
|
42
|
+
}
|
|
43
|
+
return env;
|
|
44
|
+
}
|
|
28
45
|
function parseLicense(raw, where, path) {
|
|
29
46
|
if (!isRecord(raw))
|
|
30
47
|
throw new ProductsParseError(path, `${where}.license must be an object`);
|
package/product-service.d.ts
CHANGED
|
@@ -82,8 +82,10 @@ export type RefreshProductTemplateBytesFn = ImportProductTemplateBytesFn;
|
|
|
82
82
|
export interface ProductContainerOps {
|
|
83
83
|
pull(image: string): Promise<void>;
|
|
84
84
|
/** Start the container. The host port comes back from docker rather than
|
|
85
|
-
* going in: docker owns the host port space (see docker-runner.ts).
|
|
86
|
-
|
|
85
|
+
* going in: docker owns the host port space (see docker-runner.ts). `env`
|
|
86
|
+
* is the spec's deploy-time environment, passed straight through as
|
|
87
|
+
* `-e KEY=VALUE`. */
|
|
88
|
+
run(image: string, env?: Record<string, string>): Promise<ProductPlacement>;
|
|
87
89
|
remove(containerId: string): Promise<void>;
|
|
88
90
|
rename(containerId: string, name: string): Promise<void>;
|
|
89
91
|
waitForReady(baseUrl: string): Promise<void>;
|
package/product-service.js
CHANGED
|
@@ -29,7 +29,7 @@ async function fetchProductTemplateBytes(baseUrl, url) {
|
|
|
29
29
|
* (typically `pull`, onto its own docker adapter) and keep the rest. */
|
|
30
30
|
export const defaultProductContainerOps = {
|
|
31
31
|
pull: dockerPull,
|
|
32
|
-
run: dockerRun,
|
|
32
|
+
run: (image, env) => dockerRun(image, env ? { env } : {}),
|
|
33
33
|
remove: dockerRm,
|
|
34
34
|
rename: dockerRename,
|
|
35
35
|
waitForReady,
|
|
@@ -117,7 +117,7 @@ export class ProductService {
|
|
|
117
117
|
else {
|
|
118
118
|
await this.refreshImage(spec.image);
|
|
119
119
|
logger.info(`Starting product container: ${spec.image} (reach: ${this.reach})`);
|
|
120
|
-
const placement = await this.containerOps.run(spec.image);
|
|
120
|
+
const placement = await this.containerOps.run(spec.image, spec.env);
|
|
121
121
|
containerId = placement.containerId;
|
|
122
122
|
port = placement.hostPort;
|
|
123
123
|
logger.info(`Product container ${containerId.slice(0, 12)} published on host port ${port}`);
|
|
@@ -309,7 +309,7 @@ export class ProductService {
|
|
|
309
309
|
// Already gone (crashed / --rm reaped) — nothing to stop.
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
|
-
const { containerId, hostPort } = await this.containerOps.run(target.spec.image);
|
|
312
|
+
const { containerId, hostPort } = await this.containerOps.run(target.spec.image, target.spec.env);
|
|
313
313
|
const reachHost = await this.reachHostFor(containerId);
|
|
314
314
|
await this.store.update((products) => products.map((p) => (p.name === name ? withPlacement(p, containerId, hostPort, reachHost) : p)));
|
|
315
315
|
await this.containerOps.waitForReady(specBaseUrl(target.spec, hostPort, reachHost));
|
|
@@ -400,7 +400,7 @@ export class ProductService {
|
|
|
400
400
|
return;
|
|
401
401
|
}
|
|
402
402
|
try {
|
|
403
|
-
const { containerId, hostPort } = await this.containerOps.run(reg.spec.image);
|
|
403
|
+
const { containerId, hostPort } = await this.containerOps.run(reg.spec.image, reg.spec.env);
|
|
404
404
|
const reachHost = await this.reachHostFor(containerId);
|
|
405
405
|
await this.containerOps.waitForReady(specBaseUrl(reg.spec, hostPort, reachHost));
|
|
406
406
|
await this.containerOps.rename(containerId, productContainerName(reg.name));
|
|
@@ -449,7 +449,7 @@ export class ProductService {
|
|
|
449
449
|
// Already gone (crashed / --rm reaped) — nothing to stop.
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
|
-
const { containerId, hostPort } = await this.containerOps.run(target.spec.image);
|
|
452
|
+
const { containerId, hostPort } = await this.containerOps.run(target.spec.image, target.spec.env);
|
|
453
453
|
const reachHost = await this.reachHostFor(containerId);
|
|
454
454
|
await this.store.update((products) => products.map((p) => (p.name === name ? withPlacement(p, containerId, hostPort, reachHost) : p)));
|
|
455
455
|
await this.containerOps.waitForReady(specBaseUrl(target.spec, hostPort, reachHost));
|
package/product-types.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { Manifest } from "./manifest-schema.js";
|
|
2
|
-
export type ProductSpec =
|
|
2
|
+
export type ProductSpec =
|
|
3
|
+
/** `env` is the deploy-time environment for the control-plane container,
|
|
4
|
+
* supplied at `product add --env KEY=VALUE`. It lives on the spec (not just
|
|
5
|
+
* the add call) so it persists with the registration and every later
|
|
6
|
+
* restart/reload/restore re-runs the container with the same environment. */
|
|
7
|
+
{
|
|
3
8
|
kind: "container";
|
|
4
9
|
image: string;
|
|
10
|
+
env?: Record<string, string>;
|
|
5
11
|
} | {
|
|
6
12
|
kind: "dev";
|
|
7
13
|
url: string;
|