@kapeta/local-cluster-service 0.11.1 → 0.12.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 (42) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/definitions.d.ts +7 -0
  3. package/dist/cjs/src/config/routes.js +1 -1
  4. package/dist/cjs/src/containerManager.d.ts +3 -2
  5. package/dist/cjs/src/containerManager.js +127 -34
  6. package/dist/cjs/src/definitionsManager.d.ts +1 -0
  7. package/dist/cjs/src/definitionsManager.js +7 -4
  8. package/dist/cjs/src/instanceManager.d.ts +8 -1
  9. package/dist/cjs/src/instanceManager.js +56 -21
  10. package/dist/cjs/src/instances/routes.js +2 -0
  11. package/dist/cjs/src/operatorManager.d.ts +2 -0
  12. package/dist/cjs/src/operatorManager.js +70 -67
  13. package/dist/cjs/src/socketManager.d.ts +1 -0
  14. package/dist/cjs/src/socketManager.js +3 -0
  15. package/dist/cjs/src/types.d.ts +1 -0
  16. package/dist/cjs/src/utils/BlockInstanceRunner.js +2 -3
  17. package/dist/esm/src/config/routes.js +1 -1
  18. package/dist/esm/src/containerManager.d.ts +3 -2
  19. package/dist/esm/src/containerManager.js +128 -35
  20. package/dist/esm/src/definitionsManager.d.ts +1 -0
  21. package/dist/esm/src/definitionsManager.js +8 -5
  22. package/dist/esm/src/instanceManager.d.ts +8 -1
  23. package/dist/esm/src/instanceManager.js +56 -21
  24. package/dist/esm/src/instances/routes.js +2 -0
  25. package/dist/esm/src/operatorManager.d.ts +2 -0
  26. package/dist/esm/src/operatorManager.js +68 -65
  27. package/dist/esm/src/socketManager.d.ts +1 -0
  28. package/dist/esm/src/socketManager.js +3 -0
  29. package/dist/esm/src/types.d.ts +1 -0
  30. package/dist/esm/src/utils/BlockInstanceRunner.js +2 -3
  31. package/dist/esm/src/utils/utils.js +1 -1
  32. package/package.json +1 -1
  33. package/src/config/routes.ts +1 -1
  34. package/src/containerManager.ts +181 -60
  35. package/src/definitionsManager.ts +9 -5
  36. package/src/instanceManager.ts +82 -42
  37. package/src/instances/routes.ts +3 -1
  38. package/src/operatorManager.ts +73 -69
  39. package/src/socketManager.ts +4 -0
  40. package/src/types.ts +1 -1
  41. package/src/utils/BlockInstanceRunner.ts +12 -24
  42. package/src/utils/utils.ts +2 -2
@@ -11,9 +11,9 @@ import { BlockInstance, Resource } from '@kapeta/schemas';
11
11
  import { definitionsManager } from './definitionsManager';
12
12
  import { getBindHost, normalizeKapetaUri } from './utils/utils';
13
13
  import _ from 'lodash';
14
- import { Container } from 'node-docker-api/lib/container';
14
+ import AsyncLock from 'async-lock';
15
15
 
16
- const KIND_OPERATOR = 'core/resource-type-operator';
16
+ export const KIND_OPERATOR = 'core/resource-type-operator';
17
17
 
