@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/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# [0.36.0](https://github.com/kapetacom/local-cluster-service/compare/v0.35.0...v0.36.0) (2024-02-06)
|
2
|
+
|
3
|
+
|
4
|
+
### Features
|
5
|
+
|
6
|
+
* Ensure instance operators only start 1 instance ([#123](https://github.com/kapetacom/local-cluster-service/issues/123)) ([10d8614](https://github.com/kapetacom/local-cluster-service/commit/10d86143ae6d9b41bae8e8dee345628b6d2cbe0a))
|
7
|
+
|
8
|
+
# [0.35.0](https://github.com/kapetacom/local-cluster-service/compare/v0.34.2...v0.35.0) (2024-01-31)
|
9
|
+
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* Adds support for getting instance operator info ([#122](https://github.com/kapetacom/local-cluster-service/issues/122)) ([8fa18ac](https://github.com/kapetacom/local-cluster-service/commit/8fa18ac58226ce9776f79a64d3a99cf56456e834))
|
14
|
+
|
1
15
|
## [0.34.2](https://github.com/kapetacom/local-cluster-service/compare/v0.34.1...v0.34.2) (2024-01-22)
|
2
16
|
|
3
17
|
|
@@ -148,4 +148,13 @@ router.get('/consumes/resource/:resourceType/:portType/:name', async (req, res)
|
|
148
148
|
router.get('/consumes/:resourceName/:type', (req, res) => {
|
149
149
|
res.send(serviceManager_1.serviceManager.getConsumerAddress(req.kapeta.systemId, req.kapeta.instanceId, req.params.resourceName, req.params.type, req.kapeta.environment));
|
150
150
|
});
|
151
|
+
/**
|
152
|
+
* Used by services to information about a block operator
|
153
|
+
*
|
154
|
+
* If the remote service is not already running it will be started
|
155
|
+
*/
|
156
|
+
router.get('/operator/:instanceId', async (req, res) => {
|
157
|
+
const operatorInfo = await instanceManager_1.instanceManager.getInstanceOperator(req.kapeta.systemId, req.params.instanceId, req.kapeta.environment);
|
158
|
+
res.send(operatorInfo);
|
159
|
+
});
|
151
160
|
exports.default = router;
|
@@ -5,7 +5,7 @@
|
|
5
5
|
/// <reference types="node" />
|
6
6
|
import FSExtra from 'fs-extra';
|
7
7
|
import Docker from 'dockerode';
|
8
|
-
import { InstanceInfo, LogEntry } from './types';
|
8
|
+
import { Health, InstanceInfo, LogEntry } from './types';
|
9
9
|
type StringMap = {
|
10
10
|
[key: string]: string;
|
11
11
|
};
|
@@ -26,12 +26,6 @@ export interface DockerMounts {
|
|
26
26
|
}
|
27
27
|
export type DockerContainerStatus = 'created' | 'running' | 'paused' | 'restarting' | 'removing' | 'exited' | 'dead';
|
28
28
|
export type DockerContainerHealth = 'starting' | 'healthy' | 'unhealthy' | 'none';
|
29
|
-
interface Health {
|
30
|
-
cmd: string;
|
31
|
-
interval?: number;
|
32
|
-
timeout?: number;
|
33
|
-
retries?: number;
|
34
|
-
}
|
35
29
|
export declare const CONTAINER_LABEL_PORT_PREFIX = "kapeta_port-";
|
36
30
|
export declare const COMPOSE_LABEL_PROJECT = "com.docker.compose.project";
|
37
31
|
export declare const COMPOSE_LABEL_SERVICE = "com.docker.compose.service";
|
@@ -50,7 +44,7 @@ declare class ContainerManager {
|
|
50
44
|
isAlive(): boolean;
|
51
45
|
getMountPoint(systemId: string, ref: string, mountName: string): string;
|
52
46
|
createMounts(systemId: string, kind: string, mountOpts: StringMap | null | undefined): Promise<StringMap>;
|
53
|
-
createVolumes(systemId: string,
|
47
|
+
createVolumes(systemId: string, serviceId: string, mountOpts: StringMap | null | undefined): Promise<DockerMounts[]>;
|
54
48
|
ping(): Promise<void>;
|
55
49
|
docker(): Docker;
|
56
50
|
getContainerByName(containerName: string): Promise<ContainerInfo | undefined>;
|
@@ -168,12 +168,12 @@ class ContainerManager {
|
|
168
168
|
}
|
169
169
|
return mounts;
|
170
170
|
}
|
171
|
-
async createVolumes(systemId,
|
171
|
+
async createVolumes(systemId, serviceId, mountOpts) {
|
172
172
|
const Mounts = [];
|
173
173
|
if (mountOpts) {
|
174
174
|
const mountOptList = Object.entries(mountOpts);
|
175
175
|
for (const [mountName, containerPath] of mountOptList) {
|
176
|
-
const volumeName = `${systemId}_${
|
176
|
+
const volumeName = `${systemId}_${serviceId}_${mountName}`.replace(/[^a-z0-9]/gi, '_');
|
177
177
|
Mounts.push({
|
178
178
|
Target: containerPath,
|
179
179
|
Source: volumeName,
|
@@ -182,7 +182,7 @@ class ContainerManager {
|
|
182
182
|
Consistency: 'consistent',
|
183
183
|
Labels: {
|
184
184
|
[exports.COMPOSE_LABEL_PROJECT]: systemId.replace(/[^a-z0-9]/gi, '_'),
|
185
|
-
[exports.COMPOSE_LABEL_SERVICE]:
|
185
|
+
[exports.COMPOSE_LABEL_SERVICE]: serviceId.replace(/[^a-z0-9]/gi, '_'),
|
186
186
|
},
|
187
187
|
});
|
188
188
|
}
|
@@ -533,7 +533,7 @@ class ContainerManager {
|
|
533
533
|
return new ContainerInfo(dockerContainer);
|
534
534
|
}
|
535
535
|
async getLogs(instance) {
|
536
|
-
const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
|
536
|
+
const containerName = await (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
|
537
537
|
const containerInfo = await this.getContainerByName(containerName);
|
538
538
|
if (!containerInfo) {
|
539
539
|
return [
|
@@ -548,7 +548,7 @@ class ContainerManager {
|
|
548
548
|
return containerInfo.getLogs();
|
549
549
|
}
|
550
550
|
async stopLogListening(systemId, instanceId) {
|
551
|
-
const containerName = (0, utils_1.getBlockInstanceContainerName)(systemId, instanceId);
|
551
|
+
const containerName = await (0, utils_1.getBlockInstanceContainerName)(systemId, instanceId);
|
552
552
|
if (this.logStreams[containerName]) {
|
553
553
|
if (this.logStreams[containerName]?.timer) {
|
554
554
|
clearTimeout(this.logStreams[containerName].timer);
|
@@ -566,7 +566,7 @@ class ContainerManager {
|
|
566
566
|
}
|
567
567
|
}
|
568
568
|
async ensureLogListening(systemId, instanceId, handler) {
|
569
|
-
const containerName = (0, utils_1.getBlockInstanceContainerName)(systemId, instanceId);
|
569
|
+
const containerName = await (0, utils_1.getBlockInstanceContainerName)(systemId, instanceId);
|
570
570
|
try {
|
571
571
|
if (this.logStreams[containerName]?.stream) {
|
572
572
|
// Already listening - will shut itself down
|
@@ -791,7 +791,7 @@ class ContainerInfo {
|
|
791
791
|
if (!name.startsWith(exports.CONTAINER_LABEL_PORT_PREFIX)) {
|
792
792
|
return;
|
793
793
|
}
|
794
|
-
const hostPort = name.
|
794
|
+
const hostPort = name.substring(exports.CONTAINER_LABEL_PORT_PREFIX.length);
|
795
795
|
portTypes[hostPort] = portType;
|
796
796
|
});
|
797
797
|
lodash_1.default.forEach(inspectResult.HostConfig.PortBindings, (portBindings, containerPortSpec) => {
|
@@ -2,7 +2,7 @@
|
|
2
2
|
* Copyright 2023 Kapeta Inc.
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
|
-
import { InstanceInfo, LogEntry } from './types';
|
5
|
+
import { EnvironmentType, InstanceInfo, LogEntry, OperatorInstanceInfo } from './types';
|
6
6
|
import { Task } from './taskManager';
|
7
7
|
export declare class InstanceManager {
|
8
8
|
private _interval;
|
@@ -24,10 +24,11 @@ export declare class InstanceManager {
|
|
24
24
|
private getHealthUrl;
|
25
25
|
markAsStopped(systemId: string, instanceId: string): Promise<void>;
|
26
26
|
startAllForPlan(systemId: string): Promise<Task<InstanceInfo[]>>;
|
27
|
+
stopAllForPlan(systemId: string): Task<void>;
|
28
|
+
getInstanceOperator(systemId: string, instanceId: string, environment?: EnvironmentType): Promise<OperatorInstanceInfo>;
|
27
29
|
stop(systemId: string, instanceId: string): Promise<void>;
|
28
30
|
private stopInner;
|
29
|
-
|
30
|
-
start(systemId: string, instanceId: string): Promise<InstanceInfo | Task<InstanceInfo>>;
|
31
|
+
start(systemId: string, instanceId: string, checkForSingleton?: boolean): Promise<InstanceInfo | Task<InstanceInfo>>;
|
31
32
|
/**
|
32
33
|
* Stops an instance but does not remove it from the list of active instances
|
33
34
|
*
|
@@ -42,5 +43,9 @@ export declare class InstanceManager {
|
|
42
43
|
private checkInstances;
|
43
44
|
private getExternalStatus;
|
44
45
|
private requestInstanceStatus;
|
46
|
+
private isSingletonOperator;
|
47
|
+
private getKindForAssetRef;
|
48
|
+
private isUsingKind;
|
49
|
+
private getAllInstancesForKind;
|
45
50
|
}
|
46
51
|
export declare const instanceManager: InstanceManager;
|
@@ -256,10 +256,79 @@ class InstanceManager {
|
|
256
256
|
name: `Starting plan ${systemId}`,
|
257
257
|
});
|
258
258
|
}
|
259
|
+
stopAllForPlan(systemId) {
|
260
|
+
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|
261
|
+
const instancesForPlan = this._instances.filter((instance) => instance.systemId === systemId);
|
262
|
+
return taskManager_1.taskManager.add(`plan:stop:${systemId}`, async () => {
|
263
|
+
return this.stopInstances(instancesForPlan);
|
264
|
+
}, {
|
265
|
+
name: `Stopping plan ${systemId}`,
|
266
|
+
});
|
267
|
+
}
|
268
|
+
async getInstanceOperator(systemId, instanceId, environment) {
|
269
|
+
const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
|
270
|
+
if (!blockInstance) {
|
271
|
+
throw new Error(`Instance not found: ${systemId}/${instanceId}`);
|
272
|
+
}
|
273
|
+
const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockInstance.block.ref);
|
274
|
+
const block = await assetManager_1.assetManager.getAsset(blockRef, true);
|
275
|
+
if (!block) {
|
276
|
+
throw new Error(`Block not found: ${blockRef}`);
|
277
|
+
}
|
278
|
+
const operatorDefinition = await definitionsManager_1.definitionsManager.getDefinition(block.kind);
|
279
|
+
if (!operatorDefinition?.definition.spec.local) {
|
280
|
+
throw new Error(`Operator block has no local definition: ${blockRef}`);
|
281
|
+
}
|
282
|
+
const localConfig = operatorDefinition.definition.spec.local;
|
283
|
+
let instance = await this.start(systemId, instanceId);
|
284
|
+
if (instance instanceof taskManager_1.Task) {
|
285
|
+
instance = await instance.wait();
|
286
|
+
}
|
287
|
+
const container = await containerManager_1.containerManager.get(instance.pid);
|
288
|
+
if (!container) {
|
289
|
+
throw new Error(`Container not found: ${instance.pid}`);
|
290
|
+
}
|
291
|
+
const portInfo = await container.getPorts();
|
292
|
+
if (!portInfo) {
|
293
|
+
throw new Error(`No ports found for instance: ${instanceId}`);
|
294
|
+
}
|
295
|
+
const hostname = serviceManager_1.serviceManager.getLocalHost(environment);
|
296
|
+
const ports = {};
|
297
|
+
Object.entries(portInfo).forEach(([key, value]) => {
|
298
|
+
ports[key] = {
|
299
|
+
protocol: value.protocol,
|
300
|
+
port: parseInt(value.hostPort),
|
301
|
+
};
|
302
|
+
});
|
303
|
+
return {
|
304
|
+
hostname,
|
305
|
+
ports,
|
306
|
+
credentials: localConfig.credentials,
|
307
|
+
options: localConfig.options,
|
308
|
+
};
|
309
|
+
}
|
259
310
|
async stop(systemId, instanceId) {
|
260
311
|
return this.stopInner(systemId, instanceId, true);
|
261
312
|
}
|
262
|
-
async stopInner(systemId, instanceId, changeDesired = false) {
|
313
|
+
async stopInner(systemId, instanceId, changeDesired = false, checkForSingleton = true) {
|
314
|
+
if (checkForSingleton) {
|
315
|
+
const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
|
316
|
+
const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockInstance.block.ref);
|
317
|
+
const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
|
318
|
+
if (!blockAsset) {
|
319
|
+
throw new Error('Block not found: ' + blockRef);
|
320
|
+
}
|
321
|
+
if (await this.isSingletonOperator(blockAsset)) {
|
322
|
+
const instances = await this.getAllInstancesForKind(systemId, blockAsset.data.kind);
|
323
|
+
if (instances.length > 1) {
|
324
|
+
const promises = instances.map((id) => {
|
325
|
+
return this.stopInner(systemId, id, changeDesired, false);
|
326
|
+
});
|
327
|
+
await Promise.all(promises);
|
328
|
+
return;
|
329
|
+
}
|
330
|
+
}
|
331
|
+
}
|
263
332
|
return this.exclusive(systemId, instanceId, async () => {
|
264
333
|
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|
265
334
|
const instance = this.getInstance(systemId, instanceId);
|
@@ -277,11 +346,11 @@ class InstanceManager {
|
|
277
346
|
}
|
278
347
|
instance.status = types_1.InstanceStatus.STOPPING;
|
279
348
|
socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
|
280
|
-
console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
|
349
|
+
console.log('Stopping instance: %s::%s [desired: %s] [intentional: %s]', systemId, instanceId, instance.desiredStatus, changeDesired);
|
281
350
|
this.save();
|
282
351
|
try {
|
283
352
|
if (instance.type === 'docker') {
|
284
|
-
const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
|
353
|
+
const containerName = await (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
|
285
354
|
const container = await containerManager_1.containerManager.getContainerByName(containerName);
|
286
355
|
if (container) {
|
287
356
|
try {
|
@@ -314,26 +383,34 @@ class InstanceManager {
|
|
314
383
|
}
|
315
384
|
});
|
316
385
|
}
|
317
|
-
|
386
|
+
async start(systemId, instanceId, checkForSingleton = true) {
|
318
387
|
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|
319
|
-
const
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
}
|
325
|
-
|
326
|
-
|
388
|
+
const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
|
389
|
+
const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockInstance.block.ref);
|
390
|
+
const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
|
391
|
+
if (!blockAsset) {
|
392
|
+
throw new Error('Block not found: ' + blockRef);
|
393
|
+
}
|
394
|
+
if (checkForSingleton && (await this.isSingletonOperator(blockAsset))) {
|
395
|
+
const instances = await this.getAllInstancesForKind(systemId, blockAsset.data.kind);
|
396
|
+
if (instances.length > 1) {
|
397
|
+
const promises = instances.map((id) => {
|
398
|
+
return this.start(systemId, id, false);
|
399
|
+
});
|
400
|
+
await Promise.all(promises);
|
401
|
+
return promises[0];
|
402
|
+
}
|
403
|
+
}
|
327
404
|
return this.exclusive(systemId, instanceId, async () => {
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
405
|
+
let existingInstance = this.getInstance(systemId, instanceId);
|
406
|
+
if (existingInstance && existingInstance.pid) {
|
407
|
+
const container = await containerManager_1.containerManager.get(existingInstance.pid);
|
408
|
+
if (!container) {
|
409
|
+
// The container is not running
|
410
|
+
existingInstance = undefined;
|
411
|
+
}
|
334
412
|
}
|
335
|
-
|
336
|
-
if (existingInstance) {
|
413
|
+
if (existingInstance?.pid) {
|
337
414
|
if (existingInstance.status === types_1.InstanceStatus.READY) {
|
338
415
|
// Instance is already running
|
339
416
|
return existingInstance;
|
@@ -372,7 +449,7 @@ class InstanceManager {
|
|
372
449
|
// Definition not found
|
373
450
|
return Promise.resolve();
|
374
451
|
}
|
375
|
-
if (operatorManager_1.
|
452
|
+
if (operatorManager_1.KIND_RESOURCE_OPERATOR.toLowerCase() !== asset.definition.kind.toLowerCase()) {
|
376
453
|
// Not an operator
|
377
454
|
return Promise.resolve();
|
378
455
|
}
|
@@ -382,7 +459,7 @@ class InstanceManager {
|
|
382
459
|
return Promise.resolve();
|
383
460
|
}
|
384
461
|
console.log('Ensuring resource: %s in %s', consumerUri.id, systemId);
|
385
|
-
return operatorManager_1.operatorManager.
|
462
|
+
return operatorManager_1.operatorManager.ensureOperator(systemId, consumerUri.fullName, consumerUri.version);
|
386
463
|
});
|
387
464
|
await Promise.all(promises);
|
388
465
|
}
|
@@ -449,6 +526,7 @@ class InstanceManager {
|
|
449
526
|
*/
|
450
527
|
async prepareForRestart(systemId, instanceId) {
|
451
528
|
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|
529
|
+
console.log('Stopping instance during restart...', systemId, instanceId);
|
452
530
|
await this.stopInner(systemId, instanceId);
|
453
531
|
}
|
454
532
|
async stopAll() {
|
@@ -476,8 +554,13 @@ class InstanceManager {
|
|
476
554
|
while (all.length > 0) {
|
477
555
|
// Check a few instances at a time - docker doesn't like too many concurrent requests
|
478
556
|
const chunk = all.splice(0, 30);
|
479
|
-
const promises = chunk.map(async (
|
480
|
-
if (!
|
557
|
+
const promises = chunk.map(async (oldInstance) => {
|
558
|
+
if (!oldInstance.systemId) {
|
559
|
+
return;
|
560
|
+
}
|
561
|
+
// Grab the latest here
|
562
|
+
const instance = this.getInstance(oldInstance.systemId, oldInstance.instanceId);
|
563
|
+
if (!instance) {
|
481
564
|
return;
|
482
565
|
}
|
483
566
|
instance.systemId = (0, nodejs_utils_1.normalizeKapetaUri)(instance.systemId);
|
@@ -567,6 +650,7 @@ class InstanceManager {
|
|
567
650
|
[types_1.InstanceStatus.READY, types_1.InstanceStatus.STARTING, types_1.InstanceStatus.UNHEALTHY].includes(newStatus)) {
|
568
651
|
//If the instance is running but we want it to stop, stop it
|
569
652
|
try {
|
653
|
+
console.log('Stopping instance since it is its desired state', instance.systemId, instance.instanceId);
|
570
654
|
await this.stopInner(instance.systemId, instance.instanceId);
|
571
655
|
}
|
572
656
|
catch (e) {
|
@@ -596,7 +680,7 @@ class InstanceManager {
|
|
596
680
|
}
|
597
681
|
async getExternalStatus(instance) {
|
598
682
|
if (instance.type === types_1.InstanceType.DOCKER) {
|
599
|
-
const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
|
683
|
+
const containerName = await (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
|
600
684
|
const container = await containerManager_1.containerManager.getContainerByName(containerName);
|
601
685
|
if (!container) {
|
602
686
|
// If the container doesn't exist, we consider the instance stopped
|
@@ -687,6 +771,44 @@ class InstanceManager {
|
|
687
771
|
});
|
688
772
|
});
|
689
773
|
}
|
774
|
+
async isSingletonOperator(blockAsset) {
|
775
|
+
const provider = await assetManager_1.assetManager.getAsset(blockAsset.data.kind);
|
776
|
+
if (!provider) {
|
777
|
+
return false;
|
778
|
+
}
|
779
|
+
if ((0, nodejs_utils_1.parseKapetaUri)(provider.kind).fullName === operatorManager_1.KIND_BLOCK_OPERATOR) {
|
780
|
+
const localConfig = provider.data.spec.local;
|
781
|
+
return localConfig.singleton ?? false;
|
782
|
+
}
|
783
|
+
return false;
|
784
|
+
}
|
785
|
+
async getKindForAssetRef(assetRef) {
|
786
|
+
const block = await assetManager_1.assetManager.getAsset(assetRef);
|
787
|
+
if (!block) {
|
788
|
+
return null;
|
789
|
+
}
|
790
|
+
return block.data.kind;
|
791
|
+
}
|
792
|
+
async isUsingKind(ref, kind) {
|
793
|
+
const assetKind = await this.getKindForAssetRef(ref);
|
794
|
+
if (!assetKind) {
|
795
|
+
return false;
|
796
|
+
}
|
797
|
+
return (0, nodejs_utils_1.parseKapetaUri)(assetKind).fullName === (0, nodejs_utils_1.parseKapetaUri)(kind).fullName;
|
798
|
+
}
|
799
|
+
async getAllInstancesForKind(systemId, kind) {
|
800
|
+
const plan = await assetManager_1.assetManager.getPlan(systemId);
|
801
|
+
if (!plan?.spec?.blocks) {
|
802
|
+
return [];
|
803
|
+
}
|
804
|
+
const out = [];
|
805
|
+
for (const block of plan.spec.blocks) {
|
806
|
+
if (await this.isUsingKind(block.block.ref, kind)) {
|
807
|
+
out.push(block.id);
|
808
|
+
}
|
809
|
+
}
|
810
|
+
return out;
|
811
|
+
}
|
690
812
|
}
|
691
813
|
exports.InstanceManager = InstanceManager;
|
692
814
|
exports.instanceManager = new InstanceManager();
|
@@ -4,12 +4,13 @@
|
|
4
4
|
*/
|
5
5
|
import { DefinitionInfo } from '@kapeta/local-cluster-config';
|
6
6
|
import { ContainerInfo } from './containerManager';
|
7
|
-
import { EnvironmentType, OperatorInfo } from './types';
|
8
|
-
export declare const
|
7
|
+
import { EnvironmentType, LocalImageOptions, OperatorInfo } from './types';
|
8
|
+
export declare const KIND_RESOURCE_OPERATOR = "core/resource-type-operator";
|
9
|
+
export declare const KIND_BLOCK_OPERATOR = "core/block-type-operator";
|
9
10
|
declare class Operator {
|
10
11
|
private readonly _data;
|
11
12
|
constructor(data: DefinitionInfo);
|
12
|
-
getLocalData():
|
13
|
+
getLocalData(): LocalImageOptions;
|
13
14
|
getDefinitionInfo(): DefinitionInfo;
|
14
15
|
getCredentials(): any;
|
15
16
|
}
|
@@ -21,7 +22,7 @@ declare class OperatorManager {
|
|
21
22
|
/**
|
22
23
|
* Get operator definition for resource type
|
23
24
|
*/
|
24
|
-
getOperator(
|
25
|
+
getOperator(fullName: string, version: string): Promise<Operator>;
|
25
26
|
/**
|
26
27
|
* Get information about a specific consumed resource
|
27
28
|
*/
|
@@ -29,12 +30,11 @@ declare class OperatorManager {
|
|
29
30
|
/**
|
30
31
|
* Ensure we have a running operator of given type
|
31
32
|
*
|
32
|
-
* @param
|
33
|
-
* @param
|
34
|
-
* @param
|
35
|
-
* @return {Promise<ContainerInfo>}
|
33
|
+
* @param systemId the plan ref
|
34
|
+
* @param kind the full name - e.g. myhandle/rabbitmq
|
35
|
+
* @param version the version of the operator
|
36
36
|
*/
|
37
|
-
|
37
|
+
ensureOperator(systemId: string, kind: string, version: string): Promise<ContainerInfo>;
|
38
38
|
}
|
39
39
|
export declare const operatorManager: OperatorManager;
|
40
40
|
export {};
|
@@ -7,7 +7,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
7
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
8
8
|
};
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
10
|
-
exports.operatorManager = exports.
|
10
|
+
exports.operatorManager = exports.KIND_BLOCK_OPERATOR = exports.KIND_RESOURCE_OPERATOR = void 0;
|
11
11
|
const path_1 = __importDefault(require("path"));
|
12
12
|
const md5_1 = __importDefault(require("md5"));
|
13
13
|
const serviceManager_1 = require("./serviceManager");
|
@@ -20,7 +20,8 @@ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
|
20
20
|
const lodash_1 = __importDefault(require("lodash"));
|
21
21
|
const async_lock_1 = __importDefault(require("async-lock"));
|
22
22
|
const taskManager_1 = require("./taskManager");
|
23
|
-
exports.
|
23
|
+
exports.KIND_RESOURCE_OPERATOR = 'core/resource-type-operator';
|
24
|
+
exports.KIND_BLOCK_OPERATOR = 'core/block-type-operator';
|
24
25
|
const KIND_PLAN = 'core/plan';
|
25
26
|
class Operator {
|
26
27
|
_data;
|
@@ -50,18 +51,18 @@ class OperatorManager {
|
|
50
51
|
/**
|
51
52
|
* Get operator definition for resource type
|
52
53
|
*/
|
53
|
-
async getOperator(
|
54
|
-
const operators = await definitionsManager_1.definitionsManager.getDefinitions(exports.
|
54
|
+
async getOperator(fullName, version) {
|
55
|
+
const operators = await definitionsManager_1.definitionsManager.getDefinitions([exports.KIND_RESOURCE_OPERATOR, exports.KIND_BLOCK_OPERATOR]);
|
55
56
|
const operator = operators.find((operator) => operator.definition &&
|
56
57
|
operator.definition.metadata &&
|
57
58
|
operator.definition.metadata.name &&
|
58
|
-
operator.definition.metadata.name.toLowerCase() ===
|
59
|
+
operator.definition.metadata.name.toLowerCase() === fullName.toLowerCase() &&
|
59
60
|
operator.version === version);
|
60
61
|
if (!operator) {
|
61
|
-
throw new Error(`Unknown
|
62
|
+
throw new Error(`Unknown operator type: ${fullName}:${version}`);
|
62
63
|
}
|
63
64
|
if (!operator.definition.spec || !operator.definition.spec.local) {
|
64
|
-
throw new Error(`Operator missing local definition: ${
|
65
|
+
throw new Error(`Operator missing local definition: ${fullName}:${version}`);
|
65
66
|
}
|
66
67
|
return new Operator(operator);
|
67
68
|
}
|
@@ -96,7 +97,7 @@ class OperatorManager {
|
|
96
97
|
const kindUri = (0, nodejs_utils_1.parseKapetaUri)(blockResource.kind);
|
97
98
|
const operator = await this.getOperator(resourceType, kindUri.version);
|
98
99
|
const credentials = operator.getCredentials();
|
99
|
-
const container = await this.
|
100
|
+
const container = await this.ensureOperator(systemId, resourceType, kindUri.version);
|
100
101
|
const portInfo = await container.getPort(portType);
|
101
102
|
if (!portInfo) {
|
102
103
|
throw new Error('Unknown resource port type : ' + resourceType + '#' + portType);
|
@@ -119,16 +120,15 @@ class OperatorManager {
|
|
119
120
|
/**
|
120
121
|
* Ensure we have a running operator of given type
|
121
122
|
*
|
122
|
-
* @param
|
123
|
-
* @param
|
124
|
-
* @param
|
125
|
-
* @return {Promise<ContainerInfo>}
|
123
|
+
* @param systemId the plan ref
|
124
|
+
* @param kind the full name - e.g. myhandle/rabbitmq
|
125
|
+
* @param version the version of the operator
|
126
126
|
*/
|
127
|
-
async
|
127
|
+
async ensureOperator(systemId, kind, version) {
|
128
128
|
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|
129
|
-
const key = `${systemId}#${
|
129
|
+
const key = `${systemId}#${kind}:${version}`;
|
130
130
|
return await this.operatorLock.acquire(key, async () => {
|
131
|
-
const operator = await this.getOperator(
|
131
|
+
const operator = await this.getOperator(kind, version);
|
132
132
|
const operatorData = operator.getLocalData();
|
133
133
|
const portTypes = Object.keys(operatorData.ports);
|
134
134
|
portTypes.sort();
|
@@ -136,20 +136,15 @@ class OperatorManager {
|
|
136
136
|
for (let i = 0; i < portTypes.length; i++) {
|
137
137
|
const portType = portTypes[i];
|
138
138
|
let containerPortInfo = operatorData.ports[portType];
|
139
|
-
const hostPort = await serviceManager_1.serviceManager.ensureServicePort(systemId,
|
140
|
-
|
141
|
-
|
142
|
-
}
|
143
|
-
if (!containerPortInfo.type) {
|
144
|
-
containerPortInfo.type = 'tcp';
|
145
|
-
}
|
146
|
-
const portId = containerPortInfo.port + '/' + containerPortInfo.type;
|
139
|
+
const hostPort = await serviceManager_1.serviceManager.ensureServicePort(systemId, kind, portType);
|
140
|
+
const portInfo = (0, utils_1.toPortInfo)(containerPortInfo);
|
141
|
+
const portId = portInfo.port + '/' + portInfo.type;
|
147
142
|
ports[portId] = {
|
148
143
|
type: portType,
|
149
144
|
hostPort,
|
150
145
|
};
|
151
146
|
}
|
152
|
-
const nameParts = [systemId,
|
147
|
+
const nameParts = [systemId, kind.toLowerCase(), version];
|
153
148
|
const containerName = `kapeta-resource-${(0, md5_1.default)(nameParts.join('_'))}`;
|
154
149
|
const PortBindings = {};
|
155
150
|
const Env = [];
|
@@ -157,7 +152,7 @@ class OperatorManager {
|
|
157
152
|
const Labels = {
|
158
153
|
kapeta: 'true',
|
159
154
|
[containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
160
|
-
[containerManager_1.COMPOSE_LABEL_SERVICE]: [
|
155
|
+
[containerManager_1.COMPOSE_LABEL_SERVICE]: [kind, version].join('_').replace(/[^a-z0-9]/gi, '_'),
|
161
156
|
};
|
162
157
|
const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
|
163
158
|
const bindHost = (0, utils_1.getBindHost)();
|
@@ -172,7 +167,7 @@ class OperatorManager {
|
|
172
167
|
];
|
173
168
|
Labels[containerManager_1.CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
|
174
169
|
});
|
175
|
-
const Mounts = await containerManager_1.containerManager.createVolumes(systemId,
|
170
|
+
const Mounts = await containerManager_1.containerManager.createVolumes(systemId, kind, operatorData.mounts);
|
176
171
|
lodash_1.default.forEach(operatorData.env, (value, name) => {
|
177
172
|
Env.push(name + '=' + value);
|
178
173
|
});
|
@@ -9,6 +9,7 @@ export declare const HTTP_PORTS: string[];
|
|
9
9
|
declare class ServiceManager {
|
10
10
|
private _systems;
|
11
11
|
constructor();
|
12
|
+
getLocalHost(environmentType?: EnvironmentType): string;
|
12
13
|
_forLocal(port: string | number, path?: string, environmentType?: EnvironmentType): string;
|
13
14
|
_ensureSystem(systemId: string): any;
|
14
15
|
_ensureService(systemId: string, serviceId: string): any;
|
@@ -31,22 +31,22 @@ class ServiceManager {
|
|
31
31
|
});
|
32
32
|
});
|
33
33
|
}
|
34
|
-
|
35
|
-
if (!path) {
|
36
|
-
path = '';
|
37
|
-
}
|
38
|
-
let host;
|
34
|
+
getLocalHost(environmentType) {
|
39
35
|
if (environmentType === 'docker') {
|
40
36
|
//We're inside a docker container, so we can use this special host name to access the host machine
|
41
|
-
|
37
|
+
return 'host.docker.internal';
|
42
38
|
}
|
43
|
-
|
44
|
-
|
39
|
+
return clusterService_1.clusterService.getClusterServiceHost();
|
40
|
+
}
|
41
|
+
_forLocal(port, path, environmentType) {
|
42
|
+
if (!path) {
|
43
|
+
path = '';
|
45
44
|
}
|
45
|
+
const hostname = this.getLocalHost(environmentType);
|
46
46
|
if (path.startsWith('/')) {
|
47
47
|
path = path.substring(1);
|
48
48
|
}
|
49
|
-
return `http://${
|
49
|
+
return `http://${hostname}:${port}/${path}`;
|
50
50
|
}
|
51
51
|
_ensureSystem(systemId) {
|
52
52
|
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|