@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.
Files changed (55) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/index.js +2 -0
  3. package/dist/cjs/src/assetManager.d.ts +3 -1
  4. package/dist/cjs/src/assetManager.js +20 -4
  5. package/dist/cjs/src/assets/routes.js +22 -1
  6. package/dist/cjs/src/containerManager.d.ts +1 -1
  7. package/dist/cjs/src/containerManager.js +132 -122
  8. package/dist/cjs/src/instanceManager.d.ts +4 -3
  9. package/dist/cjs/src/instanceManager.js +87 -60
  10. package/dist/cjs/src/instances/routes.js +21 -11
  11. package/dist/cjs/src/operatorManager.d.ts +5 -3
  12. package/dist/cjs/src/operatorManager.js +34 -22
  13. package/dist/cjs/src/providerManager.js +1 -1
  14. package/dist/cjs/src/repositoryManager.d.ts +2 -4
  15. package/dist/cjs/src/repositoryManager.js +51 -66
  16. package/dist/cjs/src/socketManager.js +1 -1
  17. package/dist/cjs/src/taskManager.d.ts +64 -0
  18. package/dist/cjs/src/taskManager.js +163 -0
  19. package/dist/cjs/src/tasks/routes.d.ts +3 -0
  20. package/dist/cjs/src/tasks/routes.js +35 -0
  21. package/dist/cjs/src/utils/BlockInstanceRunner.js +0 -1
  22. package/dist/esm/index.js +2 -0
  23. package/dist/esm/src/assetManager.d.ts +3 -1
  24. package/dist/esm/src/assetManager.js +20 -4
  25. package/dist/esm/src/assets/routes.js +22 -1
  26. package/dist/esm/src/containerManager.d.ts +1 -1
  27. package/dist/esm/src/containerManager.js +132 -122
  28. package/dist/esm/src/instanceManager.d.ts +4 -3
  29. package/dist/esm/src/instanceManager.js +87 -60
  30. package/dist/esm/src/instances/routes.js +21 -11
  31. package/dist/esm/src/operatorManager.d.ts +5 -3
  32. package/dist/esm/src/operatorManager.js +34 -22
  33. package/dist/esm/src/providerManager.js +1 -1
  34. package/dist/esm/src/repositoryManager.d.ts +2 -4
  35. package/dist/esm/src/repositoryManager.js +51 -66
  36. package/dist/esm/src/socketManager.js +1 -1
  37. package/dist/esm/src/taskManager.d.ts +64 -0
  38. package/dist/esm/src/taskManager.js +159 -0
  39. package/dist/esm/src/tasks/routes.d.ts +3 -0
  40. package/dist/esm/src/tasks/routes.js +30 -0
  41. package/dist/esm/src/utils/BlockInstanceRunner.js +0 -1
  42. package/index.ts +2 -0
  43. package/package.json +1 -1
  44. package/src/assetManager.ts +28 -4
  45. package/src/assets/routes.ts +23 -1
  46. package/src/containerManager.ts +153 -142
  47. package/src/instanceManager.ts +116 -70
  48. package/src/instances/routes.ts +20 -12
  49. package/src/operatorManager.ts +46 -26
  50. package/src/providerManager.ts +1 -1
  51. package/src/repositoryManager.ts +65 -63
  52. package/src/socketManager.ts +1 -1
  53. package/src/taskManager.ts +225 -0
  54. package/src/tasks/routes.ts +38 -0
  55. package/src/utils/BlockInstanceRunner.ts +0 -4
@@ -19,6 +19,7 @@ const utils_1 = require("./utils/utils");
19
19
  const operatorManager_1 = require("./operatorManager");
20
20
  const nodejs_utils_1 = require("@kapeta/nodejs-utils");
21
21
  const definitionsManager_1 = require("./definitionsManager");
22
+ const taskManager_1 = require("./taskManager");
22
23
  const CHECK_INTERVAL = 5000;
23
24
  const DEFAULT_HEALTH_PORT_TYPE = 'rest';
24
25
  const EVENT_STATUS_CHANGED = 'status-changed';
@@ -55,7 +56,13 @@ class InstanceManager {
55
56
  return [];
56
57
  }