18
18
  class Operator {
19
19
  private _data: any;
@@ -33,6 +33,8 @@ class Operator {
33
33
  class OperatorManager {
34
34
  private _mountDir: string;
35
35
 
36
+ private operatorLock: AsyncLock = new AsyncLock();
37
+
36
38
  constructor() {
37
39
  this._mountDir = Path.join(storageService.getKapetaBasedir(), 'mounts');
38
40
 
@@ -154,98 +156,100 @@ class OperatorManager {
154
156
  * @return {Promise<ContainerInfo>}
155
157
  */
156
158
  async ensureResource(systemId: string, resourceType: string, version: string): Promise<ContainerInfo> {
157
- const operator = this.getOperator(resourceType, version);
159
+ systemId = normalizeKapetaUri(systemId);
160
+ const key = `${systemId}#${resourceType}:${version}`;
161
+ return await this.operatorLock.acquire(key, async () => {
162
+ const operator = this.getOperator(resourceType, version);
158
163
 
159
- const operatorData = operator.getData();
164
+ const operatorData = operator.getData();
160
165
 
161
- const portTypes = Object.keys(operatorData.ports);
166
+ const portTypes = Object.keys(operatorData.ports);
162
167
 
163
- portTypes.sort();
168
+ portTypes.sort();
164
169
 
165
- const ports: AnyMap = {};
170
+ const ports: AnyMap = {};
166
171
 
167
- for (let i = 0; i < portTypes.length; i++) {
168
- const portType = portTypes[i];
169
- let containerPortInfo = operatorData.ports[portType];
170
- const hostPort = await serviceManager.ensureServicePort(systemId, resourceType, portType);
172
+ for (let i = 0; i < portTypes.length; i++) {
173
+ const portType = portTypes[i];
174
+ let containerPortInfo = operatorData.ports[portType];
175
+ const hostPort = await serviceManager.ensureServicePort(systemId, resourceType, portType);
171
176
 
172
- if (typeof containerPortInfo === 'number' || typeof containerPortInfo === 'string') {
173
- containerPortInfo = { port: containerPortInfo, type: 'tcp' };
174
- }
177
+ if (typeof containerPortInfo === 'number' || typeof containerPortInfo === 'string') {
178
+ containerPortInfo = { port: containerPortInfo, type: 'tcp' };
179
+ }
180
+
181
+ if (!containerPortInfo.type) {
182
+ containerPortInfo.type = 'tcp';
183
+ }
184
+
185
+ const portId = containerPortInfo.port + '/' + containerPortInfo.type;
175
186
 
176
- if (!containerPortInfo.type) {
177
- containerPortInfo.type = 'tcp';
187
+ ports[portId] = {
188
+ type: portType,
189
+ hostPort,
190
+ };
178
191
  }
179
192
 
180
- const portId = containerPortInfo.port + '/' + containerPortInfo.type;
193
+ const mounts = await containerManager.createMounts(systemId, resourceType, operatorData.mounts);
181
194
 
182
- ports[portId] = {
183
- type: portType,
184
- hostPort,
185
- };
186
- }
195
+ const nameParts = [systemId, resourceType.toLowerCase(), version];
187
196
 
188
- const mounts = await containerManager.createMounts(systemId, resourceType, operatorData.mounts);
197
+ const containerName = `kapeta-resource-${md5(nameParts.join('_'))}`;
189
198
 
190
- const nameParts = [
191
- systemId,
192
- resourceType.toLowerCase(),
193
- version
194
- ];
199
+ const PortBindings: { [key: string]: any } = {};
200
+ const Env: string[] = [];
195
201
 
196
- const containerName = `kapeta-resource-${md5(nameParts.join('_'))}`;
202
+ const Labels: StringMap = {
203
+ kapeta: 'true',
204
+ };
197
205
 
198
- const PortBindings: { [key: string]: any } = {};
199
- const Env: string[] = [];
206
+ const bindHost = getBindHost();
200
207
 
201
- const Labels: StringMap = {
202
- kapeta: 'true',
203
- };
208
+ const ExposedPorts: { [key: string]: any } = {};
204
209
 
205
- const bindHost = getBindHost();
210
+ _.forEach(ports, (portInfo: any, containerPort) => {
211
+ ExposedPorts['' + containerPort] = {};
212
+ PortBindings['' + containerPort] = [
213
+ {
214
+ HostPort: '' + portInfo.hostPort,
215
+ HostIp: bindHost,
216
+ },
217
+ ];
206
218
 
207
- const ExposedPorts: { [key: string]: any } = {};
219
+ Labels[CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
220
+ });
208
221
 
209
- _.forEach(ports, (portInfo: any, containerPort) => {
210
- ExposedPorts['' + containerPort] = {};
211
- PortBindings['' + containerPort] = [
212
- {
213
- HostPort: '' + portInfo.hostPort,
214
- HostIp: bindHost,
215
- },
216
- ];
222
+ const Mounts = containerManager.toDockerMounts(mounts);
217
223
 
218
- Labels[CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
219
- });
224
+ _.forEach(operatorData.env, (value, name) => {
225
+ Env.push(name + '=' + value);
226
+ });
220
227
 
221
- const Mounts = containerManager.toDockerMounts(mounts);
228
+ let HealthCheck = undefined;
222
229
 
223
- _.forEach(operatorData.env, (value, name) => {
224
- Env.push(name + '=' + value);
225
- });
230
+ if (operatorData.health) {
231
+ HealthCheck = containerManager.toDockerHealth(operatorData.health);
232
+ }
226
233
 
227
- let HealthCheck = undefined;
234
+ const container = await containerManager.ensureContainer({
235
+ name: containerName,
236
+ Image: operatorData.image,
237
+ Hostname: containerName + '.kapeta',
238
+ Labels,
239
+ Cmd: operatorData.cmd,
240
+ ExposedPorts,
241
+ Env,
242
+ HealthCheck,
243
+ HostConfig: {
244
+ PortBindings,
245
+ Mounts,
246
+ },
247
+ });
228
248
 
229
- if (operatorData.health) {
230
- HealthCheck = containerManager.toDockerHealth(operatorData.health);
231
- }
249
+ await containerManager.waitForReady(container);
232
250
 
233
- const container = await containerManager.ensureContainer({
234
- name: containerName,
235
- Image: operatorData.image,
236
- Hostname: containerName + '.kapeta',
237
- Labels,
238
- Cmd: operatorData.cmd,
239
- ExposedPorts,
240
- Env,
241
- HealthCheck,
242
- HostConfig: {
243
- PortBindings,
244
- Mounts,
245
- },
251
+ return new ContainerInfo(container);
246
252
  });
247
-
248
- return new ContainerInfo(container);
249
253
  }
250
254
  }
251
255
 
@@ -31,6 +31,10 @@ export class SocketManager {
31
31
  this.io.to(context).emit(type, { context, payload });
32
32
  }
33
33
 
34
+ emitGlobal(type: string, payload: any) {
35
+ this.io.emit(type, { payload });
36
+ }
37
+
34
38
  _bindIO() {
35
39
  this.io.on('connection', (socket) => this._handleSocketCreated(socket));
36
40
  }
package/src/types.ts CHANGED
@@ -64,6 +64,7 @@ export type InstanceInfo = {
64
64
  type: InstanceType;
65
65
  owner: InstanceOwner;
66
66
  status: InstanceStatus;
67
+ errorMessage?: string;
67
68
  desiredStatus: DesiredInstanceStatus;
68
69
  address?: string;
69
70
 
@@ -71,7 +72,6 @@ export type InstanceInfo = {
71
72
  health?: string | null;
72
73
  pid?: number | string | null;
73
74
  portType?: string;
74
-
75
75
  };
76
76
 
77
77
  interface ResourceRef {
@@ -175,11 +175,7 @@ export class BlockInstanceRunner {
175
175
  const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
176
176
  const workingDir = localContainer.workingDir ? localContainer.workingDir : '/workspace';
177
177
 
178
- const {
179
- PortBindings,
180
- ExposedPorts,
181
- addonEnv
182
- } = await this.getDockerPortBindings(blockInstance, assetVersion);
178
+ const { PortBindings, ExposedPorts, addonEnv } = await this.getDockerPortBindings(blockInstance, assetVersion);
183
179
 
184
180
  let HealthCheck = undefined;
185
181
  if (localContainer.healthcheck) {
@@ -215,7 +211,12 @@ export class BlockInstanceRunner {
215
211
  });
216
212
  }
217
213
 
218
- private async _startDockerProcess(blockInstance: BlockProcessParams, blockInfo: KapetaURI, env: StringMap, assetVersion: DefinitionInfo) {
214
+ private async _startDockerProcess(
215
+ blockInstance: BlockProcessParams,
216
+ blockInfo: KapetaURI,
217
+ env: StringMap,
218
+ assetVersion: DefinitionInfo
219
+ ) {
219
220
  const { versionFile } = ClusterConfig.getRepositoryAssetInfoPath(
220
221
  blockInfo.handle,
221
222
  blockInfo.name,
@@ -237,11 +238,7 @@ export class BlockInstanceRunner {
237
238
  throw new Error(`Missing docker image information: ${JSON.stringify(versionInfo?.artifact?.details)}`);
238
239
  }
239
240
 
240
- const {
241
- PortBindings,
242
- ExposedPorts,
243
- addonEnv
244
- } = await this.getDockerPortBindings(blockInstance, assetVersion);
241
+ const { PortBindings, ExposedPorts, addonEnv } = await this.getDockerPortBindings(blockInstance, assetVersion);
245
242
 
246
243
  const containerName = getBlockInstanceContainerName(this._systemId, blockInstance.id);
247
244
 
@@ -260,9 +257,8 @@ export class BlockInstanceRunner {
260
257
  `KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
261
258
  ...Object.entries({
262
259
  ...env,
263
- ...addonEnv
260
+ ...addonEnv,
264
261
  }).map(([key, value]) => `${key}=${value}`),
265
-
266
262
  ],
267
263
  HostConfig: {
268
264
  Binds: [`${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${innerHome}`],
@@ -388,7 +384,6 @@ export class BlockInstanceRunner {
388
384
  return out;
389
385
  }
390
386
 
391
-
392
387
  private async getDockerPortBindings(blockInstance: BlockProcessParams, assetVersion: DefinitionInfo) {
393
388
  const bindHost = getBindHost();
394
389
  const ExposedPorts: AnyMap = {};
@@ -412,28 +407,21 @@ export class BlockInstanceRunner {
412
407
  ];
413
408
  });
414
409
 
415
-
416
410
  await Promise.all(promises);
417
411
 
418
- return {PortBindings,ExposedPorts, addonEnv};
412
+ return { PortBindings, ExposedPorts, addonEnv };
419
413
  }
420
414
 
421
415
  private async ensureContainer(opts: any) {
422
416
  const container = await containerManager.ensureContainer(opts);
423
417
 
424
- await containerManager.waitForReady(container);
425
-
426
418
  return this._handleContainer(container);
427
-
428
419
  }
429
420
 
430
- private async _handleContainer(
431
- container: Container
432
- ): Promise<ProcessInfo> {
433
-
421
+ private async _handleContainer(container: Container): Promise<ProcessInfo> {
434
422
  return {
435
423
  type: InstanceType.DOCKER,
436
- pid: container.id
424
+ pid: container.id,
437
425
  };
438
426
  }
439
427
  }
@@ -1,9 +1,9 @@
1
1
  import FS from 'node:fs';
2
2
  import YAML from 'yaml';
3
3
  import { parseKapetaUri } from '@kapeta/nodejs-utils';
4
- import md5 from "md5";
4
+ import md5 from 'md5';
5
5
 
6
- export function getBlockInstanceContainerName(systemId:string, instanceId: string) {
6
+ export function getBlockInstanceContainerName(systemId: string, instanceId: string) {
7
7
  return `kapeta-block-instance-${md5(systemId + instanceId)}`;
8
8
  }
9
9