@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
@@ -5,22 +5,24 @@
|
|
5
5
|
|
6
6
|
import FSExtra from 'fs-extra';
|
7
7
|
import ClusterConfig, { DefinitionInfo } from '@kapeta/local-cluster-config';
|
8
|
-
import { getBindHost, getBlockInstanceContainerName, readYML } from './utils';
|
8
|
+
import { getBindHost, getBlockInstanceContainerName, readYML, toPortInfo } from './utils';
|
9
9
|
import { KapetaURI, parseKapetaUri, normalizeKapetaUri } from '@kapeta/nodejs-utils';
|
10
10
|
import { DEFAULT_PORT_TYPE, HTTP_PORT_TYPE, HTTP_PORTS, serviceManager } from '../serviceManager';
|
11
11
|
import {
|
12
12
|
COMPOSE_LABEL_PROJECT,
|
13
13
|
COMPOSE_LABEL_SERVICE,
|
14
|
+
CONTAINER_LABEL_PORT_PREFIX,
|
14
15
|
containerManager,
|
15
16
|
DockerMounts,
|
16
17
|
toLocalBindVolume,
|
17
18
|
} from '../containerManager';
|
18
19
|
import { LogData } from './LogData';
|
19
20
|
import { clusterService } from '../clusterService';
|
20
|
-
import { AnyMap, BlockProcessParams, InstanceType, ProcessInfo, StringMap } from '../types';
|
21
|
+
import { AnyMap, BlockProcessParams, InstanceType, LocalImageOptions, ProcessInfo, StringMap } from '../types';
|
21
22
|
import { definitionsManager } from '../definitionsManager';
|
22
23
|
import Docker from 'dockerode';
|
23
24
|
import OS from 'node:os';
|
25
|
+
import { taskManager } from '../taskManager';
|
24
26
|
|
25
27
|
const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
|
26
28
|
const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
|
@@ -200,7 +202,7 @@ export class BlockInstanceRunner {
|
|
200
202
|
throw new Error(`Missing docker image information: ${JSON.stringify(localContainer)}`);
|
201
203
|
}
|
202
204
|
|
203
|
-
const containerName = getBlockInstanceContainerName(this._systemId, blockInstance.id);
|
205
|
+
const containerName = await getBlockInstanceContainerName(this._systemId, blockInstance.id, targetKindUri.id);
|
204
206
|
const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
|
205
207
|
const dockerOpts = localContainer.options ?? {};
|
206
208
|
const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
|
@@ -321,7 +323,7 @@ export class BlockInstanceRunner {
|
|
321
323
|
providerVersion
|
322
324
|
);
|
323
325
|
|
324
|
-
const containerName = getBlockInstanceContainerName(this._systemId, blockInstance.id);
|
326
|
+
const containerName = await getBlockInstanceContainerName(this._systemId, blockInstance.id, kindUri.id);
|
325
327
|
|
326
328
|
// For windows we need to default to root
|
327
329
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
@@ -351,16 +353,7 @@ export class BlockInstanceRunner {
|
|
351
353
|
});
|
352
354
|
}
|
353
355
|
|
354
|
-
|
355
|
-
*
|
356
|
-
* @param blockInstance
|
357
|
-
* @param blockUri
|
358
|
-
* @param providerDefinition
|
359
|
-
* @param {{[key:string]:string}} env
|
360
|
-
* @return {Promise<ProcessDetails>}
|
361
|
-
* @private
|
362
|
-
*/
|
363
|
-
async _startOperatorProcess(
|
356
|
+
private async _startOperatorProcess(
|
364
357
|
blockInstance: BlockProcessParams,
|
365
358
|
blockUri: KapetaURI,
|
366
359
|
providerDefinition: DefinitionInfo,
|
@@ -384,90 +377,124 @@ export class BlockInstanceRunner {
|
|
384
377
|
throw new Error(`Provider did not have local image: ${providerRef}`);
|
385
378
|
}
|
386
379
|
|
387
|
-
const
|
388
|
-
|
389
|
-
//We only want 1 operator per operator type - across all local systems
|
390
|
-
const containerName = getBlockInstanceContainerName(this._systemId, blockInstance.id);
|
391
|
-
const logs = new LogData();
|
392
|
-
|
393
|
-
const bindHost = getBindHost();
|
394
|
-
|
395
|
-
const ExposedPorts: AnyMap = {};
|
396
|
-
const addonEnv: StringMap = {};
|
397
|
-
const PortBindings: AnyMap = {};
|
398
|
-
let HealthCheck = undefined;
|
399
|
-
let Mounts: DockerMounts[] = [];
|
400
|
-
const localPorts = spec.local.ports as { [p: string]: { port: string; type: string } };
|
401
|
-
const promises = Object.entries(localPorts).map(async ([portType, value]) => {
|
402
|
-
const dockerPort = `${value.port}/${value.type}`;
|
403
|
-
ExposedPorts[dockerPort] = {};
|
404
|
-
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = value.port;
|
405
|
-
const publicPort = await serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
406
|
-
PortBindings[dockerPort] = [
|
407
|
-
{
|
408
|
-
HostIp: bindHost,
|
409
|
-
HostPort: `${publicPort}`,
|
410
|
-
},
|
411
|
-
];
|
412
|
-
});
|
413
|
-
|
414
|
-
await Promise.all(promises);
|
415
|
-
|
416
|
-
if (spec.local?.env) {
|
417
|
-
Object.entries(spec.local.env).forEach(([key, value]) => {
|
418
|
-
addonEnv[key] = value as string;
|
419
|
-
});
|
420
|
-
}
|
380
|
+
const local = spec.local as LocalImageOptions;
|
421
381
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
382
|
+
const dockerImage = local.image;
|
383
|
+
const operatorUri = local.singleton ? parseKapetaUri(providerRef) : blockUri;
|
384
|
+
const operatorId = local.singleton ? providerRef : blockInstance.id;
|
385
|
+
const operatorRef = local.singleton ? providerRef : blockInstance.ref;
|
426
386
|
|
427
|
-
if (
|
428
|
-
|
387
|
+
if (local.singleton && env) {
|
388
|
+
env[KAPETA_BLOCK_REF] = operatorRef;
|
389
|
+
env[KAPETA_INSTANCE_ID] = operatorId;
|
429
390
|
}
|
430
391
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
392
|
+
const containerName = await getBlockInstanceContainerName(this._systemId, blockInstance.id, providerRef);
|
393
|
+
|
394
|
+
const task = taskManager.add(
|
395
|
+
`container:start:${containerName}`,
|
396
|
+
async () => {
|
397
|
+
const logs = new LogData();
|
398
|
+
|
399
|
+
const bindHost = getBindHost();
|
400
|
+
|
401
|
+
const ExposedPorts: AnyMap = {};
|
402
|
+
const addonEnv: StringMap = {};
|
403
|
+
const PortBindings: AnyMap = {};
|
404
|
+
let HealthCheck = undefined;
|
405
|
+
let Mounts: DockerMounts[] = [];
|
406
|
+
const localPorts = local.ports ?? {};
|
407
|
+
const labels: { [key: string]: string } = {};
|
408
|
+
const promises = Object.entries(localPorts).map(async ([portType, value]) => {
|
409
|
+
const portInfo = toPortInfo(value);
|
410
|
+
const dockerPort = `${portInfo.port}/${portInfo.type}`;
|
411
|
+
ExposedPorts[dockerPort] = {};
|
412
|
+
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = `${portInfo.port}`;
|
413
|
+
const publicPort = await serviceManager.ensureServicePort(this._systemId, operatorId, portType);
|
414
|
+
PortBindings[dockerPort] = [
|
415
|
+
{
|
416
|
+
HostIp: bindHost,
|
417
|
+
HostPort: `${publicPort}`,
|
418
|
+
},
|
419
|
+
];
|
420
|
+
|
421
|
+
labels[CONTAINER_LABEL_PORT_PREFIX + publicPort] = portType;
|
422
|
+
});
|
423
|
+
|
424
|
+
await Promise.all(promises);
|
425
|
+
|
426
|
+
if (local.env) {
|
427
|
+
Object.entries(local.env).forEach(([key, value]) => {
|
428
|
+
addonEnv[key] = value as string;
|
429
|
+
});
|
430
|
+
}
|
431
|
+
|
432
|
+
if (local.mounts) {
|
433
|
+
Mounts = await containerManager.createVolumes(this._systemId, operatorUri.id, local.mounts);
|
434
|
+
}
|
435
|
+
|
436
|
+
if (local.health) {
|
437
|
+
HealthCheck = containerManager.toDockerHealth(local.health);
|
438
|
+
}
|
439
|
+
|
440
|
+
// For windows we need to default to root
|
441
|
+
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
|
442
|
+
|
443
|
+
const Binds = local.singleton
|
444
|
+
? [`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`]
|
445
|
+
: [
|
446
|
+
`${toLocalBindVolume(kapetaYmlPath)}:/kapeta.yml:ro`,
|
447
|
+
`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`,
|
448
|
+
];
|
449
|
+
|
450
|
+
const systemUri = parseKapetaUri(this._systemId);
|
451
|
+
|
452
|
+
console.log(
|
453
|
+
`Ensuring container for operator block: ${containerName} [singleton: ${!!local.singleton}]`
|
454
|
+
);
|
455
|
+
|
456
|
+
logs.addLog(`Ensuring container for operator block: ${containerName}`);
|
457
|
+
const out = await this.ensureContainer({
|
458
|
+
Image: dockerImage,
|
459
|
+
name: containerName,
|
460
|
+
ExposedPorts,
|
461
|
+
HealthCheck,
|
462
|
+
HostConfig: {
|
463
|
+
Binds,
|
464
|
+
PortBindings,
|
465
|
+
Mounts,
|
466
|
+
},
|
467
|
+
Labels: {
|
468
|
+
...labels,
|
469
|
+
instance: operatorId,
|
470
|
+
[COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
471
|
+
[COMPOSE_LABEL_SERVICE]: operatorUri.id.replace(/[^a-z0-9]/gi, '_'),
|
472
|
+
},
|
473
|
+
Env: [
|
474
|
+
`KAPETA_INSTANCE_NAME=${operatorRef}`,
|
475
|
+
`KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
|
476
|
+
...DOCKER_ENV_VARS,
|
477
|
+
...Object.entries({
|
478
|
+
...env,
|
479
|
+
...addonEnv,
|
480
|
+
}).map(([key, value]) => `${key}=${value}`),
|
481
|
+
],
|
482
|
+
});
|
483
|
+
|
484
|
+
const portTypes = local.ports ? Object.keys(local.ports) : [];
|
485
|
+
if (portTypes.length > 0) {
|
486
|
+
out.portType = portTypes[0];
|
487
|
+
}
|
488
|
+
|
489
|
+
return out;
|
453
490
|
},
|
454
|
-
|
455
|
-
`
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
...env,
|
460
|
-
...addonEnv,
|
461
|
-
}).map(([key, value]) => `${key}=${value}`),
|
462
|
-
],
|
463
|
-
});
|
464
|
-
|
465
|
-
const portTypes = spec.local.ports ? Object.keys(spec.local.ports) : [];
|
466
|
-
if (portTypes.length > 0) {
|
467
|
-
out.portType = portTypes[0];
|
468
|
-
}
|
491
|
+
{
|
492
|
+
name: `Starting container for ${providerRef}`,
|
493
|
+
systemId: this._systemId,
|
494
|
+
}
|
495
|
+
);
|
469
496
|
|
470
|
-
return
|
497
|
+
return task.wait();
|
471
498
|
}
|
472
499
|
|
473
500
|
private async getDockerPortBindings(
|
package/src/utils/utils.ts
CHANGED
@@ -5,17 +5,54 @@
|
|
5
5
|
|
6
6
|
import FS from 'node:fs';
|
7
7
|
import YAML from 'yaml';
|
8
|
-
import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
9
8
|
import md5 from 'md5';
|
10
9
|
import { EntityList } from '@kapeta/schemas';
|
11
10
|
import _ from 'lodash';
|
12
|
-
import { AnyMap } from '../types';
|
11
|
+
import { AnyMap, PortInfo } from '../types';
|
13
12
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
13
|
+
import { definitionsManager } from '../definitionsManager';
|
14
|
+
import { 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(systemId + blockType)}`;
|
39
|
+
}
|
14
40
|
|
15
|
-
export function getBlockInstanceContainerName(systemId: string, instanceId: string) {
|
16
41
|
return `kapeta-block-instance-${md5(systemId + instanceId)}`;
|
17
42
|
}
|
18
43
|
|
44
|
+
export function toPortInfo(port: PortInfo) {
|
45
|
+
if (typeof port === 'number' || typeof port === 'string') {
|
46
|
+
return { port: parseInt(`${port}`), type: 'tcp' };
|
47
|
+
}
|
48
|
+
|
49
|
+
if (!port.type) {
|
50
|
+
port.type = 'tcp';
|
51
|
+
}
|
52
|
+
|
53
|
+
return port;
|
54
|
+
}
|
55
|
+
|
19
56
|
export function getRemoteUrl(id: string, defautValue: string) {
|
20
57
|
const remoteConfig = ClusterConfiguration.getClusterConfig().remote;
|
21
58
|
return remoteConfig?.[id] ?? defautValue;
|