@nosana/node 1.1.71 → 1.1.73-rc

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.
Files changed (74) hide show
  1. package/README.md +2 -2
  2. package/dist/.env +2 -0
  3. package/dist/package.json +9 -6
  4. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/createDockerRunOptions.d.ts +1 -1
  5. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/createDockerRunOptions.js +5 -1
  6. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/dockerExtended/index.d.ts +3 -0
  7. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/dockerExtended/index.js +12 -5
  8. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/index.d.ts +11 -10
  9. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/index.js +18 -37
  10. package/dist/packages/container-orchestrator/src/hooks.d.ts +32 -0
  11. package/dist/packages/container-orchestrator/src/index.d.ts +17 -0
  12. package/dist/packages/container-orchestrator/src/index.js +16 -0
  13. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/interface.d.ts +14 -5
  14. package/dist/packages/container-orchestrator/src/interface.js +1 -0
  15. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/labels.d.ts +2 -0
  16. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/labels.js +2 -0
  17. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/podman/index.d.ts +3 -3
  18. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/podman/index.js +7 -17
  19. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/podman/utils/createPodmanRunOptions.d.ts +12 -0
  20. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/podman/utils/createPodmanRunOptions.js +4 -2
  21. package/dist/packages/container-orchestrator/src/selectContainerOrchestration.d.ts +3 -0
  22. package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/selectContainerOrchestration.js +3 -3
  23. package/dist/packages/container-orchestrator/src/types.d.ts +15 -0
  24. package/dist/packages/container-orchestrator/src/types.js +1 -0
  25. package/dist/packages/container-orchestrator/src/utils/ifStringCastToArray.d.ts +2 -0
  26. package/dist/packages/container-orchestrator/src/utils/ifStringCastToArray.js +2 -0
  27. package/dist/src/NodeManager/configs/configs.d.ts +6 -0
  28. package/dist/src/NodeManager/configs/configs.js +6 -0
  29. package/dist/src/NodeManager/index.js +7 -0
  30. package/dist/src/NodeManager/node/Node.d.ts +5 -0
  31. package/dist/src/NodeManager/node/Node.js +23 -2
  32. package/dist/src/NodeManager/node/health/healthHandler.d.ts +1 -1
  33. package/dist/src/NodeManager/node/resource/definition/index.d.ts +0 -1
  34. package/dist/src/NodeManager/node/resource/definition/index.js +0 -1
  35. package/dist/src/NodeManager/node/resource/helpers/createHFArgs.d.ts +1 -1
  36. package/dist/src/NodeManager/node/resource/helpers/createHFArgs.js +2 -3
  37. package/dist/src/NodeManager/node/resource/helpers/createOllamaArgs.d.ts +1 -1
  38. package/dist/src/NodeManager/node/resource/helpers/createOllamaArgs.js +2 -3
  39. package/dist/src/NodeManager/node/resource/helpers/createS3Args.d.ts +1 -1
  40. package/dist/src/NodeManager/node/resource/helpers/createS3Args.js +3 -3
  41. package/dist/src/NodeManager/node/resource/image/imageManager.d.ts +1 -1
  42. package/dist/src/NodeManager/node/resource/resourceManager.d.ts +1 -1
  43. package/dist/src/NodeManager/node/resource/volume/volumeManager.d.ts +1 -1
  44. package/dist/src/NodeManager/node/resource/volume/volumeManager.js +11 -8
  45. package/dist/src/NodeManager/node/task/TaskManager.js +25 -0
  46. package/dist/src/NodeManager/provider/ContainerStateManager.js +1 -1
  47. package/dist/src/NodeManager/provider/Provider.d.ts +34 -10
  48. package/dist/src/NodeManager/provider/Provider.js +217 -93
  49. package/dist/src/NodeManager/provider/orchestrationHooks.d.ts +8 -0
  50. package/dist/src/NodeManager/provider/orchestrationHooks.js +13 -0
  51. package/dist/src/NodeManager/provider/sshSidecar.d.ts +2 -2
  52. package/dist/src/NodeManager/provider/sshSidecar.js +2 -2
  53. package/dist/src/NodeManager/provider/types.d.ts +0 -9
  54. package/dist/src/NodeManager/provider/webTerminal.d.ts +1 -1
  55. package/dist/src/NodeManager/provider/webTerminal.js +1 -1
  56. package/dist/src/NodeManager/utils/utils.d.ts +1 -7
  57. package/dist/src/NodeManager/utils/utils.js +1 -7
  58. package/dist/src/cli/commands/prune/action.js +3 -2
  59. package/dist/src/cli/commands/prune/command.js +1 -1
  60. package/dist/src/cli/commands/run/action.js +3 -2
  61. package/dist/src/cli/commands/run/command.js +1 -1
  62. package/dist/src/cli/commands/start/command.js +1 -1
  63. package/npm-shrinkwrap.json +21 -2
  64. package/package.json +9 -6
  65. package/dist/src/NodeManager/provider/containerOrchestration/docker/utils/createServerObject.d.ts +0 -5
  66. package/dist/src/NodeManager/provider/containerOrchestration/docker/utils/createServerObject.js +0 -15
  67. package/dist/src/NodeManager/provider/containerOrchestration/docker/utils/deadline.d.ts +0 -1
  68. package/dist/src/NodeManager/provider/containerOrchestration/docker/utils/deadline.js +0 -15
  69. package/dist/src/NodeManager/provider/containerOrchestration/selectContainerOrchestration.d.ts +0 -3
  70. /package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/utils/repoTagsContainsImage.d.ts +0 -0
  71. /package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/utils/repoTagsContainsImage.js +0 -0
  72. /package/dist/{src/NodeManager/provider/containerOrchestration/interface.js → packages/container-orchestrator/src/hooks.js} +0 -0
  73. /package/dist/{src/NodeManager → packages/container-orchestrator/src}/utils/liveAbortSignal.d.ts +0 -0
  74. /package/dist/{src/NodeManager → packages/container-orchestrator/src}/utils/liveAbortSignal.js +0 -0
