@nosana/node 1.1.63-rc → 1.1.64-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 (82) hide show
  1. package/README.md +0 -1
  2. package/dist/.env +2 -4
  3. package/dist/.env.dev +1 -1
  4. package/dist/.env.prd +1 -1
  5. package/dist/package.json +3 -2
  6. package/dist/src/NodeManager/configs/NodeConfigs.d.ts +1 -0
  7. package/dist/src/NodeManager/configs/NodeConfigs.js +4 -1
  8. package/dist/src/NodeManager/configs/configs.d.ts +0 -3
  9. package/dist/src/NodeManager/configs/configs.js +2 -5
  10. package/dist/src/NodeManager/db/index.d.ts +23 -0
  11. package/dist/src/NodeManager/index.js +9 -4
  12. package/dist/src/NodeManager/monitoring/reportPodmanDiagnostics.d.ts +2 -3
  13. package/dist/src/NodeManager/monitoring/reportPodmanDiagnostics.js +8 -12
  14. package/dist/src/NodeManager/node/Node.d.ts +0 -3
  15. package/dist/src/NodeManager/node/Node.js +0 -32
  16. package/dist/src/NodeManager/node/api/ApiHandler.js +16 -3
  17. package/dist/src/NodeManager/node/api/middlewares/index.d.ts +0 -2
  18. package/dist/src/NodeManager/node/api/middlewares/index.js +0 -2
  19. package/dist/src/NodeManager/node/api/middlewares/rest/verifyJobOwnerSignatureMiddleware.js +1 -0
  20. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSJobOwnerSignatureMiddleware.js +1 -0
  21. package/dist/src/NodeManager/node/api/routes/get/node-info.js +9 -7
  22. package/dist/src/NodeManager/node/api/routes/post/index.d.ts +1 -0
  23. package/dist/src/NodeManager/node/api/routes/post/index.js +1 -0
  24. package/dist/src/NodeManager/node/api/routes/post/ssh-authorize.d.ts +17 -0
  25. package/dist/src/NodeManager/node/api/routes/post/ssh-authorize.js +53 -0
  26. package/dist/src/NodeManager/node/api/routes/wss/index.d.ts +2 -0
  27. package/dist/src/NodeManager/node/api/routes/wss/index.js +2 -0
  28. package/dist/src/NodeManager/node/api/routes/wss/log.js +2 -1
  29. package/dist/src/NodeManager/node/api/routes/wss/terminal/terminal.d.ts +21 -0
  30. package/dist/src/NodeManager/node/api/routes/wss/terminal/terminal.js +165 -0
  31. package/dist/src/NodeManager/node/api/routes/wss/terminal/terminalAuthorization.d.ts +18 -0
  32. package/dist/src/NodeManager/node/api/routes/wss/terminal/terminalAuthorization.js +61 -0
  33. package/dist/src/NodeManager/node/market/hostManager.d.ts +3 -3
  34. package/dist/src/NodeManager/node/resource/helpers/isAuthGatedResource.d.ts +9 -0
  35. package/dist/src/NodeManager/node/resource/helpers/isAuthGatedResource.js +11 -0
  36. package/dist/src/NodeManager/node/resource/image/imageManager.d.ts +14 -1
  37. package/dist/src/NodeManager/node/resource/image/imageManager.js +30 -4
  38. package/dist/src/NodeManager/node/resource/resourceManager.d.ts +1 -1
  39. package/dist/src/NodeManager/node/resource/resourceManager.js +21 -5
  40. package/dist/src/NodeManager/node/resource/volume/volumeManager.d.ts +31 -4
  41. package/dist/src/NodeManager/node/resource/volume/volumeManager.js +69 -12
  42. package/dist/src/NodeManager/node/task/TaskManagerRegistry.d.ts +0 -8
  43. package/dist/src/NodeManager/node/task/TaskManagerRegistry.js +0 -22
  44. package/dist/src/NodeManager/provider/Provider.d.ts +30 -14
  45. package/dist/src/NodeManager/provider/Provider.js +157 -116
  46. package/dist/src/NodeManager/provider/SshAccessManager.d.ts +49 -0
  47. package/dist/src/NodeManager/provider/SshAccessManager.js +148 -0
  48. package/dist/src/NodeManager/provider/containerOrchestration/docker/createDockerRunOptions.d.ts +1 -1
  49. package/dist/src/NodeManager/provider/containerOrchestration/docker/createDockerRunOptions.js +6 -3
  50. package/dist/src/NodeManager/provider/containerOrchestration/docker/index.d.ts +5 -1
  51. package/dist/src/NodeManager/provider/containerOrchestration/docker/index.js +40 -2
  52. package/dist/src/NodeManager/provider/containerOrchestration/interface.d.ts +28 -1
  53. package/dist/src/NodeManager/provider/containerOrchestration/labels.d.ts +10 -0
  54. package/dist/src/NodeManager/provider/containerOrchestration/labels.js +12 -0
  55. package/dist/src/NodeManager/provider/containerOrchestration/podman/index.js +14 -9
  56. package/dist/src/NodeManager/provider/containerOrchestration/podman/utils/createPodmanRunOptions.d.ts +39 -0
  57. package/dist/src/NodeManager/provider/containerOrchestration/podman/utils/createPodmanRunOptions.js +16 -2
  58. package/dist/src/NodeManager/provider/sshSidecar.d.ts +66 -0
  59. package/dist/src/NodeManager/provider/sshSidecar.js +176 -0
  60. package/dist/src/NodeManager/provider/webTerminal.d.ts +24 -0
  61. package/dist/src/NodeManager/provider/webTerminal.js +98 -0
  62. package/dist/src/NodeManager/repository/NodeRepository.d.ts +5 -1
  63. package/dist/src/NodeManager/repository/NodeRepository.js +20 -0
  64. package/dist/src/cli/commands/prune/action.js +1 -2
  65. package/dist/src/cli/commands/run/action.js +1 -14
  66. package/dist/src/cli/commands/run/command.js +1 -2
  67. package/dist/src/cli/commands/sharedOptions/index.d.ts +0 -1
  68. package/dist/src/cli/commands/sharedOptions/index.js +0 -1
  69. package/dist/src/cli/commands/start/action.js +1 -2
  70. package/dist/src/cli/commands/start/command.js +1 -2
  71. package/npm-shrinkwrap.json +28 -2
  72. package/package.json +3 -2
  73. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSMiddleware.d.ts +0 -3
  74. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSMiddleware.js +0 -20
  75. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSNodeOrJobOwnerSignatureMiddleware.d.ts +0 -3
  76. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSNodeOrJobOwnerSignatureMiddleware.js +0 -33
  77. package/dist/src/NodeManager/provider/gpuRuntime.d.ts +0 -13
  78. package/dist/src/NodeManager/provider/gpuRuntime.js +0 -27
  79. package/dist/src/NodeManager/provider/podmanManager/PodmanManager.d.ts +0 -72
  80. package/dist/src/NodeManager/provider/podmanManager/PodmanManager.js +0 -238
  81. package/dist/src/cli/commands/sharedOptions/--managed-podman.d.ts +0 -16
  82. 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
- }