@kapeta/local-cluster-service 0.35.0 → 0.36.1
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/containerManager.js +3 -3
- package/dist/cjs/src/instanceManager.d.ts +7 -3
- package/dist/cjs/src/instanceManager.js +143 -63
- package/dist/cjs/src/operatorManager.d.ts +7 -7
- package/dist/cjs/src/operatorManager.js +19 -19
- package/dist/cjs/src/types.d.ts +1 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +4 -12
- package/dist/cjs/src/utils/BlockInstanceRunner.js +107 -89
- package/dist/cjs/src/utils/utils.d.ts +1 -1
- package/dist/cjs/src/utils/utils.js +25 -2
- package/dist/esm/src/containerManager.js +3 -3
- package/dist/esm/src/instanceManager.d.ts +7 -3
- package/dist/esm/src/instanceManager.js +143 -63
- package/dist/esm/src/operatorManager.d.ts +7 -7
- package/dist/esm/src/operatorManager.js +19 -19
- package/dist/esm/src/types.d.ts +1 -0
- package/dist/esm/src/utils/BlockInstanceRunner.d.ts +4 -12
- package/dist/esm/src/utils/BlockInstanceRunner.js +107 -89
- package/dist/esm/src/utils/utils.d.ts +1 -1
- package/dist/esm/src/utils/utils.js +25 -2
- package/package.json +1 -1
- package/src/containerManager.ts +3 -3
- package/src/instanceManager.ts +193 -77
- package/src/operatorManager.ts +21 -18
- package/src/types.ts +1 -0
- package/src/utils/BlockInstanceRunner.ts +132 -102
- package/src/utils/utils.ts +28 -2
@@ -22,6 +22,7 @@ import { AnyMap, BlockProcessParams, InstanceType, LocalImageOptions, ProcessInf
|
|
22
22
|
import { definitionsManager } from '../definitionsManager';
|
23
23
|
import Docker from 'dockerode';
|
24
24
|
import OS from 'node:os';
|
25
|
+
import { taskManager } from '../taskManager';
|
25
26
|
|
26
27
|
const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
|
27
28
|
const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
|
@@ -47,17 +48,24 @@ async function getProvider(uri: KapetaURI) {
|
|
47
48
|
}
|
48
49
|
|
49
50
|
export function resolvePortType(portType: string) {
|
50
|
-
if (HTTP_PORTS.includes(portType)) {
|
51
|
+
if (portType && HTTP_PORTS.includes(portType.toLowerCase())) {
|
51
52
|
return HTTP_PORT_TYPE;
|
52
53
|
}
|
53
54
|
return portType;
|
54
55
|
}
|
55
56
|
|
56
|
-
|
57
|
+
/**
|
58
|
+
* Get the port types for a non-operator block instance
|
59
|
+
*/
|
60
|
+
function getServiceProviderPorts(assetVersion: DefinitionInfo, providerVersion: DefinitionInfo): string[] {
|
57
61
|
const out =
|
58
62
|
assetVersion.definition?.spec?.providers
|
63
|
+
?.filter((provider: any) => {
|
64
|
+
// We only support HTTP provider ports for now. Need to figure out how to handle other types
|
65
|
+
return HTTP_PORTS.includes(provider.spec?.port?.type?.toLowerCase());
|
66
|
+
})
|
59
67
|
?.map((provider: any) => {
|
60
|
-
return resolvePortType(provider.spec?.port?.type);
|
68
|
+
return resolvePortType(provider.spec?.port?.type?.toLowerCase());
|
61
69
|
})
|
62
70
|
.filter((t: any) => !!t) ?? [];
|
63
71
|
|
@@ -136,7 +144,7 @@ export class BlockInstanceRunner {
|
|
136
144
|
processInfo = await this._startOperatorProcess(blockInstance, blockUri, providerVersion, env);
|
137
145
|
} else {
|
138
146
|
//We need a port type to know how to connect to the block consistently
|
139
|
-
const portTypes =
|
147
|
+
const portTypes = getServiceProviderPorts(assetVersion, providerVersion);
|
140
148
|
|
141
149
|
if (blockUri.version === 'local') {
|
142
150
|
processInfo = await this._startLocalProcess(blockInstance, blockUri, env, assetVersion);
|
@@ -201,7 +209,7 @@ export class BlockInstanceRunner {
|
|
201
209
|
throw new Error(`Missing docker image information: ${JSON.stringify(localContainer)}`);
|
202
210
|
}
|
203
211
|
|
204
|
-
const containerName = getBlockInstanceContainerName(this._systemId, blockInstance.id);
|
212
|
+
const containerName = await getBlockInstanceContainerName(this._systemId, blockInstance.id, targetKindUri.id);
|
205
213
|
const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
|
206
214
|
const dockerOpts = localContainer.options ?? {};
|
207
215
|
const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
|
@@ -216,7 +224,7 @@ export class BlockInstanceRunner {
|
|
216
224
|
delete localContainer.Labels;
|
217
225
|
delete localContainer.Env;
|
218
226
|
|
219
|
-
const { PortBindings, ExposedPorts, addonEnv } = await this.
|
227
|
+
const { PortBindings, ExposedPorts, addonEnv } = await this.getServiceBlockPortBindings(
|
220
228
|
blockInstance,
|
221
229
|
assetVersion,
|
222
230
|
providerVersion
|
@@ -316,13 +324,13 @@ export class BlockInstanceRunner {
|
|
316
324
|
throw new Error(`Block type not found: ${kindUri.id}`);
|
317
325
|
}
|
318
326
|
|
319
|
-
const { PortBindings, ExposedPorts, addonEnv } = await this.
|
327
|
+
const { PortBindings, ExposedPorts, addonEnv } = await this.getServiceBlockPortBindings(
|
320
328
|
blockInstance,
|
321
329
|
assetVersion,
|
322
330
|
providerVersion
|
323
331
|
);
|
324
332
|
|
325
|
-
const containerName = getBlockInstanceContainerName(this._systemId, blockInstance.id);
|
333
|
+
const containerName = await getBlockInstanceContainerName(this._systemId, blockInstance.id, kindUri.id);
|
326
334
|
|
327
335
|
// For windows we need to default to root
|
328
336
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
@@ -352,16 +360,7 @@ export class BlockInstanceRunner {
|
|
352
360
|
});
|
353
361
|
}
|
354
362
|
|
355
|
-
|
356
|
-
*
|
357
|
-
* @param blockInstance
|
358
|
-
* @param blockUri
|
359
|
-
* @param providerDefinition
|
360
|
-
* @param {{[key:string]:string}} env
|
361
|
-
* @return {Promise<ProcessDetails>}
|
362
|
-
* @private
|
363
|
-
*/
|
364
|
-
async _startOperatorProcess(
|
363
|
+
private async _startOperatorProcess(
|
365
364
|
blockInstance: BlockProcessParams,
|
366
365
|
blockUri: KapetaURI,
|
367
366
|
providerDefinition: DefinitionInfo,
|
@@ -388,96 +387,127 @@ export class BlockInstanceRunner {
|
|
388
387
|
const local = spec.local as LocalImageOptions;
|
389
388
|
|
390
389
|
const dockerImage = local.image;
|
390
|
+
const operatorUri = local.singleton ? parseKapetaUri(providerRef) : blockUri;
|
391
|
+
const operatorId = local.singleton ? providerRef : blockInstance.id;
|
392
|
+
const operatorRef = local.singleton ? providerRef : blockInstance.ref;
|
391
393
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
const bindHost = getBindHost();
|
397
|
-
|
398
|
-
const ExposedPorts: AnyMap = {};
|
399
|
-
const addonEnv: StringMap = {};
|
400
|
-
const PortBindings: AnyMap = {};
|
401
|
-
let HealthCheck = undefined;
|
402
|
-
let Mounts: DockerMounts[] = [];
|
403
|
-
const localPorts = local.ports ?? {};
|
404
|
-
const labels: { [key: string]: string } = {};
|
405
|
-
const promises = Object.entries(localPorts).map(async ([portType, value]) => {
|
406
|
-
const portInfo = toPortInfo(value);
|
407
|
-
const dockerPort = `${portInfo.port}/${portInfo.type}`;
|
408
|
-
ExposedPorts[dockerPort] = {};
|
409
|
-
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = `${portInfo.port}`;
|
410
|
-
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
411
|
-
PortBindings[dockerPort] = [
|
412
|
-
{
|
413
|
-
HostIp: bindHost,
|
414
|
-
HostPort: `${publicPort}`,
|
415
|
-
},
|
416
|
-
];
|
417
|
-
|
418
|
-
labels[CONTAINER_LABEL_PORT_PREFIX + publicPort] = portType;
|
419
|
-
});
|
420
|
-
|
421
|
-
await Promise.all(promises);
|
422
|
-
|
423
|
-
if (local.env) {
|
424
|
-
Object.entries(local.env).forEach(([key, value]) => {
|
425
|
-
addonEnv[key] = value as string;
|
426
|
-
});
|
427
|
-
}
|
428
|
-
|
429
|
-
if (local.mounts) {
|
430
|
-
Mounts = await containerManager.createVolumes(this._systemId, blockUri.id, local.mounts);
|
394
|
+
if (local.singleton && env) {
|
395
|
+
env[KAPETA_BLOCK_REF] = operatorRef;
|
396
|
+
env[KAPETA_INSTANCE_ID] = operatorId;
|
431
397
|
}
|
432
398
|
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
399
|
+
const containerName = await getBlockInstanceContainerName(this._systemId, blockInstance.id, providerRef);
|
400
|
+
|
401
|
+
const task = taskManager.add(
|
402
|
+
`container:start:${containerName}`,
|
403
|
+
async () => {
|
404
|
+
const logs = new LogData();
|
405
|
+
|
406
|
+
const bindHost = getBindHost();
|
407
|
+
|
408
|
+
const ExposedPorts: AnyMap = {};
|
409
|
+
const addonEnv: StringMap = {};
|
410
|
+
const PortBindings: AnyMap = {};
|
411
|
+
let HealthCheck = undefined;
|
412
|
+
let Mounts: DockerMounts[] = [];
|
413
|
+
const localPorts = local.ports ?? {};
|
414
|
+
const labels: { [key: string]: string } = {};
|
415
|
+
const promises = Object.entries(localPorts).map(async ([portType, value]) => {
|
416
|
+
const portInfo = toPortInfo(value);
|
417
|
+
const dockerPort = `${portInfo.port}/${portInfo.type}`;
|
418
|
+
ExposedPorts[dockerPort] = {};
|
419
|
+
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = `${portInfo.port}`;
|
420
|
+
const publicPort = await serviceManager.ensureServicePort(this._systemId, operatorId, portType);
|
421
|
+
PortBindings[dockerPort] = [
|
422
|
+
{
|
423
|
+
HostIp: bindHost,
|
424
|
+
HostPort: `${publicPort}`,
|
425
|
+
},
|
426
|
+
];
|
427
|
+
|
428
|
+
labels[CONTAINER_LABEL_PORT_PREFIX + publicPort] = portType;
|
429
|
+
});
|
430
|
+
|
431
|
+
await Promise.all(promises);
|
432
|
+
|
433
|
+
if (local.env) {
|
434
|
+
Object.entries(local.env).forEach(([key, value]) => {
|
435
|
+
addonEnv[key] = value as string;
|
436
|
+
});
|
437
|
+
}
|
438
|
+
|
439
|
+
if (local.mounts) {
|
440
|
+
Mounts = await containerManager.createVolumes(this._systemId, operatorUri.id, local.mounts);
|
441
|
+
}
|
442
|
+
|
443
|
+
if (local.health) {
|
444
|
+
HealthCheck = containerManager.toDockerHealth(local.health);
|
445
|
+
}
|
446
|
+
|
447
|
+
// For windows we need to default to root
|
448
|
+
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
449
|
+
|
450
|
+
const Binds = local.singleton
|
451
|
+
? [`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`]
|
452
|
+
: [
|
453
|
+
`${toLocalBindVolume(kapetaYmlPath)}:/kapeta.yml:ro`,
|
454
|
+
`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`,
|
455
|
+
];
|
456
|
+
|
457
|
+
const systemUri = parseKapetaUri(this._systemId);
|
458
|
+
|
459
|
+
console.log(
|
460
|
+
`Ensuring container for operator block: ${containerName} [singleton: ${!!local.singleton}]`
|
461
|
+
);
|
462
|
+
|
463
|
+
logs.addLog(`Ensuring container for operator block: ${containerName}`);
|
464
|
+
const out = await this.ensureContainer({
|
465
|
+
Image: dockerImage,
|
466
|
+
name: containerName,
|
467
|
+
ExposedPorts,
|
468
|
+
HealthCheck,
|
469
|
+
HostConfig: {
|
470
|
+
Binds,
|
471
|
+
PortBindings,
|
472
|
+
Mounts,
|
473
|
+
},
|
474
|
+
Labels: {
|
475
|
+
...labels,
|
476
|
+
instance: operatorId,
|
477
|
+
[COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
478
|
+
[COMPOSE_LABEL_SERVICE]: operatorUri.id.replace(/[^a-z0-9]/gi, '_'),
|
479
|
+
},
|
480
|
+
Env: [
|
481
|
+
`KAPETA_INSTANCE_NAME=${operatorRef}`,
|
482
|
+
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
483
|
+
...DOCKER_ENV_VARS,
|
484
|
+
...Object.entries({
|
485
|
+
...env,
|
486
|
+
...addonEnv,
|
487
|
+
}).map(([key, value]) => `${key}=${value}`),
|
488
|
+
],
|
489
|
+
});
|
490
|
+
|
491
|
+
const portTypes = local.ports ? Object.keys(local.ports) : [];
|
492
|
+
if (portTypes.length > 0) {
|
493
|
+
out.portType = portTypes[0];
|
494
|
+
}
|
495
|
+
|
496
|
+
return out;
|
460
497
|
},
|
461
|
-
|
462
|
-
`
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
...env,
|
467
|
-
...addonEnv,
|
468
|
-
}).map(([key, value]) => `${key}=${value}`),
|
469
|
-
],
|
470
|
-
});
|
471
|
-
|
472
|
-
const portTypes = local.ports ? Object.keys(local.ports) : [];
|
473
|
-
if (portTypes.length > 0) {
|
474
|
-
out.portType = portTypes[0];
|
475
|
-
}
|
498
|
+
{
|
499
|
+
name: `Starting container for ${providerRef}`,
|
500
|
+
systemId: this._systemId,
|
501
|
+
}
|
502
|
+
);
|
476
503
|
|
477
|
-
return
|
504
|
+
return task.wait();
|
478
505
|
}
|
479
506
|
|
480
|
-
|
507
|
+
/**
|
508
|
+
* Get the port bindings for a non-operator block
|
509
|
+
*/
|
510
|
+
private async getServiceBlockPortBindings(
|
481
511
|
blockInstance: BlockProcessParams,
|
482
512
|
assetVersion: DefinitionInfo,
|
483
513
|
providerVersion: DefinitionInfo
|
@@ -487,7 +517,7 @@ export class BlockInstanceRunner {
|
|
487
517
|
const addonEnv: StringMap = {};
|
488
518
|
const PortBindings: AnyMap = {};
|
489
519
|
|
490
|
-
const portTypes =
|
520
|
+
const portTypes = getServiceProviderPorts(assetVersion, providerVersion);
|
491
521
|
let port = 80;
|
492
522
|
const promises = portTypes.map(async (portType) => {
|
493
523
|
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
package/src/utils/utils.ts
CHANGED
@@ -10,9 +10,35 @@ import { EntityList } from '@kapeta/schemas';
|
|
10
10
|
import _ from 'lodash';
|
11
11
|
import { AnyMap, PortInfo } from '../types';
|
12
12
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
13
|
+
import { definitionsManager } from '../definitionsManager';
|
14
|
+
import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
|
15
|
+
import { KIND_BLOCK_OPERATOR } from '../operatorManager';
|
16
|
+
import { assetManager } from '../assetManager';
|
17
|
+
|
18
|
+
export async function getBlockInstanceContainerName(systemId: string, instanceId: string, blockType?: string) {
|
19
|
+
if (!blockType) {
|
20
|
+
const instance = await assetManager.getBlockInstance(systemId, instanceId);
|
21
|
+
if (!instance) {
|
22
|
+
throw new Error(`Instance ${instanceId} not found in plan ${systemId}`);
|
23
|
+
}
|
24
|
+
const block = await assetManager.getAsset(instance.block.ref);
|
25
|
+
if (!block) {
|
26
|
+
throw new Error(`Block ${instance.block.ref} not found`);
|
27
|
+
}
|
28
|
+
blockType = block.data.kind;
|
29
|
+
}
|
30
|
+
const typeDefinition = await definitionsManager.getDefinition(blockType);
|
31
|
+
if (!typeDefinition) {
|
32
|
+
throw new Error(`Block type ${blockType} not found`);
|
33
|
+
}
|
34
|
+
if (
|
35
|
+
parseKapetaUri(typeDefinition.definition.kind).fullName === KIND_BLOCK_OPERATOR &&
|
36
|
+
typeDefinition.definition.spec?.local?.singleton
|
37
|
+
) {
|
38
|
+
return `kapeta-instance-operator-${md5(normalizeKapetaUri(systemId) + normalizeKapetaUri(blockType))}`;
|
39
|
+
}
|
13
40
|
|
14
|
-
|
15
|
-
return `kapeta-block-instance-${md5(systemId + instanceId)}`;
|
41
|
+
return `kapeta-block-instance-${md5(normalizeKapetaUri(systemId) + instanceId)}`;
|
16
42
|
}
|
17
43
|
|
18
44
|
export function toPortInfo(port: PortInfo) {
|