package/README.md CHANGED
@@ -66,7 +66,7 @@ Options:
66
66
  ```
67
67
  --provider <provider> provider used to run the job (choices: "docker", "podman", default: "podman")
68
68
  -w, --wallet <wallet> path to wallet private key (default: "~/.nosana/nosana_key.json")
69
- --docker, --podman <URI> Podman/Docker connection URI (default: "~/.nosana/podman/podman.sock")
69
+ --docker, --podman <path> Podman/Docker socket path (default: "~/.nosana/podman/podman.sock")
70
70
  -h, --help display help for command
71
71
  ```
72
72
 
@@ -79,7 +79,7 @@ Options:
79
79
  ```
80
80
  --provider <provider> provider used to run the job (choices: "docker", "podman", default: "podman")
81
81
  -w, --wallet <wallet> path to wallet private key (default: "~/.nosana/nosana_key.json")
82
- --docker, --podman <URI> Podman/Docker connection URI (default: "~/.nosana/podman/podman.sock")
82
+ --docker, --podman <path> Podman/Docker socket path (default: "~/.nosana/podman/podman.sock")
83
83
  -h, --help display help for command
84
84
  ```
85
85
 
package/dist/.env CHANGED
@@ -1,5 +1,7 @@
1
1
  BACKEND_SOLANA_ADDRESS=7rFPFnxjXHC2sfDy3qrDa9pEb4j49oivMuV7e8sYDPmB
2
2
  FRP_SERVER_IMAGE=registry.hub.docker.com/nosana/frpc:v1.0.45
3
+ JOB_RUNTIME_IMAGE="nosana/job-runtime:v0.1.0"
4
+ RESOURCE_MANAGER_IMAGE=registry.hub.docker.com/nosana/resource-manager:v1.1.19
3
5
  FRP_SERVER_PORT=7000
4
6
  MIN_DISK_SPACE=25
5
7
  SIGN_MESSAGE="Hello Nosana Node!"
package/dist/package.json CHANGED
@@ -1,17 +1,20 @@
1
1
  {
2
2
  "name": "@nosana/node",
3
- "version": "1.1.71",
3
+ "version": "1.1.73-rc",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "nosana-node": "./dist/src/index.js"
7
7
  },
8
8
  "type": "module",
9
+ "workspaces": [
10
+ "packages/*"
11
+ ],
9
12
  "scripts": {
10
13
  "dev:start": "./docker-run.sh start --docker /var/run/docker.sock",
11
14
  "dev:run": "./docker-run.sh run --docker /var/run/docker.sock",
12
15
  "dev:podman:start": "./podman-run.sh start",
13
16
  "dev:podman:run": "./podman-run.sh run",
14
- "build": "npm run prepare && tsc && cp .env ./dist/ && cp .env.prd ./dist/ && cp .env.dev ./dist/",
17
+ "build": "npm run prepare && tsc && node scripts/rewrite-dist-imports.mjs dist && cp .env ./dist/ && cp .env.prd ./dist/ && cp .env.dev ./dist/",
15
18
  "generate": "npm run generate:host-manager",
16
19
  "generate:host-manager": "openapi-typescript https://host-manager.k8s.dev.nos.ci/swagger/json -o ./src/NodeManager/clients/hostManager/schema.d.ts",
17
20
  "test": "APP_ENV=development DOCKER_CERT_PATH= vitest run",
@@ -21,10 +24,10 @@
21
24
  "publish:next": "npm run build && npm publish --tag=next --access public",
22
25
  "publish:public": "npm run build && npm publish --access public",
23
26
  "prepare": "npm run generate && ts-patch install && typia patch",
24
- "prettier": "prettier --check ./src",
25
- "prettier:fix": "prettier --write ./src",
26
- "eslint": "eslint ./src",
27
- "eslint:fix": "eslint --fix ./src",
27
+ "prettier": "prettier --check ./src ./packages",
28
+ "prettier:fix": "prettier --write ./src ./packages",
29
+ "eslint": "eslint ./src ./packages",
30
+ "eslint:fix": "eslint --fix ./src ./packages",
28
31
  "lint": " npm run eslint",
29
32
  "lint:fix": "npm run prettier:fix && npm run eslint:fix"
30
33
  },
