@kapeta/local-cluster-service 0.12.0 → 0.13.0
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/index.js +2 -0
- package/dist/cjs/src/assetManager.d.ts +3 -1
- package/dist/cjs/src/assetManager.js +20 -4
- package/dist/cjs/src/assets/routes.js +22 -1
- package/dist/cjs/src/containerManager.d.ts +1 -1
- package/dist/cjs/src/containerManager.js +132 -122
- package/dist/cjs/src/instanceManager.d.ts +4 -3
- package/dist/cjs/src/instanceManager.js +87 -60
- package/dist/cjs/src/instances/routes.js +21 -11
- package/dist/cjs/src/operatorManager.d.ts +5 -3
- package/dist/cjs/src/operatorManager.js +34 -22
- package/dist/cjs/src/providerManager.js +1 -1
- package/dist/cjs/src/repositoryManager.d.ts +2 -4
- package/dist/cjs/src/repositoryManager.js +51 -66
- package/dist/cjs/src/socketManager.js +1 -1
- package/dist/cjs/src/taskManager.d.ts +64 -0
- package/dist/cjs/src/taskManager.js +163 -0
- package/dist/cjs/src/tasks/routes.d.ts +3 -0
- package/dist/cjs/src/tasks/routes.js +35 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.js +0 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/src/assetManager.d.ts +3 -1
- package/dist/esm/src/assetManager.js +20 -4
- package/dist/esm/src/assets/routes.js +22 -1
- package/dist/esm/src/containerManager.d.ts +1 -1
- package/dist/esm/src/containerManager.js +132 -122
- package/dist/esm/src/instanceManager.d.ts +4 -3
- package/dist/esm/src/instanceManager.js +87 -60
- package/dist/esm/src/instances/routes.js +21 -11
- package/dist/esm/src/operatorManager.d.ts +5 -3
- package/dist/esm/src/operatorManager.js +34 -22
- package/dist/esm/src/providerManager.js +1 -1
- package/dist/esm/src/repositoryManager.d.ts +2 -4
- package/dist/esm/src/repositoryManager.js +51 -66
- package/dist/esm/src/socketManager.js +1 -1
- package/dist/esm/src/taskManager.d.ts +64 -0
- package/dist/esm/src/taskManager.js +159 -0
- package/dist/esm/src/tasks/routes.d.ts +3 -0
- package/dist/esm/src/tasks/routes.js +30 -0
- package/dist/esm/src/utils/BlockInstanceRunner.js +0 -1
- package/index.ts +2 -0
- package/package.json +1 -1
- package/src/assetManager.ts +28 -4
- package/src/assets/routes.ts +23 -1
- package/src/containerManager.ts +153 -142
- package/src/instanceManager.ts +116 -70
- package/src/instances/routes.ts +20 -12
- package/src/operatorManager.ts +46 -26
- package/src/providerManager.ts +1 -1
- package/src/repositoryManager.ts +65 -63
- package/src/socketManager.ts +1 -1
- package/src/taskManager.ts +225 -0
- package/src/tasks/routes.ts +38 -0
- package/src/utils/BlockInstanceRunner.ts +0 -4
package/src/instanceManager.ts
CHANGED
@@ -9,11 +9,12 @@ import { assetManager } from './assetManager';
|
|
9
9
|
import { containerManager, HEALTH_CHECK_TIMEOUT } from './containerManager';
|
10
10
|
import { configManager } from './configManager';
|
11
11
|
import { DesiredInstanceStatus, InstanceInfo, InstanceOwner, InstanceStatus, InstanceType, LogEntry } from './types';
|
12
|
-
import { BlockDefinitionSpec, BlockInstance } from '@kapeta/schemas';
|
12
|
+
import { BlockDefinitionSpec, BlockInstance, Plan } from '@kapeta/schemas';
|
13
13
|
import { getBlockInstanceContainerName, normalizeKapetaUri } from './utils/utils';
|
14
14
|
import { KIND_OPERATOR, operatorManager } from './operatorManager';
|
15
15
|
import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
16
16
|
import { definitionsManager } from './definitionsManager';
|
17
|
+
import { Task, taskManager } from './taskManager';
|
17
18
|
|
18
19
|
const CHECK_INTERVAL = 5000;
|
19
20
|
const DEFAULT_HEALTH_PORT_TYPE = 'rest';
|
@@ -64,7 +65,19 @@ export class InstanceManager {
|
|
64
65
|
|
65
66
|
systemId = normalizeKapetaUri(systemId);
|
66
67
|
|
67
|
-
|
68
|
+
const planInfo = definitionsManager.getDefinition(systemId);
|
69
|
+
|
70
|
+
if (!planInfo) {
|
71
|
+
return [];
|
72
|
+
}
|
73
|
+
|
74
|
+
const plan = planInfo.definition as Plan;
|
75
|
+
|
76
|
+
const instanceIds = plan.spec.blocks.map((block) => block.id);
|
77
|
+
|
78
|
+
return this._instances.filter(
|
79
|
+
(instance) => instance.systemId === systemId && instanceIds.includes(instance.instanceId)
|
80
|
+
);
|
68
81
|
}
|
69
82
|
|
70
83
|
public getInstance(systemId: string, instanceId: string) {
|
@@ -261,35 +274,51 @@ export class InstanceManager {
|
|
261
274
|
});
|
262
275
|
}
|
263
276
|
|
264
|
-
public async startAllForPlan(systemId: string): Promise<InstanceInfo[]
|
277
|
+
public async startAllForPlan(systemId: string): Promise<Task<InstanceInfo[]>> {
|
265
278
|
systemId = normalizeKapetaUri(systemId);
|
266
279
|
const plan = await assetManager.getPlan(systemId, true);
|
267
280
|
if (!plan) {
|
268
|
-
throw new Error(
|
281
|
+
throw new Error(`Plan not found: ${systemId}`);
|
269
282
|
}
|
270
283
|
|
271
284
|
if (!plan.spec.blocks) {
|
272
|
-
|
273
|
-
return [];
|
285
|
+
throw new Error(`No blocks found in plan: ${systemId}`);
|
274
286
|
}
|
275
287
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
288
|
+
return taskManager.add(
|
289
|
+
`plan:start:${systemId}`,
|
290
|
+
async () => {
|
291
|
+
let promises: Promise<InstanceInfo>[] = [];
|
292
|
+
let errors = [];
|
293
|
+
for (let blockInstance of Object.values(plan.spec.blocks as BlockInstance[])) {
|
294
|
+
try {
|
295
|
+
promises.push(
|
296
|
+
this.start(systemId, blockInstance.id).then((taskOrInstance) => {
|
297
|
+
if (taskOrInstance instanceof Task) {
|
298
|
+
return taskOrInstance.wait();
|
299
|
+
}
|
300
|
+
return taskOrInstance;
|
301
|
+
})
|
302
|
+
);
|
303
|
+
} catch (e) {
|
304
|
+
errors.push(e);
|
305
|
+
}
|
306
|
+
}
|
285
307
|
|
286
|
-
|
308
|
+
const settled = await Promise.allSettled(promises);
|
287
309
|
|
288
|
-
|
289
|
-
|
290
|
-
|
310
|
+
if (errors.length > 0) {
|
311
|
+
throw errors[0];
|
312
|
+
}
|
291
313
|
|
292
|
-
|
314
|
+
return settled
|
315
|
+
.map((p) => (p.status === 'fulfilled' ? p.value : null))
|
316
|
+
.filter((p) => !!p) as InstanceInfo[];
|
317
|
+
},
|
318
|
+
{
|
319
|
+
name: `Starting plan ${systemId}`,
|
320
|
+
}
|
321
|
+
);
|
293
322
|
}
|
294
323
|
|
295
324
|
public async stop(systemId: string, instanceId: string) {
|
@@ -353,14 +382,21 @@ export class InstanceManager {
|
|
353
382
|
});
|
354
383
|
}
|
355
384
|
|
356
|
-
public
|
385
|
+
public stopAllForPlan(systemId: string) {
|
357
386
|
systemId = normalizeKapetaUri(systemId);
|
358
387
|
const instancesForPlan = this._instances.filter((instance) => instance.systemId === systemId);
|
359
|
-
|
360
|
-
|
388
|
+
return taskManager.add(
|
389
|
+
`plan:stop:${systemId}`,
|
390
|
+
async () => {
|
391
|
+
return this.stopInstances(instancesForPlan);
|
392
|
+
},
|
393
|
+
{
|
394
|
+
name: `Stopping plan ${systemId}`,
|
395
|
+
}
|
396
|
+
);
|
361
397
|
}
|
362
398
|
|
363
|
-
public async start(systemId: string, instanceId: string): Promise<InstanceInfo
|
399
|
+
public async start(systemId: string, instanceId: string): Promise<InstanceInfo | Task<InstanceInfo>> {
|
364
400
|
return this.exclusive(systemId, instanceId, async () => {
|
365
401
|
systemId = normalizeKapetaUri(systemId);
|
366
402
|
const plan = await assetManager.getPlan(systemId, true);
|
@@ -453,53 +489,63 @@ export class InstanceManager {
|
|
453
489
|
}
|
454
490
|
|
455
491
|
const instanceConfig = await configManager.getConfigForSection(systemId, instanceId);
|
456
|
-
const
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
492
|
+
const task = taskManager.add(
|
493
|
+
`instance:start:${systemId}:${instanceId}`,
|
494
|
+
async () => {
|
495
|
+
const runner = new BlockInstanceRunner(systemId);
|
496
|
+
const startTime = Date.now();
|
497
|
+
try {
|
498
|
+
const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
|
499
|
+
|
500
|
+
instance.status = InstanceStatus.READY;
|
501
|
+
|
502
|
+
return this.saveInternalInstance({
|
503
|
+
...instance,
|
504
|
+
type: processInfo.type,
|
505
|
+
pid: processInfo.pid ?? -1,
|
506
|
+
health: null,
|
507
|
+
portType: processInfo.portType,
|
508
|
+
status: InstanceStatus.READY,
|
509
|
+
});
|
510
|
+
} catch (e: any) {
|
511
|
+
console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e.message);
|
512
|
+
const logs: LogEntry[] = [
|
513
|
+
{
|
514
|
+
source: 'stdout',
|
515
|
+
level: 'ERROR',
|
516
|
+
message: e.message,
|
517
|
+
time: Date.now(),
|
518
|
+
},
|
519
|
+
];
|
520
|
+
|
521
|
+
const out = await this.saveInternalInstance({
|
522
|
+
...instance,
|
523
|
+
type: InstanceType.UNKNOWN,
|
524
|
+
pid: null,
|
525
|
+
health: null,
|
526
|
+
portType: DEFAULT_HEALTH_PORT_TYPE,
|
527
|
+
status: InstanceStatus.FAILED,
|
528
|
+
errorMessage: e.message ?? 'Failed to start - Check logs for details.',
|
529
|
+
});
|
530
|
+
|
531
|
+
this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
|
532
|
+
|
533
|
+
this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
|
534
|
+
error: `Failed to start instance: ${e.message}`,
|
535
|
+
status: EVENT_INSTANCE_EXITED,
|
536
|
+
instanceId: blockInstance.id,
|
537
|
+
});
|
538
|
+
|
539
|
+
return out;
|
540
|
+
}
|
541
|
+
},
|
542
|
+
{
|
543
|
+
name: `Starting instance: ${instance.name}`,
|
544
|
+
systemId,
|
545
|
+
}
|
546
|
+
);
|
500
547
|
|
501
|
-
|
502
|
-
}
|
548
|
+
return task;
|
503
549
|
});
|
504
550
|
}
|
505
551
|
|
package/src/instances/routes.ts
CHANGED
@@ -6,6 +6,7 @@ import { NextFunction, Request, Response } from 'express';
|
|
6
6
|
import { kapetaHeaders, KapetaRequest } from '../middleware/kapeta';
|
7
7
|
import { stringBody } from '../middleware/stringBody';
|
8
8
|
import { DesiredInstanceStatus, InstanceInfo, InstanceOwner, InstanceType, KapetaBodyRequest } from '../types';
|
9
|
+
import { Task } from '../taskManager';
|
9
10
|
|
10
11
|
const router = Router();
|
11
12
|
router.use('/', corsHandler);
|
@@ -28,13 +29,11 @@ router.get('/:systemId/instances', (req: Request, res: Response) => {
|
|
28
29
|
* Start all instances in a plan
|
29
30
|
*/
|
30
31
|
router.post('/:systemId/start', async (req: Request, res: Response) => {
|
31
|
-
const
|
32
|
+
const task = await instanceManager.startAllForPlan(req.params.systemId);
|
32
33
|
|
33
34
|
res.status(202).send({
|
34
35
|
ok: true,
|
35
|
-
|
36
|
-
return { pid: p.pid, type: p.type };
|
37
|
-
}),
|
36
|
+
taskId: task.id,
|
38
37
|
});
|
39
38
|
});
|
40
39
|
|
@@ -42,10 +41,11 @@ router.post('/:systemId/start', async (req: Request, res: Response) => {
|
|
42
41
|
* Stop all instances in plan
|
43
42
|
*/
|
44
43
|
router.post('/:systemId/stop', async (req: Request, res: Response) => {
|
45
|
-
|
44
|
+
const task = instanceManager.stopAllForPlan(req.params.systemId);
|
46
45
|
|
47
46
|
res.status(202).send({
|
48
47
|
ok: true,
|
48
|
+
taskId: task.id,
|
49
49
|
});
|
50
50
|
});
|
51
51
|
|
@@ -53,13 +53,19 @@ router.post('/:systemId/stop', async (req: Request, res: Response) => {
|
|
53
53
|
* Start single instance in a plan
|
54
54
|
*/
|
55
55
|
router.post('/:systemId/:instanceId/start', async (req: Request, res: Response) => {
|
56
|
-
const
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
}
|
56
|
+
const result = await instanceManager.start(req.params.systemId, req.params.instanceId);
|
57
|
+
if (result instanceof Task) {
|
58
|
+
res.status(202).send({
|
59
|
+
ok: true,
|
60
|
+
taskId: result.id,
|
61
|
+
});
|
62
|
+
} else {
|
63
|
+
res.status(202).send({
|
64
|
+
ok: true,
|
65
|
+
pid: result.pid,
|
66
|
+
type: result.type,
|
67
|
+
});
|
68
|
+
}
|
63
69
|
});
|
64
70
|
|
65
71
|
/**
|
@@ -146,8 +152,10 @@ router.put('/', async (req: KapetaBodyRequest, res: Response) => {
|
|
146
152
|
const oldInstance = instanceManager.getInstance(req.kapeta!.systemId, req.kapeta!.instanceId);
|
147
153
|
if (oldInstance) {
|
148
154
|
instance.pid = oldInstance.pid;
|
155
|
+
instance.desiredStatus = oldInstance.desiredStatus;
|
149
156
|
}
|
150
157
|
instance.type = InstanceType.DOCKER;
|
158
|
+
instance.owner = InstanceOwner.INTERNAL;
|
151
159
|
} else {
|
152
160
|
// Coming from user starting the instance outside of kapeta
|
153
161
|
instance.type = InstanceType.LOCAL;
|
package/src/operatorManager.ts
CHANGED
@@ -12,21 +12,26 @@ import { definitionsManager } from './definitionsManager';
|
|
12
12
|
import { getBindHost, normalizeKapetaUri } from './utils/utils';
|
13
13
|
import _ from 'lodash';
|
14
14
|
import AsyncLock from 'async-lock';
|
15
|
+
import { taskManager } from './taskManager';
|
15
16
|
|
16
17
|
export const KIND_OPERATOR = 'core/resource-type-operator';
|
17
18
|
|
18
19
|
class Operator {
|
19
|
-
private _data:
|
20
|
-
constructor(data:
|
20
|
+
private readonly _data: DefinitionInfo;
|
21
|
+
constructor(data: DefinitionInfo) {
|
21
22
|
this._data = data;
|
22
23
|
}
|
23
24
|
|
24
|
-
|
25
|
+
getLocalData() {
|
26
|
+
return this._data.definition.spec.local;
|
27
|
+
}
|
28
|
+
|
29
|
+
getDefinitionInfo() {
|
25
30
|
return this._data;
|
26
31
|
}
|
27
32
|
|
28
33
|
getCredentials() {
|
29
|
-
return this._data.credentials;
|
34
|
+
return this._data.definition.spec.local.credentials;
|
30
35
|
}
|
31
36
|
}
|
32
37
|
|
@@ -72,7 +77,7 @@ class OperatorManager {
|
|
72
77
|
throw new Error(`Operator missing local definition: ${resourceType}:${version}`);
|
73
78
|
}
|
74
79
|
|
75
|
-
return new Operator(operator
|
80
|
+
return new Operator(operator);
|
76
81
|
}
|
77
82
|
|
78
83
|
/**
|
@@ -161,7 +166,7 @@ class OperatorManager {
|
|
161
166
|
return await this.operatorLock.acquire(key, async () => {
|
162
167
|
const operator = this.getOperator(resourceType, version);
|
163
168
|
|
164
|
-
const operatorData = operator.
|
169
|
+
const operatorData = operator.getLocalData();
|
165
170
|
|
166
171
|
const portTypes = Object.keys(operatorData.ports);
|
167
172
|
|
@@ -203,6 +208,8 @@ class OperatorManager {
|
|
203
208
|
kapeta: 'true',
|
204
209
|
};
|
205
210
|
|
211
|
+
const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
|
212
|
+
|
206
213
|
const bindHost = getBindHost();
|
207
214
|
|
208
215
|
const ExposedPorts: { [key: string]: any } = {};
|
@@ -225,28 +232,41 @@ class OperatorManager {
|
|
225
232
|
Env.push(name + '=' + value);
|
226
233
|
});
|
227
234
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
235
|
+
const task = taskManager.add(
|
236
|
+
`operator:ensure:${key}`,
|
237
|
+
async () => {
|
238
|
+
let HealthCheck = undefined;
|
239
|
+
|
240
|
+
if (operatorData.health) {
|
241
|
+
HealthCheck = containerManager.toDockerHealth(operatorData.health);
|
242
|
+
}
|
243
|
+
|
244
|
+
const container = await containerManager.ensureContainer({
|
245
|
+
name: containerName,
|
246
|
+
Image: operatorData.image,
|
247
|
+
Hostname: containerName + '.kapeta',
|
248
|
+
Labels,
|
249
|
+
Cmd: operatorData.cmd,
|
250
|
+
ExposedPorts,
|
251
|
+
Env,
|
252
|
+
HealthCheck,
|
253
|
+
HostConfig: {
|
254
|
+
PortBindings,
|
255
|
+
Mounts,
|
256
|
+
},
|
257
|
+
});
|
258
|
+
|
259
|
+
await containerManager.waitForReady(container);
|
260
|
+
|
261
|
+
return new ContainerInfo(container);
|
246
262
|
},
|
247
|
-
|
263
|
+
{
|
264
|
+
name: `Ensuring ${operatorMetadata.title ?? operatorMetadata.name}`,
|
265
|
+
systemId,
|
266
|
+
}
|
267
|
+
);
|
248
268
|
|
249
|
-
return
|
269
|
+
return task.wait();
|
250
270
|
});
|
251
271
|
}
|
252
272
|
}
|
package/src/providerManager.ts
CHANGED
@@ -23,7 +23,7 @@ class ProviderManager {
|
|
23
23
|
return FSExtra.readFile(this._webAssetCache[id], 'utf8');
|
24
24
|
}
|
25
25
|
|
26
|
-
await repositoryManager.ensureAsset(handle, name, version);
|
26
|
+
await repositoryManager.ensureAsset(handle, name, version, true);
|
27
27
|
|
28
28
|
const installedProvider = this.getWebProviders().find((providerDefinition) => {
|
29
29
|
return providerDefinition.definition.metadata.name === fullName && providerDefinition.version === version;
|
package/src/repositoryManager.ts
CHANGED
@@ -10,6 +10,9 @@ import { progressListener } from './progressListener';
|
|
10
10
|
import { Dependency } from '@kapeta/schemas';
|
11
11
|
import { Actions, Config, RegistryService } from '@kapeta/nodejs-registry-utils';
|
12
12
|
import { definitionsManager } from './definitionsManager';
|
13
|
+
import { Task, taskManager } from './taskManager';
|
14
|
+
import { normalizeKapetaUri } from './utils/utils';
|
15
|
+
import { assetManager } from './assetManager';
|
13
16
|
|
14
17
|
const INSTALL_ATTEMPTED: { [p: string]: boolean } = {};
|
15
18
|
|
@@ -18,14 +21,12 @@ class RepositoryManager {
|
|
18
21
|
private _registryService: RegistryService;
|
19
22
|
private _cache: { [key: string]: boolean };
|
20
23
|
private watcher?: () => void;
|
21
|
-
|
22
|
-
private _processing: boolean = false;
|
24
|
+
|
23
25
|
constructor() {
|
24
26
|
this.changeEventsEnabled = true;
|
25
27
|
this.listenForChanges();
|
26
28
|
this._registryService = new RegistryService(Config.data.registry.url);
|
27
29
|
this._cache = {};
|
28
|
-
this._installQueue = [];
|
29
30
|
}
|
30
31
|
|
31
32
|
setChangeEventsEnabled(enabled: boolean) {
|
@@ -113,75 +114,69 @@ class RepositoryManager {
|
|
113
114
|
this.watcher = undefined;
|
114
115
|
}
|
115
116
|
|
116
|
-
private async _install(refs: string[]): Promise<
|
117
|
+
private async _install(refs: string[]): Promise<Task[]> {
|
117
118
|
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
118
|
-
const
|
119
|
-
|
119
|
+
const createInstaller = (ref: string) => {
|
120
|
+
return async () => {
|
121
|
+
if (INSTALL_ATTEMPTED[ref]) {
|
122
|
+
return;
|
123
|
+
}
|
124
|
+
|
125
|
+
if (definitionsManager.exists(ref)) {
|
126
|
+
return;
|
127
|
+
}
|
128
|
+
console.log(`Installing asset: ${ref}`);
|
129
|
+
INSTALL_ATTEMPTED[ref] = true;
|
130
|
+
//Auto-install missing asset
|
120
131
|
try {
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
console.log(`Auto-installing dependencies: ${filteredRefs.join(', ')}`);
|
127
|
-
filteredRefs.forEach((ref) => (INSTALL_ATTEMPTED[ref] = true));
|
128
|
-
//Auto-install missing asset
|
129
|
-
try {
|
130
|
-
//We change to a temp dir to avoid issues with the current working directory
|
131
|
-
process.chdir(os.tmpdir());
|
132
|
-
//Disable change events while installing
|
133
|
-
this.setChangeEventsEnabled(false);
|
134
|
-
socketManager.emit(`install`, 'install:action', {
|
135
|
-
type: 'start',
|
136
|
-
refs,
|
137
|
-
});
|
138
|
-
await Actions.install(progressListener, normalizedRefs, {});
|
139
|
-
socketManager.emit(`install`, 'install:action', {
|
140
|
-
type: 'done',
|
141
|
-
refs,
|
142
|
-
});
|
143
|
-
} catch (e: any) {
|
144
|
-
socketManager.emit(`install`, 'install:action', {
|
145
|
-
type: 'failed',
|
146
|
-
refs,
|
147
|
-
error: e.message,
|
148
|
-
});
|
149
|
-
} finally {
|
150
|
-
this.setChangeEventsEnabled(true);
|
151
|
-
}
|
152
|
-
}
|
153
|
-
resolve();
|
154
|
-
} catch (e) {
|
155
|
-
reject(e);
|
132
|
+
//We change to a temp dir to avoid issues with the current working directory
|
133
|
+
process.chdir(os.tmpdir());
|
134
|
+
//Disable change events while installing
|
135
|
+
this.setChangeEventsEnabled(false);
|
136
|
+
await Actions.install(progressListener, [ref], {});
|
156
137
|
} finally {
|
157
|
-
this.
|
138
|
+
this.setChangeEventsEnabled(true);
|
158
139
|
}
|
159
|
-
|
160
|
-
|
140
|
+
definitionsManager.clearCache();
|
141
|
+
assetManager.clearCache();
|
142
|
+
console.log(`Asset installed: ${ref}`);
|
143
|
+
};
|
144
|
+
};
|
161
145
|
|
162
|
-
|
146
|
+
const tasks: Task[] = [];
|
163
147
|
|
164
|
-
|
165
|
-
|
148
|
+
while (refs.length > 0) {
|
149
|
+
let ref = refs.shift();
|
150
|
+
if (!ref) {
|
151
|
+
continue;
|
152
|
+
}
|
153
|
+
ref = normalizeKapetaUri(ref);
|
166
154
|
|
167
|
-
|
168
|
-
|
169
|
-
return;
|
170
|
-
}
|
171
|
-
this._processing = true;
|
172
|
-
try {
|
173
|
-
while (this._installQueue.length > 0) {
|
174
|
-
const item = this._installQueue.shift();
|
175
|
-
if (item) {
|
176
|
-
await item();
|
177
|
-
}
|
155
|
+
if (INSTALL_ATTEMPTED[ref]) {
|
156
|
+
continue;
|
178
157
|
}
|
179
|
-
|
180
|
-
|
158
|
+
|
159
|
+
if (definitionsManager.exists(ref)) {
|
160
|
+
continue;
|
161
|
+
}
|
162
|
+
|
163
|
+
const task = taskManager.add(`asset:install:${ref}`, createInstaller(ref), {
|
164
|
+
name: `Installing ${ref}`,
|
165
|
+
group: 'asset:install:',
|
166
|
+
});
|
167
|
+
|
168
|
+
tasks.push(task);
|
181
169
|
}
|
170
|
+
|
171
|
+
return tasks;
|
182
172
|
}
|
183
173
|
|
184
|
-
async ensureAsset(
|
174
|
+
async ensureAsset(
|
175
|
+
handle: string,
|
176
|
+
name: string,
|
177
|
+
version: string,
|
178
|
+
wait: boolean = true
|
179
|
+
): Promise<undefined | Task[]> {
|
185
180
|
const fullName = `${handle}/${name}`;
|
186
181
|
const ref = `${fullName}:${version}`;
|
187
182
|
|
@@ -219,15 +214,22 @@ class RepositoryManager {
|
|
219
214
|
}
|
220
215
|
|
221
216
|
this._cache[ref] = true;
|
217
|
+
let tasks: Task[] | undefined = undefined;
|
222
218
|
if (!installedAsset) {
|
223
|
-
await this._install([ref]);
|
219
|
+
tasks = await this._install([ref]);
|
224
220
|
} else {
|
225
221
|
//Ensure dependencies are installed
|
226
222
|
const refs = assetVersion.dependencies.map((dep: Dependency) => dep.name);
|
227
223
|
if (refs.length > 0) {
|
228
|
-
await this._install(refs);
|
224
|
+
tasks = await this._install(refs);
|
229
225
|
}
|
230
226
|
}
|
227
|
+
|
228
|
+
if (tasks && wait) {
|
229
|
+
await Promise.all(tasks.map((t) => t.future.promise));
|
230
|
+
}
|
231
|
+
|
232
|
+
return tasks;
|
231
233
|
}
|
232
234
|
}
|
233
235
|
|