57
58
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
58
- return this._instances.filter((instance) => instance.systemId === systemId);
59
+ const planInfo = definitionsManager_1.definitionsManager.getDefinition(systemId);
60
+ if (!planInfo) {
61
+ return [];
62
+ }
63
+ const plan = planInfo.definition;
64
+ const instanceIds = plan.spec.blocks.map((block) => block.id);
65
+ return this._instances.filter((instance) => instance.systemId === systemId && instanceIds.includes(instance.instanceId));
59
66
  }
60
67
  getInstance(systemId, instanceId) {
61
68
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
@@ -211,27 +218,37 @@ class InstanceManager {
211
218
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
212
219
  const plan = await assetManager_1.assetManager.getPlan(systemId, true);
213
220
  if (!plan) {
214
- throw new Error('Plan not found: ' + systemId);
221
+ throw new Error(`Plan not found: ${systemId}`);
215
222
  }
216
223
  if (!plan.spec.blocks) {
217
- console.warn('No blocks found in plan', systemId);
218
- return [];
224
+ throw new Error(`No blocks found in plan: ${systemId}`);
219
225
  }
220
- let promises = [];
221
- let errors = [];
222
- for (let blockInstance of Object.values(plan.spec.blocks)) {
223
- try {
224
- promises.push(this.start(systemId, blockInstance.id));
226
+ return taskManager_1.taskManager.add(`plan:start:${systemId}`, async () => {
227
+ let promises = [];
228
+ let errors = [];
229
+ for (let blockInstance of Object.values(plan.spec.blocks)) {
230
+ try {
231
+ promises.push(this.start(systemId, blockInstance.id).then((taskOrInstance) => {
232
+ if (taskOrInstance instanceof taskManager_1.Task) {
233
+ return taskOrInstance.wait();
234
+ }
235
+ return taskOrInstance;
236
+ }));
237
+ }
238
+ catch (e) {
239
+ errors.push(e);
240
+ }
225
241
  }
226
- catch (e) {
227
- errors.push(e);
242
+ const settled = await Promise.allSettled(promises);
243
+ if (errors.length > 0) {
244
+ throw errors[0];
228
245
  }
229
- }
230
- const settled = await Promise.allSettled(promises);
231
- if (errors.length > 0) {
232
- throw errors[0];
233
- }
234
- return settled.map((p) => (p.status === 'fulfilled' ? p.value : null)).filter((p) => !!p);
246
+ return settled
247
+ .map((p) => (p.status === 'fulfilled' ? p.value : null))
248
+ .filter((p) => !!p);
249
+ }, {
250
+ name: `Starting plan ${systemId}`,
251
+ });
235
252
  }
236
253
  async stop(systemId, instanceId) {
237
254
  return this.stopInner(systemId, instanceId, true);
@@ -288,10 +305,14 @@ class InstanceManager {
288
305
  }
289
306
  });
290
307
  }
291
- async stopAllForPlan(systemId) {
308
+ stopAllForPlan(systemId) {
292
309
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
293
310
  const instancesForPlan = this._instances.filter((instance) => instance.systemId === systemId);
294
- return this.stopInstances(instancesForPlan);
311
+ return taskManager_1.taskManager.add(`plan:stop:${systemId}`, async () => {
312
+ return this.stopInstances(instancesForPlan);
313
+ }, {
314
+ name: `Stopping plan ${systemId}`,
315
+ });
295
316
  }
296
317
  async start(systemId, instanceId) {
297
318
  return this.exclusive(systemId, instanceId, async () => {
@@ -367,47 +388,53 @@ class InstanceManager {
367
388
  }
368
389
  }
369
390
  const instanceConfig = await configManager_1.configManager.getConfigForSection(systemId, instanceId);
370
- const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
371
- const startTime = Date.now();
372
- try {
373
- const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
374
- instance.status = types_1.InstanceStatus.READY;
375
- return this.saveInternalInstance({
376
- ...instance,
377
- type: processInfo.type,
378
- pid: processInfo.pid ?? -1,
379
- health: null,
380
- portType: processInfo.portType,
381
- status: types_1.InstanceStatus.READY,
382
- });
383
- }
384
- catch (e) {
385
- console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e.message);
386
- const logs = [
387
- {
388
- source: 'stdout',
389
- level: 'ERROR',
390
- message: e.message,
391
- time: Date.now(),
392
- },
393
- ];
394
- const out = await this.saveInternalInstance({
395
- ...instance,
396
- type: types_1.InstanceType.LOCAL,
397
- pid: null,
398
- health: null,
399
- portType: DEFAULT_HEALTH_PORT_TYPE,
400
- status: types_1.InstanceStatus.FAILED,
401
- errorMessage: e.message ?? 'Failed to start - Check logs for details.',
402
- });
403
- this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
404
- this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
405
- error: `Failed to start instance: ${e.message}`,
406
- status: EVENT_INSTANCE_EXITED,
407
- instanceId: blockInstance.id,
408
- });
409
- return out;
410
- }
391
+ const task = taskManager_1.taskManager.add(`instance:start:${systemId}:${instanceId}`, async () => {
392
+ const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
393
+ const startTime = Date.now();
394
+ try {
395
+ const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
396
+ instance.status = types_1.InstanceStatus.READY;
397
+ return this.saveInternalInstance({
398
+ ...instance,
399
+ type: processInfo.type,
400
+ pid: processInfo.pid ?? -1,
401
+ health: null,
402
+ portType: processInfo.portType,
403
+ status: types_1.InstanceStatus.READY,
404
+ });
405
+ }
406
+ catch (e) {
407
+ console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e.message);
408
+ const logs = [
409
+ {
410
+ source: 'stdout',
411
+ level: 'ERROR',
412
+ message: e.message,
413
+ time: Date.now(),
414
+ },
415
+ ];
416
+ const out = await this.saveInternalInstance({
417
+ ...instance,
418
+ type: types_1.InstanceType.UNKNOWN,
419
+ pid: null,
420
+ health: null,
421
+ portType: DEFAULT_HEALTH_PORT_TYPE,
422
+ status: types_1.InstanceStatus.FAILED,
423
+ errorMessage: e.message ?? 'Failed to start - Check logs for details.',
424
+ });
425
+ this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
426
+ this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
427
+ error: `Failed to start instance: ${e.message}`,
428
+ status: EVENT_INSTANCE_EXITED,
429
+ instanceId: blockInstance.id,
430
+ });
431
+ return out;
432
+ }
433
+ }, {
434
+ name: `Starting instance: ${instance.name}`,
435
+ systemId,
436
+ });
437
+ return task;
411
438
  });
