@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.
Files changed (39) hide show
  1. package/.github/workflows/check-license.yml +0 -1
  2. package/CHANGELOG.md +14 -0
  3. package/dist/cjs/src/config/routes.js +9 -0
  4. package/dist/cjs/src/containerManager.d.ts +2 -8
  5. package/dist/cjs/src/containerManager.js +7 -7
  6. package/dist/cjs/src/instanceManager.d.ts +8 -3
  7. package/dist/cjs/src/instanceManager.js +147 -25
  8. package/dist/cjs/src/operatorManager.d.ts +9 -9
  9. package/dist/cjs/src/operatorManager.js +21 -26
  10. package/dist/cjs/src/serviceManager.d.ts +1 -0
  11. package/dist/cjs/src/serviceManager.js +9 -9
  12. package/dist/cjs/src/types.d.ts +40 -0
  13. package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +2 -13
  14. package/dist/cjs/src/utils/BlockInstanceRunner.js +91 -79
  15. package/dist/cjs/src/utils/utils.d.ts +6 -2
  16. package/dist/cjs/src/utils/utils.js +35 -2
  17. package/dist/esm/src/config/routes.js +9 -0
  18. package/dist/esm/src/containerManager.d.ts +2 -8
  19. package/dist/esm/src/containerManager.js +7 -7
  20. package/dist/esm/src/instanceManager.d.ts +8 -3
  21. package/dist/esm/src/instanceManager.js +147 -25
  22. package/dist/esm/src/operatorManager.d.ts +9 -9
  23. package/dist/esm/src/operatorManager.js +21 -26
  24. package/dist/esm/src/serviceManager.d.ts +1 -0
  25. package/dist/esm/src/serviceManager.js +9 -9
  26. package/dist/esm/src/types.d.ts +40 -0
  27. package/dist/esm/src/utils/BlockInstanceRunner.d.ts +2 -13
  28. package/dist/esm/src/utils/BlockInstanceRunner.js +91 -79
  29. package/dist/esm/src/utils/utils.d.ts +6 -2
  30. package/dist/esm/src/utils/utils.js +35 -2
  31. package/package.json +1 -1
  32. package/src/config/routes.ts +15 -0
  33. package/src/containerManager.ts +8 -15
  34. package/src/instanceManager.ts +218 -34
  35. package/src/operatorManager.ts +26 -31
  36. package/src/serviceManager.ts +11 -8
  37. package/src/types.ts +36 -0
  38. package/src/utils/BlockInstanceRunner.ts +119 -92
  39. package/src/utils/utils.ts +40 -3
@@ -10,7 +10,7 @@ import { BlockInstanceRunner } from './utils/BlockInstanceRunner';
10
10
  import { storageService } from './storageService';
11
11
  import { EVENT_INSTANCE_CREATED, EVENT_STATUS_CHANGED, socketManager } from './socketManager';
12
12
  import { serviceManager } from './serviceManager';
13
- import { assetManager } from './assetManager';
13
+ import { assetManager, EnrichedAsset } from './assetManager';
14
14
  import {
15
15
  containerManager,
16
16
  DockerContainerHealth,
@@ -18,10 +18,21 @@ import {
18
18
  HEALTH_CHECK_TIMEOUT,
19
19
  } from './containerManager';
20
20
  import { configManager } from './configManager';
21
- import { DesiredInstanceStatus, InstanceInfo, InstanceOwner, InstanceStatus, InstanceType, LogEntry } from './types';
21
+ import {
22
+ DesiredInstanceStatus,
23
+ EnvironmentType,
24
+ InstanceInfo,
25
+ InstanceOwner,
26
+ InstanceStatus,
27
+ InstanceType,
28
+ LocalImageOptions,
29
+ LogEntry,
30
+ OperatorInstanceInfo,
31
+ OperatorInstancePort,
32
+ } from './types';
22
33
  import { BlockDefinitionSpec, BlockInstance, Plan } from '@kapeta/schemas';
23
34
  import { getBlockInstanceContainerName, getResolvedConfiguration } from './utils/utils';
24
- import { KIND_OPERATOR, operatorManager } from './operatorManager';
35
+ import { KIND_BLOCK_OPERATOR, KIND_RESOURCE_OPERATOR, operatorManager } from './operatorManager';
25
36
  import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
26
37
  import { definitionsManager } from './definitionsManager';
27
38
  import { Task, taskManager } from './taskManager';
@@ -335,11 +346,108 @@ export class InstanceManager {
335
346
  );
336
347
  }
