@kapeta/local-cluster-service 0.34.2 → 0.36.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/.github/workflows/check-license.yml +0 -1
- package/CHANGELOG.md +14 -0
- package/dist/cjs/src/config/routes.js +9 -0
- package/dist/cjs/src/containerManager.d.ts +2 -8
- package/dist/cjs/src/containerManager.js +7 -7
- package/dist/cjs/src/instanceManager.d.ts +8 -3
- package/dist/cjs/src/instanceManager.js +147 -25
- package/dist/cjs/src/operatorManager.d.ts +9 -9
- package/dist/cjs/src/operatorManager.js +21 -26
- package/dist/cjs/src/serviceManager.d.ts +1 -0
- package/dist/cjs/src/serviceManager.js +9 -9
- package/dist/cjs/src/types.d.ts +40 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +2 -13
- package/dist/cjs/src/utils/BlockInstanceRunner.js +91 -79
- package/dist/cjs/src/utils/utils.d.ts +6 -2
- package/dist/cjs/src/utils/utils.js +35 -2
- package/dist/esm/src/config/routes.js +9 -0
- package/dist/esm/src/containerManager.d.ts +2 -8
- package/dist/esm/src/containerManager.js +7 -7
- package/dist/esm/src/instanceManager.d.ts +8 -3
- package/dist/esm/src/instanceManager.js +147 -25
- package/dist/esm/src/operatorManager.d.ts +9 -9
- package/dist/esm/src/operatorManager.js +21 -26
- package/dist/esm/src/serviceManager.d.ts +1 -0
- package/dist/esm/src/serviceManager.js +9 -9
- package/dist/esm/src/types.d.ts +40 -0
- package/dist/esm/src/utils/BlockInstanceRunner.d.ts +2 -13
- package/dist/esm/src/utils/BlockInstanceRunner.js +91 -79
- package/dist/esm/src/utils/utils.d.ts +6 -2
- package/dist/esm/src/utils/utils.js +35 -2
- package/package.json +1 -1
- package/src/config/routes.ts +15 -0
- package/src/containerManager.ts +8 -15
- package/src/instanceManager.ts +218 -34
- package/src/operatorManager.ts +26 -31
- package/src/serviceManager.ts +11 -8
- package/src/types.ts +36 -0
- package/src/utils/BlockInstanceRunner.ts +119 -92
- package/src/utils/utils.ts +40 -3
package/src/instanceManager.ts
CHANGED
@@ -10,7 +10,7 @@ import { BlockInstanceRunner } from './utils/BlockInstanceRunner';
|
|
10
10
|
import { storageService } from './storageService';
|
11
11
|
import { EVENT_INSTANCE_CREATED, EVENT_STATUS_CHANGED, socketManager } from './socketManager';
|
12
12
|
import { serviceManager } from './serviceManager';
|
13
|
-
import { assetManager } from './assetManager';
|
13
|
+
import { assetManager, EnrichedAsset } from './assetManager';
|
14
14
|
import {
|
15
15
|
containerManager,
|
16
16
|
DockerContainerHealth,
|
@@ -18,10 +18,21 @@ import {
|
|
18
18
|
HEALTH_CHECK_TIMEOUT,
|
19
19
|
} from './containerManager';
|
20
20
|
import { configManager } from './configManager';
|
21
|
-
import {
|
21
|
+
import {
|
22
|
+
DesiredInstanceStatus,
|
23
|
+
EnvironmentType,
|
24
|
+
InstanceInfo,
|
25
|
+
InstanceOwner,
|
26
|
+
InstanceStatus,
|
27
|
+
InstanceType,
|
28
|
+
LocalImageOptions,
|
29
|
+
LogEntry,
|
30
|
+
OperatorInstanceInfo,
|
31
|
+
OperatorInstancePort,
|
32
|
+
} from './types';
|
22
33
|
import { BlockDefinitionSpec, BlockInstance, Plan } from '@kapeta/schemas';
|
23
34
|
import { getBlockInstanceContainerName, getResolvedConfiguration } from './utils/utils';
|
24
|
-
import {
|
35
|
+
import { KIND_BLOCK_OPERATOR, KIND_RESOURCE_OPERATOR, operatorManager } from './operatorManager';
|
25
36
|
import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
|
26
37
|
import { definitionsManager } from './definitionsManager';
|
27
38
|
import { Task, taskManager } from './taskManager';
|
@@ -335,11 +346,108 @@ export class InstanceManager {
|
|
335
346
|
);
|
336
347
|
}
|
337
348
|
|
349
|
+
public stopAllForPlan(systemId: string) {
|
350
|
+
systemId = normalizeKapetaUri(systemId);
|
351
|
+
const instancesForPlan = this._instances.filter((instance) => instance.systemId === systemId);
|
352
|
+
return taskManager.add(
|
353
|
+
`plan:stop:${systemId}`,
|
354
|
+
async () => {
|
355
|
+
return this.stopInstances(instancesForPlan);
|
356
|
+
},
|
357
|
+
{
|
358
|
+
name: `Stopping plan ${systemId}`,
|
359
|
+
}
|
360
|
+
);
|
361
|
+
}
|
362
|
+
|
363
|
+
public async getInstanceOperator(
|
364
|
+
systemId: string,
|
365
|
+
instanceId: string,
|
366
|
+
environment?: EnvironmentType
|
367
|
+
): Promise<OperatorInstanceInfo> {
|
368
|
+
const blockInstance = await assetManager.getBlockInstance(systemId, instanceId);
|
369
|
+
if (!blockInstance) {
|
370
|
+
throw new Error(`Instance not found: ${systemId}/${instanceId}`);
|
371
|
+
}
|
372
|
+
const blockRef = normalizeKapetaUri(blockInstance.block.ref);
|
373
|
+
const block = await assetManager.getAsset(blockRef, true);
|
374
|
+
if (!block) {
|
375
|
+
throw new Error(`Block not found: ${blockRef}`);
|
376
|
+
}
|
377
|
+
|
378
|
+
const operatorDefinition = await definitionsManager.getDefinition(block.kind);
|
379
|
+
|
380
|
+
if (!operatorDefinition?.definition.spec.local) {
|
381
|
+
throw new Error(`Operator block has no local definition: ${blockRef}`);
|
382
|
+
}
|
383
|
+
|
384
|
+
const localConfig = operatorDefinition.definition.spec.local as LocalImageOptions;
|
385
|
+
|
386
|
+
let instance = await this.start(systemId, instanceId);
|
387
|
+
if (instance instanceof Task) {
|
388
|
+
instance = await instance.wait();
|
389
|
+
}
|
390
|
+
|
391
|
+
const container = await containerManager.get(instance.pid as string);
|
392
|
+
if (!container) {
|
393
|
+
throw new Error(`Container not found: ${instance.pid}`);
|
394
|
+
}
|
395
|
+
|
396
|
+
const portInfo = await container.getPorts();
|
397
|
+
if (!portInfo) {
|
398
|
+
throw new Error(`No ports found for instance: ${instanceId}`);
|
399
|
+
}
|
400
|
+
|
401
|
+
const hostname = serviceManager.getLocalHost(environment);
|
402
|
+
const ports: { [key: string]: OperatorInstancePort } = {};
|
403
|
+
|
404
|
+
Object.entries(portInfo).forEach(([key, value]) => {
|
405
|
+
ports[key] = {
|
406
|
+
protocol: value.protocol,
|
407
|
+
port: parseInt(value.hostPort),
|
408
|
+
};
|
409
|
+
});
|
410
|
+
|
411
|
+
return {
|
412
|
+
hostname,
|
413
|
+
ports,
|
414
|
+
credentials: localConfig.credentials,
|
415
|
+
options: localConfig.options,
|
416
|
+
};
|
417
|
+
}
|
418
|
+
|
338
419
|
public async stop(systemId: string, instanceId: string) {
|
339
420
|
return this.stopInner(systemId, instanceId, true);
|
340
421
|
}
|
341
422
|
|
342
|
-
private async stopInner(
|
423
|
+
private async stopInner(
|
424
|
+
systemId: string,
|
425
|
+
instanceId: string,
|
426
|
+
changeDesired: boolean = false,
|
427
|
+
checkForSingleton: boolean = true
|
428
|
+
) {
|
429
|
+
if (checkForSingleton) {
|
430
|
+
const blockInstance = await assetManager.getBlockInstance(systemId, instanceId);
|
431
|
+
const blockRef = normalizeKapetaUri(blockInstance.block.ref);
|
432
|
+
|
433
|
+
const blockAsset = await assetManager.getAsset(blockRef, true);
|
434
|
+
if (!blockAsset) {
|
435
|
+
throw new Error('Block not found: ' + blockRef);
|
436
|
+
}
|
437
|
+
|
438
|
+
if (await this.isSingletonOperator(blockAsset)) {
|
439
|
+
const instances = await this.getAllInstancesForKind(systemId, blockAsset.data.kind);
|
440
|
+
if (instances.length > 1) {
|
441
|
+
const promises = instances.map((id) => {
|
442
|
+
return this.stopInner(systemId, id, changeDesired, false);
|
443
|
+
});
|
444
|
+
|
445
|
+
await Promise.all(promises);
|
446
|
+
return;
|
447
|
+
}
|
448
|
+
}
|
449
|
+
}
|
450
|
+
|
343
451
|
return this.exclusive(systemId, instanceId, async () => {
|
344
452
|
systemId = normalizeKapetaUri(systemId);
|
345
453
|
const instance = this.getInstance(systemId, instanceId);
|
@@ -362,12 +470,18 @@ export class InstanceManager {
|
|
362
470
|
instance.status = InstanceStatus.STOPPING;
|
363
471
|
|
364
472
|
socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
365
|
-
console.log(
|
473
|
+
console.log(
|
474
|
+
'Stopping instance: %s::%s [desired: %s] [intentional: %s]',
|
475
|
+
systemId,
|
476
|
+
instanceId,
|
477
|
+
instance.desiredStatus,
|
478
|
+
changeDesired
|
479
|
+
);
|
366
480
|
this.save();
|
367
481
|
|
368
482
|
try {
|
369
483
|
if (instance.type === 'docker') {
|
370
|
-
const containerName = getBlockInstanceContainerName(instance.systemId, instance.instanceId);
|
484
|
+
const containerName = await getBlockInstanceContainerName(instance.systemId, instance.instanceId);
|
371
485
|
const container = await containerManager.getContainerByName(containerName);
|
372
486
|
if (container) {
|
373
487
|
try {
|
@@ -400,34 +514,44 @@ export class InstanceManager {
|
|
400
514
|
});
|
401
515
|
}
|
402
516
|
|
403
|
-
public
|
517
|
+
public async start(
|
518
|
+
systemId: string,
|
519
|
+
instanceId: string,
|
520
|
+
checkForSingleton: boolean = true
|
521
|
+
): Promise<InstanceInfo | Task<InstanceInfo>> {
|
404
522
|
systemId = normalizeKapetaUri(systemId);
|
405
|
-
const
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
523
|
+
const blockInstance = await assetManager.getBlockInstance(systemId, instanceId);
|
524
|
+
const blockRef = normalizeKapetaUri(blockInstance.block.ref);
|
525
|
+
|
526
|
+
const blockAsset = await assetManager.getAsset(blockRef, true);
|
527
|
+
if (!blockAsset) {
|
528
|
+
throw new Error('Block not found: ' + blockRef);
|
529
|
+
}
|
530
|
+
|
531
|
+
if (checkForSingleton && (await this.isSingletonOperator(blockAsset))) {
|
532
|
+
const instances = await this.getAllInstancesForKind(systemId, blockAsset.data.kind);
|
533
|
+
if (instances.length > 1) {
|
534
|
+
const promises = instances.map((id) => {
|
535
|
+
return this.start(systemId, id, false);
|
536
|
+
});
|
537
|
+
|
538
|
+
await Promise.all(promises);
|
539
|
+
return promises[0];
|
413
540
|
}
|
414
|
-
|
415
|
-
}
|
541
|
+
}
|
416
542
|
|
417
|
-
public async start(systemId: string, instanceId: string): Promise<InstanceInfo | Task<InstanceInfo>> {
|
418
543
|
return this.exclusive(systemId, instanceId, async () => {
|
419
|
-
|
420
|
-
const blockInstance = await assetManager.getBlockInstance(systemId, instanceId);
|
421
|
-
const blockRef = normalizeKapetaUri(blockInstance.block.ref);
|
544
|
+
let existingInstance = this.getInstance(systemId, instanceId);
|
422
545
|
|
423
|
-
|
424
|
-
|
425
|
-
|
546
|
+
if (existingInstance && existingInstance.pid) {
|
547
|
+
const container = await containerManager.get(existingInstance.pid as string);
|
548
|
+
if (!container) {
|
549
|
+
// The container is not running
|
550
|
+
existingInstance = undefined;
|
551
|
+
}
|
426
552
|
}
|
427
553
|
|
428
|
-
|
429
|
-
|
430
|
-
if (existingInstance) {
|
554
|
+
if (existingInstance?.pid) {
|
431
555
|
if (existingInstance.status === InstanceStatus.READY) {
|
432
556
|
// Instance is already running
|
433
557
|
return existingInstance;
|
@@ -476,17 +600,17 @@ export class InstanceManager {
|
|
476
600
|
return Promise.resolve();
|
477
601
|
}
|
478
602
|
|
479
|
-
if (
|
603
|
+
if (KIND_RESOURCE_OPERATOR.toLowerCase() !== asset.definition.kind.toLowerCase()) {
|
480
604
|
// Not an operator
|
481
605
|
return Promise.resolve();
|
482
606
|
}
|
483
607
|
// Check if the operator has a local definition, if not we skip it since we can't start it
|
484
|
-
if(!asset.definition.spec.local) {
|
485
|
-
console.log('Skipping operator since it as no local definition: %s', consumer.kind)
|
608
|
+
if (!asset.definition.spec.local) {
|
609
|
+
console.log('Skipping operator since it as no local definition: %s', consumer.kind);
|
486
610
|
return Promise.resolve();
|
487
611
|
}
|
488
612
|
console.log('Ensuring resource: %s in %s', consumerUri.id, systemId);
|
489
|
-
return operatorManager.
|
613
|
+
return operatorManager.ensureOperator(systemId, consumerUri.fullName, consumerUri.version);
|
490
614
|
});
|
491
615
|
|
492
616
|
await Promise.all(promises);
|
@@ -570,6 +694,8 @@ export class InstanceManager {
|
|
570
694
|
*/
|
571
695
|
public async prepareForRestart(systemId: string, instanceId: string) {
|
572
696
|
systemId = normalizeKapetaUri(systemId);
|
697
|
+
|
698
|
+
console.log('Stopping instance during restart...', systemId, instanceId);
|
573
699
|
await this.stopInner(systemId, instanceId);
|
574
700
|
}
|
575
701
|
|
@@ -603,8 +729,14 @@ export class InstanceManager {
|
|
603
729
|
while (all.length > 0) {
|
604
730
|
// Check a few instances at a time - docker doesn't like too many concurrent requests
|
605
731
|
const chunk = all.splice(0, 30);
|
606
|
-
const promises = chunk.map(async (
|
607
|
-
if (!
|
732
|
+
const promises = chunk.map(async (oldInstance) => {
|
733
|
+
if (!oldInstance.systemId) {
|
734
|
+
return;
|
735
|
+
}
|
736
|
+
|
737
|
+
// Grab the latest here
|
738
|
+
const instance = this.getInstance(oldInstance.systemId, oldInstance.instanceId);
|
739
|
+
if (!instance) {
|
608
740
|
return;
|
609
741
|
}
|
610
742
|
|
@@ -721,6 +853,11 @@ export class InstanceManager {
|
|
721
853
|
) {
|
722
854
|
//If the instance is running but we want it to stop, stop it
|
723
855
|
try {
|
856
|
+
console.log(
|
857
|
+
'Stopping instance since it is its desired state',
|
858
|
+
instance.systemId,
|
859
|
+
instance.instanceId
|
860
|
+
);
|
724
861
|
await this.stopInner(instance.systemId, instance.instanceId);
|
725
862
|
} catch (e) {
|
726
863
|
console.warn('Failed to stop instance', instance.systemId, instance.instanceId, e);
|
@@ -755,7 +892,7 @@ export class InstanceManager {
|
|
755
892
|
|
756
893
|
private async getExternalStatus(instance: InstanceInfo): Promise<InstanceStatus> {
|
757
894
|
if (instance.type === InstanceType.DOCKER) {
|
758
|
-
const containerName = getBlockInstanceContainerName(instance.systemId, instance.instanceId);
|
895
|
+
const containerName = await getBlockInstanceContainerName(instance.systemId, instance.instanceId);
|
759
896
|
const container = await containerManager.getContainerByName(containerName);
|
760
897
|
if (!container) {
|
761
898
|
// If the container doesn't exist, we consider the instance stopped
|
@@ -864,6 +1001,53 @@ export class InstanceManager {
|
|
864
1001
|
});
|
865
1002
|
});
|
866
1003
|
}
|
1004
|
+
|
1005
|
+
private async isSingletonOperator(blockAsset: EnrichedAsset): Promise<boolean> {
|
1006
|
+
const provider = await assetManager.getAsset(blockAsset.data.kind);
|
1007
|
+
if (!provider) {
|
1008
|
+
return false;
|
1009
|
+
}
|
1010
|
+
|
1011
|
+
if (parseKapetaUri(provider.kind).fullName === KIND_BLOCK_OPERATOR) {
|
1012
|
+
const localConfig = provider.data.spec.local as LocalImageOptions;
|
1013
|
+
return localConfig.singleton ?? false;
|
1014
|
+
}
|
1015
|
+
|
1016
|
+
return false;
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
private async getKindForAssetRef(assetRef: string): Promise<string | null> {
|
1020
|
+
const block = await assetManager.getAsset(assetRef);
|
1021
|
+
if (!block) {
|
1022
|
+
return null;
|
1023
|
+
}
|
1024
|
+
|
1025
|
+
return block.data.kind;
|
1026
|
+
}
|
1027
|
+
|
1028
|
+
private async isUsingKind(ref: string, kind: string): Promise<boolean> {
|
1029
|
+
const assetKind = await this.getKindForAssetRef(ref);
|
1030
|
+
if (!assetKind) {
|
1031
|
+
return false;
|
1032
|
+
}
|
1033
|
+
|
1034
|
+
return parseKapetaUri(assetKind).fullName === parseKapetaUri(kind).fullName;
|
1035
|
+
}
|
1036
|
+
|
1037
|
+
private async getAllInstancesForKind(systemId: string, kind: string): Promise<string[]> {
|
1038
|
+
const plan = await assetManager.getPlan(systemId);
|
1039
|
+
if (!plan?.spec?.blocks) {
|
1040
|
+
return [];
|
1041
|
+
}
|
1042
|
+
const out: string[] = [];
|
1043
|
+
for (const block of plan.spec.blocks) {
|
1044
|
+
if (await this.isUsingKind(block.block.ref, kind)) {
|
1045
|
+
out.push(block.id);
|
1046
|
+
}
|
1047
|
+
}
|
1048
|
+
|
1049
|
+
return out;
|
1050
|
+
}
|
867
1051
|
}
|
868
1052
|
|
869
1053
|
export const instanceManager = new InstanceManager();
|
package/src/operatorManager.ts
CHANGED
@@ -16,25 +16,27 @@ import {
|
|
16
16
|
containerManager,
|
17
17
|
} from './containerManager';
|
18
18
|
import FSExtra from 'fs-extra';
|
19
|
-
import { AnyMap, EnvironmentType, OperatorInfo, StringMap } from './types';
|
19
|
+
import { AnyMap, EnvironmentType, LocalImageOptions, OperatorInfo, StringMap } from './types';
|
20
20
|
import { BlockInstance, Resource } from '@kapeta/schemas';
|
21
21
|
import { definitionsManager } from './definitionsManager';
|
22
|
-
import { getBindHost } from './utils/utils';
|
22
|
+
import { getBindHost, toPortInfo } from './utils/utils';
|
23
23
|
import { parseKapetaUri, normalizeKapetaUri } from '@kapeta/nodejs-utils';
|
24
24
|
import _ from 'lodash';
|
25
25
|
import AsyncLock from 'async-lock';
|
26
26
|
import { taskManager } from './taskManager';
|
27
27
|
|
28
|
-
export const
|
28
|
+
export const KIND_RESOURCE_OPERATOR = 'core/resource-type-operator';
|
29
|
+
export const KIND_BLOCK_OPERATOR = 'core/block-type-operator';
|
29
30
|
const KIND_PLAN = 'core/plan';
|
30
31
|
|
31
32
|
class Operator {
|
32
33
|
private readonly _data: DefinitionInfo;
|
34
|
+
|
33
35
|
constructor(data: DefinitionInfo) {
|
34
36
|
this._data = data;
|
35
37
|
}
|
36
38
|
|
37
|
-
getLocalData() {
|
39
|
+
getLocalData(): LocalImageOptions {
|
38
40
|
return this._data.definition.spec.local;
|
39
41
|
}
|
40
42
|
|
@@ -65,24 +67,24 @@ class OperatorManager {
|
|
65
67
|
/**
|
66
68
|
* Get operator definition for resource type
|
67
69
|
*/
|
68
|
-
async getOperator(
|
69
|
-
const operators = await definitionsManager.getDefinitions(
|
70
|
+
async getOperator(fullName: string, version: string) {
|
71
|
+
const operators = await definitionsManager.getDefinitions([KIND_RESOURCE_OPERATOR, KIND_BLOCK_OPERATOR]);
|
70
72
|
|
71
73
|
const operator: DefinitionInfo | undefined = operators.find(
|
72
74
|
(operator) =>
|
73
75
|
operator.definition &&
|
74
76
|
operator.definition.metadata &&
|
75
77
|
operator.definition.metadata.name &&
|
76
|
-
operator.definition.metadata.name.toLowerCase() ===
|
78
|
+
operator.definition.metadata.name.toLowerCase() === fullName.toLowerCase() &&
|
77
79
|
operator.version === version
|
78
80
|
);
|
79
81
|
|
80
82
|
if (!operator) {
|
81
|
-
throw new Error(`Unknown
|
83
|
+
throw new Error(`Unknown operator type: ${fullName}:${version}`);
|
82
84
|
}
|
83
85
|
|
84
86
|
if (!operator.definition.spec || !operator.definition.spec.local) {
|
85
|
-
throw new Error(`Operator missing local definition: ${
|
87
|
+
throw new Error(`Operator missing local definition: ${fullName}:${version}`);
|
86
88
|
}
|
87
89
|
|
88
90
|
return new Operator(operator);
|
@@ -137,7 +139,7 @@ class OperatorManager {
|
|
137
139
|
const kindUri = parseKapetaUri(blockResource.kind);
|
138
140
|
const operator = await this.getOperator(resourceType, kindUri.version);
|
139
141
|
const credentials = operator.getCredentials();
|
140
|
-
const container = await this.
|
142
|
+
const container = await this.ensureOperator(systemId, resourceType, kindUri.version);
|
141
143
|
const portInfo = await container.getPort(portType);
|
142
144
|
|
143
145
|
if (!portInfo) {
|
@@ -164,16 +166,17 @@ class OperatorManager {
|
|
164
166
|
/**
|
165
167
|
* Ensure we have a running operator of given type
|
166
168
|
*
|
167
|
-
* @param
|
168
|
-
* @param
|
169
|
-
* @param
|
170
|
-
* @return {Promise<ContainerInfo>}
|
169
|
+
* @param systemId the plan ref
|
170
|
+
* @param kind the full name - e.g. myhandle/rabbitmq
|
171
|
+
* @param version the version of the operator
|
171
172
|
*/
|
172
|
-
async
|
173
|
+
async ensureOperator(systemId: string, kind: string, version: string): Promise<ContainerInfo> {
|
173
174
|
systemId = normalizeKapetaUri(systemId);
|
174
|
-
|
175
|
+
|
176
|
+
const key = `${systemId}#${kind}:${version}`;
|
177
|
+
|
175
178
|
return await this.operatorLock.acquire(key, async () => {
|
176
|
-
const operator = await this.getOperator(
|
179
|
+
const operator = await this.getOperator(kind, version);
|
177
180
|
|
178
181
|
const operatorData = operator.getLocalData();
|
179
182
|
|
@@ -186,17 +189,9 @@ class OperatorManager {
|
|
186
189
|
for (let i = 0; i < portTypes.length; i++) {
|
187
190
|
const portType = portTypes[i];
|
188
191
|
let containerPortInfo = operatorData.ports[portType];
|
189
|
-
const hostPort = await serviceManager.ensureServicePort(systemId,
|
190
|
-
|
191
|
-
|
192
|
-
containerPortInfo = { port: containerPortInfo, type: 'tcp' };
|
193
|
-
}
|
194
|
-
|
195
|
-
if (!containerPortInfo.type) {
|
196
|
-
containerPortInfo.type = 'tcp';
|
197
|
-
}
|
198
|
-
|
199
|
-
const portId = containerPortInfo.port + '/' + containerPortInfo.type;
|
192
|
+
const hostPort = await serviceManager.ensureServicePort(systemId, kind, portType);
|
193
|
+
const portInfo = toPortInfo(containerPortInfo);
|
194
|
+
const portId = portInfo.port + '/' + portInfo.type;
|
200
195
|
|
201
196
|
ports[portId] = {
|
202
197
|
type: portType,
|
@@ -204,7 +199,7 @@ class OperatorManager {
|
|
204
199
|
};
|
205
200
|
}
|
206
201
|
|
207
|
-
const nameParts = [systemId,
|
202
|
+
const nameParts = [systemId, kind.toLowerCase(), version];
|
208
203
|
|
209
204
|
const containerName = `kapeta-resource-${md5(nameParts.join('_'))}`;
|
210
205
|
|
@@ -216,7 +211,7 @@ class OperatorManager {
|
|
216
211
|
const Labels: StringMap = {
|
217
212
|
kapeta: 'true',
|
218
213
|
[COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
219
|
-
[COMPOSE_LABEL_SERVICE]: [
|
214
|
+
[COMPOSE_LABEL_SERVICE]: [kind, version].join('_').replace(/[^a-z0-9]/gi, '_'),
|
220
215
|
};
|
221
216
|
|
222
217
|
const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
|
@@ -237,7 +232,7 @@ class OperatorManager {
|
|
237
232
|
Labels[CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
|
238
233
|
});
|
239
234
|
|
240
|
-
const Mounts = await containerManager.createVolumes(systemId,
|
235
|
+
const Mounts = await containerManager.createVolumes(systemId, kind, operatorData.mounts);
|
241
236
|
|
242
237
|
_.forEach(operatorData.env, (value, name) => {
|
243
238
|
Env.push(name + '=' + value);
|
package/src/serviceManager.ts
CHANGED
@@ -34,22 +34,25 @@ class ServiceManager {
|
|
34
34
|
});
|
35
35
|
}
|
36
36
|
|
37
|
+
public getLocalHost(environmentType?: EnvironmentType) {
|
38
|
+
if (environmentType === 'docker') {
|
39
|
+
//We're inside a docker container, so we can use this special host name to access the host machine
|
40
|
+
return 'host.docker.internal';
|
41
|
+
}
|
42
|
+
|
43
|
+
return clusterService.getClusterServiceHost();
|
44
|
+
}
|
45
|
+
|
37
46
|
_forLocal(port: string | number, path?: string, environmentType?: EnvironmentType) {
|
38
47
|
if (!path) {
|
39
48
|
path = '';
|
40
49
|
}
|
41
|
-
|
42
|
-
if (environmentType === 'docker') {
|
43
|
-
//We're inside a docker container, so we can use this special host name to access the host machine
|
44
|
-
host = 'host.docker.internal';
|
45
|
-
} else {
|
46
|
-
host = clusterService.getClusterServiceHost();
|
47
|
-
}
|
50
|
+
const hostname = this.getLocalHost(environmentType);
|
48
51
|
|
49
52
|
if (path.startsWith('/')) {
|
50
53
|
path = path.substring(1);
|
51
54
|
}
|
52
|
-
return `http://${
|
55
|
+
return `http://${hostname}:${port}/${path}`;
|
53
56
|
}
|
54
57
|
|
55
58
|
_ensureSystem(systemId: string) {
|
package/src/types.ts
CHANGED
@@ -61,6 +61,27 @@ export type ProcessInfo = {
|
|
61
61
|
portType?: string;
|
62
62
|
};
|
63
63
|
|
64
|
+
export interface Health {
|
65
|
+
cmd: string;
|
66
|
+
interval?: number;
|
67
|
+
timeout?: number;
|
68
|
+
retries?: number;
|
69
|
+
}
|
70
|
+
|
71
|
+
export type PortInfo = { port: number; type: 'tcp' | 'udp' } | number | string;
|
72
|
+
|
73
|
+
export type LocalImageOptions<Credentials = AnyMap, Options = AnyMap> = {
|
74
|
+
image: string;
|
75
|
+
ports: { [key: string]: PortInfo };
|
76
|
+
credentials?: Credentials;
|
77
|
+
options?: Options;
|
78
|
+
cmd?: string;
|
79
|
+
env?: AnyMap;
|
80
|
+
health?: Health;
|
81
|
+
singleton?: boolean;
|
82
|
+
mounts?: { [key: string]: string };
|
83
|
+
};
|
84
|
+
|
64
85
|
export type InstanceInfo = {
|
65
86
|
systemId: string;
|
66
87
|
instanceId: string;
|
@@ -86,6 +107,21 @@ interface ResourceRef {
|
|
86
107
|
|
87
108
|
export type ProxyRequestHandler = (req: StringBodyRequest, res: express.Response, info: ProxyRequestInfo) => void;
|
88
109
|
|
110
|
+
export interface OperatorInstancePort {
|
111
|
+
protocol: string;
|
112
|
+
port: number;
|
113
|
+
}
|
114
|
+
|
115
|
+
export interface OperatorInstanceInfo {
|
116
|
+
hostname: string;
|
117
|
+
ports: { [portType: string]: OperatorInstancePort };
|
118
|
+
path?: string;
|
119
|
+
query?: string;
|
120
|
+
hash?: string;
|
121
|
+
options?: AnyMap;
|
122
|
+
credentials?: AnyMap;
|
123
|
+
}
|
124
|
+
|
89
125
|
export interface OperatorInfo {
|
90
126
|
host: string;
|
91
127
|
port: string;
|