@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.
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
@@ -0,0 +1,98 @@
1
+ import { JOB_LABEL, OP_LABEL, isSafeIdentifier, } from './containerOrchestration/labels.js';
2
+ const DEFAULT_TERMINAL_COLS = 80;
3
+ const DEFAULT_TERMINAL_ROWS = 24;
4
+ const MAX_TERMINAL_DIMENSION = 1000;
5
+ /**
6
+ * Finds the single running container that belongs to a job (optionally scoped
7
+ * to a specific operation) and opens an interactive PTY exec session into it.
8
+ *
9
+ * Mirrors the selection sshgw performs in the sidecar, but runs in-process
10
+ * over the node's own runtime socket.
11
+ */
12
+ export async function createJobTerminalExec(orchestration, jobId, options = {}) {
13
+ assertSafeSelector(jobId, 'job id');
14
+ if (options.op !== undefined) {
15
+ assertSafeSelector(options.op, 'operation selector');
16
+ }
17
+ const container = await findJobContainer(orchestration, jobId, options.op);
18
+ const exec = await container.exec({
19
+ AttachStdin: true,
20
+ AttachStdout: true,
21
+ AttachStderr: true,
22
+ Tty: true,
23
+ // Pick bash when it is available, otherwise fall back to a POSIX shell.
24
+ Cmd: [
25
+ '/bin/sh',
26
+ '-c',
27
+ 'if command -v bash >/dev/null 2>&1; then exec bash; else exec sh; fi',
28
+ ],
29
+ });
30
+ const stream = (await exec.start({
31
+ hijack: true,
32
+ stdin: true,
33
+ Tty: true,
34
+ }));
35
+ const resize = async ({ cols, rows }) => {
36
+ await exec
37
+ .resize({
38
+ h: clampDimension(rows, DEFAULT_TERMINAL_ROWS),
39
+ w: clampDimension(cols, DEFAULT_TERMINAL_COLS),
40
+ })
41
+ .catch(() => undefined);
42
+ };
43
+ await resize(options);
44
+ return {
45
+ stream,
46
+ resize,
47
+ inspectExitCode: () => inspectExitCode(exec),
48
+ close: () => {
49
+ try {
50
+ stream.destroy();
51
+ }
52
+ catch {
53
+ // best effort
54
+ }
55
+ },
56
+ };
57
+ }
58
+ async function findJobContainer(orchestration, jobId, op) {
59
+ // Requiring the operation label keeps the job's SSH sidecars, which carry
60
+ // the job label too, out of the selection.
61
+ const containers = await orchestration.getContainersByLabels([
62
+ `${JOB_LABEL}=${jobId}`,
63
+ op ? `${OP_LABEL}=${op}` : OP_LABEL,
64
+ ]);
65
+ if (containers.length === 0) {
66
+ throw new Error(`No running container found for job ${jobId}`);
67
+ }
68
+ if (containers.length > 1) {
69
+ throw new Error(`Multiple running containers found for job ${jobId}; specify an operation`);
70
+ }
71
+ return containers[0];
72
+ }
73
+ async function inspectExitCode(exec) {
74
+ try {
75
+ const info = await exec.inspect();
76
+ if (info.Running)
77
+ return null;
78
+ return typeof info.ExitCode === 'number' ? info.ExitCode : null;
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ function clampDimension(value, fallback) {
85
+ if (!Number.isFinite(value) || value === undefined)
86
+ return fallback;
87
+ const rounded = Math.floor(value);
88
+ if (rounded < 1)
89
+ return 1;
90
+ if (rounded > MAX_TERMINAL_DIMENSION)
91
+ return MAX_TERMINAL_DIMENSION;
92
+ return rounded;
93
+ }
94
+ function assertSafeSelector(value, what) {
95
+ if (!isSafeIdentifier(value)) {
96
+ throw new Error(`Invalid ${what} for terminal access: ${value}`);
97
+ }
98
+ }
@@ -1,6 +1,6 @@
1
1
  import type { LowSync } from 'lowdb';
2
2
  import type { Flow, OpState, FlowState } from '@nosana/sdk';
3
- import type { NodeDb, ResourceHistory, VolumeResource } from '../db/index.js';
3
+ import type { NodeDb, ResourceHistory, SshKeyGrant, VolumeResource } from '../db/index.js';
4
4
  export declare class NodeRepository {
5
5
  private db;
6
6
  constructor(db: LowSync<NodeDb>);
@@ -15,6 +15,10 @@ export declare class NodeRepository {
15
15
  * depends on this.
16
16
  */
17
17
  private persist;
18
+ getSshKeyGrants(id: string): SshKeyGrant[] | undefined;
19
+ setSshKeyGrants(id: string, keys: SshKeyGrant[]): void;
20
+ getSshHostKey(id: string): string | undefined;
21
+ setSshHostKey(id: string, privateKeyPem: string): void;
18
22
  getFlow(id: string): Flow;
19
23
  getFlows(): {
20
24
  [key: string]: Flow;
@@ -66,6 +66,22 @@ export class NodeRepository {
66
66
  });
67
67
  }
68
68
  }
69
+ getSshKeyGrants(id) {
70
+ return this.db.data.sshKeyGrants?.[id];
71
+ }
72
+ setSshKeyGrants(id, keys) {
73
+ this.db.data.sshKeyGrants ??= {};
74
+ this.db.data.sshKeyGrants[id] = keys.map((key) => ({ ...key }));
75
+ this.persist();
76
+ }
77
+ getSshHostKey(id) {
78
+ return this.db.data.sshHostKeys?.[id];
79
+ }
80
+ setSshHostKey(id, privateKeyPem) {
81
+ this.db.data.sshHostKeys ??= {};
82
+ this.db.data.sshHostKeys[id] = privateKeyPem;
83
+ this.persist();
84
+ }
69
85
  getFlow(id) {
70
86
  return this.db.data.flows[id];
71
87
  }
@@ -78,6 +94,10 @@ export class NodeRepository {
78
94
  }
79
95
  deleteflow(id) {
80
96
  delete this.db.data.flows[id];
97
+ if (this.db.data.sshKeyGrants)
98
+ delete this.db.data.sshKeyGrants[id];
99
+ if (this.db.data.sshHostKeys)
100
+ delete this.db.data.sshHostKeys[id];
81
101
  this.persist();
82
102
  }
83
103
  addOpstate(id, opstate) {
@@ -2,10 +2,9 @@ import { DB } from '../../../NodeManager/db/index.js';
2
2
  import { ResourceManager } from '../../../NodeManager/node/resource/resourceManager.js';
3
3
  import { selectContainerOrchestrationProvider } from '../../../NodeManager/provider/containerOrchestration/selectContainerOrchestration.js';
4
4
  import { NodeRepository } from '../../../NodeManager/repository/NodeRepository.js';
5
- import { resolveProvider } from '../sharedOptions/index.js';
6
5
  export * from './action.js';
7
6
  export async function pruneResources(options) {
8
- resolveProvider(options);
7
+ options.provider = process.argv.some(arg => arg === '--docker') ? 'docker' : 'podman';
9
8
  try {
10
9
  const db = new DB(options.config).db;
11
10
  const repository = new NodeRepository(db);
@@ -13,11 +13,8 @@ import { generateDeploymentEndpointsTable } from './generateDeploymentEndpointsT
13
13
  import { generateRandomId } from '../../../NodeManager/node/utils/generateRandomId.js';
14
14
  import { getSDK } from '../../../NodeManager/sdk/index.js';
15
15
  import { createLoggingProxy } from '../../../NodeManager/monitoring/proxy/loggingProxy.js';
16
- import { PodmanManager } from '../../../NodeManager/provider/podmanManager/PodmanManager.js';
17
- import { DockerContainerOrchestration } from '../../../NodeManager/provider/containerOrchestration/docker/index.js';
18
- import { resolveProvider } from '../sharedOptions/index.js';
19
16
  export async function runJob(jobDefinitionFile, options) {
20
- resolveProvider(options);
17
+ options.provider = process.argv.some(arg => arg === '--docker') ? 'docker' : 'podman';
21
18
  try {
22
19
  const sdk = getSDK();
23
20
  const jobDefinition = await resolveJobDefinition(options, jobDefinitionFile);
@@ -35,16 +32,6 @@ export async function runJob(jobDefinitionFile, options) {
35
32
  const containerOrchestration = selectContainerOrchestrationProvider(options.provider, options.podman, options.gpu, options.trustedExecutionRuntime);
36
33
  const resourceManager = new ResourceManager(containerOrchestration, repository);
37
34
  const provider = new Provider(containerOrchestration, repository, resourceManager);
38
- /**
39
- * the podman container, when this node owns it: brought up before the job
40
- * needs it, and repaired under the job should a container fail to start on
41
- * the GPU runtime
42
- */
43
- if (options.managedPodman) {
44
- const podmanManager = new PodmanManager(new DockerContainerOrchestration(options.dockerSocket, options.gpu), options.config, containerOrchestration);
45
- provider.gpuRuntime = podmanManager;
46
- await podmanManager.ensure();
47
- }
48
35
  const logger = new ConsoleLogger(false);
49
36
  logger.addObserver();
50
37
  const job = generateRandomId(32);
@@ -1,6 +1,6 @@
1
1
  import { Argument, Command, Option } from 'commander';
2
2
  import { runJob } from './action.js';
3
- import { gpuOption, managedPodmanOption, verboseOption, walletOption, } from '../sharedOptions/index.js';
3
+ import { gpuOption, verboseOption, walletOption } from '../sharedOptions/index.js';
4
4
  export * from './action.js';
5
5
  export const runNodeCommand = new Command('run')
6
6
  .description('Run Job Definition File')
@@ -8,7 +8,6 @@ export const runNodeCommand = new Command('run')
8
8
  .addOption(new Option('--url <url>', 'Url path for the JSON flow'))
9
9
  .addOption(new Option('-c, --config <path>', 'Config path (to store the flows database and other config)').default('~/.nosana/'))
10
10
  .addOption(new Option('--docker, --podman <URI>', 'Podman/Docker connection URI').default('~/.nosana/podman/podman.sock'))
11
- .addOption(managedPodmanOption)
12
11
  .addOption(walletOption)
13
12
  .addOption(gpuOption)
14
13
  .addOption(verboseOption)
@@ -6,4 +6,3 @@ export * from './--port.js';
6
6
  export * from './--gpu.js';
7
7
  export * from './--tee-runtime.js';
8
8
  export * from './--verbose.js';
9
- export * from './--managed-podman.js';
@@ -7,4 +7,3 @@ export * from './--port.js';
7
7
  export * from './--gpu.js';
8
8
  export * from './--tee-runtime.js';
9
9
  export * from './--verbose.js';
10
- export * from './--managed-podman.js';
@@ -2,11 +2,10 @@ import 'rpc-websockets/dist/lib/client.js';
2
2
  import NodeManager from '../../../NodeManager/index.js';
3
3
  import { requestExit } from '../../../exitCodes.js';
4
4
  import { validateCLIVersion } from '../../../version/index.js';
5
- import { resolveProvider } from '../sharedOptions/index.js';
6
5
  /** Failures no restart resolves: the node reports them and stops. */
7
6
  const TERMINAL_ERRORS = ['NodeBannedError', 'NodeNotQualifiedError'];
8
7
  export async function startNode(market, options) {
9
- resolveProvider(options);
8
+ options.provider = process.argv.some(arg => arg === '--docker') ? 'docker' : 'podman';
10
9
  const nodeManager = new NodeManager(options);
11
10
  while (true) {
12
11
  try {
@@ -1,6 +1,6 @@
1
1
  import { Command, Option } from 'commander';
2
2
  import { startNode } from './action.js';
3
- import { networkOption, portOption, rpcOption, walletOption, verboseOption, gpuOption, teeRuntimeOption, managedPodmanOption, } from '../sharedOptions/index.js';
3
+ import { networkOption, portOption, rpcOption, walletOption, verboseOption, gpuOption, teeRuntimeOption, } from '../sharedOptions/index.js';
4
4
  export * from './action.js';
5
5
  export const startNodeCommand = new Command('start')
6
6
  .description('Start Nosana Node')
@@ -9,7 +9,6 @@ export const startNodeCommand = new Command('start')
9
9
  .addOption(rpcOption)
10
10
  .addOption(walletOption)
11
11
  .addOption(new Option('--docker, --podman <URI>', 'Podman/Docker connection URI').default('~/.nosana/podman/podman.sock'))
12
- .addOption(managedPodmanOption)
13
12
  .addOption(new Option('-c, --config <path>', 'Config path (to store the flows database and other config)').default('~/.nosana/'))
14
13
  .addOption(teeRuntimeOption)
15
14
  .addOption(gpuOption)
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "@nosana/node",
3
- "version": "1.1.63-rc",
3
+ "version": "1.1.65-rc",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@nosana/node",
9
- "version": "1.1.63-rc",
9
+ "version": "1.1.65-rc",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "@coral-xyz/anchor": "^0.28.1-beta.1",
13
13
  "@inquirer/prompts": "^4.3.0",
14
14
  "@nosana/ipfs": "^2.2.6",
15
15
  "@nosana/sdk": "^0.4.87",
16
+ "@nosana/ssh": "^2.13.1",
16
17
  "@solana/web3.js": "^1.78.0",
17
18
  "bn.js": "^5.2.1",
18
19
  "chalk": "^5.3.0",
@@ -1865,6 +1866,31 @@
1865
1866
  "typescript": ">=4.8.0 <5.8.0"
1866
1867
  }
1867
1868
  },
1869
+ "node_modules/@nosana/ssh": {
1870
+ "version": "2.13.1",
1871
+ "resolved": "https://registry.npmjs.org/@nosana/ssh/-/ssh-2.13.1.tgz",
1872
+ "integrity": "sha512-rxLcFt+GJ51plZcMnH31v8EN9iPzeKuG3CAQIztDjgZNfAuLsp5uq5udOf94ImL+Bxoe7qPzQgUi1t6sVXLAxA==",
1873
+ "license": "MIT",
1874
+ "dependencies": {
1875
+ "@nosana/types": "2.13.0",
1876
+ "tweetnacl": "^1.0.3"
1877
+ },
1878
+ "engines": {
1879
+ "node": ">=20.18.0"
1880
+ }
1881
+ },
1882
+ "node_modules/@nosana/types": {
1883
+ "version": "2.13.0",
1884
+ "resolved": "https://registry.npmjs.org/@nosana/types/-/types-2.13.0.tgz",
1885
+ "integrity": "sha512-5lHB+9wpjHoZZunbzLl211QJFI8dmcM7XmUoGXnQLSszqiJS4QlC5VBOuztLRJEdNi3r8P/5pdGzZ5gsDUTNqA==",
1886
+ "license": "MIT",
1887
+ "dependencies": {
1888
+ "typia": "^10.0.2"
1889
+ },
1890
+ "engines": {
1891
+ "node": ">=20.18.0"
1892
+ }
1893
+ },
1868
1894
  "node_modules/@redocly/ajv": {
1869
1895
  "version": "8.11.0",
1870
1896
  "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nosana/node",
3
- "version": "1.1.63-rc",
3
+ "version": "1.1.65-rc",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "nosana-node": "./dist/src/index.js"
@@ -15,7 +15,7 @@
15
15
  "generate": "npm run generate:host-manager",
16
16
  "generate:host-manager": "openapi-typescript https://host-manager.k8s.dev.nos.ci/swagger/json -o ./src/NodeManager/clients/hostManager/schema.d.ts",
17
17
  "test": "APP_ENV=development DOCKER_CERT_PATH= vitest run",
18
- "ci:test": "APP_ENV=development DOCKER_CERT_PATH= vitest run --coverage --reporter=junit --outputFile=./test-report.xml",
18
+ "ci:test": "APP_ENV=development DOCKER_CERT_PATH= vitest run --coverage --reporter=verbose --reporter=junit --outputFile.junit=./test-report.xml",
19
19
  "test:watch": "APP_ENV=development DOCKER_CERT_PATH= vitest",
20
20
  "test:coverage": "APP_ENV=development DOCKER_CERT_PATH= vitest run --coverage",
21
21
  "publish:next": "npm run build && npm publish --tag=next --access public",
@@ -74,6 +74,7 @@
74
74
  "@inquirer/prompts": "^4.3.0",
75
75
  "@nosana/ipfs": "^2.2.6",
76
76
  "@nosana/sdk": "^0.4.87",
77
+ "@nosana/ssh": "^2.13.1",
77
78
  "@solana/web3.js": "^1.78.0",
78
79
  "bn.js": "^5.2.1",
79
80
  "chalk": "^5.3.0",
@@ -1,3 +0,0 @@
1
- import WebSocket from 'ws';
2
- import { WSBody } from '../../types/index.js';
3
- export declare function verifyWSMiddleware(ws: WebSocket, headers: string, body: WSBody, nextFunction: (ws: WebSocket, headers: string, body: WSBody) => void): Promise<void>;
@@ -1,20 +0,0 @@
1
- import { getSDK } from '../../../../sdk/index.js';
2
- export async function verifyWSMiddleware(ws, headers, body, nextFunction) {
3
- const sdk = getSDK();
4
- const jobId = body.jobAddress;
5
- if (!jobId) {
6
- ws.close(1008, 'Expected body to contain jobAddress.');
7
- return;
8
- }
9
- try {
10
- const job = await sdk.jobs.get(jobId);
11
- if (!job) {
12
- ws.close(1008, `Could not find job with id ${jobId}`);
13
- return;
14
- }
15
- nextFunction(ws, headers, body);
16
- }
17
- catch (error) {
18
- ws.close(4001, `Unauthorized Request: ${error.message}`);
19
- }
20
- }
@@ -1,3 +0,0 @@
1
- import WebSocket from 'ws';
2
- import { WSBody } from '../../types/index.js';
3
- export declare function verifyWSNodeOrJobOwnerSignatureMiddleware(ws: WebSocket, headers: string, body: WSBody, nextFunction: (ws: WebSocket, headers: string, body: WSBody) => void): Promise<void>;
@@ -1,33 +0,0 @@
1
- import { PublicKey } from '@solana/web3.js';
2
- import { getSDK } from '../../../../sdk/index.js';
3
- export async function verifyWSNodeOrJobOwnerSignatureMiddleware(ws, headers, body, nextFunction) {
4
- const sdk = getSDK();
5
- const jobId = body.jobAddress;
6
- if (!jobId) {
7
- ws.close(1008, 'Expected body to contain jobAddress.');
8
- return;
9
- }
10
- try {
11
- const job = await sdk.jobs.get(jobId);
12
- if (!job) {
13
- ws.close(1008, `Could not find job with id ${jobId}`);
14
- return;
15
- }
16
- try {
17
- if (sdk.authorization.validate(headers, {
18
- publicKey: new PublicKey(job.project),
19
- }) ||
20
- sdk.authorization.validate(headers, {
21
- publicKey: sdk.solana.wallet.publicKey,
22
- })) {
23
- await nextFunction(ws, headers, body);
24
- }
25
- }
26
- catch (_) {
27
- ws.close(4001, 'Unauthorized Request');
28
- }
29
- }
30
- catch (error) {
31
- ws.close(4001, `Unauthorized Request: ${error.message}`);
32
- }
33
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * What the provider needs of the node's podman manager, when the node
3
- * manages podman.
4
- */
5
- export interface GpuRuntime {
6
- /** Resolves once no recreate is in flight: operation starts wait on this. */
7
- ready(): Promise<void>;
8
- /** Whether podman's own watcher has found the GPU runtime stale. */
9
- isStale(): boolean;
10
- /** Replace the runtime; the operations running on it are restarted around it. */
11
- recreate(reason: string): Promise<void>;
12
- }
13
- export declare function isStaleGpuRuntimeError(error: unknown): boolean;
@@ -1,27 +0,0 @@
1
- /**
2
- * How a container start fails when the podman container's copy of the host's
3
- * NVIDIA driver no longer matches the host. Recreating podman is what fixes
4
- * it, so these are told apart from failures of the container itself.
5
- *
6
- * - At create, podman could not resolve or inject the CDI spec:
7
- * "setting up CDI devices: unresolvable CDI devices nvidia.com/gpu=all".
8
- * - At start, the OCI runtime could not bind a driver file the spec names,
9
- * because the host replaced it: "crun: error mounting
10
- * `/usr/lib/x86_64-linux-gnu/libcuda.so.550.120.04` to rootfs ...: No such
11
- * file or directory". The file need not carry "nvidia" in its name.
12
- * - The toolkit's own hook failed: "nvidia-container-cli: mount error".
13
- *
14
- * An image that happens to be NVIDIA's ("nvidia/cuda: image not known") is
15
- * not a match: that is the container's failure, not the runtime's.
16
- */
17
- const STALE_GPU_RUNTIME = [
18
- /setting up CDI devices|unresolvable CDI devices/i,
19
- /nvidia-container-(cli|runtime)/i,
20
- /\b(mount|mounting|stat)\b[^\n]*?(\/dev\/nvidia|libnvidia|libcuda|libnv|nvidia-smi|nvidia-persistenced)/i,
21
- ];
22
- export function isStaleGpuRuntimeError(error) {
23
- const message = error instanceof Error
24
- ? error.message
25
- : String(error?.message ?? error);
26
- return STALE_GPU_RUNTIME.some((pattern) => pattern.test(message));
27
- }
@@ -1,72 +0,0 @@
1
- import type { ContainerCreateOptions } from 'dockerode';
2
- import type { DockerContainerOrchestration } from '../containerOrchestration/docker/index.js';
3
- import type { ContainerOrchestrationInterface } from '../containerOrchestration/interface.js';
4
- /** A CDI event line, as the podman container logs them, that asks for a recreate. */
5
- export declare function isStaleCdiEvent(line: string): boolean;
6
- /**
7
- * Owns the podman container the node uses as its provider, over the host's
8
- * docker socket. The node runs beside podman rather than inside it, so it can
9
- * recreate podman without taking itself down.
10
- *
11
- * The container is a snapshot of the host's NVIDIA driver, taken when it is
12
- * created; once the host replaces the driver underneath it, GPU containers
13
- * inside it can no longer start. Recreating the container is the fix, and it
14
- * is cheap: the image cache and the resource volumes live on docker volumes
15
- * that survive it. What does not survive is whatever was running inside, so a
16
- * recreate restarts the running operations around itself, and operation
17
- * starts wait on `ready()` while it is under way.
18
- */
19
- export declare class PodmanManager {
20
- /** The host's docker, which also runs the node's API proxy. */
21
- readonly hostDocker: DockerContainerOrchestration;
22
- private podman;
23
- private docker;
24
- private configDir;
25
- private staleReason?;
26
- private recreating?;
27
- /** What a create needs and nothing changes afterwards, settled once. */
28
- private prepared?;
29
- /**
30
- * Created by this process and not used by any operation since: it already
31
- * carries the host's current driver, so a recreate would change nothing.
32
- */
33
- private fresh;
34
- constructor(
35
- /** The host's docker, which also runs the node's API proxy. */
36
- hostDocker: DockerContainerOrchestration, configDir: string, podman: ContainerOrchestrationInterface);
37
- /**
38
- * Note a fault the podman container's watcher reported. Acted on by the
39
- * next recreate, and while the node is queued it leaves the queue for one.
40
- */
41
- markStale(reason: string): void;
42
- isStale(): boolean;
43
- /** Resolves once no recreate is in flight: operation starts wait on this. */
44
- ready(): Promise<void>;
45
- /** The podman container exists, runs, and answers on its socket. */
46
- ensure(): Promise<void>;
47
- /**
48
- * Remove the podman container and bring up a fresh one; the volumes stay.
49
- * Operations running inside are restarted around it: aborted now, while
50
- * their containers can still be stopped, and relaunched once the new
51
- * socket answers, which `ready()` holds them for. A recreate already under
52
- * way is joined rather than repeated, and a container this process created
53
- * that nothing has used since is left as it is, unless it was marked stale.
54
- */
55
- recreate(reason: string): Promise<void>;
56
- private doRecreate;
57
- private ensureRunning;
58
- private create;
59
- /** @returns the host path of the node's config dir, for the bind mount. */
60
- private prepare;
61
- private waitForSocket;
62
- private ensureVolume;
63
- private ensureNetwork;
64
- /**
65
- * Where the node's config dir lives on the host, read off this container's
66
- * own mounts: podman needs it at the host path, and the node only knows its
67
- * own.
68
- */
69
- private hostConfigDir;
70
- }
71
- /** Mirrors the podman container the hosted start script creates. */
72
- export declare function podmanContainerConfig(image: string, hostConfigDir: string): ContainerCreateOptions;