@kapeta/local-cluster-service 0.9.1 → 0.10.1
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/CHANGELOG.md +14 -0
- package/dist/cjs/src/containerManager.d.ts +4 -8
- package/dist/cjs/src/containerManager.js +69 -64
- package/dist/cjs/src/instanceManager.js +4 -2
- package/dist/cjs/src/operatorManager.js +40 -25
- package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +3 -1
- package/dist/cjs/src/utils/BlockInstanceRunner.js +159 -242
- package/dist/esm/src/containerManager.d.ts +4 -8
- package/dist/esm/src/containerManager.js +69 -64
- package/dist/esm/src/instanceManager.js +4 -2
- package/dist/esm/src/operatorManager.js +42 -27
- package/dist/esm/src/utils/BlockInstanceRunner.d.ts +3 -1
- package/dist/esm/src/utils/BlockInstanceRunner.js +159 -242
- package/package.json +1 -1
- package/src/containerManager.ts +69 -73
- package/src/instanceManager.ts +8 -2
- package/src/operatorManager.ts +52 -26
- package/src/utils/BlockInstanceRunner.ts +203 -272
@@ -7,9 +7,10 @@ import { containerManager, DockerMounts, toLocalBindVolume } from '../containerM
|
|
7
7
|
import { LogData } from './LogData';
|
8
8
|
import EventEmitter from 'events';
|
9
9
|
import { clusterService } from '../clusterService';
|
10
|
-
import { AnyMap, BlockProcessParams,
|
10
|
+
import { AnyMap, BlockProcessParams, InstanceType, ProcessInfo, StringMap } from '../types';
|
11
11
|
import { Container } from 'node-docker-api/lib/container';
|
12
12
|
import { definitionsManager } from '../definitionsManager';
|
13
|
+
import md5 from 'md5';
|
13
14
|
|
14
15
|
const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
|
15
16
|
const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
|
@@ -116,7 +117,7 @@ export class BlockInstanceRunner {
|
|
116
117
|
if (blockUri.version === 'local') {
|
117
118
|
processInfo = await this._startLocalProcess(blockInstance, blockUri, env, assetVersion);
|
118
119
|
} else {
|
119
|
-
processInfo = await this._startDockerProcess(blockInstance, blockUri, env);
|
120
|
+
processInfo = await this._startDockerProcess(blockInstance, blockUri, env, assetVersion);
|
120
121
|
}
|
121
122
|
|
122
123
|
if (portTypes.length > 0) {
|
@@ -169,67 +170,23 @@ export class BlockInstanceRunner {
|
|
169
170
|
}
|
170
171
|
|
171
172
|
const containerName = getBlockInstanceContainerName(blockInstance.id);
|
172
|
-
const logs = new LogData();
|
173
|
-
logs.addLog(`Starting block ${blockInstance.ref}`);
|
174
|
-
let containerInfo = await containerManager.getContainerByName(containerName);
|
175
|
-
let container = containerInfo?.native;
|
176
|
-
|
177
|
-
console.log('Starting dev container', containerName);
|
178
|
-
|
179
|
-
if (containerInfo) {
|
180
|
-
console.log(`Dev container already exists. Deleting...`);
|
181
|
-
try {
|
182
|
-
await containerInfo.remove({
|
183
|
-
force: true,
|
184
|
-
});
|
185
|
-
} catch (e: any) {
|
186
|
-
throw new Error('Failed to delete existing container: ' + e.message);
|
187
|
-
}
|
188
|
-
container = undefined;
|
189
|
-
containerInfo = undefined;
|
190
|
-
}
|
191
|
-
|
192
|
-
logs.addLog(`Creating new container for block: ${containerName}`);
|
193
|
-
console.log('Creating new dev container', containerName, dockerImage);
|
194
|
-
await containerManager.pull(dockerImage);
|
195
|
-
|
196
173
|
const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
|
197
174
|
const dockerOpts = localContainer.options ?? {};
|
198
175
|
const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
|
199
176
|
const workingDir = localContainer.workingDir ? localContainer.workingDir : '/workspace';
|
200
177
|
|
201
|
-
const
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
const portTypes = getProviderPorts(assetVersion);
|
208
|
-
let port = 80;
|
209
|
-
const promises = portTypes.map(async (portType) => {
|
210
|
-
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
211
|
-
const thisPort = port++; //TODO: Not sure how we should handle multiple ports or non-HTTP ports
|
212
|
-
const dockerPort = `${thisPort}/tcp`;
|
213
|
-
ExposedPorts[dockerPort] = {};
|
214
|
-
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = '' + thisPort;
|
215
|
-
|
216
|
-
PortBindings[dockerPort] = [
|
217
|
-
{
|
218
|
-
HostIp: bindHost,
|
219
|
-
HostPort: `${publicPort}`,
|
220
|
-
},
|
221
|
-
];
|
222
|
-
});
|
223
|
-
|
224
|
-
await Promise.all(promises);
|
178
|
+
const {
|
179
|
+
PortBindings,
|
180
|
+
ExposedPorts,
|
181
|
+
addonEnv
|
182
|
+
} = await this.getDockerPortBindings(blockInstance, assetVersion);
|
225
183
|
|
226
184
|
let HealthCheck = undefined;
|
227
185
|
if (localContainer.healthcheck) {
|
228
186
|
HealthCheck = containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
|
229
187
|
}
|
230
188
|
|
231
|
-
|
232
|
-
container = await containerManager.startContainer({
|
189
|
+
return this.ensureContainer({
|
233
190
|
Image: dockerImage,
|
234
191
|
name: containerName,
|
235
192
|
WorkingDir: workingDir,
|
@@ -256,79 +213,9 @@ export class BlockInstanceRunner {
|
|
256
213
|
},
|
257
214
|
...dockerOpts,
|
258
215
|
});
|
259
|
-
|
260
|
-
try {
|
261
|
-
if (HealthCheck) {
|
262
|
-
await containerManager.waitForHealthy(container);
|
263
|
-
} else {
|
264
|
-
await containerManager.waitForReady(container);
|
265
|
-
}
|
266
|
-
} catch (e: any) {
|
267
|
-
logs.addLog(e.message, 'ERROR');
|
268
|
-
}
|
269
|
-
|
270
|
-
return this._handleContainer(container, logs);
|
271
216
|
}
|
272
217
|
|
273
|
-
private async
|
274
|
-
container: Container,
|
275
|
-
logs: LogData,
|
276
|
-
deleteOnExit: boolean = false
|
277
|
-
): Promise<ProcessInfo> {
|
278
|
-
let localContainer: Container | null = container;
|
279
|
-
const logStream = (await container.logs({
|
280
|
-
follow: true,
|
281
|
-
stdout: true,
|
282
|
-
stderr: true,
|
283
|
-
tail: LogData.MAX_LINES,
|
284
|
-
})) as EventEmitter;
|
285
|
-
|
286
|
-
const outputEvents = new EventEmitter();
|
287
|
-
logStream.on('data', (data) => {
|
288
|
-
logs.addLog(data.toString());
|
289
|
-
outputEvents.emit('data', data);
|
290
|
-
});
|
291
|
-
|
292
|
-
logStream.on('error', (data) => {
|
293
|
-
logs.addLog(data.toString());
|
294
|
-
outputEvents.emit('data', data);
|
295
|
-
});
|
296
|
-
|
297
|
-
logStream.on('close', async () => {
|
298
|
-
const status = await container.status();
|
299
|
-
const data = status.data as any;
|
300
|
-
if (deleteOnExit) {
|
301
|
-
try {
|
302
|
-
await containerManager.remove(container);
|
303
|
-
} catch (e: any) {}
|
304
|
-
}
|
305
|
-
outputEvents.emit('exit', data?.State?.ExitCode ?? 0);
|
306
|
-
});
|
307
|
-
|
308
|
-
return {
|
309
|
-
type: InstanceType.DOCKER,
|
310
|
-
pid: container.id,
|
311
|
-
output: outputEvents,
|
312
|
-
stop: async () => {
|
313
|
-
if (!localContainer) {
|
314
|
-
return;
|
315
|
-
}
|
316
|
-
|
317
|
-
try {
|
318
|
-
await localContainer.stop();
|
319
|
-
if (deleteOnExit) {
|
320
|
-
await containerManager.remove(localContainer);
|
321
|
-
}
|
322
|
-
} catch (e) {}
|
323
|
-
localContainer = null;
|
324
|
-
},
|
325
|
-
logs: () => {
|
326
|
-
return logs.getLogs();
|
327
|
-
},
|
328
|
-
};
|
329
|
-
}
|
330
|
-
|
331
|
-
private async _startDockerProcess(blockInstance: BlockProcessParams, blockInfo: KapetaURI, env: StringMap) {
|
218
|
+
private async _startDockerProcess(blockInstance: BlockProcessParams, blockInfo: KapetaURI, env: StringMap, assetVersion: DefinitionInfo) {
|
332
219
|
const { versionFile } = ClusterConfig.getRepositoryAssetInfoPath(
|
333
220
|
blockInfo.handle,
|
334
221
|
blockInfo.name,
|
@@ -350,49 +237,38 @@ export class BlockInstanceRunner {
|
|
350
237
|
throw new Error(`Missing docker image information: ${JSON.stringify(versionInfo?.artifact?.details)}`);
|
351
238
|
}
|
352
239
|
|
240
|
+
const {
|
241
|
+
PortBindings,
|
242
|
+
ExposedPorts,
|
243
|
+
addonEnv
|
244
|
+
} = await this.getDockerPortBindings(blockInstance, assetVersion);
|
245
|
+
|
353
246
|
const containerName = getBlockInstanceContainerName(blockInstance.id);
|
354
|
-
const logs = new LogData();
|
355
|
-
const containerInfo = await containerManager.getContainerByName(containerName);
|
356
|
-
let container = containerInfo?.native;
|
357
247
|
|
358
248
|
// For windows we need to default to root
|
359
249
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
360
250
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
Labels: {
|
376
|
-
instance: blockInstance.id,
|
377
|
-
},
|
378
|
-
Env: [
|
379
|
-
...DOCKER_ENV_VARS,
|
380
|
-
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
381
|
-
...Object.entries(env).map(([key, value]) => `${key}=${value}`),
|
382
|
-
],
|
383
|
-
HostConfig: {
|
384
|
-
Binds: [`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`],
|
385
|
-
},
|
386
|
-
});
|
387
|
-
|
388
|
-
try {
|
389
|
-
await containerManager.waitForReady(container);
|
390
|
-
} catch (e: any) {
|
391
|
-
logs.addLog(e.message, 'ERROR');
|
392
|
-
}
|
393
|
-
}
|
251
|
+
return this.ensureContainer({
|
252
|
+
Image: dockerImage,
|
253
|
+
name: containerName,
|
254
|
+
ExposedPorts,
|
255
|
+
Labels: {
|
256
|
+
instance: blockInstance.id,
|
257
|
+
},
|
258
|
+
Env: [
|
259
|
+
...DOCKER_ENV_VARS,
|
260
|
+
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
261
|
+
...Object.entries({
|
262
|
+
...env,
|
263
|
+
...addonEnv
|
264
|
+
}).map(([key, value]) => `${key}=${value}`),
|
394
265
|
|
395
|
-
|
266
|
+
],
|
267
|
+
HostConfig: {
|
268
|
+
Binds: [`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`],
|
269
|
+
PortBindings,
|
270
|
+
},
|
271
|
+
});
|
396
272
|
}
|
397
273
|
|
398
274
|
/**
|
@@ -430,130 +306,79 @@ export class BlockInstanceRunner {
|
|
430
306
|
|
431
307
|
const dockerImage = spec?.local?.image;
|
432
308
|
|
433
|
-
try {
|
434
|
-
await containerManager.pull(dockerImage);
|
435
|
-
} catch (e) {
|
436
|
-
console.warn('Failed to pull image. Continuing...', e);
|
437
|
-
}
|
438
|
-
|
439
309
|
const containerName = getBlockInstanceContainerName(blockInstance.id);
|
440
310
|
const logs = new LogData();
|
441
|
-
const containerInfo = await containerManager.getContainerByName(containerName);
|
442
|
-
let container = containerInfo?.native;
|
443
|
-
|
444
|
-
if (container) {
|
445
|
-
const containerData = container.data as any;
|
446
|
-
if (containerData.State === 'running') {
|
447
|
-
logs.addLog(`Found existing running container for block: ${containerName}`);
|
448
|
-
} else {
|
449
|
-
if (containerData.State?.ExitCode > 0) {
|
450
|
-
logs.addLog(`Container exited with code: ${containerData.State.ExitCode}. Deleting...`);
|
451
|
-
try {
|
452
|
-
await containerManager.remove(container);
|
453
|
-
} catch (e) {}
|
454
|
-
container = undefined;
|
455
|
-
} else {
|
456
|
-
logs.addLog(`Found existing container for block: ${containerName}. Starting now`);
|
457
|
-
try {
|
458
|
-
await container.start();
|
459
|
-
} catch (e) {
|
460
|
-
console.warn('Failed to start container. Deleting...', e);
|
461
|
-
try {
|
462
|
-
await containerManager.remove(container);
|
463
|
-
} catch (e) {}
|
464
|
-
container = undefined;
|
465
|
-
}
|
466
|
-
}
|
467
|
-
}
|
468
|
-
}
|
469
311
|
|
470
312
|
const bindHost = getBindHost();
|
471
313
|
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
{
|
490
|
-
HostIp: bindHost,
|
491
|
-
HostPort: `${publicPort}`,
|
492
|
-
},
|
493
|
-
];
|
494
|
-
}
|
495
|
-
);
|
496
|
-
|
497
|
-
await Promise.all(promises);
|
498
|
-
|
499
|
-
if (spec.local?.env) {
|
500
|
-
Object.entries(spec.local.env).forEach(([key, value]) => {
|
501
|
-
addonEnv[key] = value as string;
|
502
|
-
});
|
503
|
-
}
|
504
|
-
|
505
|
-
if (spec.local?.mounts) {
|
506
|
-
const mounts = containerManager.createMounts(blockUri.id, spec.local.mounts);
|
507
|
-
Mounts = containerManager.toDockerMounts(mounts);
|
314
|
+
const ExposedPorts: AnyMap = {};
|
315
|
+
const addonEnv: StringMap = {};
|
316
|
+
const PortBindings: AnyMap = {};
|
317
|
+
let HealthCheck = undefined;
|
318
|
+
let Mounts: DockerMounts[] = [];
|
319
|
+
const promises = Object.entries(spec.local.ports as { [p: string]: { port: string; type: string } }).map(
|
320
|
+
async ([portType, value]) => {
|
321
|
+
const dockerPort = `${value.port}/${value.type}`;
|
322
|
+
ExposedPorts[dockerPort] = {};
|
323
|
+
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = value.port;
|
324
|
+
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
325
|
+
PortBindings[dockerPort] = [
|
326
|
+
{
|
327
|
+
HostIp: bindHost,
|
328
|
+
HostPort: `${publicPort}`,
|
329
|
+
},
|
330
|
+
];
|
508
331
|
}
|
332
|
+
);
|
509
333
|
|
510
|
-
|
511
|
-
HealthCheck = containerManager.toDockerHealth(spec.local?.health);
|
512
|
-
}
|
334
|
+
await Promise.all(promises);
|
513
335
|
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
logs.addLog(`Creating new container for block: ${containerName}`);
|
518
|
-
container = await containerManager.startContainer({
|
519
|
-
Image: dockerImage,
|
520
|
-
name: containerName,
|
521
|
-
ExposedPorts,
|
522
|
-
HealthCheck,
|
523
|
-
HostConfig: {
|
524
|
-
Binds: [
|
525
|
-
`${toLocalBindVolume(kapetaYmlPath)}:/kapeta.yml:ro`,
|
526
|
-
`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`,
|
527
|
-
],
|
528
|
-
PortBindings,
|
529
|
-
Mounts,
|
530
|
-
},
|
531
|
-
Labels: {
|
532
|
-
instance: blockInstance.id,
|
533
|
-
},
|
534
|
-
Env: [
|
535
|
-
`KAPETA_INSTANCE_NAME=${blockInstance.ref}`,
|
536
|
-
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
537
|
-
...DOCKER_ENV_VARS,
|
538
|
-
...Object.entries({
|
539
|
-
...env,
|
540
|
-
...addonEnv,
|
541
|
-
}).map(([key, value]) => `${key}=${value}`),
|
542
|
-
],
|
336
|
+
if (spec.local?.env) {
|
337
|
+
Object.entries(spec.local.env).forEach(([key, value]) => {
|
338
|
+
addonEnv[key] = value as string;
|
543
339
|
});
|
340
|
+
}
|
544
341
|
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
logs.addLog(e.message, 'ERROR');
|
553
|
-
}
|
342
|
+
if (spec.local?.mounts) {
|
343
|
+
const mounts = containerManager.createMounts(blockUri.id, spec.local.mounts);
|
344
|
+
Mounts = containerManager.toDockerMounts(mounts);
|
345
|
+
}
|
346
|
+
|
347
|
+
if (spec.local?.health) {
|
348
|
+
HealthCheck = containerManager.toDockerHealth(spec.local?.health);
|
554
349
|
}
|
555
350
|
|
556
|
-
|
351
|
+
// For windows we need to default to root
|
352
|
+
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
353
|
+
|
354
|
+
logs.addLog(`Creating new container for block: ${containerName}`);
|
355
|
+
const out = await this.ensureContainer({
|
356
|
+
Image: dockerImage,
|
357
|
+
name: containerName,
|
358
|
+
ExposedPorts,
|
359
|
+
HealthCheck,
|
360
|
+
HostConfig: {
|
361
|
+
Binds: [
|
362
|
+
`${toLocalBindVolume(kapetaYmlPath)}:/kapeta.yml:ro`,
|
363
|
+
`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`,
|
364
|
+
],
|
365
|
+
PortBindings,
|
366
|
+
Mounts,
|
367
|
+
},
|
368
|
+
Labels: {
|
369
|
+
instance: blockInstance.id,
|
370
|
+
},
|
371
|
+
Env: [
|
372
|
+
`KAPETA_INSTANCE_NAME=${blockInstance.ref}`,
|
373
|
+
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
374
|
+
...DOCKER_ENV_VARS,
|
375
|
+
...Object.entries({
|
376
|
+
...env,
|
377
|
+
...addonEnv,
|
378
|
+
}).map(([key, value]) => `${key}=${value}`),
|
379
|
+
],
|
380
|
+
});
|
381
|
+
|
557
382
|
const portTypes = spec.local.ports ? Object.keys(spec.local.ports) : [];
|
558
383
|
if (portTypes.length > 0) {
|
559
384
|
out.portType = portTypes[0];
|
@@ -561,4 +386,110 @@ export class BlockInstanceRunner {
|
|
561
386
|
|
562
387
|
return out;
|
563
388
|
}
|
389
|
+
|
390
|
+
|
391
|
+
private async getDockerPortBindings(blockInstance: BlockProcessParams, assetVersion: DefinitionInfo) {
|
392
|
+
const bindHost = getBindHost();
|
393
|
+
const ExposedPorts: AnyMap = {};
|
394
|
+
const addonEnv: StringMap = {};
|
395
|
+
const PortBindings: AnyMap = {};
|
396
|
+
|
397
|
+
const portTypes = getProviderPorts(assetVersion);
|
398
|
+
let port = 80;
|
399
|
+
const promises = portTypes.map(async (portType) => {
|
400
|
+
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
401
|
+
const thisPort = port++; //TODO: Not sure how we should handle multiple ports or non-HTTP ports
|
402
|
+
const dockerPort = `${thisPort}/tcp`;
|
403
|
+
ExposedPorts[dockerPort] = {};
|
404
|
+
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = '' + thisPort;
|
405
|
+
|
406
|
+
PortBindings[dockerPort] = [
|
407
|
+
{
|
408
|
+
HostIp: bindHost,
|
409
|
+
HostPort: `${publicPort}`,
|
410
|
+
},
|
411
|
+
];
|
412
|
+
});
|
413
|
+
|
414
|
+
|
415
|
+
await Promise.all(promises);
|
416
|
+
|
417
|
+
return {PortBindings,ExposedPorts, addonEnv};
|
418
|
+
}
|
419
|
+
|
420
|
+
private async ensureContainer(opts: any) {
|
421
|
+
const logs = new LogData();
|
422
|
+
|
423
|
+
const container = await containerManager.ensureContainer(opts);
|
424
|
+
|
425
|
+
try {
|
426
|
+
if (opts.HealthCheck) {
|
427
|
+
await containerManager.waitForHealthy(container);
|
428
|
+
} else {
|
429
|
+
await containerManager.waitForReady(container);
|
430
|
+
}
|
431
|
+
} catch (e: any) {
|
432
|
+
logs.addLog(e.message, 'ERROR');
|
433
|
+
}
|
434
|
+
|
435
|
+
return this._handleContainer(container, logs);
|
436
|
+
}
|
437
|
+
|
438
|
+
private async _handleContainer(
|
439
|
+
container: Container,
|
440
|
+
logs: LogData,
|
441
|
+
deleteOnExit: boolean = false
|
442
|
+
): Promise<ProcessInfo> {
|
443
|
+
let localContainer: Container | null = container;
|
444
|
+
const logStream = (await container.logs({
|
445
|
+
follow: true,
|
446
|
+
stdout: true,
|
447
|
+
stderr: true,
|
448
|
+
tail: LogData.MAX_LINES,
|
449
|
+
})) as EventEmitter;
|
450
|
+
|
451
|
+
const outputEvents = new EventEmitter();
|
452
|
+
logStream.on('data', (data) => {
|
453
|
+
logs.addLog(data.toString());
|
454
|
+
outputEvents.emit('data', data);
|
455
|
+
});
|
456
|
+
|
457
|
+
logStream.on('error', (data) => {
|
458
|
+
logs.addLog(data.toString());
|
459
|
+
outputEvents.emit('data', data);
|
460
|
+
});
|
461
|
+
|
462
|
+
logStream.on('close', async () => {
|
463
|
+
const status = await container.status();
|
464
|
+
const data = status.data as any;
|
465
|
+
if (deleteOnExit) {
|
466
|
+
try {
|
467
|
+
await containerManager.remove(container);
|
468
|
+
} catch (e: any) {}
|
469
|
+
}
|
470
|
+
outputEvents.emit('exit', data?.State?.ExitCode ?? 0);
|
471
|
+
});
|
472
|
+
|
473
|
+
return {
|
474
|
+
type: InstanceType.DOCKER,
|
475
|
+
pid: container.id,
|
476
|
+
output: outputEvents,
|
477
|
+
stop: async () => {
|
478
|
+
if (!localContainer) {
|
479
|
+
return;
|
480
|
+
}
|
481
|
+
|
482
|
+
try {
|
483
|
+
await localContainer.stop();
|
484
|
+
if (deleteOnExit) {
|
485
|
+
await containerManager.remove(localContainer);
|
486
|
+
}
|
487
|
+
} catch (e) {}
|
488
|
+
localContainer = null;
|
489
|
+
},
|
490
|
+
logs: () => {
|
491
|
+
return logs.getLogs();
|
492
|
+
},
|
493
|
+
};
|
494
|
+
}
|
564
495
|
}
|