337
348
 
349
+ public stopAllForPlan(systemId: string) {
350
+ systemId = normalizeKapetaUri(systemId);
351
+ const instancesForPlan = this._instances.filter((instance) => instance.systemId === systemId);
352
+ return taskManager.add(
353
+ `plan:stop:${systemId}`,
354
+ async () => {
355
+ return this.stopInstances(instancesForPlan);
356
+ },
357
+ {
358
+ name: `Stopping plan ${systemId}`,
359
+ }
360
+ );
361
+ }
362
+
363
+ public async getInstanceOperator(
364
+ systemId: string,
365
+ instanceId: string,
366
+ environment?: EnvironmentType
367
+ ): Promise<OperatorInstanceInfo> {
368
+ const blockInstance = await assetManager.getBlockInstance(systemId, instanceId);
369
+ if (!blockInstance) {
370
+ throw new Error(`Instance not found: ${systemId}/${instanceId}`);
371
+ }
372
+ const blockRef = normalizeKapetaUri(blockInstance.block.ref);
373
+ const block = await assetManager.getAsset(blockRef, true);
374
+ if (!block) {
375
+ throw new Error(`Block not found: ${blockRef}`);
376
+ }
377
+
378
+ const operatorDefinition = await definitionsManager.getDefinition(block.kind);
379
+
380
+ if (!operatorDefinition?.definition.spec.local) {
381
+ throw new Error(`Operator block has no local definition: ${blockRef}`);
382
+ }
383
+
384
+ const localConfig = operatorDefinition.definition.spec.local as LocalImageOptions;
385
+
386
+ let instance = await this.start(systemId, instanceId);
387
+ if (instance instanceof Task) {
388
+ instance = await instance.wait();
389
+ }
390
+
391
+ const container = await containerManager.get(instance.pid as string);
392
+ if (!container) {
393
+ throw new Error(`Container not found: ${instance.pid}`);
394
+ }
395
+
396
+ const portInfo = await container.getPorts();
397
+ if (!portInfo) {
398
+ throw new Error(`No ports found for instance: ${instanceId}`);
399
+ }
400
+
401
+ const hostname = serviceManager.getLocalHost(environment);
402
+ const ports: { [key: string]: OperatorInstancePort } = {};
403
+
404
+ Object.entries(portInfo).forEach(([key, value]) => {
405
+ ports[key] = {
406
+ protocol: value.protocol,
407
+ port: parseInt(value.hostPort),
408
+ };
409
+ });
410
+
411
+ return {
412
+ hostname,
413
+ ports,
414
+ credentials: localConfig.credentials,
415
+ options: localConfig.options,
416
+ };
417
+ }
418
+
338
419
  public async stop(systemId: string, instanceId: string) {
339
420
  return this.stopInner(systemId, instanceId, true);
340
421
  }
341
422
 
