@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
@@ -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
|
-
|
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(
|
221
|
+
throw new Error(`Plan not found: ${systemId}`);
|
215
222
|
}
|
216
223
|
if (!plan.spec.blocks) {
|
217
|
-
|
218
|
-
return [];
|
224
|
+
throw new Error(`No blocks found in plan: ${systemId}`);
|
219
225
|
}
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
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
|
-
|
227
|
-
|
242
|
+
const settled = await Promise.allSettled(promises);
|
243
|
+
if (errors.length > 0) {
|
244
|
+
throw errors[0];
|
228
245
|
}
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
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
|
-
|
308
|
+
stopAllForPlan(systemId) {
|
292
309
|
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
293
310
|
const instancesForPlan = this._instances.filter((instance) => instance.systemId === systemId);
|
294
|
-
return
|
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
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
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
|
33
|
+
const task = await instanceManager_1.instanceManager.startAllForPlan(req.params.systemId);
|
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
|
/**
|
41
40
|
* Stop all instances in plan
|
42
41
|
*/
|
43
42
|
router.post('/:systemId/stop', async (req, res) => {
|
44
|
-
|
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
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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:
|
7
|
-
|
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
|
-
|
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
|
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.
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
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
|
-
|
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
|
113
|
-
|
114
|
-
|
115
|
-
|
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
|
-
|
152
|
-
|
117
|
+
if (definitionsManager_1.definitionsManager.exists(ref)) {
|
118
|
+
return;
|
153
119
|
}
|
154
|
-
|
155
|
-
|
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
|
-
|
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,
|
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 {};
|