412
439
  }
413
440
  /**
@@ -10,6 +10,7 @@ const cors_1 = require("../middleware/cors");
10
10
  const kapeta_1 = require("../middleware/kapeta");
11
11
  const stringBody_1 = require("../middleware/stringBody");
12
12
  const types_1 = require("../types");
13
+ const taskManager_1 = require("../taskManager");
13
14
  const router = (0, express_promise_router_1.default)();
14
15
  router.use('/', cors_1.corsHandler);
15
16
  router.use('/', kapeta_1.kapetaHeaders);
@@ -29,33 +30,40 @@ router.get('/:systemId/instances', (req, res) => {
29
30
  * Start all instances in a plan
30
31
  */
31
32
  router.post('/:systemId/start', async (req, res) => {
32
- const instances = await instanceManager_1.instanceManager.startAllForPlan(req.params.systemId);
33
+ const task = await instanceManager_1.instanceManager.startAllForPlan(req.params.systemId);
33
34
  res.status(202).send({
34
35
  ok: true,
35
- processes: instances.map((p) => {
36
- return { pid: p.pid, type: p.type };
37
- }),
36
+ taskId: task.id,
38
37
  });
39
38
  });
40
39
  /**
41
40
  * Stop all instances in plan
42
41
  */
43
42
  router.post('/:systemId/stop', async (req, res) => {
44
- await instanceManager_1.instanceManager.stopAllForPlan(req.params.systemId);
43
+ const task = instanceManager_1.instanceManager.stopAllForPlan(req.params.systemId);
45
44
  res.status(202).send({
46
45
  ok: true,
46
+ taskId: task.id,
47
47
  });
48
48
  });
49
49
  /**
50
50
  * Start single instance in a plan
51
51
  */
