@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.
Files changed (71) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/src/assetManager.js +7 -4
  3. package/dist/cjs/src/clusterService.js +2 -0
  4. package/dist/cjs/src/codeGeneratorManager.js +3 -3
  5. package/dist/cjs/src/config/routes.js +1 -1
  6. package/dist/cjs/src/configManager.js +13 -1
  7. package/dist/cjs/src/containerManager.d.ts +25 -2
  8. package/dist/cjs/src/containerManager.js +51 -16
  9. package/dist/cjs/src/definitionsManager.d.ts +11 -0
  10. package/dist/cjs/src/definitionsManager.js +44 -0
  11. package/dist/cjs/src/filesystemManager.js +0 -2
  12. package/dist/cjs/src/instanceManager.d.ts +23 -47
  13. package/dist/cjs/src/instanceManager.js +416 -235
  14. package/dist/cjs/src/instances/routes.js +23 -14
  15. package/dist/cjs/src/middleware/kapeta.js +7 -0
  16. package/dist/cjs/src/networkManager.js +6 -0
  17. package/dist/cjs/src/operatorManager.js +8 -4
  18. package/dist/cjs/src/providerManager.js +3 -3
  19. package/dist/cjs/src/repositoryManager.js +7 -3
  20. package/dist/cjs/src/serviceManager.js +5 -0
  21. package/dist/cjs/src/types.d.ts +39 -13
  22. package/dist/cjs/src/types.js +28 -0
  23. package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +3 -3
  24. package/dist/cjs/src/utils/BlockInstanceRunner.js +34 -32
  25. package/dist/cjs/src/utils/utils.d.ts +2 -0
  26. package/dist/cjs/src/utils/utils.js +17 -1
  27. package/dist/esm/src/assetManager.js +7 -4
  28. package/dist/esm/src/clusterService.js +2 -0
  29. package/dist/esm/src/codeGeneratorManager.js +3 -3
  30. package/dist/esm/src/config/routes.js +1 -1
  31. package/dist/esm/src/configManager.js +13 -1
  32. package/dist/esm/src/containerManager.d.ts +25 -2
  33. package/dist/esm/src/containerManager.js +50 -15
  34. package/dist/esm/src/definitionsManager.d.ts +11 -0
  35. package/dist/esm/src/definitionsManager.js +38 -0
  36. package/dist/esm/src/filesystemManager.js +0 -2
  37. package/dist/esm/src/instanceManager.d.ts +23 -47
  38. package/dist/esm/src/instanceManager.js +416 -236
  39. package/dist/esm/src/instances/routes.js +23 -14
  40. package/dist/esm/src/middleware/kapeta.js +7 -0
  41. package/dist/esm/src/networkManager.js +6 -0
  42. package/dist/esm/src/operatorManager.js +8 -4
  43. package/dist/esm/src/providerManager.js +3 -3
  44. package/dist/esm/src/repositoryManager.js +7 -3
  45. package/dist/esm/src/serviceManager.js +5 -0
  46. package/dist/esm/src/types.d.ts +39 -13
  47. package/dist/esm/src/types.js +27 -1
  48. package/dist/esm/src/utils/BlockInstanceRunner.d.ts +3 -3
  49. package/dist/esm/src/utils/BlockInstanceRunner.js +35 -33
  50. package/dist/esm/src/utils/utils.d.ts +2 -0
  51. package/dist/esm/src/utils/utils.js +14 -0
  52. package/package.json +2 -1
  53. package/src/assetManager.ts +7 -4
  54. package/src/clusterService.ts +3 -0
  55. package/src/codeGeneratorManager.ts +3 -2
  56. package/src/config/routes.ts +1 -1
  57. package/src/configManager.ts +13 -1
  58. package/src/containerManager.ts +72 -16
  59. package/src/definitionsManager.ts +54 -0
  60. package/src/filesystemManager.ts +0 -2
  61. package/src/instanceManager.ts +495 -266
  62. package/src/instances/routes.ts +23 -17
  63. package/src/middleware/kapeta.ts +10 -0
  64. package/src/networkManager.ts +6 -0
  65. package/src/operatorManager.ts +11 -6
  66. package/src/providerManager.ts +3 -2
  67. package/src/repositoryManager.ts +7 -3
  68. package/src/serviceManager.ts +6 -0
  69. package/src/types.ts +44 -14
  70. package/src/utils/BlockInstanceRunner.ts +39 -36
  71. package/src/utils/utils.ts +18 -0
@@ -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 = ClusterConfiguration.getDefinitions([
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 = ClusterConfiguration.getDefinitions(assetKinds);
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 = ClusterConfiguration.getDefinitions()
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
  }
@@ -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 = ClusterConfiguration.getDefinitions(TARGET_KIND);
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 = ClusterConfiguration.getDefinitions(BLOCK_TYPE_KIND);
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
  );
@@ -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.restartIfRunning(req.kapeta!.systemId, req.kapeta!.instanceId);
44
+ await instanceManager.restart(req.kapeta!.systemId, req.kapeta!.instanceId);
45
45
  } else {
46
46
  configManager.setConfigForSystem(req.kapeta!.systemId, config);
47
47
  }
@@ -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);
@@ -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 = 2000;
40
- const HEALTH_CHECK_MAX = 30;
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<Container | undefined> {
190
+ async getContainerByName(containerName: string): Promise<ContainerInfo | undefined> {
169
191
  const containers = await this.docker().container.list({ all: true });
170
- return containers.find((container) => {
171
- return (container.data as any).Names.indexOf(`/${containerName}`) > -1;
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
- const info: Container = await container.status();
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
- if (infoData?.State?.Status === 'exited') {
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
- const info = await container.status();
384
- const infoData: any = info?.data;
385
- return infoData?.State?.Health?.Status === 'healthy';
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.getStatus();
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.delete({ force: !!opts?.force });
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 getStatus() {
468
- const result = await this._container.status();
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 ? (result.data as any) : null;
526
+ return result.State as DockerState;
471
527
  }
472
528
 
473
529
  async getPorts(): Promise<PortMap | false> {
474
- const inspectResult = await this.getStatus();
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();
@@ -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);