@kapeta/local-cluster-service 0.19.6 → 0.20.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/src/config/routes.js +2 -3
- package/dist/cjs/src/configManager.d.ts +1 -0
- package/dist/cjs/src/configManager.js +2 -1
- package/dist/cjs/src/containerManager.d.ts +39 -32
- package/dist/cjs/src/containerManager.js +138 -108
- package/dist/cjs/src/instanceManager.js +30 -19
- package/dist/cjs/src/operatorManager.js +3 -0
- package/dist/cjs/src/taskManager.js +4 -1
- package/dist/cjs/src/utils/BlockInstanceRunner.js +9 -0
- package/dist/cjs/src/utils/utils.d.ts +3 -0
- package/dist/cjs/src/utils/utils.js +25 -1
- package/dist/esm/src/config/routes.js +2 -3
- package/dist/esm/src/configManager.d.ts +1 -0
- package/dist/esm/src/configManager.js +2 -1
- package/dist/esm/src/containerManager.d.ts +39 -32
- package/dist/esm/src/containerManager.js +138 -108
- package/dist/esm/src/instanceManager.js +30 -19
- package/dist/esm/src/operatorManager.js +3 -0
- package/dist/esm/src/taskManager.js +4 -1
- package/dist/esm/src/utils/BlockInstanceRunner.js +9 -0
- package/dist/esm/src/utils/utils.d.ts +3 -0
- package/dist/esm/src/utils/utils.js +25 -1
- package/package.json +5 -2
- package/src/config/routes.ts +2 -4
- package/src/configManager.ts +1 -0
- package/src/containerManager.ts +188 -140
- package/src/instanceManager.ts +51 -22
- package/src/operatorManager.ts +11 -1
- package/src/taskManager.ts +4 -1
- package/src/utils/BlockInstanceRunner.ts +19 -3
- package/src/utils/utils.ts +29 -0
@@ -3,13 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.containerManager = exports.toLocalBindVolume = exports.getExtraHosts = exports.ContainerInfo = exports.HEALTH_CHECK_TIMEOUT = exports.CONTAINER_LABEL_PORT_PREFIX = void 0;
|
6
|
+
exports.containerManager = exports.toLocalBindVolume = exports.getExtraHosts = exports.ContainerInfo = exports.HEALTH_CHECK_TIMEOUT = exports.COMPOSE_LABEL_SERVICE = exports.COMPOSE_LABEL_PROJECT = exports.CONTAINER_LABEL_PORT_PREFIX = void 0;
|
7
7
|
const path_1 = __importDefault(require("path"));
|
8
8
|
const storageService_1 = require("./storageService");
|
9
9
|
const os_1 = __importDefault(require("os"));
|
10
10
|
const lodash_1 = __importDefault(require("lodash"));
|
11
11
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
12
|
-
const
|
12
|
+
const dockerode_1 = __importDefault(require("dockerode"));
|
13
13
|
const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
14
14
|
const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-config"));
|
15
15
|
const node_uuid_1 = __importDefault(require("node-uuid"));
|
@@ -18,15 +18,45 @@ const utils_1 = require("./utils/utils");
|
|
18
18
|
const nodejs_api_client_1 = require("@kapeta/nodejs-api-client");
|
19
19
|
const taskManager_1 = require("./taskManager");
|
20
20
|
const node_events_1 = require("node:events");
|
21
|
+
const StreamValues_1 = __importDefault(require("stream-json/streamers/StreamValues"));
|
21
22
|
exports.CONTAINER_LABEL_PORT_PREFIX = 'kapeta_port-';
|
22
23
|
const NANO_SECOND = 1000000;
|
23
24
|
const HEALTH_CHECK_INTERVAL = 3000;
|
24
25
|
const HEALTH_CHECK_MAX = 20;
|
26
|
+
exports.COMPOSE_LABEL_PROJECT = 'com.docker.compose.project';
|
27
|
+
exports.COMPOSE_LABEL_SERVICE = 'com.docker.compose.service';
|
25
28
|
exports.HEALTH_CHECK_TIMEOUT = HEALTH_CHECK_INTERVAL * HEALTH_CHECK_MAX * 2;
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
var DockerPullEventTypes;
|
30
|
+
(function (DockerPullEventTypes) {
|
31
|
+
DockerPullEventTypes["PreparingPhase"] = "Preparing";
|
32
|
+
DockerPullEventTypes["WaitingPhase"] = "Waiting";
|
33
|
+
DockerPullEventTypes["PullingFsPhase"] = "Pulling fs layer";
|
34
|
+
DockerPullEventTypes["DownloadingPhase"] = "Downloading";
|
35
|
+
DockerPullEventTypes["DownloadCompletePhase"] = "Download complete";
|
36
|
+
DockerPullEventTypes["ExtractingPhase"] = "Extracting";
|
37
|
+
DockerPullEventTypes["VerifyingChecksumPhase"] = "Verifying Checksum";
|
38
|
+
DockerPullEventTypes["AlreadyExistsPhase"] = "Already exists";
|
39
|
+
DockerPullEventTypes["PullCompletePhase"] = "Pull complete";
|
40
|
+
})(DockerPullEventTypes || (DockerPullEventTypes = {}));
|
41
|
+
const processJsonStream = (purpose, stream, handler) => new Promise((resolve, reject) => {
|
42
|
+
const jsonStream = StreamValues_1.default.withParser();
|
43
|
+
jsonStream.on('data', (data) => {
|
44
|
+
try {
|
45
|
+
handler(data.value);
|
46
|
+
}
|
47
|
+
catch (e) {
|
48
|
+
console.error('Failed while processing data for stream: %s', purpose, e);
|
49
|
+
}
|
50
|
+
});
|
51
|
+
jsonStream.on('end', () => {
|
52
|
+
console.log('Docker stream ended: %s', purpose);
|
53
|
+
resolve();
|
54
|
+
});
|
55
|
+
jsonStream.on('error', (err) => {
|
56
|
+
console.error('Docker stream failed: %s', purpose, err);
|
57
|
+
reject(err);
|
58
|
+
});
|
59
|
+
stream.pipe(jsonStream);
|
30
60
|
});
|
31
61
|
class ContainerManager {
|
32
62
|
_docker;
|
@@ -64,9 +94,9 @@ class ContainerManager {
|
|
64
94
|
];
|
65
95
|
for (const opts of connectOptions) {
|
66
96
|
try {
|
67
|
-
const client = new
|
97
|
+
const client = new dockerode_1.default({
|
68
98
|
...opts,
|
69
|
-
timeout:
|
99
|
+
timeout: 15 * 60 * 1000, //15 minutes should be enough for any operation
|
70
100
|
});
|
71
101
|
await client.ping();
|
72
102
|
this._docker = client;
|
@@ -143,13 +173,12 @@ class ContainerManager {
|
|
143
173
|
return this._docker;
|
144
174
|
}
|
145
175
|
async getContainerByName(containerName) {
|
146
|
-
const containers = await this.docker().
|
176
|
+
const containers = await this.docker().listContainers({ all: true });
|
147
177
|
const out = containers.find((container) => {
|
148
|
-
|
149
|
-
return containerData.Names.indexOf(`/${containerName}`) > -1;
|
178
|
+
return container.Names.indexOf(`/${containerName}`) > -1;
|
150
179
|
});
|
151
180
|
if (out) {
|
152
|
-
return
|
181
|
+
return this.get(out.Id);
|
153
182
|
}
|
154
183
|
return undefined;
|
155
184
|
}
|
@@ -158,8 +187,7 @@ class ContainerManager {
|
|
158
187
|
if (!tag) {
|
159
188
|
tag = 'latest';
|
160
189
|
}
|
161
|
-
const imageTagList = (await this.docker().
|
162
|
-
.map((image) => image.data)
|
190
|
+
const imageTagList = (await this.docker().listImages({}))
|
163
191
|
.filter((imageData) => !!imageData.RepoTags)
|
164
192
|
.map((imageData) => imageData.RepoTags);
|
165
193
|
if (imageTagList.some((imageTags) => imageTags.indexOf(image) > -1)) {
|
@@ -184,66 +212,61 @@ class ContainerManager {
|
|
184
212
|
serveraddress: 'docker.kapeta.com',
|
185
213
|
}
|
186
214
|
: {};
|
187
|
-
const stream =
|
188
|
-
|
189
|
-
|
190
|
-
}));
|
215
|
+
const stream = await this.docker().pull(image, {
|
216
|
+
authconfig: auth,
|
217
|
+
});
|
191
218
|
const chunks = {};
|
192
219
|
let lastEmitted = Date.now();
|
193
|
-
await
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
220
|
+
await processJsonStream(`image:pull:${image}`, stream, (data) => {
|
221
|
+
if (!chunks[data.id]) {
|
222
|
+
chunks[data.id] = {
|
223
|
+
downloading: {
|
224
|
+
total: 0,
|
225
|
+
current: 0,
|
226
|
+
},
|
227
|
+
extracting: {
|
228
|
+
total: 0,
|
229
|
+
current: 0,
|
230
|
+
},
|
231
|
+
done: false,
|
232
|
+
};
|
233
|
+
}
|
234
|
+
const chunk = chunks[data.id];
|
235
|
+
switch (data.status) {
|
236
|
+
case DockerPullEventTypes.PreparingPhase:
|
237
|
+
case DockerPullEventTypes.WaitingPhase:
|
238
|
+
case DockerPullEventTypes.PullingFsPhase:
|
239
|
+
//Do nothing
|
240
|
+
break;
|
241
|
+
case DockerPullEventTypes.DownloadingPhase:
|
242
|
+
case DockerPullEventTypes.VerifyingChecksumPhase:
|
243
|
+
chunk.downloading = {
|
244
|
+
total: data.progressDetail?.total ?? 0,
|
245
|
+
current: data.progressDetail?.current ?? 0,
|
218
246
|
};
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
break;
|
243
|
-
}
|
244
|
-
});
|
245
|
-
if (Date.now() - lastEmitted < 1000) {
|
246
|
-
return;
|
247
|
+
break;
|
248
|
+
case DockerPullEventTypes.ExtractingPhase:
|
249
|
+
chunk.extracting = {
|
250
|
+
total: data.progressDetail?.total ?? 0,
|
251
|
+
current: data.progressDetail?.current ?? 0,
|
252
|
+
};
|
253
|
+
break;
|
254
|
+
case DockerPullEventTypes.DownloadCompletePhase:
|
255
|
+
chunk.downloading.current = chunks[data.id].downloading.total;
|
256
|
+
break;
|
257
|
+
case DockerPullEventTypes.PullCompletePhase:
|
258
|
+
chunk.extracting.current = chunks[data.id].extracting.total;
|
259
|
+
chunk.done = true;
|
260
|
+
break;
|
261
|
+
}
|
262
|
+
if (data.status === DockerPullEventTypes.AlreadyExistsPhase ||
|
263
|
+
data.status.includes('Image is up to date') ||
|
264
|
+
data.status.includes('Downloaded newer image')) {
|
265
|
+
chunk.downloading.current = 1;
|
266
|
+
chunk.downloading.total = 1;
|
267
|
+
chunk.extracting.current = 1;
|
268
|
+
chunk.extracting.total = 1;
|
269
|
+
chunk.done = true;
|
247
270
|
}
|
248
271
|
const chunkList = Object.values(chunks);
|
249
272
|
let totals = {
|
@@ -255,6 +278,7 @@ class ContainerManager {
|
|
255
278
|
total: 0,
|
256
279
|
current: 0,
|
257
280
|
},
|
281
|
+
percent: 0,
|
258
282
|
total: chunkList.length,
|
259
283
|
done: 0,
|
260
284
|
};
|
@@ -275,14 +299,17 @@ class ContainerManager {
|
|
275
299
|
totals.done++;
|
276
300
|
}
|
277
301
|
});
|
278
|
-
|
302
|
+
totals.percent = totals.total > 0 ? (totals.done / totals.total) * 100 : 0;
|
279
303
|
task.metadata = {
|
280
304
|
...task.metadata,
|
281
305
|
image,
|
282
|
-
progress,
|
306
|
+
progress: totals.percent,
|
283
307
|
status: totals,
|
284
308
|
timeTaken: Date.now() - timeStarted,
|
285
309
|
};
|
310
|
+
if (Date.now() - lastEmitted < 1000) {
|
311
|
+
return;
|
312
|
+
}
|
286
313
|
task.emitUpdate();
|
287
314
|
lastEmitted = Date.now();
|
288
315
|
//console.log('Pulling image %s: %s % [done: %s, total: %s]', image, Math.round(percent), totals.done, totals.total);
|
@@ -299,6 +326,7 @@ class ContainerManager {
|
|
299
326
|
name: taskName,
|
300
327
|
image,
|
301
328
|
progress: -1,
|
329
|
+
group: 'docker:pull', //It's faster to pull images one at a time
|
302
330
|
});
|
303
331
|
await task.wait();
|
304
332
|
return true;
|
@@ -344,32 +372,32 @@ class ContainerManager {
|
|
344
372
|
console.log('Starting unnamed container: %s', opts.Image);
|
345
373
|
return this.startContainer(opts);
|
346
374
|
}
|
347
|
-
const
|
375
|
+
const container = await this.getContainerByName(opts.name);
|
348
376
|
if (imagePulled) {
|
377
|
+
// If image was pulled always recreate
|
349
378
|
console.log('New version of image was pulled: %s', opts.Image);
|
350
379
|
}
|
351
380
|
else {
|
352
|
-
|
353
|
-
if (!containerInfo) {
|
381
|
+
if (!container) {
|
354
382
|
console.log('Starting new container: %s', opts.name);
|
355
383
|
return this.startContainer(opts);
|
356
384
|
}
|
357
|
-
const containerData =
|
358
|
-
if (containerData?.Labels?.HASH === opts.Labels.HASH) {
|
359
|
-
if (!(await
|
385
|
+
const containerData = await container.inspect();
|
386
|
+
if (containerData?.Config.Labels?.HASH === opts.Labels.HASH) {
|
387
|
+
if (!(await container.isRunning())) {
|
360
388
|
console.log('Starting previously created container: %s', opts.name);
|
361
|
-
await
|
389
|
+
await container.start();
|
362
390
|
}
|
363
391
|
else {
|
364
392
|
console.log('Previously created container already running: %s', opts.name);
|
365
393
|
}
|
366
|
-
return
|
394
|
+
return container.native;
|
367
395
|
}
|
368
396
|
}
|
369
|
-
if (
|
397
|
+
if (container) {
|
370
398
|
// Remove the container and start a new one
|
371
399
|
console.log('Replacing previously created container: %s', opts.name);
|
372
|
-
await
|
400
|
+
await container.remove({ force: true });
|
373
401
|
}
|
374
402
|
console.log('Starting new container: %s', opts.name);
|
375
403
|
return this.startContainer(opts);
|
@@ -385,7 +413,7 @@ class ContainerManager {
|
|
385
413
|
}
|
386
414
|
opts.HostConfig.ExtraHosts = opts.HostConfig.ExtraHosts.concat(extraHosts);
|
387
415
|
}
|
388
|
-
const dockerContainer = await this.docker().
|
416
|
+
const dockerContainer = await this.docker().createContainer(opts);
|
389
417
|
await dockerContainer.start();
|
390
418
|
return dockerContainer;
|
391
419
|
}
|
@@ -414,30 +442,28 @@ class ContainerManager {
|
|
414
442
|
async _isReady(container) {
|
415
443
|
let info;
|
416
444
|
try {
|
417
|
-
info = await container.
|
445
|
+
info = await container.inspect();
|
418
446
|
}
|
419
447
|
catch (err) {
|
420
448
|
return false;
|
421
449
|
}
|
422
|
-
const
|
423
|
-
|
424
|
-
if (state?.Status === 'exited' || state?.Status === 'removing' || state?.Status === 'dead') {
|
450
|
+
const state = info.State;
|
451
|
+
if (state.Status === 'exited' || state?.Status === 'removing' || state?.Status === 'dead') {
|
425
452
|
throw new Error('Container exited unexpectedly');
|
426
453
|
}
|
427
|
-
if (
|
454
|
+
if (state.Health) {
|
428
455
|
// If container has health info - wait for it to become healthy
|
429
|
-
return
|
456
|
+
return state.Health.Status === 'healthy';
|
430
457
|
}
|
431
458
|
else {
|
432
|
-
return
|
459
|
+
return state.Running ?? false;
|
433
460
|
}
|
434
461
|
}
|
435
462
|
async remove(container, opts) {
|
436
463
|
const newName = 'deleting-' + node_uuid_1.default.v4();
|
437
|
-
const containerData = container.data;
|
438
464
|
// Rename the container first to avoid name conflicts if people start the same container
|
439
465
|
await container.rename({ name: newName });
|
440
|
-
await container.
|
466
|
+
await container.remove({ force: !!opts?.force });
|
441
467
|
}
|
442
468
|
/**
|
443
469
|
*
|
@@ -447,15 +473,15 @@ class ContainerManager {
|
|
447
473
|
async get(name) {
|
448
474
|
let dockerContainer = null;
|
449
475
|
try {
|
450
|
-
dockerContainer = await this.docker().
|
451
|
-
await dockerContainer.
|
476
|
+
dockerContainer = await this.docker().getContainer(name);
|
477
|
+
await dockerContainer.stats();
|
452
478
|
}
|
453
479
|
catch (err) {
|
454
480
|
//Ignore
|
455
481
|
dockerContainer = null;
|
456
482
|
}
|
457
483
|
if (!dockerContainer) {
|
458
|
-
return
|
484
|
+
return undefined;
|
459
485
|
}
|
460
486
|
return new ContainerInfo(dockerContainer);
|
461
487
|
}
|
@@ -647,12 +673,12 @@ class ContainerInfo {
|
|
647
673
|
_container;
|
648
674
|
/**
|
649
675
|
*
|
650
|
-
* @param {Container} dockerContainer
|
676
|
+
* @param {Docker.Container} dockerContainer
|
651
677
|
*/
|
652
678
|
constructor(dockerContainer) {
|
653
679
|
/**
|
654
680
|
*
|
655
|
-
* @type {Container}
|
681
|
+
* @type {Docker.Container}
|
656
682
|
* @private
|
657
683
|
*/
|
658
684
|
this._container = dockerContainer;
|
@@ -668,12 +694,21 @@ class ContainerInfo {
|
|
668
694
|
return inspectResult.State.Running || inspectResult.State.Restarting;
|
669
695
|
}
|
670
696
|
async start() {
|
697
|
+
if (await this.isRunning()) {
|
698
|
+
return;
|
699
|
+
}
|
671
700
|
await this._container.start();
|
672
701
|
}
|
673
702
|
async restart() {
|
703
|
+
if (!(await this.isRunning())) {
|
704
|
+
return this.start();
|
705
|
+
}
|
674
706
|
await this._container.restart();
|
675
707
|
}
|
676
708
|
async stop() {
|
709
|
+
if (!(await this.isRunning())) {
|
710
|
+
return;
|
711
|
+
}
|
677
712
|
await this._container.stop();
|
678
713
|
}
|
679
714
|
async remove(opts) {
|
@@ -688,16 +723,15 @@ class ContainerInfo {
|
|
688
723
|
}
|
689
724
|
async inspect() {
|
690
725
|
try {
|
691
|
-
|
692
|
-
return result ? result.data : null;
|
726
|
+
return await this._container.inspect();
|
693
727
|
}
|
694
728
|
catch (err) {
|
695
|
-
return
|
729
|
+
return undefined;
|
696
730
|
}
|
697
731
|
}
|
698
732
|
async status() {
|
699
733
|
const result = await this.inspect();
|
700
|
-
return result
|
734
|
+
return result?.State;
|
701
735
|
}
|
702
736
|
async getPorts() {
|
703
737
|
const inspectResult = await this.inspect();
|
@@ -742,17 +776,13 @@ class ContainerInfo {
|
|
742
776
|
}
|
743
777
|
}
|
744
778
|
async getLogs() {
|
745
|
-
const
|
779
|
+
const logs = await this.native.logs({
|
746
780
|
stdout: true,
|
747
781
|
stderr: true,
|
748
782
|
follow: false,
|
749
783
|
timestamps: true,
|
750
|
-
}));
|
751
|
-
const chunks = [];
|
752
|
-
await promisifyStream(logStream, (data) => {
|
753
|
-
chunks.push(data);
|
754
784
|
});
|
755
|
-
const out = readLogBuffer(
|
785
|
+
const out = readLogBuffer(logs);
|
756
786
|
if (out.length === 0) {
|
757
787
|
out.push({
|
758
788
|
time: Date.now(),
|
@@ -262,6 +262,9 @@ class InstanceManager {
|
|
262
262
|
if (instance.status === types_1.InstanceStatus.STOPPED) {
|
263
263
|
return;
|
264
264
|
}
|
265
|
+
if (instance.status === types_1.InstanceStatus.STOPPING) {
|
266
|
+
return;
|
267
|
+
}
|
265
268
|
if (changeDesired && instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
|
266
269
|
instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
|
267
270
|
}
|
@@ -387,23 +390,24 @@ class InstanceManager {
|
|
387
390
|
}
|
388
391
|
}
|
389
392
|
const instanceConfig = await configManager_1.configManager.getConfigForSection(systemId, instanceId);
|
393
|
+
const resolvedConfig = (0, utils_1.getResolvedConfiguration)(blockSpec.configuration, instanceConfig, blockInstance.defaultConfiguration);
|
390
394
|
const task = taskManager_1.taskManager.add(`instance:start:${systemId}:${instanceId}`, async () => {
|
391
395
|
const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
|
392
396
|
const startTime = Date.now();
|
393
397
|
try {
|
394
|
-
const processInfo = await runner.start(blockRef, instanceId,
|
395
|
-
instance.status = types_1.InstanceStatus.
|
398
|
+
const processInfo = await runner.start(blockRef, instanceId, resolvedConfig);
|
399
|
+
instance.status = types_1.InstanceStatus.STARTING;
|
396
400
|
return this.saveInternalInstance({
|
397
401
|
...instance,
|
398
402
|
type: processInfo.type,
|
399
403
|
pid: processInfo.pid ?? -1,
|
400
404
|
health: null,
|
401
405
|
portType: processInfo.portType,
|
402
|
-
status: types_1.InstanceStatus.
|
406
|
+
status: types_1.InstanceStatus.STARTING,
|
403
407
|
});
|
404
408
|
}
|
405
409
|
catch (e) {
|
406
|
-
console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e
|
410
|
+
console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e);
|
407
411
|
const logs = [
|
408
412
|
{
|
409
413
|
source: 'stdout',
|
@@ -550,7 +554,7 @@ class InstanceManager {
|
|
550
554
|
await this.start(instance.systemId, instance.instanceId);
|
551
555
|
}
|
552
556
|
catch (e) {
|
553
|
-
console.warn('Failed to start instance', instance.systemId, instance.instanceId, e);
|
557
|
+
console.warn('Failed to start previously stopped instance', instance.systemId, instance.instanceId, e);
|
554
558
|
}
|
555
559
|
return;
|
556
560
|
}
|
@@ -594,31 +598,38 @@ class InstanceManager {
|
|
594
598
|
return types_1.InstanceStatus.STOPPED;
|
595
599
|
}
|
596
600
|
const state = await container.status();
|
597
|
-
if (state
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
601
|
+
if (!state) {
|
602
|
+
return types_1.InstanceStatus.STOPPED;
|
603
|
+
}
|
604
|
+
const statusType = state.Status;
|
605
|
+
if (statusType === 'running') {
|
606
|
+
if (state.Health?.Status) {
|
607
|
+
const healthStatusType = state.Health.Status;
|
608
|
+
if (healthStatusType === 'healthy' || healthStatusType === 'none') {
|
609
|
+
return types_1.InstanceStatus.READY;
|
610
|
+
}
|
611
|
+
if (healthStatusType === 'starting') {
|
612
|
+
return types_1.InstanceStatus.STARTING;
|
613
|
+
}
|
614
|
+
if (healthStatusType === 'unhealthy') {
|
615
|
+
return types_1.InstanceStatus.UNHEALTHY;
|
616
|
+
}
|
606
617
|
}
|
607
618
|
return types_1.InstanceStatus.READY;
|
608
619
|
}
|
609
|
-
if (
|
620
|
+
if (statusType === 'created') {
|
610
621
|
return types_1.InstanceStatus.STARTING;
|
611
622
|
}
|
612
|
-
if (
|
623
|
+
if (statusType === 'exited' || statusType === 'dead') {
|
613
624
|
return types_1.InstanceStatus.STOPPED;
|
614
625
|
}
|
615
|
-
if (
|
626
|
+
if (statusType === 'removing') {
|
616
627
|
return types_1.InstanceStatus.BUSY;
|
617
628
|
}
|
618
|
-
if (
|
629
|
+
if (statusType === 'restarting') {
|
619
630
|
return types_1.InstanceStatus.BUSY;
|
620
631
|
}
|
621
|
-
if (
|
632
|
+
if (statusType === 'paused') {
|
622
633
|
return types_1.InstanceStatus.BUSY;
|
623
634
|
}
|
624
635
|
return types_1.InstanceStatus.STOPPED;
|
@@ -146,8 +146,11 @@ class OperatorManager {
|
|
146
146
|
const containerName = `kapeta-resource-${(0, md5_1.default)(nameParts.join('_'))}`;
|
147
147
|
const PortBindings = {};
|
148
148
|
const Env = [];
|
149
|
+
const systemUri = (0, nodejs_utils_1.parseKapetaUri)(systemId);
|
149
150
|
const Labels = {
|
150
151
|
kapeta: 'true',
|
152
|
+
[containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
153
|
+
[containerManager_1.COMPOSE_LABEL_SERVICE]: [resourceType, version].join('_').replace(/[^a-z0-9]/gi, '_'),
|
151
154
|
};
|
152
155
|
const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
|
153
156
|
const bindHost = (0, utils_1.getBindHost)();
|
@@ -90,7 +90,9 @@ class TaskManager {
|
|
90
90
|
});
|
91
91
|
this._tasks.push(task);
|
92
92
|
socketManager_1.socketManager.emitGlobal(EVENT_TASK_ADDED, task.toData());
|
93
|
-
this.invokeTask(task).catch(() => {
|
93
|
+
this.invokeTask(task).catch((err) => {
|
94
|
+
console.warn(`Task ${task.id} failed`, err);
|
95
|
+
});
|
94
96
|
return task;
|
95
97
|
}
|
96
98
|
async waitFor(filter) {
|
@@ -145,6 +147,7 @@ class TaskManager {
|
|
145
147
|
task.emitUpdate();
|
146
148
|
}
|
147
149
|
catch (e) {
|
150
|
+
console.warn(`Task ${task.id} failed while waiting for it to resolve`, e);
|
148
151
|
task.errorMessage = e.message;
|
149
152
|
task.status = TaskStatus.FAILED;
|
150
153
|
task.future.reject(e);
|
@@ -146,6 +146,7 @@ class BlockInstanceRunner {
|
|
146
146
|
if (localContainer.healthcheck) {
|
147
147
|
HealthCheck = containerManager_1.containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
|
148
148
|
}
|
149
|
+
const systemUri = (0, nodejs_utils_1.parseKapetaUri)(this._systemId);
|
149
150
|
return this.ensureContainer({
|
150
151
|
...dockerOpts,
|
151
152
|
Image: dockerImage,
|
@@ -154,6 +155,8 @@ class BlockInstanceRunner {
|
|
154
155
|
Labels: {
|
155
156
|
...customLabels,
|
156
157
|
instance: blockInstance.id,
|
158
|
+
[containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
159
|
+
[containerManager_1.COMPOSE_LABEL_SERVICE]: blockInfo.id.replace(/[^a-z0-9]/gi, '_'),
|
157
160
|
},
|
158
161
|
HealthCheck,
|
159
162
|
ExposedPorts,
|
@@ -195,12 +198,15 @@ class BlockInstanceRunner {
|
|
195
198
|
const containerName = (0, utils_1.getBlockInstanceContainerName)(this._systemId, blockInstance.id);
|
196
199
|
// For windows we need to default to root
|
197
200
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : local_cluster_config_1.default.getKapetaBasedir();
|
201
|
+
const systemUri = (0, nodejs_utils_1.parseKapetaUri)(this._systemId);
|
198
202
|
return this.ensureContainer({
|
199
203
|
Image: dockerImage,
|
200
204
|
name: containerName,
|
201
205
|
ExposedPorts,
|
202
206
|
Labels: {
|
203
207
|
instance: blockInstance.id,
|
208
|
+
[containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
209
|
+
[containerManager_1.COMPOSE_LABEL_SERVICE]: blockInfo.id.replace(/[^a-z0-9]/gi, '_'),
|
204
210
|
},
|
205
211
|
Env: [
|
206
212
|
...DOCKER_ENV_VARS,
|
@@ -273,6 +279,7 @@ class BlockInstanceRunner {
|
|
273
279
|
}
|
274
280
|
// For windows we need to default to root
|
275
281
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : local_cluster_config_1.default.getKapetaBasedir();
|
282
|
+
const systemUri = (0, nodejs_utils_1.parseKapetaUri)(this._systemId);
|
276
283
|
logs.addLog(`Creating new container for block: ${containerName}`);
|
277
284
|
const out = await this.ensureContainer({
|
278
285
|
Image: dockerImage,
|
@@ -289,6 +296,8 @@ class BlockInstanceRunner {
|
|
289
296
|
},
|
290
297
|
Labels: {
|
291
298
|
instance: blockInstance.id,
|
299
|
+
[containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
300
|
+
[containerManager_1.COMPOSE_LABEL_SERVICE]: blockUri.id.replace(/[^a-z0-9]/gi, '_'),
|
292
301
|
},
|
293
302
|
Env: [
|
294
303
|
`KAPETA_INSTANCE_NAME=${blockInstance.ref}`,
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import { EntityList } from '@kapeta/schemas';
|
2
|
+
import { AnyMap } from '../types';
|
1
3
|
export declare function getBlockInstanceContainerName(systemId: string, instanceId: string): string;
|
2
4
|
export declare function normalizeKapetaUri(uri: string): string;
|
3
5
|
export declare function readYML(path: string): any;
|
@@ -5,3 +7,4 @@ export declare function isWindows(): boolean;
|
|
5
7
|
export declare function isMac(): boolean;
|
6
8
|
export declare function isLinux(): boolean;
|
7
9
|
export declare function getBindHost(preferredHost?: string): string;
|
10
|
+
export declare function getResolvedConfiguration(entities?: EntityList, config?: AnyMap, globalConfiguration?: AnyMap): AnyMap;
|