@@ -1,3 +1,3 @@
1
1
  import { ContainerCreateOptions } from "dockerode";
2
2
  import { RunContainerArgs } from "../interface";
3
- export declare function createDockerRunOptions(image: string, { name, cmd, gpu, volumes, env, work_dir, entrypoint, aliases, restart_policy, runtime, bind_network_to_container, labels, security, }: RunContainerArgs, gpuOption: string): ContainerCreateOptions;
3
+ export declare function createDockerRunOptions(image: string, { name, cmd, gpu, volumes, env, work_dir, entrypoint, aliases, restart_policy, runtime, bind_network_to_container, labels, security, pid_mode, }: RunContainerArgs, gpuOption: string): ContainerCreateOptions;
@@ -10,7 +10,7 @@ function parseRestartPolicy(restart_policy) {
10
10
  }
11
11
  return undefined;
12
12
  }
13
- export function createDockerRunOptions(image, { name, cmd, gpu, volumes, env, work_dir, entrypoint, aliases, restart_policy, runtime, bind_network_to_container, labels, security, }, gpuOption) {
13
+ export function createDockerRunOptions(image, { name, cmd, gpu, volumes, env, work_dir, entrypoint, aliases, restart_policy, runtime, bind_network_to_container, labels, security, pid_mode, }, gpuOption) {
14
14
  const devices = [];
15
15
  if (gpu) {
16
16
  devices.push({
@@ -73,6 +73,10 @@ export function createDockerRunOptions(image, { name, cmd, gpu, volumes, env, wo
73
73
  ...(runtime === 'SEV-SNP' ? { Runtime: runtime } : {}),
74
74
  ...(security?.capDrop ? { CapDrop: security.capDrop } : {}),
75
75
  ...(security?.pidsLimit ? { PidsLimit: security.pidsLimit } : {}),
76
+ ...(security?.noNewPrivileges
77
+ ? { SecurityOpt: ['no-new-privileges'] }
78
+ : {}),
79
+ ...(pid_mode ? { PidMode: pid_mode } : {}),
76
80
  },
77
81
  };
78
82
  }
@@ -1,6 +1,9 @@
1
1
  import Dockerode from 'dockerode';
2
2
  import { DockerAuth } from '@nosana/sdk';
3
+ import type { PullProgress } from '../../hooks.js';
3
4
  export declare class DockerExtended extends Dockerode {
5
+ private readonly pullProgress?;
6
+ constructor(options: Dockerode.DockerOptions, pullProgress?: (() => PullProgress) | undefined);
4
7
  promisePull(image: string, controller: AbortController, authorisation?: DockerAuth): Promise<unknown>;
5
8
  hasImage(image: string): Promise<boolean>;
6
9
  }
@@ -1,15 +1,22 @@
1
1
  import Dockerode from 'dockerode';
2
2
  import { repoTagsContainsImage } from '../utils/repoTagsContainsImage.js';
3
- import { createLoggingProxy } from '../../../..//monitoring/proxy/loggingProxy.js';
4
- import { abortControllerSelector } from '../../../../node/abort/abortControllerSelector.js';
5
- import { MultiProgressBarReporter } from '../../../../node/utils/multiProgressBarReporter.js';
3
+ /** A pull nobody watches. */
4
+ const silentProgress = {
5
+ start: () => undefined,
6
+ update: () => undefined,
7
+ stop: () => undefined,
8
+ };
6
9
  export class DockerExtended extends Dockerode {
10
+ constructor(options, pullProgress) {
11
+ super(options);
12
+ this.pullProgress = pullProgress;
13
+ }
7
14
  async promisePull(image, controller, authorisation) {
8
- const multiProgressBarReporter = createLoggingProxy(new MultiProgressBarReporter());
15
+ const multiProgressBarReporter = this.pullProgress?.() ?? silentProgress;
9
16
  return await new Promise((resolve, reject) => this.pull(image, {}, (err, stream) => {
10
17
  const destroy = () => {
11
18
  stream.destroy();
12
- reject(new Error(`Pulling image aborted due to ${abortControllerSelector().signal.reason}`));
19
+ reject(new Error(`Pulling image aborted due to ${controller.signal.reason}`));
13
20
  };
14
21
  if (controller.signal.aborted) {
15
22
  destroy();
@@ -1,26 +1,28 @@
1
- import { Container, ContainerCreateOptions, ImageInfo, Image, Volume, VolumeCreateResponse, VolumeInspectInfo, ContainerInspectInfo } from 'dockerode';
1
+ import { Container, ContainerCreateOptions, ImageInfo, Image, Volume, VolumeCreateResponse, VolumeInspectInfo, ContainerInspectInfo, ContainerInfo } from 'dockerode';
2
2
  import { DockerAuth } from '@nosana/sdk';
3
- import type { NodeRepository } from '../../../repository/NodeRepository.js';
3
+ import type { OrchestrationHooks } from '../hooks.js';
4
4
  import { DockerExtended } from './dockerExtended/index.js';
5
5
  import { ContainerOrchestrationInterface, RunContainerArgs } from '../interface.js';
6
- import { ReturnedStatus } from '../../types.js';
6
+ import { ReturnedStatus } from '../types.js';
7
7
  export declare class DockerContainerOrchestration implements ContainerOrchestrationInterface {
8
- protected repository?: NodeRepository | undefined;
8
+ protected hooks: OrchestrationHooks;
9
9
  docker: DockerExtended;
10
- host: string;
11
- port: string;
12
- protocol: 'https' | 'http' | 'ssh' | 'socket';
10
+ socketPath: string;
13
11
  name: string;
14
12
  gpu: string;
15
13
  teeRuntime?: string;
16
14
  listeners: Map<string, (() => Promise<void>)[]>;
17
- constructor(server: string, gpu: string, teeRuntime?: string, repository?: NodeRepository | undefined);
15
+ constructor(socketPath: string, gpu: string, teeRuntime?: string, hooks?: OrchestrationHooks);
18
16
  checkCustomRuntimeAvailability(runtime: string): Promise<boolean>;
19
17
  getContainerByName(name: string): Promise<Container | undefined>;
20
18
  getContainersByName(names: string[]): Promise<Container[]>;
21
19
  getContainersByLabels(labels: string[], options?: {
22
20
  all?: boolean;
23
21
  }): Promise<Container[]>;
22
+ listContainersByLabels(labels: string[], options?: {
23
+ all?: boolean;
24
+ }): Promise<ContainerInfo[]>;
25
+ getContainer(id: string): Container;
24
26
  resolveHostPath(path: string): Promise<string>;
25
27
  getConnection(): DockerExtended;
26
28
  pullImage(image: string, authorisation?: DockerAuth, controller?: AbortController, force?: boolean): Promise<void>;
@@ -38,7 +40,6 @@ export declare class DockerContainerOrchestration implements ContainerOrchestrat
38
40
  getRawVolume(name: string): Promise<Volume>;
39
41
  deleteVolume(name: string, controller?: AbortController): Promise<void>;
40
42
  healthy(): Promise<ReturnedStatus>;
41
- getProtocol(): string;
42
43
  setupContainerAbortListener(containerId: string, controller: AbortController): void;
43
44
  /**
44
45
  * Records a container we started so a resync can reap it if a crash orphans
@@ -54,5 +55,5 @@ export declare class DockerContainerOrchestration implements ContainerOrchestrat
54
55
  stopAndDeleteContainer(id: string, controller?: AbortController): Promise<ContainerInspectInfo | undefined>;
55
56
  isContainerExited(id: string): Promise<boolean>;
56
57
  doesContainerExist(id: string): Promise<boolean>;
57
- check(): Promise<string>;
58
+ check(): Promise<void>;
58
59
  }
@@ -1,38 +1,20 @@
1
1
  import os from 'os';
2
2
  import { DockerExtended } from './dockerExtended/index.js';
3
- import { createSeverObject } from './utils/createServerObject.js';
4
- import { checkDeprecationDeadline } from './utils/deadline.js';
5
3
  import { createDockerRunOptions } from './createDockerRunOptions.js';
6
- import { liveAbortSignal } from '../../../utils/liveAbortSignal.js';
4
+ import { liveAbortSignal } from '../utils/liveAbortSignal.js';
7
5
  export class DockerContainerOrchestration {
8
- constructor(server, gpu, teeRuntime, repository) {
9
- this.repository = repository;
6
+ constructor(socketPath, gpu, teeRuntime, hooks = {}) {
7
+ this.hooks = hooks;
10
8
  this.name = 'docker';
11
9
  this.gpu = 'all';
12
10
  this.listeners = new Map();
13
11
  this.gpu = gpu;
14
12
  this.teeRuntime = teeRuntime;
15
- if (server.startsWith('http') || server.startsWith('ssh')) {
16
- const { host, port, protocol } = createSeverObject(server);
17
- this.host = host;
18
- this.port = port;
19
- this.protocol = protocol;
20
- this.docker = new DockerExtended({
21
- host: this.host,
22
- port: this.port,
23
- protocol: this.protocol,
24
- });
25
- }
26
- else {
27
- // Assume server is a socket path
28
- this.protocol = 'socket';
29
- if (server.startsWith('~')) {
30
- server = server.replace('~', os.homedir());
31
- }
32
- this.host = server;
33
- this.port = '';
34
- this.docker = new DockerExtended({ socketPath: this.host });
13
+ if (socketPath.startsWith('~')) {
14
+ socketPath = socketPath.replace('~', os.homedir());
35
15
  }
16
+ this.socketPath = socketPath;
17
+ this.docker = new DockerExtended({ socketPath }, hooks.pullProgress);
36
18
  }
37
19
  async checkCustomRuntimeAvailability(runtime) {
38
20
  // TODO: implement a better check for custom runtimes by creating and starting a basic container with the runtime
@@ -75,20 +57,26 @@ export class DockerContainerOrchestration {
75
57
  }
76
58
  }
77
59
  async getContainersByLabels(labels, options = {}) {
60
+ const containers = await this.listContainersByLabels(labels, options);
61
+ return containers.map((info) => this.docker.getContainer(info.Id));
62
+ }
63
+ async listContainersByLabels(labels, options = {}) {
78
64
  try {
79
- const containers = await this.docker.listContainers({
65
+ return await this.docker.listContainers({
80
66
  all: options.all ?? false,
81
67
  filters: {
82
68
  label: labels,
83
69
  ...(options.all ? {} : { status: ['running'] }),
84
70
  },
85
71
  });
86
- return containers.map((info) => this.docker.getContainer(info.Id));
87
72
  }
88
73
  catch (_) {
89
74
  throw new Error('could not get containers');
90
75
  }
91
76
  }
77
+ getContainer(id) {
78
+ return this.docker.getContainer(id);
79
+ }
92
80
  async resolveHostPath(path) {
93
81
  try {
94
82
  // A container's hostname is its id unless overridden, so this finds the
@@ -216,9 +204,6 @@ export class DockerContainerOrchestration {
216
204
  }
217
205
  }
218
206
  async healthy() {
219
- if (this.protocol != 'socket') {
220
- checkDeprecationDeadline();
221
- }
222
207
  try {
223
208
  const info = await this.docker.info();
224
209
  if (typeof info === 'object' && info !== null && info.ID) {
@@ -230,9 +215,6 @@ export class DockerContainerOrchestration {
230
215
  return { status: false, error };
231
216
  }
232
217
  }
233
- getProtocol() {
234
- return this.protocol;
235
- }
236
218
  setupContainerAbortListener(containerId, controller) {
237
219
  if (controller.signal.aborted) {
238
220
  this.stopContainer(containerId);
@@ -255,7 +237,7 @@ export class DockerContainerOrchestration {
255
237
  recordContainer(id, name, track = true) {
256
238
  if (!track)
257
239
  return;
258
- this.repository?.trackContainer(id, { name });
240
+ this.hooks.tracker?.trackContainer(id, { name });
259
241
  }
260
242
  async runContainer(args, controller) {
261
243
  try {
@@ -338,7 +320,7 @@ export class DockerContainerOrchestration {
338
320
  });
339
321
  // Removed, so forget it. Keyed by the resolved id, matching what
340
322
  // `recordContainer` wrote.
341
- this.repository?.untrackContainer(info.Id);
323
+ this.hooks.tracker?.untrackContainer(info.Id);
342
324
  return info;
343
325
  }
344
326
  catch (error) {
@@ -348,7 +330,7 @@ export class DockerContainerOrchestration {
348
330
  // Any other failure leaves the container in place, so it stays tracked
349
331
  // for the next resync to retry rather than being forgotten while alive.
350
332
  if (error.statusCode === 404) {
351
- this.repository?.untrackContainer(id);
333
+ this.hooks.tracker?.untrackContainer(id);
352
334
  }
353
335
  return undefined;
354
336
  }
@@ -378,6 +360,5 @@ export class DockerContainerOrchestration {
378
360
  if (!status) {
379
361
  throw new Error(`error on container orchestration (docker or podman), error: ${error}`);
380
362
  }
381
- return `${this.protocol}://${this.host}:${this.port}`;
382
363
  }
383
364
  }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * What an orchestrator takes from its host. Everything is optional: the
3
+ * sidecar passes nothing, the node passes its repository and progress bars.
4
+ */
5
+ /** Records the containers started through the orchestrator, so the host can
6
+ * reap what a crash orphaned. Keyed by container id. */
7
+ export interface ContainerTracker {
8
+ trackContainer(id: string, fields: {
9
+ name?: string;
10
+ }): void;
11
+ untrackContainer(id: string): void;
12
+ }
13
+ /** Reports one image pull's layer progress (the node's terminal bars). */
14
+ export interface PullProgress {
15
+ start(name: string, options: {
16
+ format: string;
17
+ }): void;
18
+ update(event: {
19
+ status: string;
20
+ progressDetail?: {
21
+ current?: number;
22
+ total?: number;
23
+ };
24
+ id?: string;
25
+ }): void;
26
+ stop(name: string): void;
27
+ }
28
+ export interface OrchestrationHooks {
29
+ tracker?: ContainerTracker;
30
+ /** Called once per pull; absent means silent pulls. */
31
+ pullProgress?: () => PullProgress;
32
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The container runtime behind the node and the job runtime sidecar: one
3
+ * interface, a Docker and a Podman implementation, and the labels both stamp
4
+ * on and look up containers by. The package knows nothing about the node;
5
+ * what it needs from its host (container tracking, pull progress) comes in
6
+ * through {@link OrchestrationHooks}.
7
+ */
8
+ export * from './interface.js';
9
+ export * from './labels.js';
10
+ export * from './hooks.js';
11
+ export type { ReturnedStatus } from './types.js';
12
+ export { DockerContainerOrchestration } from './docker/index.js';
13
+ export { PodmanContainerOrchestration } from './podman/index.js';
14
+ export { selectContainerOrchestrationProvider } from './selectContainerOrchestration.js';
15
+ export { createDockerRunOptions } from './docker/createDockerRunOptions.js';
16
+ export { createPodmanRunOptions } from './podman/utils/createPodmanRunOptions.js';
17
+ export { liveAbortSignal } from './utils/liveAbortSignal.js';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The container runtime behind the node and the job runtime sidecar: one
3
+ * interface, a Docker and a Podman implementation, and the labels both stamp
4
+ * on and look up containers by. The package knows nothing about the node;
5
+ * what it needs from its host (container tracking, pull progress) comes in
6
+ * through {@link OrchestrationHooks}.
7
+ */
8
+ export * from './interface.js';
9
+ export * from './labels.js';
10
+ export * from './hooks.js';
11
+ export { DockerContainerOrchestration } from './docker/index.js';
12
+ export { PodmanContainerOrchestration } from './podman/index.js';
13
+ export { selectContainerOrchestrationProvider } from './selectContainerOrchestration.js';
14
+ export { createDockerRunOptions } from './docker/createDockerRunOptions.js';
15
+ export { createPodmanRunOptions } from './podman/utils/createPodmanRunOptions.js';
16
+ export { liveAbortSignal } from './utils/liveAbortSignal.js';
@@ -1,6 +1,6 @@
1
- import { Container, ImageInfo, Image, Volume, VolumeCreateResponse, VolumeInspectInfo, ContainerCreateOptions, ContainerInspectInfo } from 'dockerode';
1
+ import { Container, ImageInfo, Image, Volume, VolumeCreateResponse, VolumeInspectInfo, ContainerCreateOptions, ContainerInspectInfo, ContainerInfo } from 'dockerode';
2
2
  import { DockerAuth, RestartPolicy } from '@nosana/sdk';
3
- import { ReturnedStatus } from '../types.js';
3
+ import { ReturnedStatus } from './types.js';
4
4
  export type RunContainerArgs = {
5
5
  name?: string;
6
6
  networks?: {
@@ -34,7 +34,11 @@ export type RunContainerArgs = {
34
34
  security?: {
35
35
  capDrop?: string[];
36
36
  pidsLimit?: number;
37
+ /** Keeps the container's processes from gaining privileges (setuid etc.). */
38
+ noNewPrivileges?: boolean;
37
39
  };
40
+ /** Shares the host's pid namespace with the container (`--pid=host`). */
41
+ pid_mode?: 'host';
38
42
  /**
39
43
  * Runs between create and start with the container handle, so files can be
40
44
  * put in place before the entrypoint runs. A failure removes the created
@@ -45,7 +49,7 @@ export type RunContainerArgs = {
45
49
  export interface ContainerOrchestrationInterface {
46
50
  teeRuntime?: string;
47
51
  name: string;
48
- host: string;
52
+ socketPath: string;
49
53
  getConnection(): any;
50
54
  pullImage(image: string, auth?: DockerAuth, controller?: AbortController, force?: boolean): Promise<void>;
51
55
  hasImage(image: string): Promise<boolean>;
@@ -70,6 +74,12 @@ export interface ContainerOrchestrationInterface {
70
74
  getContainersByLabels(labels: string[], options?: {
71
75
  all?: boolean;
72
76
  }): Promise<Container[]>;
77
+ /** The same selection as {@link getContainersByLabels}, as listings. */
78
+ listContainersByLabels(labels: string[], options?: {
79
+ all?: boolean;
80
+ }): Promise<ContainerInfo[]>;
81
+ /** A handle on a container by id or name; no round trip. */
82
+ getContainer(id: string): Container;
73
83
  /**
74
84
  * The host-side path behind a path the node sees. The node may run in a
75
85
  * container with the runtime socket mounted elsewhere, and the daemon
@@ -83,6 +93,5 @@ export interface ContainerOrchestrationInterface {
83
93
  isContainerExited(id: string): Promise<boolean>;
84
94
  doesContainerExist(id: string): Promise<boolean>;
85
95
  healthy(): Promise<ReturnedStatus>;
86
- check(): Promise<string>;
87
- getProtocol(): string;
96
+ check(): Promise<void>;
88
97
  }
@@ -4,6 +4,8 @@
4
4
  */
5
5
  export declare const JOB_LABEL = "com.nosana.job";
6
6
  export declare const OP_LABEL = "com.nosana.op";
7
+ /** The operation's execution group (its id when the job set none). */
8
+ export declare const GROUP_LABEL = "com.nosana.group";
7
9
  export declare const OP_INDEX_LABEL = "com.nosana.opIndex";
8
10
  export declare const SERVICE_LABEL = "com.nosana.service";
9
11
  /** Job and operation ids become container names and label values. */
@@ -4,6 +4,8 @@
4
4
  */
5
5
  export const JOB_LABEL = 'com.nosana.job';
6
6
  export const OP_LABEL = 'com.nosana.op';
7
+ /** The operation's execution group (its id when the job set none). */
8
+ export const GROUP_LABEL = 'com.nosana.group';
7
9
  export const OP_INDEX_LABEL = 'com.nosana.opIndex';
8
10
  export const SERVICE_LABEL = 'com.nosana.service';
9
11
  /** Job and operation ids become container names and label values. */
@@ -1,12 +1,12 @@
1
1
  import { Container } from 'dockerode';
2
- import type { NodeRepository } from '../../../repository/NodeRepository.js';
2
+ import type { OrchestrationHooks } from '../hooks.js';
3
3
  import { DockerContainerOrchestration } from '../docker/index.js';
4
4
  import { RunContainerArgs } from '../interface.js';
5
5
  import { RequestInit, Response } from 'undici';
6
6
  export declare class PodmanContainerOrchestration extends DockerContainerOrchestration {
7
- private api;
7
+ private dispatcher;
8
8
  name: string;
9
- constructor(server: string, gpu: string, teeRuntime?: string, repository?: NodeRepository);
9
+ constructor(socketPath: string, gpu: string, teeRuntime?: string, hooks?: OrchestrationHooks);
10
10
  libPodAPICall(path: string, options: RequestInit): Promise<Response>;
11
11
  createNetwork(_name: string, controller?: AbortController): Promise<void>;
12
12
  runFlowContainer(image: string, args: RunContainerArgs): Promise<Container>;
@@ -1,27 +1,17 @@
1
1
  import { DockerContainerOrchestration } from '../docker/index.js';
2
2
  import { createPodmanRunOptions } from './utils/createPodmanRunOptions.js';
3
3
  import { fetch, Agent } from 'undici';
4
- import { liveAbortSignal } from '../../../utils/liveAbortSignal.js';
4
+ import { liveAbortSignal } from '../utils/liveAbortSignal.js';
5
+ const LIBPOD_API = 'http://localhost/v4.5.0/libpod';
5
6
  export class PodmanContainerOrchestration extends DockerContainerOrchestration {
6
- constructor(server, gpu, teeRuntime, repository) {
7
- super(server, gpu, teeRuntime, repository);
7
+ constructor(socketPath, gpu, teeRuntime, hooks = {}) {
8
+ super(socketPath, gpu, teeRuntime, hooks);
8
9
  this.name = 'podman';
9
- if (this.protocol === 'socket') {
10
- this.api = `http://localhost/v4.5.0/libpod`;
11
- }
12
- else {
13
- this.api = `${this.protocol}://${this.host}:${this.port}/v4.5.0/libpod`;
14
- }
10
+ // The parent's copy, with `~` expanded; undici takes the path literally.
11
+ this.dispatcher = new Agent({ connect: { socketPath: this.socketPath } });
15
12
  }
16
13
  async libPodAPICall(path, options) {
17
- if (this.protocol === 'socket') {
18
- options.dispatcher = new Agent({
19
- connect: {
20
- socketPath: this.host,
21
- },
22
- });
23
- }
24
- return fetch(`${this.api}${path}`, options);
14
+ return fetch(`${LIBPOD_API}${path}`, { ...options, dispatcher: this.dispatcher });
25
15
  }
26
16
  async createNetwork(_name, controller) {
27
17
  try {
@@ -7,6 +7,10 @@ import type { RunContainerArgs } from '../../interface.js';
7
7
  * @returns
8
8
  */
9
9
  export declare function createPodmanRunOptions(image: string, args: RunContainerArgs, gpuOption: string): {
10
+ pidns?: {
11
+ nsmode: "host";
12
+ } | undefined;
13
+ no_new_privileges?: boolean | undefined;
10
14
  resource_limits?: {
11
15
  pids: {
12
16
  limit: number;
@@ -51,6 +55,10 @@ export declare function createPodmanRunOptions(image: string, args: RunContainer
51
55
  options: string[];
52
56
  }[] | undefined;
53
57
  } | {
58
+ pidns?: {
59
+ nsmode: "host";
60
+ } | undefined;
61
+ no_new_privileges?: boolean | undefined;
54
62
  resource_limits?: {
55
63
  pids: {
56
64
  limit: number;
@@ -96,6 +104,10 @@ export declare function createPodmanRunOptions(image: string, args: RunContainer
96
104
  options: string[];
97
105
  }[] | undefined;
98
106
  } | {
107
+ pidns?: {
108
+ nsmode: "host";
109
+ } | undefined;
110
+ no_new_privileges?: boolean | undefined;
99
111
  resource_limits?: {
100
112
  pids: {
101
113
  limit: number;
@@ -1,4 +1,4 @@
1
- import { ifStringCastToArray } from '../../../../utils/utils.js';
1
+ import { ifStringCastToArray } from '../../utils/ifStringCastToArray.js';
2
2
  // These extra types are required because interface.ts
3
3
  function parseRestartPolicy(restart_policy) {
4
4
  if (typeof restart_policy === 'string') {
@@ -24,7 +24,7 @@ function parseRestartPolicy(restart_policy) {
24
24
  * @returns
25
25
  */
26
26
  export function createPodmanRunOptions(image, args, gpuOption) {
27
- const { name, cmd, gpu, volumes, env, aliases, work_dir, entrypoint, restart_policy, labels, security, runtime, } = args;
27
+ const { name, cmd, gpu, volumes, env, aliases, work_dir, entrypoint, restart_policy, labels, security, runtime, pid_mode, } = args;
28
28
  const devices = gpu
29
29
  ? gpuOption === 'all'
30
30
  ? [
@@ -79,5 +79,7 @@ export function createPodmanRunOptions(image, args, gpuOption) {
79
79
  ...(security?.pidsLimit
80
80
  ? { resource_limits: { pids: { limit: security.pidsLimit } } }
81
81
  : {}),
82
+ ...(security?.noNewPrivileges ? { no_new_privileges: true } : {}),
83
+ ...(pid_mode ? { pidns: { nsmode: pid_mode } } : {}),
82
84
  };
83
85
  }
@@ -0,0 +1,3 @@
1
+ import type { OrchestrationHooks } from './hooks.js';
2
+ import type { ContainerOrchestrationInterface } from './interface.js';
3
+ export declare function selectContainerOrchestrationProvider(provider: string, socketPath: string, gpu: string, teeRuntime: string | undefined, hooks?: OrchestrationHooks): ContainerOrchestrationInterface;
@@ -1,14 +1,14 @@
1
1
  import { DockerContainerOrchestration } from './docker/index.js';
2
2
  import { PodmanContainerOrchestration } from './podman/index.js';
3
- export function selectContainerOrchestrationProvider(provider, url, gpu, teeRuntime, repository) {
3
+ export function selectContainerOrchestrationProvider(provider, socketPath, gpu, teeRuntime, hooks = {}) {
4
4
  if (provider !== 'docker' && teeRuntime === 'SEV-SNP') {
5
5
  throw new Error('Custom runtimes are only supported with the Docker provider.');
6
6
  }
7
7
  switch (provider) {
8
8
  case 'podman':
9
- return new PodmanContainerOrchestration(url, gpu, teeRuntime, repository);
9
+ return new PodmanContainerOrchestration(socketPath, gpu, teeRuntime, hooks);
10
10
  case 'docker':
11
11
  default:
12
- return new DockerContainerOrchestration(url, gpu, teeRuntime, repository);
12
+ return new DockerContainerOrchestration(socketPath, gpu, teeRuntime, hooks);
13
13
  }
14
14
  }
@@ -0,0 +1,15 @@
1
+ /** COPY of the node's provider/types.ts ReturnedStatus; the package has no node imports. */
2
+ export type ReturnedStatus<T = undefined> = {
3
+ status: true;
4
+ result?: T;
5
+ error?: never;
6
+ } | {
7
+ status: false;
8
+ error: Error | unknown;
9
+ result?: never;
10
+ };
11
+ declare global {
12
+ interface Error {
13
+ eventType?: string;
14
+ }
15
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ /** COPY of the node's utils.ifStringCastToArray. */
2
+ export declare const ifStringCastToArray: (value: string | string[]) => string[];
@@ -0,0 +1,2 @@
1
+ /** COPY of the node's utils.ifStringCastToArray. */
2
+ export const ifStringCastToArray = (value) => typeof value === 'string' ? [value] : value;
@@ -12,6 +12,12 @@ export type configType = {
12
12
  api: {
13
13
  port: number;
14
14
  };
15
+ jobRuntime: {
16
+ containerImage: string;
17
+ };
18
+ resourceManager: {
19
+ containerImage: string;
20
+ };
15
21
  minDiskSpace: number;
16
22
  network: 'devnet' | 'mainnet';
17
23
  };