@nosana/node 1.1.42-rc → 1.1.43-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/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nosana/node",
3
- "version": "1.1.42-rc",
3
+ "version": "1.1.43-rc",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "nosana-node": "./dist/src/index.js"
@@ -307,6 +307,12 @@ export default class TaskManager {
307
307
  });
308
308
  // Queue each operation in the group for execution
309
309
  for (const id of p.ops) {
310
+ // Only an operation still pending has yet to run: one released by
311
+ // the operation it depends on runs in that operation's group, which
312
+ // can be earlier than this one.
313
+ if (this.operationStatus.get(id) !== OperationProgressStatuses.PENDING) {
314
+ continue;
315
+ }
310
316
  const dependencyContext = this.dependecyMap.get(id);
311
317
  const dependencies = dependencyContext.dependencies || [];
312
318
  const depsSatisfied = dependencies.length === 0 ||
@@ -135,153 +135,146 @@ export class Provider {
135
135
  // the name below is the only handle on it.
136
136
  const index = getOpStateIndex(flow.jobDefinition.ops, op.id);
137
137
  const name = flow.id + '-' + index;
138
- let container = await this.containerOrchestration.getContainer(name);
139
- const exist = await this.containerOrchestration.doesContainerExist(name);
140
- const exited = await this.containerOrchestration.isContainerExited(name);
141
- // An exited container holds its name, and offers nothing to resume from.
142
- if (exist && exited) {
143
- await this.containerOrchestration.stopAndDeleteContainer(name);
138
+ // An operation runs the same way whether or not the flow it belongs to has
139
+ // already run, so whatever a previous run left behind under these names is
140
+ // removed rather than resumed from. The runtime refuses to create a
141
+ // container whose name is still taken, and all three are recreated below.
142
+ for (const leftover of [name, `frpc-${name}`, `bridge-${name}`]) {
143
+ if (await this.containerOrchestration.doesContainerExist(leftover)) {
144
+ await this.containerOrchestration.stopAndDeleteContainer(leftover);
145
+ }
144
146
  }
145
- if (exist && !exited && container.id) {
146
- stateManager = new ContainerStateManager(container, controller, emitter, op.args.restart_policy);
147
- await stateManager.startMonitoring();
148
- // Reused container is already running (no pull needed) — start the
149
- // op-execution timeout clock here too.
150
- emitter.emit('container:started');
147
+ await this.containerOrchestration.pullImage(op.args.image, op.args.authentication?.docker, controller);
148
+ if (!op.args.authentication?.docker) {
149
+ this.resourceManager.images.setImage(op.args.image);
151
150
  }
152
- else {
153
- await this.containerOrchestration.pullImage(op.args.image, op.args.authentication?.docker, controller);
154
- if (!op.args.authentication?.docker) {
155
- this.resourceManager.images.setImage(op.args.image);
156
- }
157
- const volumes = getVolumes(op.args, flow);
158
- const gpu = getGpu(op.args, flow);
159
- const entrypoint = getEntrypoint(op.args, flow);
160
- const work_dir = getWorkingDir(op.args, flow);
161
- const cmd = parseOpArgsCmd(op.args.cmd);
162
- const env = {
163
- ...getGlobalEnv(flow),
164
- ...op.args.env,
165
- NOSANA_ID: flow.id,
166
- };
167
- const aliases = getAliases(op.args);
168
- const networks = {};
169
- let idMaps = new Map();
170
- const ports = getExposePorts(op);
171
- networks[name] = {};
172
- await this.containerOrchestration.createNetwork(name, controller);
173
- if (isOpExposed(op)) {
174
- await this.containerOrchestration.pullImage(this.frpcImage, undefined, controller);
175
- this.resourceManager.images.setImage(this.frpcImage);
176
- /**
177
- * because of the introduction of deployment_id(load balancing key) instead of using flow.id we use deployment_id for more
178
- * deterministic url generation
179
- */
180
- const config = NodeConfigsSingleton.getInstance();
181
- const isLoadBalanced = !!flow.jobDefinition.deployment_id;
182
- const deploymentHash = isLoadBalanced
183
- ? // @ts-ignore
184
- config.options.isNodeRun
185
- ? flow.jobDefinition.deployment_id
186
- : createHash(`${flow.jobDefinition.deployment_id}:${flow.project}`, 45)
187
- : undefined;
188
- const { proxies, idMap } = generateProxies(flow.id, op, index, ports, name, op.id, deploymentHash);
189
- idMaps = idMap;
190
- frpcContainer = await this.containerOrchestration.runFlowContainer(this.frpcImage, this.generateFrpcContainerConfig(name, networks, flow, proxies, isLoadBalanced, deploymentHash));
191
- if (op.args.private) {
192
- this.repository.updateflowStateSecret(flow.id, {
193
- [flow.id]: generateUrlSecretObject(idMap, op.id),
194
- urlmode: 'private',
195
- });
196
- emitter.emit('flow:secrets-updated', {
197
- flowId: flow.id,
198
- opId: op.id,
199
- });
200
- }
201
- else {
202
- const newSecret = generateUrlSecretObject(idMap, op.id);
203
- const currentSecrets = this.repository.getFlowSecret(flow.id, flow.id) || {};
204
- const mergedSecrets = {
205
- ...currentSecrets,
206
- ...newSecret,
207
- };
208
- this.repository.updateflowStateSecret(flow.id, {
209
- [flow.id]: mergedSecrets,
210
- urlmode: 'public',
211
- });
212
- emitter.emit('flow:secrets-updated', {
213
- flowId: flow.id,
214
- opId: op.id,
215
- });
216
- }
217
- }
218
- if (op.args.resources) {
219
- await this.containerOrchestration.pullImage(s3HelperImage, undefined, controller);
220
- this.resourceManager.images.setImage(s3HelperImage);
221
- // transformCollections has already resolved any SpreadMarker objects by this point
222
- const resourceVolumes = await this.resourceManager.getResourceVolumes(op.args.resources ?? [], controller, { id: flow.id, opIndex: index });
223
- volumes.push(...resourceVolumes);
224
- }
225
- // The op only declares that it wants a TEE; the node's configured
226
- // runtime decides how that is fulfilled.
227
- const teeRuntime = op.args.trusted_execution_env
228
- ? this.containerOrchestration.teeRuntime
151
+ const volumes = getVolumes(op.args, flow);
152
+ const gpu = getGpu(op.args, flow);
153
+ const entrypoint = getEntrypoint(op.args, flow);
154
+ const work_dir = getWorkingDir(op.args, flow);
155
+ const cmd = parseOpArgsCmd(op.args.cmd);
156
+ const env = {
157
+ ...getGlobalEnv(flow),
158
+ ...op.args.env,
159
+ NOSANA_ID: flow.id,
160
+ };
161
+ const aliases = getAliases(op.args);
162
+ const networks = {};
163
+ let idMaps = new Map();
164
+ const ports = getExposePorts(op);
165
+ networks[name] = {};
166
+ await this.containerOrchestration.createNetwork(name, controller);
167
+ if (isOpExposed(op)) {
168
+ await this.containerOrchestration.pullImage(this.frpcImage, undefined, controller);
169
+ this.resourceManager.images.setImage(this.frpcImage);
170
+ /**
171
+ * because of the introduction of deployment_id(load balancing key) instead of using flow.id we use deployment_id for more
172
+ * deterministic url generation
173
+ */
174
+ const config = NodeConfigsSingleton.getInstance();
175
+ const isLoadBalanced = !!flow.jobDefinition.deployment_id;
176
+ const deploymentHash = isLoadBalanced
177
+ ? // @ts-ignore
178
+ config.options.isNodeRun
179
+ ? flow.jobDefinition.deployment_id
180
+ : createHash(`${flow.jobDefinition.deployment_id}:${flow.project}`, 45)
229
181
  : undefined;
230
- const createContainerOptions = {
231
- name,
232
- cmd,
233
- env,
234
- networks,
235
- requires_network_mode: isOpExposed(op),
236
- gpu,
237
- entrypoint,
238
- work_dir,
239
- volumes,
240
- aliases,
241
- restart_policy: op.args.restart_policy,
242
- runtime: teeRuntime,
243
- };
244
- if (teeRuntime === 'SEV-SNP') {
245
- await this.containerOrchestration.pullImage("busybox");
246
- await this.resourceManager.images.setImage("busybox");
247
- const bridgeContainer = await this.containerOrchestration.runFlowContainer("busybox", {
248
- name: `bridge-${name}`,
249
- cmd: ['sleep', 'infinity'],
250
- aliases: createContainerOptions.aliases
182
+ const { proxies, idMap } = generateProxies(flow.id, op, index, ports, name, op.id, deploymentHash);
183
+ idMaps = idMap;
184
+ frpcContainer = await this.containerOrchestration.runFlowContainer(this.frpcImage, this.generateFrpcContainerConfig(name, networks, flow, proxies, isLoadBalanced, deploymentHash));
185
+ if (op.args.private) {
186
+ this.repository.updateflowStateSecret(flow.id, {
187
+ [flow.id]: generateUrlSecretObject(idMap, op.id),
188
+ urlmode: 'private',
189
+ });
190
+ emitter.emit('flow:secrets-updated', {
191
+ flowId: flow.id,
192
+ opId: op.id,
251
193
  });
252
- delete createContainerOptions.aliases;
253
- createContainerOptions.bind_network_to_container = `container:bridge-${name}`;
254
- const bridgeInfo = await bridgeContainer.inspect();
255
- if (bridgeInfo.NetworkSettings?.Networks["NOSANA_GATEWAY"].IPAddress) {
256
- emitter.emit('setContainerInternalIp', bridgeInfo.NetworkSettings.Networks["NOSANA_GATEWAY"].IPAddress);
257
- }
258
- }
259
- container = await this.containerOrchestration.runFlowContainer(op.args.image ?? flow.jobDefinition.global?.image, createContainerOptions);
260
- // The container is now running (image pull is already done). This is the
261
- // point the op-execution timeout clock starts from, so pull time isn't
262
- // counted against it.
263
- emitter.emit('container:started');
264
- const info = await container.inspect();
265
- emitter.emit('updateOpState', { providerId: container.id });
266
- if (teeRuntime !== 'SEV-SNP' && info.NetworkSettings?.Networks["NOSANA_GATEWAY"].IPAddress) {
267
- emitter.emit('setContainerInternalIp', info.NetworkSettings.Networks["NOSANA_GATEWAY"].IPAddress);
268
- }
269
- stateManager = new ContainerStateManager(container, controller, emitter, op.args.restart_policy);
270
- await stateManager.startMonitoring();
271
- if (isOpExposed(op)) {
272
- exposedPortHealthCheck = new ExposedPortHealthCheck(flow.id, frpcContainer, emitter, name);
273
- exposedPortHealthCheck.addExposedPortsMap(idMaps);
274
- exposedPortHealthCheck.startServiceExposedUrlHealthCheck();
275
194
  }
276
195
  else {
277
- emitter.emit('healthcheck:startup:success');
196
+ const newSecret = generateUrlSecretObject(idMap, op.id);
197
+ const currentSecrets = this.repository.getFlowSecret(flow.id, flow.id) || {};
198
+ const mergedSecrets = {
199
+ ...currentSecrets,
200
+ ...newSecret,
201
+ };
202
+ this.repository.updateflowStateSecret(flow.id, {
203
+ [flow.id]: mergedSecrets,
204
+ urlmode: 'public',
205
+ });
206
+ emitter.emit('flow:secrets-updated', {
207
+ flowId: flow.id,
208
+ opId: op.id,
209
+ });
210
+ }
211
+ }
212
+ if (op.args.resources) {
213
+ await this.containerOrchestration.pullImage(s3HelperImage, undefined, controller);
214
+ this.resourceManager.images.setImage(s3HelperImage);
215
+ // transformCollections has already resolved any SpreadMarker objects by this point
216
+ const resourceVolumes = await this.resourceManager.getResourceVolumes(op.args.resources ?? [], controller, { id: flow.id, opIndex: index });
217
+ volumes.push(...resourceVolumes);
218
+ }
219
+ // The op only declares that it wants a TEE; the node's configured
220
+ // runtime decides how that is fulfilled.
221
+ const teeRuntime = op.args.trusted_execution_env
222
+ ? this.containerOrchestration.teeRuntime
223
+ : undefined;
224
+ const createContainerOptions = {
225
+ name,
226
+ cmd,
227
+ env,
228
+ networks,
229
+ requires_network_mode: isOpExposed(op),
230
+ gpu,
231
+ entrypoint,
232
+ work_dir,
233
+ volumes,
234
+ aliases,
235
+ restart_policy: op.args.restart_policy,
236
+ runtime: teeRuntime,
237
+ };
238
+ if (teeRuntime === 'SEV-SNP') {
239
+ await this.containerOrchestration.pullImage("busybox");
240
+ await this.resourceManager.images.setImage("busybox");
241
+ const bridgeContainer = await this.containerOrchestration.runFlowContainer("busybox", {
242
+ name: `bridge-${name}`,
243
+ cmd: ['sleep', 'infinity'],
244
+ aliases: createContainerOptions.aliases
245
+ });
246
+ delete createContainerOptions.aliases;
247
+ createContainerOptions.bind_network_to_container = `container:bridge-${name}`;
248
+ const bridgeInfo = await bridgeContainer.inspect();
249
+ if (bridgeInfo.NetworkSettings?.Networks["NOSANA_GATEWAY"].IPAddress) {
250
+ emitter.emit('setContainerInternalIp', bridgeInfo.NetworkSettings.Networks["NOSANA_GATEWAY"].IPAddress);
278
251
  }
279
- await stateManager.waitForExit();
280
- // `wait()` reporting the exit and the log stream finishing its final
281
- // flush are independent; drain the stream before results are read off
282
- // the logs so the last line (e.g. the op's result) isn't lost.
283
- await stateManager.waitForLogsDrained();
284
252
  }
253
+ const container = await this.containerOrchestration.runFlowContainer(op.args.image ?? flow.jobDefinition.global?.image, createContainerOptions);
254
+ // The container is now running (image pull is already done). This is the
255
+ // point the op-execution timeout clock starts from, so pull time isn't
256
+ // counted against it.
257
+ emitter.emit('container:started');
258
+ const startedInfo = await container.inspect();
259
+ emitter.emit('updateOpState', { providerId: container.id });
260
+ if (teeRuntime !== 'SEV-SNP' && startedInfo.NetworkSettings?.Networks["NOSANA_GATEWAY"].IPAddress) {
261
+ emitter.emit('setContainerInternalIp', startedInfo.NetworkSettings.Networks["NOSANA_GATEWAY"].IPAddress);
262
+ }
263
+ stateManager = new ContainerStateManager(container, controller, emitter, op.args.restart_policy);
264
+ await stateManager.startMonitoring();
265
+ if (isOpExposed(op)) {
266
+ exposedPortHealthCheck = new ExposedPortHealthCheck(flow.id, frpcContainer, emitter, name);
267
+ exposedPortHealthCheck.addExposedPortsMap(idMaps);
268
+ exposedPortHealthCheck.startServiceExposedUrlHealthCheck();
269
+ }
270
+ else {
271
+ emitter.emit('healthcheck:startup:success');
272
+ }
273
+ await stateManager.waitForExit();
274
+ // `wait()` reporting the exit and the log stream finishing its final
275
+ // flush are independent; drain the stream before results are read off
276
+ // the logs so the last line (e.g. the op's result) isn't lost.
277
+ await stateManager.waitForLogsDrained();
285
278
  const info = await promiseTimeoutWrapper(container.inspect({
286
279
  abortSignal: liveAbortSignal(controller.signal),
287
280
  }), 360, controller);
@@ -32,7 +32,6 @@ export declare class DockerContainerOrchestration implements ContainerOrchestrat
32
32
  getRawVolume(name: string): Promise<Volume>;
33
33
  deleteVolume(name: string, controller?: AbortController): Promise<void>;
34
34
  healthy(): Promise<ReturnedStatus>;
35
- getContainer(id: string): Promise<Container>;
36
35
  getProtocol(): string;
37
36
  setupContainerAbortListener(containerId: string, controller: AbortController): void;
38
37
  runContainer(args: ContainerCreateOptions, controller?: AbortController): Promise<Container>;
@@ -193,10 +193,6 @@ export class DockerContainerOrchestration {
193
193
  return { status: false, error };
194
194
  }
195
195
  }
196
- async getContainer(id) {
197
- const container = this.docker.getContainer(id);
198
- return container;
199
- }
200
196
  getProtocol() {
201
197
  return this.protocol;
202
198
  }
@@ -43,7 +43,6 @@ export interface ContainerOrchestrationInterface {
43
43
  deleteVolume(name: string, controller?: AbortController): Promise<void>;
44
44
  getContainerByName(name: string): Promise<Container | undefined>;
45
45
  getContainersByName(names: string[]): Promise<Container[]>;
46
- getContainer(id: string): Promise<Container>;
47
46
  runContainer(args: ContainerCreateOptions, controller?: AbortController): Promise<Container>;
48
47
  runFlowContainer(image: string, args: RunContainerArgs): Promise<Container>;
49
48
  stopContainer(id: string, controller?: AbortController): Promise<void>;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@nosana/node",
3
- "version": "1.1.42-rc",
3
+ "version": "1.1.43-rc",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@nosana/node",
9
- "version": "1.1.42-rc",
9
+ "version": "1.1.43-rc",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "@coral-xyz/anchor": "^0.28.1-beta.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nosana/node",
3
- "version": "1.1.42-rc",
3
+ "version": "1.1.43-rc",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "nosana-node": "./dist/src/index.js"