52
52
  router.post('/:systemId/:instanceId/start', async (req, res) => {
53
- const process = await instanceManager_1.instanceManager.start(req.params.systemId, req.params.instanceId);
54
- res.status(202).send({
55
- ok: true,
56
- pid: process.pid,
57
- type: process.type,
58
- });
53
+ const result = await instanceManager_1.instanceManager.start(req.params.systemId, req.params.instanceId);
54
+ if (result instanceof taskManager_1.Task) {
55
+ res.status(202).send({
56
+ ok: true,
57
+ taskId: result.id,
58
+ });
59
+ }
60
+ else {
61
+ res.status(202).send({
62
+ ok: true,
63
+ pid: result.pid,
64
+ type: result.type,
65
+ });
66
+ }
59
67
  });
60
68
  /**
61
69
  * Stop single instance in a plan
@@ -122,8 +130,10 @@ router.put('/', async (req, res) => {
122
130
  const oldInstance = instanceManager_1.instanceManager.getInstance(req.kapeta.systemId, req.kapeta.instanceId);
123
131
  if (oldInstance) {
124
132
  instance.pid = oldInstance.pid;
133
+ instance.desiredStatus = oldInstance.desiredStatus;
125
134
  }
126
135
  instance.type = types_1.InstanceType.DOCKER;
136
+ instance.owner = types_1.InstanceOwner.INTERNAL;
127
137
  }
128
138
  else {
129
139
  // Coming from user starting the instance outside of kapeta
@@ -1,10 +1,12 @@
1
+ import { DefinitionInfo } from '@kapeta/local-cluster-config';
1
2
  import { ContainerInfo } from './containerManager';
2
3
  import { EnvironmentType, OperatorInfo } from './types';
3
4
  export declare const KIND_OPERATOR = "core/resource-type-operator";
4
5
  declare class Operator {
5
- private _data;
6
- constructor(data: any);
7
- getData(): any;
6
+ private readonly _data;
7
+ constructor(data: DefinitionInfo);
8
+ getLocalData(): any;
9
+ getDefinitionInfo(): DefinitionInfo;
8
10
  getCredentials(): any;
9
11
  }
10
12
  declare class OperatorManager {
@@ -15,17 +15,21 @@ const definitionsManager_1 = require("./definitionsManager");
15
15
  const utils_1 = require("./utils/utils");
16
16
  const lodash_1 = __importDefault(require("lodash"));
17
17
  const async_lock_1 = __importDefault(require("async-lock"));
18
+ const taskManager_1 = require("./taskManager");
18
19
  exports.KIND_OPERATOR = 'core/resource-type-operator';
19
20
  class Operator {
20
21
  _data;
21
22
  constructor(data) {
22
23
  this._data = data;
23
24
  }
24
- getData() {
25
+ getLocalData() {
26
+ return this._data.definition.spec.local;
27
+ }
28
+ getDefinitionInfo() {
25
29
  return this._data;
26
30
  }
27
31
  getCredentials() {
28
- return this._data.credentials;
32
+ return this._data.definition.spec.local.credentials;
29
33
  }
30
34
  }
31
35
  class OperatorManager {
@@ -58,7 +62,7 @@ class OperatorManager {
58
62
  if (!operator.definition.spec || !operator.definition.spec.local) {
59
63
  throw new Error(`Operator missing local definition: ${resourceType}:${version}`);
60
64
  }
61
- return new Operator(operator.definition.spec.local);
65
+ return new Operator(operator);
62
66
  }
63
67
  /**
64
68
  * Get information about a specific consumed resource
@@ -119,7 +123,7 @@ class OperatorManager {
119
123
  const key = `${systemId}#${resourceType}:${version}`;
120
124
  return await this.operatorLock.acquire(key, async () => {
121
125
  const operator = this.getOperator(resourceType, version);
122
- const operatorData = operator.getData();
126
+ const operatorData = operator.getLocalData();
123
127
  const portTypes = Object.keys(operatorData.ports);
124
128
  portTypes.sort();
125
129
  const ports = {};
@@ -147,6 +151,7 @@ class OperatorManager {
147
151
  const Labels = {
148
152
  kapeta: 'true',
149
153
  };
154
+ const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
150
155
  const bindHost = (0, utils_1.getBindHost)();
151
156
  const ExposedPorts = {};
152
157
  lodash_1.default.forEach(ports, (portInfo, containerPort) => {
@@ -163,25 +168,32 @@ class OperatorManager {
163
168
  lodash_1.default.forEach(operatorData.env, (value, name) => {
164
169
  Env.push(name + '=' + value);
165
170
  });
166
- let HealthCheck = undefined;
167
- if (operatorData.health) {
168
- HealthCheck = containerManager_1.containerManager.toDockerHealth(operatorData.health);
169
- }
170
- const container = await containerManager_1.containerManager.ensureContainer({
171
- name: containerName,
172
- Image: operatorData.image,
173
- Hostname: containerName + '.kapeta',
174
- Labels,
175
- Cmd: operatorData.cmd,
176
- ExposedPorts,
177
- Env,
178
- HealthCheck,
179
- HostConfig: {
180
- PortBindings,
181
- Mounts,
182
- },
171
+ const task = taskManager_1.taskManager.add(`operator:ensure:${key}`, async () => {
172
+ let HealthCheck = undefined;
173
+ if (operatorData.health) {
174
+ HealthCheck = containerManager_1.containerManager.toDockerHealth(operatorData.health);
175
+ }
176
+ const container = await containerManager_1.containerManager.ensureContainer({
177
+ name: containerName,
178
+ Image: operatorData.image,
179
+ Hostname: containerName + '.kapeta',
180
+ Labels,
181
+ Cmd: operatorData.cmd,
182
+ ExposedPorts,
183
+ Env,
184
+ HealthCheck,
185
+ HostConfig: {
186
+ PortBindings,
187
+ Mounts,
188
+ },
189
+ });
190
+ await containerManager_1.containerManager.waitForReady(container);
191
+ return new containerManager_1.ContainerInfo(container);
192
+ }, {
193
+ name: `Ensuring ${operatorMetadata.title ?? operatorMetadata.name}`,
194
+ systemId,
183
195
  });
184
- return new containerManager_1.ContainerInfo(container);
196
+ return task.wait();
185
197
  });
186
198
  }
187
199
  }
@@ -22,7 +22,7 @@ class ProviderManager {
22
22
  if (this._webAssetCache[id] && (await fs_extra_1.default.pathExists(this._webAssetCache[id]))) {
23
23
  return fs_extra_1.default.readFile(this._webAssetCache[id], 'utf8');
24
24
  }
25
- await repositoryManager_1.repositoryManager.ensureAsset(handle, name, version);
25
+ await repositoryManager_1.repositoryManager.ensureAsset(handle, name, version, true);
26
26
  const installedProvider = this.getWebProviders().find((providerDefinition) => {
27
27
  return providerDefinition.definition.metadata.name === fullName && providerDefinition.version === version;
28
28
  });
@@ -1,17 +1,15 @@
1
+ import { Task } from './taskManager';
1
2
  declare class RepositoryManager {
2
3
  private changeEventsEnabled;
3
4
  private _registryService;
4
5
  private _cache;
5
6
  private watcher?;
6
- private _installQueue;
7
- private _processing;
8
7
  constructor();
9
8
  setChangeEventsEnabled(enabled: boolean): void;
10
9
  listenForChanges(): void;
11
10
  stopListening(): void;
12
11
  private _install;
13
- _processNext(): Promise<void>;
14
- ensureAsset(handle: string, name: string, version: string): Promise<void>;
12
+ ensureAsset(handle: string, name: string, version: string, wait?: boolean): Promise<undefined | Task[]>;
15
13
  }
16
14
  export declare const repositoryManager: RepositoryManager;
17
15
  export {};
@@ -15,20 +15,20 @@ const socketManager_1 = require("./socketManager");
15
15
  const progressListener_1 = require("./progressListener");
16
16
  const nodejs_registry_utils_1 = require("@kapeta/nodejs-registry-utils");
17
17
  const definitionsManager_1 = require("./definitionsManager");
18
+ const taskManager_1 = require("./taskManager");
19
+ const utils_1 = require("./utils/utils");
20
+ const assetManager_1 = require("./assetManager");
18
21
  const INSTALL_ATTEMPTED = {};
19
22
  class RepositoryManager {
20
23
  changeEventsEnabled;
21
24
  _registryService;
22
25
  _cache;
23
26
  watcher;
24
- _installQueue;
25
- _processing = false;
26
27
  constructor() {
27
28
  this.changeEventsEnabled = true;
28
29
  this.listenForChanges();
29
30
  this._registryService = new nodejs_registry_utils_1.RegistryService(nodejs_registry_utils_1.Config.data.registry.url);
30
31
  this._cache = {};
31
- this._installQueue = [];
32
32
  }
33
33
  setChangeEventsEnabled(enabled) {
34
34
  this.changeEventsEnabled = enabled;
@@ -109,74 +109,54 @@ class RepositoryManager {
109
109
  }
110
110
  async _install(refs) {
111
111
  //We make sure to only install one asset at a time - otherwise unexpected things might happen
112
- const out = new Promise((resolve, reject) => {
113
- this._installQueue.push(async () => {
114
- try {
115
- const normalizedRefs = refs.map((ref) => (0, nodejs_utils_1.parseKapetaUri)(ref).id);
116
- const filteredRefs = normalizedRefs
117
- .filter((ref) => !INSTALL_ATTEMPTED[ref])
118
- .filter((ref) => !definitionsManager_1.definitionsManager.exists(ref));
119
- if (filteredRefs.length > 0) {
120
- console.log(`Auto-installing dependencies: ${filteredRefs.join(', ')}`);
121
- filteredRefs.forEach((ref) => (INSTALL_ATTEMPTED[ref] = true));
122
- //Auto-install missing asset
123
- try {
124
- //We change to a temp dir to avoid issues with the current working directory
125
- process.chdir(node_os_1.default.tmpdir());
126
- //Disable change events while installing
127
- this.setChangeEventsEnabled(false);
128
- socketManager_1.socketManager.emit(`install`, 'install:action', {
129
- type: 'start',
130
- refs,
131
- });
132
- await nodejs_registry_utils_1.Actions.install(progressListener_1.progressListener, normalizedRefs, {});
133
- socketManager_1.socketManager.emit(`install`, 'install:action', {
134
- type: 'done',
135
- refs,
136
- });
137
- }
138
- catch (e) {
139
- socketManager_1.socketManager.emit(`install`, 'install:action', {
140
- type: 'failed',
141
- refs,
142
- error: e.message,
143
- });
144
- }
145
- finally {
146
- this.setChangeEventsEnabled(true);
147
- }
148
- }
149
- resolve();
112
+ const createInstaller = (ref) => {
113
+ return async () => {
114
+ if (INSTALL_ATTEMPTED[ref]) {
115
+ return;
150
116
  }
151
- catch (e) {
152
- reject(e);
117
+ if (definitionsManager_1.definitionsManager.exists(ref)) {
118
+ return;
153
119
  }
154
- finally {
155
- this._processNext().catch((e) => console.error(e));
120
+ console.log(`Installing asset: ${ref}`);
121
+ INSTALL_ATTEMPTED[ref] = true;
122
+ //Auto-install missing asset
123
+ try {
124
+ //We change to a temp dir to avoid issues with the current working directory
125
+ process.chdir(node_os_1.default.tmpdir());
126
+ //Disable change events while installing
127
+ this.setChangeEventsEnabled(false);
128
+ await nodejs_registry_utils_1.Actions.install(progressListener_1.progressListener, [ref], {});
156
129
  }
157
- });
158
- });
159
- this._processNext().catch((e) => console.error(e));
160
- return out;
161
- }
162
- async _processNext() {
163
- if (this._processing) {
164
- return;
165
- }
166
- this._processing = true;
167
- try {
168
- while (this._installQueue.length > 0) {
169
- const item = this._installQueue.shift();
170
- if (item) {
171
- await item();
130
+ finally {
131
+ this.setChangeEventsEnabled(true);
172
132
  }
133
+ definitionsManager_1.definitionsManager.clearCache();
134
+ assetManager_1.assetManager.clearCache();
135
+ console.log(`Asset installed: ${ref}`);
136
+ };
137
+ };
138
+ const tasks = [];
139
+ while (refs.length > 0) {
140
+ let ref = refs.shift();
141
+ if (!ref) {
142
+ continue;
173
143
  }
144
+ ref = (0, utils_1.normalizeKapetaUri)(ref);
145
+ if (INSTALL_ATTEMPTED[ref]) {
146
+ continue;
147
+ }
148
+ if (definitionsManager_1.definitionsManager.exists(ref)) {
149
+ continue;
150
+ }
151
+ const task = taskManager_1.taskManager.add(`asset:install:${ref}`, createInstaller(ref), {
152
+ name: `Installing ${ref}`,
153
+ group: 'asset:install:',
154
+ });
155
+ tasks.push(task);
174
156
  }
175
- finally {
176
- this._processing = false;
177
- }
157
+ return tasks;
178
158
  }
179
- async ensureAsset(handle, name, version) {
159
+ async ensureAsset(handle, name, version, wait = true) {
180
160
  const fullName = `${handle}/${name}`;
181
161
  const ref = `${fullName}:${version}`;
182
162
  if (version === 'local') {
@@ -207,16 +187,21 @@ class RepositoryManager {
207
187
  throw e;
208
188
  }
209
189
  this._cache[ref] = true;
190
+ let tasks = undefined;
210
191
  if (!installedAsset) {
211
- await this._install([ref]);
192
+ tasks = await this._install([ref]);
212
193
  }
213
194
  else {
214
195
  //Ensure dependencies are installed
215
196
  const refs = assetVersion.dependencies.map((dep) => dep.name);
216
197
  if (refs.length > 0) {
217
- await this._install(refs);
198
+ tasks = await this._install(refs);
218
199
  }
219
200
  }
201
+ if (tasks && wait) {
202
+ await Promise.all(tasks.map((t) => t.future.promise));
203
+ }
204
+ return tasks;
220
205
  }
221
206
  }
222
207
  exports.repositoryManager = new RepositoryManager();
@@ -30,7 +30,7 @@ class SocketManager {
30
30
  this.io.to(context).emit(type, { context, payload });
31
31
  }
32
32
  emitGlobal(type, payload) {
33
- this.io.emit(type, { payload });
33
+ this.io.emit(type, payload);
34
34
  }
35
35
  _bindIO() {
36
36
  this.io.on('connection', (socket) => this._handleSocketCreated(socket));
@@ -0,0 +1,64 @@
1
+ export type TaskRunner<T> = (task: Task<T>) => Promise<T>;
2
+ export declare enum TaskStatus {
3
+ PENDING = "PENDING",
4
+ RUNNING = "RUNNING",
5
+ COMPLETED = "COMPLETED",
6
+ FAILED = "FAILED"
7
+ }
8
+ interface Future<T = void> {
9
+ promise: Promise<T>;
10
+ resolve: (result: T) => void;
11
+ reject: (e: any) => void;
12
+ }
13
+ interface TaskMetadata {
14
+ name: string;
15
+ /**
16
+ * A unique prefix for the task. If defined only 1 task with this ID prefix will be executed at a time
17
+ */
18
+ group?: string;
19
+ progress?: number;
20
+ [key: string]: any;
21
+ }
22
+ interface TaskData<T = void> {
23
+ id: string;
24
+ status: TaskStatus;
25
+ errorMessage?: string;
26
+ metadata: TaskMetadata;
27
+ future: Future<T>;
28
+ run: TaskRunner<T>;
29
+ }
30
+ export declare class Task<T = void> implements TaskData<T> {
31
+ private data;
32
+ constructor(task: TaskData<T>);
33
+ get id(): string;
34
+ get status(): TaskStatus;
35
+ get errorMessage(): string | undefined;
36
+ get metadata(): TaskMetadata;
37
+ get future(): Future<T>;
38
+ get run(): TaskRunner<T>;
39
+ set status(status: TaskStatus);
40
+ set errorMessage(errorMessage: string | undefined);
41
+ set metadata(metadata: TaskMetadata);
42
+ emitUpdate(): void;
43
+ wait(): Promise<T>;
44
+ toData(): {
45
+ id: string;
46
+ status: TaskStatus;
47
+ errorMessage?: string | undefined;
48
+ metadata: TaskMetadata;
49
+ future: Future<T>;
50
+ run: TaskRunner<T>;
51
+ };
52
+ }
53
+ declare class TaskManager {
54
+ private _tasks;
55
+ add<T>(id: string, runner: TaskRunner<T>, metadata: TaskMetadata): Task<T>;
56
+ waitFor(filter: (task: Task<any>) => boolean): Promise<void>;
57
+ get(taskId: string): Task<any> | undefined;
58
+ exists(taskId: string): boolean;
59
+ remove(taskId: string): void;
60
+ list(): TaskData[];
61
+ private invokeTask;
62
+ }
63
+ export declare const taskManager: TaskManager;
64
+ export {};