@nosana/node 1.1.63-rc → 1.1.65-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 +0 -1
- package/dist/.env +2 -4
- package/dist/.env.dev +1 -1
- package/dist/.env.prd +1 -1
- package/dist/package.json +3 -2
- package/dist/src/NodeManager/configs/NodeConfigs.d.ts +1 -0
- package/dist/src/NodeManager/configs/NodeConfigs.js +4 -1
- package/dist/src/NodeManager/configs/configs.d.ts +0 -3
- package/dist/src/NodeManager/configs/configs.js +2 -5
- package/dist/src/NodeManager/db/index.d.ts +23 -0
- package/dist/src/NodeManager/index.js +9 -4
- package/dist/src/NodeManager/monitoring/reportPodmanDiagnostics.d.ts +2 -3
- package/dist/src/NodeManager/monitoring/reportPodmanDiagnostics.js +8 -12
- package/dist/src/NodeManager/node/Node.d.ts +0 -3
- package/dist/src/NodeManager/node/Node.js +0 -32
- package/dist/src/NodeManager/node/api/ApiHandler.js +16 -3
- package/dist/src/NodeManager/node/api/middlewares/index.d.ts +0 -2
- package/dist/src/NodeManager/node/api/middlewares/index.js +0 -2
- package/dist/src/NodeManager/node/api/middlewares/rest/verifyJobOwnerSignatureMiddleware.js +1 -0
- package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSJobOwnerSignatureMiddleware.js +1 -0
- package/dist/src/NodeManager/node/api/routes/get/node-info.js +9 -7
- package/dist/src/NodeManager/node/api/routes/post/index.d.ts +1 -0
- package/dist/src/NodeManager/node/api/routes/post/index.js +1 -0
- package/dist/src/NodeManager/node/api/routes/post/ssh-authorize.d.ts +17 -0
- package/dist/src/NodeManager/node/api/routes/post/ssh-authorize.js +53 -0
- package/dist/src/NodeManager/node/api/routes/wss/index.d.ts +2 -0
- package/dist/src/NodeManager/node/api/routes/wss/index.js +2 -0
- package/dist/src/NodeManager/node/api/routes/wss/log.js +2 -1
- package/dist/src/NodeManager/node/api/routes/wss/terminal/terminal.d.ts +21 -0
- package/dist/src/NodeManager/node/api/routes/wss/terminal/terminal.js +165 -0
- package/dist/src/NodeManager/node/api/routes/wss/terminal/terminalAuthorization.d.ts +18 -0
- package/dist/src/NodeManager/node/api/routes/wss/terminal/terminalAuthorization.js +61 -0
- package/dist/src/NodeManager/node/market/hostManager.d.ts +3 -3
- package/dist/src/NodeManager/node/resource/helpers/isAuthGatedResource.d.ts +9 -0
- package/dist/src/NodeManager/node/resource/helpers/isAuthGatedResource.js +11 -0
- package/dist/src/NodeManager/node/resource/image/imageManager.d.ts +14 -1
- package/dist/src/NodeManager/node/resource/image/imageManager.js +30 -4
- package/dist/src/NodeManager/node/resource/resourceManager.d.ts +1 -1
- package/dist/src/NodeManager/node/resource/resourceManager.js +21 -5
- package/dist/src/NodeManager/node/resource/volume/volumeManager.d.ts +31 -4
- package/dist/src/NodeManager/node/resource/volume/volumeManager.js +69 -12
- package/dist/src/NodeManager/node/task/TaskManagerRegistry.d.ts +0 -8
- package/dist/src/NodeManager/node/task/TaskManagerRegistry.js +0 -22
- package/dist/src/NodeManager/provider/Provider.d.ts +30 -14
- package/dist/src/NodeManager/provider/Provider.js +157 -116
- package/dist/src/NodeManager/provider/SshAccessManager.d.ts +49 -0
- package/dist/src/NodeManager/provider/SshAccessManager.js +148 -0
- package/dist/src/NodeManager/provider/containerOrchestration/docker/createDockerRunOptions.d.ts +1 -1
- package/dist/src/NodeManager/provider/containerOrchestration/docker/createDockerRunOptions.js +6 -3
- package/dist/src/NodeManager/provider/containerOrchestration/docker/index.d.ts +5 -1
- package/dist/src/NodeManager/provider/containerOrchestration/docker/index.js +40 -2
- package/dist/src/NodeManager/provider/containerOrchestration/interface.d.ts +28 -1
- package/dist/src/NodeManager/provider/containerOrchestration/labels.d.ts +10 -0
- package/dist/src/NodeManager/provider/containerOrchestration/labels.js +12 -0
- package/dist/src/NodeManager/provider/containerOrchestration/podman/index.js +14 -9
- package/dist/src/NodeManager/provider/containerOrchestration/podman/utils/createPodmanRunOptions.d.ts +39 -0
- package/dist/src/NodeManager/provider/containerOrchestration/podman/utils/createPodmanRunOptions.js +16 -2
- package/dist/src/NodeManager/provider/sshSidecar.d.ts +66 -0
- package/dist/src/NodeManager/provider/sshSidecar.js +176 -0
- package/dist/src/NodeManager/provider/webTerminal.d.ts +24 -0
- package/dist/src/NodeManager/provider/webTerminal.js +98 -0
- package/dist/src/NodeManager/repository/NodeRepository.d.ts +5 -1
- package/dist/src/NodeManager/repository/NodeRepository.js +20 -0
- package/dist/src/cli/commands/prune/action.js +1 -2
- package/dist/src/cli/commands/run/action.js +1 -14
- package/dist/src/cli/commands/run/command.js +1 -2
- package/dist/src/cli/commands/sharedOptions/index.d.ts +0 -1
- package/dist/src/cli/commands/sharedOptions/index.js +0 -1
- package/dist/src/cli/commands/start/action.js +1 -2
- package/dist/src/cli/commands/start/command.js +1 -2
- package/npm-shrinkwrap.json +28 -2
- package/package.json +3 -2
- package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSMiddleware.d.ts +0 -3
- package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSMiddleware.js +0 -20
- package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSNodeOrJobOwnerSignatureMiddleware.d.ts +0 -3
- package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSNodeOrJobOwnerSignatureMiddleware.js +0 -33
- package/dist/src/NodeManager/provider/gpuRuntime.d.ts +0 -13
- package/dist/src/NodeManager/provider/gpuRuntime.js +0 -27
- package/dist/src/NodeManager/provider/podmanManager/PodmanManager.d.ts +0 -72
- package/dist/src/NodeManager/provider/podmanManager/PodmanManager.js +0 -238
- package/dist/src/cli/commands/sharedOptions/--managed-podman.d.ts +0 -16
- package/dist/src/cli/commands/sharedOptions/--managed-podman.js +0 -25
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import os from 'os';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { configs } from '../../configs/configs.js';
|
|
4
|
-
import { sleep } from '../../utils/utils.js';
|
|
5
|
-
import { TaskManagerRegistry } from '../../node/task/TaskManagerRegistry.js';
|
|
6
|
-
const PODMAN_CONTAINER_NAME = 'podman';
|
|
7
|
-
/**
|
|
8
|
-
* The docker network the node, the podman container and the node's API proxy
|
|
9
|
-
* share. It carries the name the docker provider hard-codes into every
|
|
10
|
-
* container it starts (see createDockerRunOptions), which is how the API
|
|
11
|
-
* proxy lands on it and reaches the node by container name. It is not the
|
|
12
|
-
* NOSANA_GATEWAY podman creates inside itself for the jobs, hence the
|
|
13
|
-
* different subnet.
|
|
14
|
-
*/
|
|
15
|
-
const HOST_NETWORK = 'NOSANA_GATEWAY';
|
|
16
|
-
const CACHE_VOLUME = 'podman-cache';
|
|
17
|
-
const SOCKET_VOLUME = 'podman-socket';
|
|
18
|
-
const SOCKET_WAIT_S = 60;
|
|
19
|
-
const SOCKET_POLL_S = 2;
|
|
20
|
-
/**
|
|
21
|
-
* Events the podman container's CDI watcher writes when a GPU path it needs
|
|
22
|
-
* is gone and cannot be repaired from in there: only recreating the container
|
|
23
|
-
* re-injects the host's current driver.
|
|
24
|
-
*/
|
|
25
|
-
const STALE_CDI_EVENTS = ['unrepairable', 'repair-failed'];
|
|
26
|
-
/** A CDI event line, as the podman container logs them, that asks for a recreate. */
|
|
27
|
-
export function isStaleCdiEvent(line) {
|
|
28
|
-
try {
|
|
29
|
-
return STALE_CDI_EVENTS.includes(JSON.parse(line).event);
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Owns the podman container the node uses as its provider, over the host's
|
|
37
|
-
* docker socket. The node runs beside podman rather than inside it, so it can
|
|
38
|
-
* recreate podman without taking itself down.
|
|
39
|
-
*
|
|
40
|
-
* The container is a snapshot of the host's NVIDIA driver, taken when it is
|
|
41
|
-
* created; once the host replaces the driver underneath it, GPU containers
|
|
42
|
-
* inside it can no longer start. Recreating the container is the fix, and it
|
|
43
|
-
* is cheap: the image cache and the resource volumes live on docker volumes
|
|
44
|
-
* that survive it. What does not survive is whatever was running inside, so a
|
|
45
|
-
* recreate restarts the running operations around itself, and operation
|
|
46
|
-
* starts wait on `ready()` while it is under way.
|
|
47
|
-
*/
|
|
48
|
-
export class PodmanManager {
|
|
49
|
-
constructor(
|
|
50
|
-
/** The host's docker, which also runs the node's API proxy. */
|
|
51
|
-
hostDocker, configDir, podman) {
|
|
52
|
-
this.hostDocker = hostDocker;
|
|
53
|
-
this.podman = podman;
|
|
54
|
-
/**
|
|
55
|
-
* Created by this process and not used by any operation since: it already
|
|
56
|
-
* carries the host's current driver, so a recreate would change nothing.
|
|
57
|
-
*/
|
|
58
|
-
this.fresh = false;
|
|
59
|
-
this.docker = hostDocker.docker;
|
|
60
|
-
this.configDir = configDir.replace(/^~/, os.homedir()).replace(/\/+$/, '');
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Note a fault the podman container's watcher reported. Acted on by the
|
|
64
|
-
* next recreate, and while the node is queued it leaves the queue for one.
|
|
65
|
-
*/
|
|
66
|
-
markStale(reason) {
|
|
67
|
-
if (this.staleReason)
|
|
68
|
-
return;
|
|
69
|
-
this.staleReason = reason;
|
|
70
|
-
console.warn(chalk.yellow(`The podman container's GPU mounts are stale (${reason}); it is recreated before the next job.`));
|
|
71
|
-
}
|
|
72
|
-
isStale() {
|
|
73
|
-
return this.staleReason !== undefined;
|
|
74
|
-
}
|
|
75
|
-
/** Resolves once no recreate is in flight: operation starts wait on this. */
|
|
76
|
-
ready() {
|
|
77
|
-
this.fresh = false;
|
|
78
|
-
return this.recreating ?? Promise.resolve();
|
|
79
|
-
}
|
|
80
|
-
/** The podman container exists, runs, and answers on its socket. */
|
|
81
|
-
async ensure() {
|
|
82
|
-
await this.ensureRunning();
|
|
83
|
-
await this.waitForSocket();
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Remove the podman container and bring up a fresh one; the volumes stay.
|
|
87
|
-
* Operations running inside are restarted around it: aborted now, while
|
|
88
|
-
* their containers can still be stopped, and relaunched once the new
|
|
89
|
-
* socket answers, which `ready()` holds them for. A recreate already under
|
|
90
|
-
* way is joined rather than repeated, and a container this process created
|
|
91
|
-
* that nothing has used since is left as it is, unless it was marked stale.
|
|
92
|
-
*/
|
|
93
|
-
recreate(reason) {
|
|
94
|
-
if (this.recreating)
|
|
95
|
-
return this.recreating;
|
|
96
|
-
if (this.fresh && !this.staleReason)
|
|
97
|
-
return Promise.resolve();
|
|
98
|
-
this.recreating = this.doRecreate(reason).finally(() => {
|
|
99
|
-
this.recreating = undefined;
|
|
100
|
-
});
|
|
101
|
-
return this.recreating;
|
|
102
|
-
}
|
|
103
|
-
async doRecreate(reason) {
|
|
104
|
-
console.log(chalk.yellow(`Recreating the podman container: ${reason}`));
|
|
105
|
-
this.staleReason = undefined;
|
|
106
|
-
TaskManagerRegistry.getInstance().restartRunningOperations();
|
|
107
|
-
try {
|
|
108
|
-
await this.docker
|
|
109
|
-
.getContainer(PODMAN_CONTAINER_NAME)
|
|
110
|
-
.remove({ force: true });
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
if (error.statusCode !== 404)
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
await this.ensureRunning();
|
|
117
|
-
await this.waitForSocket();
|
|
118
|
-
}
|
|
119
|
-
async ensureRunning() {
|
|
120
|
-
const container = this.docker.getContainer(PODMAN_CONTAINER_NAME);
|
|
121
|
-
let info;
|
|
122
|
-
try {
|
|
123
|
-
info = await container.inspect();
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
if (error.statusCode !== 404)
|
|
127
|
-
throw error;
|
|
128
|
-
return this.create();
|
|
129
|
-
}
|
|
130
|
-
if (!info.State.Running)
|
|
131
|
-
await container.start();
|
|
132
|
-
}
|
|
133
|
-
async create() {
|
|
134
|
-
const image = configs().podman.containerImage;
|
|
135
|
-
this.prepared ??= this.prepare(image).catch((error) => {
|
|
136
|
-
this.prepared = undefined; // so the next create tries again
|
|
137
|
-
throw error;
|
|
138
|
-
});
|
|
139
|
-
const hostConfigDir = await this.prepared;
|
|
140
|
-
console.log(chalk.cyan(`Creating the podman container from ${image}`));
|
|
141
|
-
const container = await this.docker.createContainer(podmanContainerConfig(image, hostConfigDir));
|
|
142
|
-
await container.start();
|
|
143
|
-
this.fresh = true;
|
|
144
|
-
}
|
|
145
|
-
/** @returns the host path of the node's config dir, for the bind mount. */
|
|
146
|
-
async prepare(image) {
|
|
147
|
-
const [hostConfigDir] = await Promise.all([
|
|
148
|
-
this.hostConfigDir(),
|
|
149
|
-
this.ensureVolume(CACHE_VOLUME),
|
|
150
|
-
this.ensureVolume(SOCKET_VOLUME),
|
|
151
|
-
this.ensureNetwork(),
|
|
152
|
-
this.hostDocker.pullImage(image),
|
|
153
|
-
]);
|
|
154
|
-
return hostConfigDir;
|
|
155
|
-
}
|
|
156
|
-
async waitForSocket() {
|
|
157
|
-
const deadline = Date.now() + SOCKET_WAIT_S * 1000;
|
|
158
|
-
while (Date.now() < deadline) {
|
|
159
|
-
if ((await this.podman.healthy()).status)
|
|
160
|
-
return;
|
|
161
|
-
await sleep(SOCKET_POLL_S);
|
|
162
|
-
}
|
|
163
|
-
throw new Error(`The podman container is running but its socket did not answer within ${SOCKET_WAIT_S}s`);
|
|
164
|
-
}
|
|
165
|
-
async ensureVolume(name) {
|
|
166
|
-
if (!(await this.hostDocker.hasVolume(name))) {
|
|
167
|
-
await this.hostDocker.createVolume(name);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
async ensureNetwork() {
|
|
171
|
-
if (await this.hostDocker.hasNetwork(HOST_NETWORK))
|
|
172
|
-
return;
|
|
173
|
-
await this.docker.createNetwork({
|
|
174
|
-
Name: HOST_NETWORK,
|
|
175
|
-
Driver: 'bridge',
|
|
176
|
-
IPAM: {
|
|
177
|
-
Driver: 'default',
|
|
178
|
-
Config: [{ Subnet: '192.168.102.0/24', Gateway: '192.168.102.1' }],
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Where the node's config dir lives on the host, read off this container's
|
|
184
|
-
* own mounts: podman needs it at the host path, and the node only knows its
|
|
185
|
-
* own.
|
|
186
|
-
*/
|
|
187
|
-
async hostConfigDir() {
|
|
188
|
-
let mounts = [];
|
|
189
|
-
try {
|
|
190
|
-
({ Mounts: mounts } = await this.docker
|
|
191
|
-
.getContainer(os.hostname())
|
|
192
|
-
.inspect());
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
// Reported below, with what is needed rather than what failed.
|
|
196
|
-
}
|
|
197
|
-
const mount = mounts.find((m) => m.Destination.replace(/\/+$/, '') === this.configDir);
|
|
198
|
-
if (!mount?.Source) {
|
|
199
|
-
throw new Error(`Cannot manage podman: the node's config dir ${this.configDir} must be bind-mounted from the host, and the node must run in a docker container whose hostname is its id.`);
|
|
200
|
-
}
|
|
201
|
-
return mount.Source;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
/** Mirrors the podman container the hosted start script creates. */
|
|
205
|
-
export function podmanContainerConfig(image, hostConfigDir) {
|
|
206
|
-
return {
|
|
207
|
-
name: PODMAN_CONTAINER_NAME,
|
|
208
|
-
Image: image,
|
|
209
|
-
Cmd: ['unix:/podman/podman.sock'],
|
|
210
|
-
Env: ['ENABLE_GPU=true', 'NVIDIA_DRIVER_CAPABILITIES=all'],
|
|
211
|
-
NetworkingConfig: { EndpointsConfig: { [HOST_NETWORK]: {} } },
|
|
212
|
-
HostConfig: {
|
|
213
|
-
Privileged: true,
|
|
214
|
-
DeviceRequests: [
|
|
215
|
-
{ Driver: 'nvidia', Count: -1, Capabilities: [['gpu']] },
|
|
216
|
-
],
|
|
217
|
-
Devices: [
|
|
218
|
-
{
|
|
219
|
-
PathOnHost: '/dev/fuse',
|
|
220
|
-
PathInContainer: '/dev/fuse',
|
|
221
|
-
CgroupPermissions: 'rwm',
|
|
222
|
-
},
|
|
223
|
-
],
|
|
224
|
-
Mounts: [
|
|
225
|
-
{ Type: 'volume', Source: CACHE_VOLUME, Target: '/var/lib/containers' },
|
|
226
|
-
{ Type: 'volume', Source: SOCKET_VOLUME, Target: '/podman' },
|
|
227
|
-
{ Type: 'bind', Source: hostConfigDir, Target: '/root/.nosana' },
|
|
228
|
-
{
|
|
229
|
-
Type: 'bind',
|
|
230
|
-
Source: '/run/nvidia-persistenced',
|
|
231
|
-
Target: '/run/nvidia-persistenced',
|
|
232
|
-
BindOptions: { Propagation: 'rslave' },
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
RestartPolicy: { Name: 'unless-stopped' },
|
|
236
|
-
},
|
|
237
|
-
};
|
|
238
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Option } from 'commander';
|
|
2
|
-
/**
|
|
3
|
-
* Where the managed podman answers, inside the node: the podman-socket volume
|
|
4
|
-
* is mounted there, and at /podman in the podman container.
|
|
5
|
-
*/
|
|
6
|
-
export declare const MANAGED_PODMAN_SOCKET = "~/.nosana/podman/podman.sock";
|
|
7
|
-
export declare const managedPodmanOption: Option;
|
|
8
|
-
/**
|
|
9
|
-
* Settles which provider the --docker/--podman URI names. With
|
|
10
|
-
* --managed-podman the URI is the host's docker socket, over which the node
|
|
11
|
-
* creates its podman container; that podman answers on the volume the two
|
|
12
|
-
* share, at the default podman socket path, and runs every job.
|
|
13
|
-
*/
|
|
14
|
-
export declare function resolveProvider(options: {
|
|
15
|
-
[key: string]: any;
|
|
16
|
-
}): void;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Option } from 'commander';
|
|
2
|
-
/**
|
|
3
|
-
* Where the managed podman answers, inside the node: the podman-socket volume
|
|
4
|
-
* is mounted there, and at /podman in the podman container.
|
|
5
|
-
*/
|
|
6
|
-
export const MANAGED_PODMAN_SOCKET = '~/.nosana/podman/podman.sock';
|
|
7
|
-
export const managedPodmanOption = new Option('--managed-podman', 'Run jobs in a podman container the node creates over the docker socket given with --docker, and recreates when its GPU mounts go stale');
|
|
8
|
-
/**
|
|
9
|
-
* Settles which provider the --docker/--podman URI names. With
|
|
10
|
-
* --managed-podman the URI is the host's docker socket, over which the node
|
|
11
|
-
* creates its podman container; that podman answers on the volume the two
|
|
12
|
-
* share, at the default podman socket path, and runs every job.
|
|
13
|
-
*/
|
|
14
|
-
export function resolveProvider(options) {
|
|
15
|
-
if (options.managedPodman) {
|
|
16
|
-
options.provider = 'podman';
|
|
17
|
-
options.dockerSocket = options.podman;
|
|
18
|
-
options.podman = MANAGED_PODMAN_SOCKET;
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
options.provider = process.argv.some((arg) => arg === '--docker')
|
|
22
|
-
? 'docker'
|
|
23
|
-
: 'podman';
|
|
24
|
-
}
|
|
25
|
-
}
|