342
- private async stopInner(systemId: string, instanceId: string, changeDesired: boolean = false) {
423
+ private async stopInner(
424
+ systemId: string,
425
+ instanceId: string,
426
+ changeDesired: boolean = false,
427
+ checkForSingleton: boolean = true
428
+ ) {
429
+ if (checkForSingleton) {
430
+ const blockInstance = await assetManager.getBlockInstance(systemId, instanceId);
431
+ const blockRef = normalizeKapetaUri(blockInstance.block.ref);
432
+
433
+ const blockAsset = await assetManager.getAsset(blockRef, true);
434
+ if (!blockAsset) {
435
+ throw new Error('Block not found: ' + blockRef);
436
+ }
437
+
438
+ if (await this.isSingletonOperator(blockAsset)) {
439
+ const instances = await this.getAllInstancesForKind(systemId, blockAsset.data.kind);
440
+ if (instances.length > 1) {
441
+ const promises = instances.map((id) => {
442
+ return this.stopInner(systemId, id, changeDesired, false);
443
+ });
444
+
445
+ await Promise.all(promises);
446
+ return;
447
+ }
448
+ }
449
+ }
450
+
343
451
  return this.exclusive(systemId, instanceId, async () => {
344
452
  systemId = normalizeKapetaUri(systemId);
345
453
  const instance = this.getInstance(systemId, instanceId);
@@ -362,12 +470,18 @@ export class InstanceManager {
362
470
  instance.status = InstanceStatus.STOPPING;
363
471
 
364
472
  socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
365
- console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
473
+ console.log(
474
+ 'Stopping instance: %s::%s [desired: %s] [intentional: %s]',
475
+ systemId,
476
+ instanceId,
477
+ instance.desiredStatus,
478
+ changeDesired
479
+ );
366
480
  this.save();
367
481
 
368
482
  try {
369
483
  if (instance.type === 'docker') {
370
- const containerName = getBlockInstanceContainerName(instance.systemId, instance.instanceId);
484
+ const containerName = await getBlockInstanceContainerName(instance.systemId, instance.instanceId);
371
485
  const container = await containerManager.getContainerByName(containerName);
372
486
  if (container) {
373
487
  try {
@@ -400,34 +514,44 @@ export class InstanceManager {
400
514
  });
401
515
  }
402
516
 
403
- public stopAllForPlan(systemId: string) {
517
+ public async start(
518
+ systemId: string,
519
+ instanceId: string,
520
+ checkForSingleton: boolean = true
521
+ ): Promise<InstanceInfo | Task<InstanceInfo>> {
404
522
  systemId = normalizeKapetaUri(systemId);
405
- const instancesForPlan = this._instances.filter((instance) => instance.systemId === systemId);
406
- return taskManager.add(
407
- `plan:stop:${systemId}`,
408
- async () => {
409
- return this.stopInstances(instancesForPlan);
410
- },
411
- {
412
- name: `Stopping plan ${systemId}`,
523
+ const blockInstance = await assetManager.getBlockInstance(systemId, instanceId);
524
+ const blockRef = normalizeKapetaUri(blockInstance.block.ref);
525
+
526
+ const blockAsset = await assetManager.getAsset(blockRef, true);
527
+ if (!blockAsset) {
528
+ throw new Error('Block not found: ' + blockRef);
529
+ }
530
+
531
+ if (checkForSingleton && (await this.isSingletonOperator(blockAsset))) {
532
+ const instances = await this.getAllInstancesForKind(systemId, blockAsset.data.kind);
533
+ if (instances.length > 1) {
534
+ const promises = instances.map((id) => {
535
+ return this.start(systemId, id, false);
536
+ });
537
+
538
+ await Promise.all(promises);
539
+ return promises[0];
413
540
  }
414
- );
415
- }
541
+ }
416
542
 
417
- public async start(systemId: string, instanceId: string): Promise<InstanceInfo | Task<InstanceInfo>> {
418
543
  return this.exclusive(systemId, instanceId, async () => {
419
- systemId = normalizeKapetaUri(systemId);
420
- const blockInstance = await assetManager.getBlockInstance(systemId, instanceId);
421
- const blockRef = normalizeKapetaUri(blockInstance.block.ref);
544
+ let existingInstance = this.getInstance(systemId, instanceId);
422
545
 
423
- const blockAsset = await assetManager.getAsset(blockRef, true);
424
- if (!blockAsset) {
425
- throw new Error('Block not found: ' + blockRef);
546
+ if (existingInstance && existingInstance.pid) {
547
+ const container = await containerManager.get(existingInstance.pid as string);
548
+ if (!container) {
549
+ // The container is not running
550
+ existingInstance = undefined;
551
+ }
426
552
  }
427
553
 
428
- const existingInstance = this.getInstance(systemId, instanceId);
429
-
430
- if (existingInstance) {
554
+ if (existingInstance?.pid) {
431
555
  if (existingInstance.status === InstanceStatus.READY) {
432
556
  // Instance is already running
433
557
  return existingInstance;
@@ -476,17 +600,17 @@ export class InstanceManager {
476
600
  return Promise.resolve();
477
601
  }
478
602
 
479
- if (KIND_OPERATOR.toLowerCase() !== asset.definition.kind.toLowerCase()) {
603
+ if (KIND_RESOURCE_OPERATOR.toLowerCase() !== asset.definition.kind.toLowerCase()) {
480
604
  // Not an operator
481
605
  return Promise.resolve();
482
606
  }
483
607
  // Check if the operator has a local definition, if not we skip it since we can't start it
484
- if(!asset.definition.spec.local) {
485
- console.log('Skipping operator since it as no local definition: %s', consumer.kind)
608
+ if (!asset.definition.spec.local) {
609
+ console.log('Skipping operator since it as no local definition: %s', consumer.kind);
486
610
  return Promise.resolve();
487
611
  }
488
612
  console.log('Ensuring resource: %s in %s', consumerUri.id, systemId);
489
- return operatorManager.ensureResource(systemId, consumerUri.fullName, consumerUri.version);
613
+ return operatorManager.ensureOperator(systemId, consumerUri.fullName, consumerUri.version);
490
614
  });
491
615
 
492
616
  await Promise.all(promises);
@@ -570,6 +694,8 @@ export class InstanceManager {
570
694
  */
571
695
  public async prepareForRestart(systemId: string, instanceId: string) {
572
696
  systemId = normalizeKapetaUri(systemId);
697
+
698
+ console.log('Stopping instance during restart...', systemId, instanceId);
573
699
  await this.stopInner(systemId, instanceId);
574
700
  }
575
701
 
@@ -603,8 +729,14 @@ export class InstanceManager {
603
729
  while (all.length > 0) {
604
730
  // Check a few instances at a time - docker doesn't like too many concurrent requests
605
731
  const chunk = all.splice(0, 30);
606
- const promises = chunk.map(async (instance) => {
607
- if (!instance.systemId) {
732
+ const promises = chunk.map(async (oldInstance) => {
733
+ if (!oldInstance.systemId) {
734
+ return;
735
+ }
736
+
737
+ // Grab the latest here
738
+ const instance = this.getInstance(oldInstance.systemId, oldInstance.instanceId);
739
+ if (!instance) {
608
740
  return;
609
741
  }
610
742
 
@@ -721,6 +853,11 @@ export class InstanceManager {
721
853
  ) {
722
854
  //If the instance is running but we want it to stop, stop it
723
855
  try {
856
+ console.log(
857
+ 'Stopping instance since it is its desired state',
858
+ instance.systemId,
859
+ instance.instanceId
860
+ );
724
861
  await this.stopInner(instance.systemId, instance.instanceId);
725
862
  } catch (e) {
726
863
  console.warn('Failed to stop instance', instance.systemId, instance.instanceId, e);
@@ -755,7 +892,7 @@ export class InstanceManager {
755
892
 
756
893
  private async getExternalStatus(instance: InstanceInfo): Promise<InstanceStatus> {
757
894
  if (instance.type === InstanceType.DOCKER) {
758
- const containerName = getBlockInstanceContainerName(instance.systemId, instance.instanceId);
895
+ const containerName = await getBlockInstanceContainerName(instance.systemId, instance.instanceId);
759
896
  const container = await containerManager.getContainerByName(containerName);
760
897
  if (!container) {
761
898
  // If the container doesn't exist, we consider the instance stopped
@@ -864,6 +1001,53 @@ export class InstanceManager {
864
1001
  });
865
1002
  });
866
1003
  }
1004
+
1005
+ private async isSingletonOperator(blockAsset: EnrichedAsset): Promise<boolean> {
1006
+ const provider = await assetManager.getAsset(blockAsset.data.kind);
1007
+ if (!provider) {
1008
+ return false;
1009
+ }
1010
+
1011
+ if (parseKapetaUri(provider.kind).fullName === KIND_BLOCK_OPERATOR) {
1012
+ const localConfig = provider.data.spec.local as LocalImageOptions;
1013
+ return localConfig.singleton ?? false;
1014
+ }
1015
+
1016
+ return false;
1017
+ }
1018
+
1019
+ private async getKindForAssetRef(assetRef: string): Promise<string | null> {
1020
+ const block = await assetManager.getAsset(assetRef);
1021
+ if (!block) {
1022
+ return null;
1023
+ }
1024
+
1025
+ return block.data.kind;
1026
+ }
1027
+
1028
+ private async isUsingKind(ref: string, kind: string): Promise<boolean> {
1029
+ const assetKind = await this.getKindForAssetRef(ref);
1030
+ if (!assetKind) {
1031
+ return false;
1032
+ }
1033
+
1034
+ return parseKapetaUri(assetKind).fullName === parseKapetaUri(kind).fullName;
1035
+ }
1036
+
1037
+ private async getAllInstancesForKind(systemId: string, kind: string): Promise<string[]> {
1038
+ const plan = await assetManager.getPlan(systemId);
1039
+ if (!plan?.spec?.blocks) {
1040
+ return [];
1041
+ }
1042
+ const out: string[] = [];
1043
+ for (const block of plan.spec.blocks) {
1044
+ if (await this.isUsingKind(block.block.ref, kind)) {
1045
+ out.push(block.id);
1046
+ }
1047
+ }
1048
+
1049
+ return out;
1050
+ }
867
1051
  }
868
1052
 
869
1053
  export const instanceManager = new InstanceManager();
@@ -16,25 +16,27 @@ import {
16
16
  containerManager,
17
17
  } from './containerManager';
18
18
  import FSExtra from 'fs-extra';
19
- import { AnyMap, EnvironmentType, OperatorInfo, StringMap } from './types';
19
+ import { AnyMap, EnvironmentType, LocalImageOptions, OperatorInfo, StringMap } from './types';
20
20
  import { BlockInstance, Resource } from '@kapeta/schemas';
21
21
  import { definitionsManager } from './definitionsManager';
22
- import { getBindHost } from './utils/utils';
22
+ import { getBindHost, toPortInfo } from './utils/utils';
23
23
  import { parseKapetaUri, normalizeKapetaUri } from '@kapeta/nodejs-utils';
24
24
  import _ from 'lodash';
25
25
  import AsyncLock from 'async-lock';
26
26
  import { taskManager } from './taskManager';
27
27
 
28
- export const KIND_OPERATOR = 'core/resource-type-operator';
28
+ export const KIND_RESOURCE_OPERATOR = 'core/resource-type-operator';
29
+ export const KIND_BLOCK_OPERATOR = 'core/block-type-operator';
29
30
  const KIND_PLAN = 'core/plan';
30
31
 
31
32
  class Operator {
32
33
  private readonly _data: DefinitionInfo;
34
+
33
35
  constructor(data: DefinitionInfo) {
34
36
  this._data = data;
35
37
  }
36
38
 
37
- getLocalData() {
39
+ getLocalData(): LocalImageOptions {
38
40
  return this._data.definition.spec.local;
39
41
  }
40
42
 
@@ -65,24 +67,24 @@ class OperatorManager {
65
67
  /**
66
68
  * Get operator definition for resource type
67
69
  */
68
- async getOperator(resourceType: string, version: string) {
69
- const operators = await definitionsManager.getDefinitions(KIND_OPERATOR);
70
+ async getOperator(fullName: string, version: string) {
71
+ const operators = await definitionsManager.getDefinitions([KIND_RESOURCE_OPERATOR, KIND_BLOCK_OPERATOR]);
70
72
 
71
73
  const operator: DefinitionInfo | undefined = operators.find(
72
74
  (operator) =>
73
75
  operator.definition &&
74
76
  operator.definition.metadata &&
75
77
  operator.definition.metadata.name &&
76
- operator.definition.metadata.name.toLowerCase() === resourceType.toLowerCase() &&
78
+ operator.definition.metadata.name.toLowerCase() === fullName.toLowerCase() &&
77
79
  operator.version === version
78
80
  );
79
81
 
80
82
  if (!operator) {
81
- throw new Error(`Unknown resource type: ${resourceType}:${version}`);
83
+ throw new Error(`Unknown operator type: ${fullName}:${version}`);
82
84
  }
83
85
 
84
86
  if (!operator.definition.spec || !operator.definition.spec.local) {
85
- throw new Error(`Operator missing local definition: ${resourceType}:${version}`);
87
+ throw new Error(`Operator missing local definition: ${fullName}:${version}`);
86
88
  }
87
89
 
88
90
  return new Operator(operator);
@@ -137,7 +139,7 @@ class OperatorManager {
137
139
  const kindUri = parseKapetaUri(blockResource.kind);
138
140
  const operator = await this.getOperator(resourceType, kindUri.version);
139
141
  const credentials = operator.getCredentials();
140
- const container = await this.ensureResource(systemId, resourceType, kindUri.version);
142
+ const container = await this.ensureOperator(systemId, resourceType, kindUri.version);
141
143
  const portInfo = await container.getPort(portType);
142
144
 
143
145
  if (!portInfo) {
@@ -164,16 +166,17 @@ class OperatorManager {
164
166
  /**
165
167
  * Ensure we have a running operator of given type
166
168
  *
167
- * @param {string} systemId
168
- * @param {string} resourceType
169
- * @param {string} version
170
- * @return {Promise<ContainerInfo>}
169
+ * @param systemId the plan ref
170
+ * @param kind the full name - e.g. myhandle/rabbitmq
171
+ * @param version the version of the operator
171
172
  */
172
- async ensureResource(systemId: string, resourceType: string, version: string): Promise<ContainerInfo> {
173
+ async ensureOperator(systemId: string, kind: string, version: string): Promise<ContainerInfo> {
173
174
  systemId = normalizeKapetaUri(systemId);
174
- const key = `${systemId}#${resourceType}:${version}`;
175
+
176
+ const key = `${systemId}#${kind}:${version}`;
177
+
175
178
  return await this.operatorLock.acquire(key, async () => {
176
- const operator = await this.getOperator(resourceType, version);
179
+ const operator = await this.getOperator(kind, version);
177
180
 
178
181
  const operatorData = operator.getLocalData();
179
182
 
@@ -186,17 +189,9 @@ class OperatorManager {
186
189
  for (let i = 0; i < portTypes.length; i++) {
187
190
  const portType = portTypes[i];
188
191
  let containerPortInfo = operatorData.ports[portType];
189
- const hostPort = await serviceManager.ensureServicePort(systemId, resourceType, portType);
190
-
191
- if (typeof containerPortInfo === 'number' || typeof containerPortInfo === 'string') {
192
- containerPortInfo = { port: containerPortInfo, type: 'tcp' };
193
- }
194
-
195
- if (!containerPortInfo.type) {
196
- containerPortInfo.type = 'tcp';
197
- }
198
-
199
- const portId = containerPortInfo.port + '/' + containerPortInfo.type;
192
+ const hostPort = await serviceManager.ensureServicePort(systemId, kind, portType);
193
+ const portInfo = toPortInfo(containerPortInfo);
194
+ const portId = portInfo.port + '/' + portInfo.type;
200
195
 
201
196
  ports[portId] = {
202
197
  type: portType,
@@ -204,7 +199,7 @@ class OperatorManager {
204
199
  };
205
200
  }
206
201
 
207
- const nameParts = [systemId, resourceType.toLowerCase(), version];
202
+ const nameParts = [systemId, kind.toLowerCase(), version];
208
203
 
209
204
  const containerName = `kapeta-resource-${md5(nameParts.join('_'))}`;
210
205
 
@@ -216,7 +211,7 @@ class OperatorManager {
216
211
  const Labels: StringMap = {
217
212
  kapeta: 'true',
218
213
  [COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
219
- [COMPOSE_LABEL_SERVICE]: [resourceType, version].join('_').replace(/[^a-z0-9]/gi, '_'),
214
+ [COMPOSE_LABEL_SERVICE]: [kind, version].join('_').replace(/[^a-z0-9]/gi, '_'),
220
215
  };
221
216
 
222
217
  const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
@@ -237,7 +232,7 @@ class OperatorManager {
237
232
  Labels[CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
238
233
  });
239
234
 
240
- const Mounts = await containerManager.createVolumes(systemId, resourceType, operatorData.mounts);
235
+ const Mounts = await containerManager.createVolumes(systemId, kind, operatorData.mounts);
241
236
 
242
237
  _.forEach(operatorData.env, (value, name) => {
243
238
  Env.push(name + '=' + value);
@@ -34,22 +34,25 @@ class ServiceManager {
34
34
  });
35
35
  }
36
36
 
37
+ public getLocalHost(environmentType?: EnvironmentType) {
38
+ if (environmentType === 'docker') {
39
+ //We're inside a docker container, so we can use this special host name to access the host machine
40
+ return 'host.docker.internal';
41
+ }
42
+
43
+ return clusterService.getClusterServiceHost();
44
+ }
45
+
37
46
  _forLocal(port: string | number, path?: string, environmentType?: EnvironmentType) {
38
47
  if (!path) {
39
48
  path = '';
40
49
  }
41
- let host;
42
- if (environmentType === 'docker') {
43
- //We're inside a docker container, so we can use this special host name to access the host machine
44
- host = 'host.docker.internal';
45
- } else {
46
- host = clusterService.getClusterServiceHost();
47
- }
50
+ const hostname = this.getLocalHost(environmentType);
48
51
 
49
52
  if (path.startsWith('/')) {
50
53
  path = path.substring(1);
51
54
  }
52
- return `http://${host}:${port}/${path}`;
55
+ return `http://${hostname}:${port}/${path}`;
53
56
  }
54
57
 
55
58
  _ensureSystem(systemId: string) {
package/src/types.ts CHANGED
@@ -61,6 +61,27 @@ export type ProcessInfo = {
61
61
  portType?: string;
62
62
  };
63
63
 
64
+ export interface Health {
65
+ cmd: string;
66
+ interval?: number;
67
+ timeout?: number;
68
+ retries?: number;
69
+ }
70
+
71
+ export type PortInfo = { port: number; type: 'tcp' | 'udp' } | number | string;
72
+
73
+ export type LocalImageOptions<Credentials = AnyMap, Options = AnyMap> = {
74
+ image: string;
75
+ ports: { [key: string]: PortInfo };
76
+ credentials?: Credentials;
77
+ options?: Options;
78
+ cmd?: string;
79
+ env?: AnyMap;
80
+ health?: Health;
81
+ singleton?: boolean;
82
+ mounts?: { [key: string]: string };
83
+ };
84
+
64
85
  export type InstanceInfo = {
65
86
  systemId: string;
66
87
  instanceId: string;
@@ -86,6 +107,21 @@ interface ResourceRef {
86
107
 
87
108
  export type ProxyRequestHandler = (req: StringBodyRequest, res: express.Response, info: ProxyRequestInfo) => void;
88
109
 
110
+ export interface OperatorInstancePort {
111
+ protocol: string;
112
+ port: number;
113
+ }
114
+
115
+ export interface OperatorInstanceInfo {
116
+ hostname: string;
117
+ ports: { [portType: string]: OperatorInstancePort };
118
+ path?: string;
119
+ query?: string;
120
+ hash?: string;
121
+ options?: AnyMap;
122
+ credentials?: AnyMap;
123
+ }
124
+
89
125
  export interface OperatorInfo {
90
126
  host: string;
91
127
  port: string;