@nosana/node 1.1.41-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.41-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 ||
@@ -131,151 +131,150 @@ export class Provider {
131
131
  try {
132
132
  emitter.emit('start');
133
133
  let frpcContainer;
134
- let container = await this.containerOrchestration.getContainer(op.id);
135
- const exist = await this.containerOrchestration.doesContainerExist(op.id);
136
- const exited = await this.containerOrchestration.isContainerExited(op.id);
137
- if (exist && !exited && container.id) {
138
- stateManager = new ContainerStateManager(container, controller, emitter, op.args.restart_policy);
139
- await stateManager.startMonitoring();
140
- // Reused container is already running (no pull needed) — start the
141
- // op-execution timeout clock here too.
142
- emitter.emit('container:started');
143
- }
144
- else {
145
- await this.containerOrchestration.pullImage(op.args.image, op.args.authentication?.docker, controller);
146
- if (!op.args.authentication?.docker) {
147
- this.resourceManager.images.setImage(op.args.image);
148
- }
149
- const index = getOpStateIndex(flow.jobDefinition.ops, op.id);
150
- const name = flow.id + '-' + index;
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)
181
- : undefined;
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,
193
- });
194
- }
195
- else {
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);
134
+ // A resumed flow re-runs operations that may already have a container, and
135
+ // the name below is the only handle on it.
136
+ const index = getOpStateIndex(flow.jobDefinition.ops, op.id);
137
+ const name = flow.id + '-' + index;
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);
218
145
  }
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
146
+ }
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);
150
+ }
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)
223
181
  : 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
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,
245
193
  });
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);
251
- }
252
- }
253
- 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 info = await container.inspect();
259
- emitter.emit('updateOpState', { providerId: container.id });
260
- if (teeRuntime !== 'SEV-SNP' && info.NetworkSettings?.Networks["NOSANA_GATEWAY"].IPAddress) {
261
- emitter.emit('setContainerInternalIp', info.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
194
  }
270
195
  else {
271
- 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);
272
251
  }
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();
278
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();
279
278
  const info = await promiseTimeoutWrapper(container.inspect({
280
279
  abortSignal: liveAbortSignal(controller.signal),
281
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.41-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.41-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.41-rc",
3
+ "version": "1.1.43-rc",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "nosana-node": "./dist/src/index.js"