@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.
- package/README.md +2 -2
- package/dist/.env +2 -0
- package/dist/package.json +9 -6
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/createDockerRunOptions.d.ts +1 -1
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/createDockerRunOptions.js +5 -1
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/dockerExtended/index.d.ts +3 -0
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/dockerExtended/index.js +12 -5
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/index.d.ts +11 -10
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/index.js +18 -37
- package/dist/packages/container-orchestrator/src/hooks.d.ts +32 -0
- package/dist/packages/container-orchestrator/src/index.d.ts +17 -0
- package/dist/packages/container-orchestrator/src/index.js +16 -0
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/interface.d.ts +14 -5
- package/dist/packages/container-orchestrator/src/interface.js +1 -0
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/labels.d.ts +2 -0
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/labels.js +2 -0
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/podman/index.d.ts +3 -3
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/podman/index.js +7 -17
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/podman/utils/createPodmanRunOptions.d.ts +12 -0
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/podman/utils/createPodmanRunOptions.js +4 -2
- package/dist/packages/container-orchestrator/src/selectContainerOrchestration.d.ts +3 -0
- package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/selectContainerOrchestration.js +3 -3
- package/dist/packages/container-orchestrator/src/types.d.ts +15 -0
- package/dist/packages/container-orchestrator/src/types.js +1 -0
- package/dist/packages/container-orchestrator/src/utils/ifStringCastToArray.d.ts +2 -0
- package/dist/packages/container-orchestrator/src/utils/ifStringCastToArray.js +2 -0
- package/dist/src/NodeManager/configs/configs.d.ts +6 -0
- package/dist/src/NodeManager/configs/configs.js +6 -0
- package/dist/src/NodeManager/index.js +7 -0
- package/dist/src/NodeManager/node/Node.d.ts +5 -0
- package/dist/src/NodeManager/node/Node.js +23 -2
- package/dist/src/NodeManager/node/health/healthHandler.d.ts +1 -1
- package/dist/src/NodeManager/node/resource/definition/index.d.ts +0 -1
- package/dist/src/NodeManager/node/resource/definition/index.js +0 -1
- package/dist/src/NodeManager/node/resource/helpers/createHFArgs.d.ts +1 -1
- package/dist/src/NodeManager/node/resource/helpers/createHFArgs.js +2 -3
- package/dist/src/NodeManager/node/resource/helpers/createOllamaArgs.d.ts +1 -1
- package/dist/src/NodeManager/node/resource/helpers/createOllamaArgs.js +2 -3
- package/dist/src/NodeManager/node/resource/helpers/createS3Args.d.ts +1 -1
- package/dist/src/NodeManager/node/resource/helpers/createS3Args.js +3 -3
- package/dist/src/NodeManager/node/resource/image/imageManager.d.ts +1 -1
- package/dist/src/NodeManager/node/resource/resourceManager.d.ts +1 -1
- package/dist/src/NodeManager/node/resource/volume/volumeManager.d.ts +1 -1
- package/dist/src/NodeManager/node/resource/volume/volumeManager.js +11 -8
- package/dist/src/NodeManager/node/task/TaskManager.js +25 -0
- package/dist/src/NodeManager/provider/ContainerStateManager.js +1 -1
- package/dist/src/NodeManager/provider/Provider.d.ts +34 -10
- package/dist/src/NodeManager/provider/Provider.js +217 -93
- package/dist/src/NodeManager/provider/orchestrationHooks.d.ts +8 -0
- package/dist/src/NodeManager/provider/orchestrationHooks.js +13 -0
- package/dist/src/NodeManager/provider/sshSidecar.d.ts +2 -2
- package/dist/src/NodeManager/provider/sshSidecar.js +2 -2
- package/dist/src/NodeManager/provider/types.d.ts +0 -9
- package/dist/src/NodeManager/provider/webTerminal.d.ts +1 -1
- package/dist/src/NodeManager/provider/webTerminal.js +1 -1
- package/dist/src/NodeManager/utils/utils.d.ts +1 -7
- package/dist/src/NodeManager/utils/utils.js +1 -7
- package/dist/src/cli/commands/prune/action.js +3 -2
- package/dist/src/cli/commands/prune/command.js +1 -1
- package/dist/src/cli/commands/run/action.js +3 -2
- package/dist/src/cli/commands/run/command.js +1 -1
- package/dist/src/cli/commands/start/command.js +1 -1
- package/npm-shrinkwrap.json +21 -2
- package/package.json +9 -6
- package/dist/src/NodeManager/provider/containerOrchestration/docker/utils/createServerObject.d.ts +0 -5
- package/dist/src/NodeManager/provider/containerOrchestration/docker/utils/createServerObject.js +0 -15
- package/dist/src/NodeManager/provider/containerOrchestration/docker/utils/deadline.d.ts +0 -1
- package/dist/src/NodeManager/provider/containerOrchestration/docker/utils/deadline.js +0 -15
- package/dist/src/NodeManager/provider/containerOrchestration/selectContainerOrchestration.d.ts +0 -3
- /package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/utils/repoTagsContainsImage.d.ts +0 -0
- /package/dist/{src/NodeManager/provider/containerOrchestration → packages/container-orchestrator/src}/docker/utils/repoTagsContainsImage.js +0 -0
- /package/dist/{src/NodeManager/provider/containerOrchestration/interface.js → packages/container-orchestrator/src/hooks.js} +0 -0
- /package/dist/{src/NodeManager → packages/container-orchestrator/src}/utils/liveAbortSignal.d.ts +0 -0
- /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 <
|
|
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 <
|
|
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.
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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 =
|
|
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 ${
|
|
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 {
|
|
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 '
|
|
6
|
+
import { ReturnedStatus } from '../types.js';
|
|
7
7
|
export declare class DockerContainerOrchestration implements ContainerOrchestrationInterface {
|
|
8
|
-
protected
|
|
8
|
+
protected hooks: OrchestrationHooks;
|
|
9
9
|
docker: DockerExtended;
|
|
10
|
-
|
|
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(
|
|
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<
|
|
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 '
|
|
4
|
+
import { liveAbortSignal } from '../utils/liveAbortSignal.js';
|
|
7
5
|
export class DockerContainerOrchestration {
|
|
8
|
-
constructor(
|
|
9
|
-
this.
|
|
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 (
|
|
16
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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 '
|
|
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
|
-
|
|
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<
|
|
87
|
-
getProtocol(): string;
|
|
96
|
+
check(): Promise<void>;
|
|
88
97
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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 {
|
|
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
|
|
7
|
+
private dispatcher;
|
|
8
8
|
name: string;
|
|
9
|
-
constructor(
|
|
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 '
|
|
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(
|
|
7
|
-
super(
|
|
7
|
+
constructor(socketPath, gpu, teeRuntime, hooks = {}) {
|
|
8
|
+
super(socketPath, gpu, teeRuntime, hooks);
|
|
8
9
|
this.name = 'podman';
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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 '
|
|
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,
|
|
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(
|
|
9
|
+
return new PodmanContainerOrchestration(socketPath, gpu, teeRuntime, hooks);
|
|
10
10
|
case 'docker':
|
|
11
11
|
default:
|
|
12
|
-
return new DockerContainerOrchestration(
|
|
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 {};
|