@kapeta/local-cluster-service 0.8.3 → 0.9.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/assetManager.js +7 -4
- package/dist/cjs/src/clusterService.js +2 -0
- package/dist/cjs/src/codeGeneratorManager.js +3 -3
- package/dist/cjs/src/config/routes.js +1 -1
- package/dist/cjs/src/configManager.js +13 -1
- package/dist/cjs/src/containerManager.d.ts +25 -2
- package/dist/cjs/src/containerManager.js +51 -16
- package/dist/cjs/src/definitionsManager.d.ts +11 -0
- package/dist/cjs/src/definitionsManager.js +44 -0
- package/dist/cjs/src/filesystemManager.js +0 -2
- package/dist/cjs/src/instanceManager.d.ts +23 -47
- package/dist/cjs/src/instanceManager.js +416 -235
- package/dist/cjs/src/instances/routes.js +23 -14
- package/dist/cjs/src/middleware/kapeta.js +7 -0
- package/dist/cjs/src/networkManager.js +6 -0
- package/dist/cjs/src/operatorManager.js +8 -4
- package/dist/cjs/src/providerManager.js +3 -3
- package/dist/cjs/src/repositoryManager.js +7 -3
- package/dist/cjs/src/serviceManager.js +5 -0
- package/dist/cjs/src/types.d.ts +39 -13
- package/dist/cjs/src/types.js +28 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +3 -3
- package/dist/cjs/src/utils/BlockInstanceRunner.js +34 -32
- package/dist/cjs/src/utils/utils.d.ts +2 -0
- package/dist/cjs/src/utils/utils.js +17 -1
- package/dist/esm/src/assetManager.js +7 -4
- package/dist/esm/src/clusterService.js +2 -0
- package/dist/esm/src/codeGeneratorManager.js +3 -3
- package/dist/esm/src/config/routes.js +1 -1
- package/dist/esm/src/configManager.js +13 -1
- package/dist/esm/src/containerManager.d.ts +25 -2
- package/dist/esm/src/containerManager.js +50 -15
- package/dist/esm/src/definitionsManager.d.ts +11 -0
- package/dist/esm/src/definitionsManager.js +38 -0
- package/dist/esm/src/filesystemManager.js +0 -2
- package/dist/esm/src/instanceManager.d.ts +23 -47
- package/dist/esm/src/instanceManager.js +416 -236
- package/dist/esm/src/instances/routes.js +23 -14
- package/dist/esm/src/middleware/kapeta.js +7 -0
- package/dist/esm/src/networkManager.js +6 -0
- package/dist/esm/src/operatorManager.js +8 -4
- package/dist/esm/src/providerManager.js +3 -3
- package/dist/esm/src/repositoryManager.js +7 -3
- package/dist/esm/src/serviceManager.js +5 -0
- package/dist/esm/src/types.d.ts +39 -13
- package/dist/esm/src/types.js +27 -1
- package/dist/esm/src/utils/BlockInstanceRunner.d.ts +3 -3
- package/dist/esm/src/utils/BlockInstanceRunner.js +35 -33
- package/dist/esm/src/utils/utils.d.ts +2 -0
- package/dist/esm/src/utils/utils.js +14 -0
- package/package.json +2 -1
- package/src/assetManager.ts +7 -4
- package/src/clusterService.ts +3 -0
- package/src/codeGeneratorManager.ts +3 -2
- package/src/config/routes.ts +1 -1
- package/src/configManager.ts +13 -1
- package/src/containerManager.ts +72 -16
- package/src/definitionsManager.ts +54 -0
- package/src/filesystemManager.ts +0 -2
- package/src/instanceManager.ts +495 -266
- package/src/instances/routes.ts +23 -17
- package/src/middleware/kapeta.ts +10 -0
- package/src/networkManager.ts +6 -0
- package/src/operatorManager.ts +11 -6
- package/src/providerManager.ts +3 -2
- package/src/repositoryManager.ts +7 -3
- package/src/serviceManager.ts +6 -0
- package/src/types.ts +44 -14
- package/src/utils/BlockInstanceRunner.ts +39 -36
- package/src/utils/utils.ts +18 -0
package/src/assetManager.ts
CHANGED
@@ -10,6 +10,8 @@ import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
|
10
10
|
import { repositoryManager } from './repositoryManager';
|
11
11
|
import { BlockDefinition } from '@kapeta/schemas';
|
12
12
|
import { Actions } from '@kapeta/nodejs-registry-utils';
|
13
|
+
import { definitionsManager } from './definitionsManager';
|
14
|
+
import { normalizeKapetaUri } from './utils/utils';
|
13
15
|
|
14
16
|
export interface EnrichedAsset {
|
15
17
|
ref: string;
|
@@ -67,7 +69,7 @@ class AssetManager {
|
|
67
69
|
*/
|
68
70
|
getAssets(assetKinds?: string[]): EnrichedAsset[] {
|
69
71
|
if (!assetKinds) {
|
70
|
-
const blockTypeProviders =
|
72
|
+
const blockTypeProviders = definitionsManager.getDefinitions([
|
71
73
|
'core/block-type',
|
72
74
|
'core/block-type-operator',
|
73
75
|
]);
|
@@ -77,7 +79,7 @@ class AssetManager {
|
|
77
79
|
assetKinds.push('core/plan');
|
78
80
|
}
|
79
81
|
|
80
|
-
const assets =
|
82
|
+
const assets = definitionsManager.getDefinitions(assetKinds);
|
81
83
|
|
82
84
|
return assets.map(enrichAsset);
|
83
85
|
}
|
@@ -97,6 +99,7 @@ class AssetManager {
|
|
97
99
|
}
|
98
100
|
|
99
101
|
async getAsset(ref: string, noCache: boolean = false): Promise<EnrichedAsset | undefined> {
|
102
|
+
ref = normalizeKapetaUri(ref);
|
100
103
|
const cacheKey = `getAsset:${ref}`;
|
101
104
|
if (!noCache && this.cache.has(cacheKey)) {
|
102
105
|
return this.cache.get(cacheKey);
|
@@ -104,10 +107,10 @@ class AssetManager {
|
|
104
107
|
const uri = parseKapetaUri(ref);
|
105
108
|
await repositoryManager.ensureAsset(uri.handle, uri.name, uri.version);
|
106
109
|
|
107
|
-
let asset =
|
110
|
+
let asset = definitionsManager
|
111
|
+
.getDefinitions()
|
108
112
|
.map(enrichAsset)
|
109
113
|
.find((a) => parseKapetaUri(a.ref).equals(uri));
|
110
|
-
|
111
114
|
if (!asset) {
|
112
115
|
throw new Error('Asset not found: ' + ref);
|
113
116
|
}
|
package/src/clusterService.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import { normalizeKapetaUri } from './utils/utils';
|
2
|
+
|
1
3
|
const net = require('net');
|
2
4
|
const DEFAULT_SERVER_PORT = 35100;
|
3
5
|
const DEFAULT_START_PORT = 40000;
|
@@ -125,6 +127,7 @@ class ClusterService {
|
|
125
127
|
* @return {string}
|
126
128
|
*/
|
127
129
|
getProxyPath(systemId: string, consumerInstanceId: string, consumerResourceName: string, portType: string) {
|
130
|
+
systemId = normalizeKapetaUri(systemId);
|
128
131
|
return `/proxy/${encodeURIComponent(systemId)}/${encodeURIComponent(consumerInstanceId)}/${encodeURIComponent(
|
129
132
|
consumerResourceName
|
130
133
|
)}/${encodeURIComponent(portType)}/`;
|
@@ -2,6 +2,7 @@ import Path from 'path';
|
|
2
2
|
import { registry as Targets, BlockCodeGenerator, CodeWriter } from '@kapeta/codegen';
|
3
3
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
4
4
|
import { BlockDefinition } from '@kapeta/schemas';
|
5
|
+
import { definitionsManager } from './definitionsManager';
|
5
6
|
|
6
7
|
const TARGET_KIND = 'core/language-target';
|
7
8
|
const BLOCK_TYPE_KIND = 'core/block-type';
|
@@ -9,7 +10,7 @@ const BLOCK_TYPE_KIND = 'core/block-type';
|
|
9
10
|
class CodeGeneratorManager {
|
10
11
|
async reload() {
|
11
12
|
Targets.reset();
|
12
|
-
const languageTargets =
|
13
|
+
const languageTargets = definitionsManager.getDefinitions(TARGET_KIND);
|
13
14
|
for (const languageTarget of languageTargets) {
|
14
15
|
const key = `${languageTarget.definition.metadata.name}:${languageTarget.version}`;
|
15
16
|
try {
|
@@ -31,7 +32,7 @@ class CodeGeneratorManager {
|
|
31
32
|
return false;
|
32
33
|
}
|
33
34
|
|
34
|
-
const blockTypes =
|
35
|
+
const blockTypes = definitionsManager.getDefinitions(BLOCK_TYPE_KIND);
|
35
36
|
const blockTypeKinds = blockTypes.map(
|
36
37
|
(blockType) => blockType.definition.metadata.name.toLowerCase() + ':' + blockType.version
|
37
38
|
);
|
package/src/config/routes.ts
CHANGED
@@ -41,7 +41,7 @@ router.put('/instance', async (req: KapetaBodyRequest, res) => {
|
|
41
41
|
if (req.kapeta!.instanceId) {
|
42
42
|
configManager.setConfigForSection(req.kapeta!.systemId, req.kapeta!.instanceId, config);
|
43
43
|
//Restart the instance if it is running after config change
|
44
|
-
await instanceManager.
|
44
|
+
await instanceManager.restart(req.kapeta!.systemId, req.kapeta!.instanceId);
|
45
45
|
} else {
|
46
46
|
configManager.setConfigForSystem(req.kapeta!.systemId, config);
|
47
47
|
}
|
package/src/configManager.ts
CHANGED
@@ -3,6 +3,7 @@ import { BlockInstance } from '@kapeta/schemas';
|
|
3
3
|
import { storageService } from './storageService';
|
4
4
|
import { assetManager } from './assetManager';
|
5
5
|
import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
6
|
+
import { normalizeKapetaUri } from './utils/utils';
|
6
7
|
|
7
8
|
type AnyMap = { [key: string]: any };
|
8
9
|
|
@@ -19,6 +20,7 @@ class ConfigManager {
|
|
19
20
|
}
|
20
21
|
|
21
22
|
_forSystem(systemId: string) {
|
23
|
+
systemId = normalizeKapetaUri(systemId);
|
22
24
|
if (!this._config[systemId]) {
|
23
25
|
this._config[systemId] = {};
|
24
26
|
}
|
@@ -27,16 +29,19 @@ class ConfigManager {
|
|
27
29
|
}
|
28
30
|
|
29
31
|
setConfigForSystem(systemId: string, config: AnyMap) {
|
32
|
+
systemId = normalizeKapetaUri(systemId);
|
30
33
|
const systemConfig = config || {};
|
31
34
|
|
32
35
|
storageService.put('config', systemId, systemConfig);
|
33
36
|
}
|
34
37
|
|
35
38
|
getConfigForSystem(systemId: string): AnyMap {
|
39
|
+
systemId = normalizeKapetaUri(systemId);
|
36
40
|
return this._forSystem(systemId);
|
37
41
|
}
|
38
42
|
|
39
43
|
setConfigForSection(systemId: string, sectionId: string, config: AnyMap) {
|
44
|
+
systemId = normalizeKapetaUri(systemId);
|
40
45
|
let systemConfig = this._forSystem(systemId);
|
41
46
|
systemConfig[sectionId] = config || {};
|
42
47
|
|
@@ -44,6 +49,7 @@ class ConfigManager {
|
|
44
49
|
}
|
45
50
|
|
46
51
|
getConfigForSection(systemId: string, sectionId: string) {
|
52
|
+
systemId = normalizeKapetaUri(systemId);
|
47
53
|
const systemConfig = this._forSystem(systemId);
|
48
54
|
|
49
55
|
if (!systemConfig[sectionId]) {
|
@@ -70,6 +76,10 @@ class ConfigManager {
|
|
70
76
|
* @returns {Promise<{systemId:string,instanceId:string}>}
|
71
77
|
*/
|
72
78
|
async resolveIdentity(blockRef: string, systemId?: string) {
|
79
|
+
blockRef = normalizeKapetaUri(blockRef);
|
80
|
+
if (systemId) {
|
81
|
+
systemId = normalizeKapetaUri(systemId);
|
82
|
+
}
|
73
83
|
const planAssets = assetManager.getPlans();
|
74
84
|
|
75
85
|
const blockUri = parseKapetaUri(blockRef);
|
@@ -89,7 +99,7 @@ class ConfigManager {
|
|
89
99
|
const refUri = parseKapetaUri(blockInstance.block.ref);
|
90
100
|
if (refUri.equals(blockUri)) {
|
91
101
|
matchingIdentities.push({
|
92
|
-
systemId: planAsset.ref,
|
102
|
+
systemId: normalizeKapetaUri(planAsset.ref),
|
93
103
|
instanceId: blockInstance.id,
|
94
104
|
});
|
95
105
|
}
|
@@ -120,6 +130,8 @@ class ConfigManager {
|
|
120
130
|
}
|
121
131
|
|
122
132
|
async verifyIdentity(blockRef: string, systemId: string, instanceId: string) {
|
133
|
+
blockRef = normalizeKapetaUri(blockRef);
|
134
|
+
systemId = normalizeKapetaUri(systemId);
|
123
135
|
const planAssets = assetManager.getPlans();
|
124
136
|
const systemUri = systemId ? parseKapetaUri(systemId) : null;
|
125
137
|
const blockUri = parseKapetaUri(blockRef);
|
package/src/containerManager.ts
CHANGED
@@ -8,6 +8,7 @@ import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
|
8
8
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
9
9
|
import { Container } from 'node-docker-api/lib/container';
|
10
10
|
import { getBindHost } from './utils/utils';
|
11
|
+
import uuid from "node-uuid";
|
11
12
|
|
12
13
|
type StringMap = { [key: string]: string };
|
13
14
|
|
@@ -27,6 +28,25 @@ export interface DockerMounts {
|
|
27
28
|
Consistency: string;
|
28
29
|
}
|
29
30
|
|
31
|
+
interface DockerState {
|
32
|
+
Status: 'created' | 'running' | 'paused' | 'restarting' | 'removing' | 'exited' | 'dead';
|
33
|
+
Running: boolean;
|
34
|
+
Paused: boolean;
|
35
|
+
Restarting: boolean;
|
36
|
+
OOMKilled: boolean;
|
37
|
+
Dead: boolean;
|
38
|
+
Pid: number;
|
39
|
+
ExitCode: number;
|
40
|
+
Error: string;
|
41
|
+
StartedAt: string;
|
42
|
+
FinishedAt: string;
|
43
|
+
Health?: {
|
44
|
+
Status: 'starting' | 'healthy' | 'unhealthy' | 'none';
|
45
|
+
FailingStreak: number;
|
46
|
+
Log: any[] | null;
|
47
|
+
};
|
48
|
+
}
|
49
|
+
|
30
50
|
interface Health {
|
31
51
|
cmd: string;
|
32
52
|
interval?: number;
|
@@ -36,11 +56,13 @@ interface Health {
|
|
36
56
|
|
37
57
|
const LABEL_PORT_PREFIX = 'kapeta_port-';
|
38
58
|
const NANO_SECOND = 1000000;
|
39
|
-
const HEALTH_CHECK_INTERVAL =
|
40
|
-
const HEALTH_CHECK_MAX =
|
59
|
+
const HEALTH_CHECK_INTERVAL = 3000;
|
60
|
+
const HEALTH_CHECK_MAX = 20;
|
41
61
|
const IMAGE_PULL_CACHE_TTL = 30 * 60 * 1000;
|
42
62
|
const IMAGE_PULL_CACHE: { [key: string]: number } = {};
|
43
63
|
|
64
|
+
export const HEALTH_CHECK_TIMEOUT = HEALTH_CHECK_INTERVAL * HEALTH_CHECK_MAX * 2;
|
65
|
+
|
44
66
|
const promisifyStream = (stream: ReadStream) =>
|
45
67
|
new Promise((resolve, reject) => {
|
46
68
|
stream.on('data', (d) => console.log(d.toString()));
|
@@ -165,11 +187,17 @@ class ContainerManager {
|
|
165
187
|
return this._docker;
|
166
188
|
}
|
167
189
|
|
168
|
-
async getContainerByName(containerName: string): Promise<
|
190
|
+
async getContainerByName(containerName: string): Promise<ContainerInfo | undefined> {
|
169
191
|
const containers = await this.docker().container.list({ all: true });
|
170
|
-
|
171
|
-
|
192
|
+
const out = containers.find((container) => {
|
193
|
+
const containerData = container.data as any;
|
194
|
+
return containerData.Names.indexOf(`/${containerName}`) > -1;
|
172
195
|
});
|
196
|
+
|
197
|
+
if (out) {
|
198
|
+
return new ContainerInfo(out);
|
199
|
+
}
|
200
|
+
return undefined;
|
173
201
|
}
|
174
202
|
|
175
203
|
async pull(image: string, cacheForMS: number = IMAGE_PULL_CACHE_TTL) {
|
@@ -371,18 +399,36 @@ class ContainerManager {
|
|
371
399
|
}
|
372
400
|
|
373
401
|
async _isReady(container: Container) {
|
374
|
-
|
402
|
+
let info: Container;
|
403
|
+
try {
|
404
|
+
info = await container.status();
|
405
|
+
} catch (err) {
|
406
|
+
return false;
|
407
|
+
}
|
375
408
|
const infoData: any = info?.data;
|
376
|
-
|
409
|
+
const state = infoData?.State as DockerState;
|
410
|
+
if (state?.Status === 'exited' || state?.Status === 'removing' || state?.Status === 'dead') {
|
377
411
|
throw new Error('Container exited unexpectedly');
|
378
412
|
}
|
379
413
|
return infoData?.State?.Running ?? false;
|
380
414
|
}
|
381
415
|
|
382
416
|
async _isHealthy(container: Container) {
|
383
|
-
|
384
|
-
|
385
|
-
|
417
|
+
try {
|
418
|
+
const info = await container.status();
|
419
|
+
const infoData: any = info?.data;
|
420
|
+
return infoData?.State?.Health?.Status === 'healthy';
|
421
|
+
} catch (err) {
|
422
|
+
return false;
|
423
|
+
}
|
424
|
+
}
|
425
|
+
|
426
|
+
async remove(container:Container, opts?: { force?: boolean }) {
|
427
|
+
const newName = 'deleting-' + uuid.v4()
|
428
|
+
const containerData = container.data as any;
|
429
|
+
// Rename the container first to avoid name conflicts if people start the same container
|
430
|
+
await container.rename({ name: newName });
|
431
|
+
await container.delete({ force: !!opts?.force });
|
386
432
|
}
|
387
433
|
|
388
434
|
/**
|
@@ -429,7 +475,7 @@ export class ContainerInfo {
|
|
429
475
|
}
|
430
476
|
|
431
477
|
async isRunning() {
|
432
|
-
const inspectResult = await this.
|
478
|
+
const inspectResult = await this.inspect();
|
433
479
|
|
434
480
|
if (!inspectResult || !inspectResult.State) {
|
435
481
|
return false;
|
@@ -451,7 +497,7 @@ export class ContainerInfo {
|
|
451
497
|
}
|
452
498
|
|
453
499
|
async remove(opts?: { force?: boolean }) {
|
454
|
-
await this._container
|
500
|
+
await containerManager.remove(this._container, opts);
|
455
501
|
}
|
456
502
|
|
457
503
|
async getPort(type: string) {
|
@@ -464,14 +510,24 @@ export class ContainerInfo {
|
|
464
510
|
return null;
|
465
511
|
}
|
466
512
|
|
467
|
-
async
|
468
|
-
|
513
|
+
async inspect() {
|
514
|
+
try {
|
515
|
+
const result = await this._container.status();
|
516
|
+
|
517
|
+
return result ? (result.data as any) : null;
|
518
|
+
} catch (err) {
|
519
|
+
return null;
|
520
|
+
}
|
521
|
+
}
|
522
|
+
|
523
|
+
async status() {
|
524
|
+
const result = await this.inspect();
|
469
525
|
|
470
|
-
return result
|
526
|
+
return result.State as DockerState;
|
471
527
|
}
|
472
528
|
|
473
529
|
async getPorts(): Promise<PortMap | false> {
|
474
|
-
const inspectResult = await this.
|
530
|
+
const inspectResult = await this.inspect();
|
475
531
|
|
476
532
|
if (!inspectResult || !inspectResult.Config || !inspectResult.Config.Labels) {
|
477
533
|
return false;
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import ClusterConfiguration, { DefinitionInfo } from '@kapeta/local-cluster-config';
|
2
|
+
|
3
|
+
const CACHE_TTL = 60 * 1000; // 1 min
|
4
|
+
|
5
|
+
interface DefinitionCacheEntry {
|
6
|
+
expires: number;
|
7
|
+
definitions: DefinitionInfo[];
|
8
|
+
}
|
9
|
+
|
10
|
+
class DefinitionsManager {
|
11
|
+
private cache: { [key: string]: DefinitionCacheEntry } = {};
|
12
|
+
|
13
|
+
private getKey(kindFilter?: string | string[]) {
|
14
|
+
if (kindFilter) {
|
15
|
+
if (Array.isArray(kindFilter)) {
|
16
|
+
return kindFilter.join(',');
|
17
|
+
}
|
18
|
+
return kindFilter;
|
19
|
+
}
|
20
|
+
return 'none';
|
21
|
+
}
|
22
|
+
|
23
|
+
public clearCache() {
|
24
|
+
this.cache = {};
|
25
|
+
}
|
26
|
+
|
27
|
+
private doCached(key: string, getter: () => DefinitionInfo[]) {
|
28
|
+
if (this.cache[key]) {
|
29
|
+
if (this.cache[key].expires > Date.now()) {
|
30
|
+
return this.cache[key].definitions;
|
31
|
+
}
|
32
|
+
delete this.cache[key];
|
33
|
+
}
|
34
|
+
|
35
|
+
this.cache[key] = {
|
36
|
+
expires: Date.now() + CACHE_TTL,
|
37
|
+
definitions: getter(),
|
38
|
+
};
|
39
|
+
|
40
|
+
return this.cache[key].definitions;
|
41
|
+
}
|
42
|
+
|
43
|
+
public getDefinitions(kindFilter?: string | string[]) {
|
44
|
+
const key = this.getKey(kindFilter);
|
45
|
+
|
46
|
+
return this.doCached(key, () => ClusterConfiguration.getDefinitions(kindFilter));
|
47
|
+
}
|
48
|
+
|
49
|
+
public getProviderDefinitions() {
|
50
|
+
return this.doCached('providers', () => ClusterConfiguration.getProviderDefinitions());
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
export const definitionsManager = new DefinitionsManager();
|
package/src/filesystemManager.ts
CHANGED
@@ -17,9 +17,7 @@ function isFile(path: string) {
|
|
17
17
|
class FilesystemManager {
|
18
18
|
async writeFile(path: string, data: string | Buffer) {
|
19
19
|
const dirName = Path.dirname(path);
|
20
|
-
console.log('Dir name', dirName, path);
|
21
20
|
if (!FS.existsSync(dirName)) {
|
22
|
-
console.log('Making folder', dirName);
|
23
21
|
FSExtra.mkdirpSync(dirName, {});
|
24
22
|
}
|
25
23
|
FS.writeFileSync(path, data);
|