@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 +1 -1
- package/dist/src/NodeManager/node/task/TaskManager.js +6 -0
- package/dist/src/NodeManager/provider/Provider.js +132 -139
- package/dist/src/NodeManager/provider/containerOrchestration/docker/index.d.ts +0 -1
- package/dist/src/NodeManager/provider/containerOrchestration/docker/index.js +0 -4
- package/dist/src/NodeManager/provider/containerOrchestration/interface.d.ts +0 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/package.json
CHANGED
|
@@ -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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
await this.containerOrchestration.
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
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>;
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nosana/node",
|
|
3
|
-
"version": "1.1.
|
|
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.
|
|
9
|
+
"version": "1.1.43-rc",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@coral-xyz/anchor": "^0.28.1-beta.1",
|