@kapeta/local-cluster-service 0.38.0 → 0.39.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 (44) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/index.js +4 -1
  3. package/dist/cjs/src/config/routes.js +2 -2
  4. package/dist/cjs/src/containerManager.js +5 -3
  5. package/dist/cjs/src/instanceManager.d.ts +4 -2
  6. package/dist/cjs/src/instanceManager.js +60 -28
  7. package/dist/cjs/src/operatorManager.d.ts +4 -2
  8. package/dist/cjs/src/operatorManager.js +32 -23
  9. package/dist/cjs/src/serviceManager.d.ts +0 -1
  10. package/dist/cjs/src/serviceManager.js +2 -8
  11. package/dist/cjs/src/types.d.ts +1 -29
  12. package/dist/cjs/src/types.js +2 -1
  13. package/dist/cjs/src/utils/BlockInstanceRunner.js +30 -30
  14. package/dist/cjs/src/utils/InternalConfigProvider.d.ts +38 -0
  15. package/dist/cjs/src/utils/InternalConfigProvider.js +146 -0
  16. package/dist/cjs/src/utils/utils.d.ts +25 -3
  17. package/dist/cjs/src/utils/utils.js +46 -7
  18. package/dist/esm/index.js +4 -1
  19. package/dist/esm/src/config/routes.js +2 -2
  20. package/dist/esm/src/containerManager.js +5 -3
  21. package/dist/esm/src/instanceManager.d.ts +4 -2
  22. package/dist/esm/src/instanceManager.js +60 -28
  23. package/dist/esm/src/operatorManager.d.ts +4 -2
  24. package/dist/esm/src/operatorManager.js +32 -23
  25. package/dist/esm/src/serviceManager.d.ts +0 -1
  26. package/dist/esm/src/serviceManager.js +2 -8
  27. package/dist/esm/src/types.d.ts +1 -29
  28. package/dist/esm/src/types.js +2 -1
  29. package/dist/esm/src/utils/BlockInstanceRunner.js +30 -30
  30. package/dist/esm/src/utils/InternalConfigProvider.d.ts +38 -0
  31. package/dist/esm/src/utils/InternalConfigProvider.js +146 -0
  32. package/dist/esm/src/utils/utils.d.ts +25 -3
  33. package/dist/esm/src/utils/utils.js +46 -7
  34. package/index.ts +5 -2
  35. package/package.json +6 -6
  36. package/src/config/routes.ts +4 -2
  37. package/src/containerManager.ts +6 -4
  38. package/src/instanceManager.ts +74 -38
  39. package/src/operatorManager.ts +46 -37
  40. package/src/serviceManager.ts +3 -11
  41. package/src/types.ts +2 -31
  42. package/src/utils/BlockInstanceRunner.ts +48 -38
  43. package/src/utils/InternalConfigProvider.ts +214 -0
  44. package/src/utils/utils.ts +51 -8
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+ import { BlockInstanceDetails, ConfigProvider, DefaultCredentials, DefaultResourceOptions, InstanceOperator, ResourceInfo } from '@kapeta/sdk-config';
6
+ import { Definition, DefinitionInfo } from '@kapeta/local-cluster-config';
7
+ import { BlockDefinition, Plan } from '@kapeta/schemas';
8
+ import { AnyMap, EnvironmentType } from '../types';
9
+ /**
10
+ * A configuration provider that does the same as the LocalConfigProvider
11
+ * but without calling the API of the local cluster service (since it's running in the same process)
12
+ */
13
+ export declare class InternalConfigProvider implements ConfigProvider {
14
+ private readonly info;
15
+ private readonly systemId;
16
+ private readonly instanceId;
17
+ private readonly config;
18
+ private readonly environment;
19
+ constructor(systemId: string, instanceId: string, info: DefinitionInfo, config: AnyMap, environment?: EnvironmentType);
20
+ getBlockDefinition(): Definition;
21
+ getBlockReference(): string;
22
+ getSystemId(): string;
23
+ getInstanceId(): string;
24
+ getServerPort(portType?: string | undefined): Promise<string>;
25
+ getServiceAddress(serviceName: string, portType: string): Promise<string | null>;
26
+ getResourceInfo<Options = DefaultResourceOptions, Credentials = DefaultCredentials>(resourceType: string, portType: string, resourceName: string): Promise<ResourceInfo<Options, Credentials> | null>;
27
+ getInstanceHost(instanceId: string): Promise<string | null>;
28
+ getServerHost(): Promise<string>;
29
+ getProviderId(): string;
30
+ getOrDefault<T = any>(path: string, defaultValue: T): T;
31
+ get<T = any>(path: string): T | undefined;
32
+ getInstanceOperator<Options = any, Credentials extends DefaultCredentials = DefaultCredentials>(instanceId: string): Promise<InstanceOperator<Options, Credentials> | null>;
33
+ getInstanceForConsumer<BlockType = BlockDefinition>(resourceName: string): Promise<BlockInstanceDetails<BlockType> | null>;
34
+ getInstancesForProvider<BlockType = BlockDefinition>(resourceName: string): Promise<BlockInstanceDetails<BlockType>[]>;
35
+ getBlock(ref: any): Promise<Definition>;
36
+ getPlan(): Promise<Plan>;
37
+ }
38
+ export declare function createInternalConfigProvider(systemId: string, instanceId: string, info: DefinitionInfo): Promise<InternalConfigProvider>;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createInternalConfigProvider = exports.InternalConfigProvider = void 0;
7
+ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
8
+ const configManager_1 = require("../configManager");
9
+ const lodash_1 = __importDefault(require("lodash"));
10
+ const serviceManager_1 = require("../serviceManager");
11
+ const operatorManager_1 = require("../operatorManager");
12
+ const instanceManager_1 = require("../instanceManager");
13
+ const definitionsManager_1 = require("../definitionsManager");
14
+ const utils_1 = require("./utils");
15
+ /**
16
+ * A configuration provider that does the same as the LocalConfigProvider
17
+ * but without calling the API of the local cluster service (since it's running in the same process)
18
+ */
19
+ class InternalConfigProvider {
20
+ info;
21
+ systemId;
22
+ instanceId;
23
+ config;
24
+ environment;
25
+ constructor(systemId, instanceId, info, config, environment = 'docker') {
26
+ this.info = info;
27
+ this.systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
28
+ this.instanceId = instanceId;
29
+ this.config = config;
30
+ this.environment = environment;
31
+ }
32
+ getBlockDefinition() {
33
+ return this.info.definition;
34
+ }
35
+ getBlockReference() {
36
+ return (0, nodejs_utils_1.normalizeKapetaUri)(this.info.definition.metadata.name + ':' + this.info.version);
37
+ }
38
+ getSystemId() {
39
+ return this.systemId;
40
+ }
41
+ getInstanceId() {
42
+ return this.instanceId;
43
+ }
44
+ getServerPort(portType) {
45
+ return serviceManager_1.serviceManager.ensureServicePort(this.systemId, this.instanceId, portType);
46
+ }
47
+ async getServiceAddress(serviceName, portType) {
48
+ return serviceManager_1.serviceManager.getConsumerAddress(this.systemId, this.instanceId, serviceName, portType, this.environment);
49
+ }
50
+ getResourceInfo(resourceType, portType, resourceName) {
51
+ return operatorManager_1.operatorManager.getConsumerResourceInfo(this.systemId, this.instanceId, resourceType, portType, resourceName, this.environment, false);
52
+ }
53
+ async getInstanceHost(instanceId) {
54
+ const instance = instanceManager_1.instanceManager.getInstance(this.systemId, instanceId);
55
+ return instance?.address ?? null;
56
+ }
57
+ async getServerHost() {
58
+ return (0, utils_1.getBindAddressForEnvironment)(this.environment);
59
+ }
60
+ getProviderId() {
61
+ return 'internal';
62
+ }
63
+ getOrDefault(path, defaultValue) {
64
+ return this.get(path) ?? defaultValue;
65
+ }
66
+ get(path) {
67
+ return lodash_1.default.get(this.config, path);
68
+ }
69
+ getInstanceOperator(instanceId) {
70
+ return instanceManager_1.instanceManager.getInstanceOperator(this.systemId, instanceId, this.environment, false);
71
+ }
72
+ async getInstanceForConsumer(resourceName) {
73
+ const plan = await this.getPlan();
74
+ if (!plan) {
75
+ throw new Error('Could not find plan');
76
+ }
77
+ const instanceId = this.getInstanceId();
78
+ const connection = plan.spec.connections.find((connection) => connection.consumer.blockId === instanceId && connection.consumer.resourceName === resourceName);
79
+ if (!connection) {
80
+ throw new Error(`Could not find connection for consumer ${resourceName}`);
81
+ }
82
+ const instance = plan.spec.blocks.find((b) => b.id === connection.provider.blockId);
83
+ if (!instance) {
84
+ throw new Error(`Could not find instance ${connection.provider.blockId} in plan`);
85
+ }
86
+ const block = await this.getBlock(instance.block.ref);
87
+ if (!block) {
88
+ throw new Error(`Could not find block ${instance.block.ref} in plan`);
89
+ }
90
+ return {
91
+ instanceId: connection.provider.blockId,
92
+ connections: [connection],
93
+ block: block,
94
+ };
95
+ }
96
+ async getInstancesForProvider(resourceName) {
97
+ const plan = await this.getPlan();
98
+ if (!plan) {
99
+ throw new Error('Could not find plan');
100
+ }
101
+ const instanceId = this.getInstanceId();
102
+ const blockDetails = {};
103
+ const connections = plan.spec.connections.filter((connection) => connection.provider.blockId === instanceId && connection.provider.resourceName === resourceName);
104
+ for (const connection of connections) {
105
+ const blockInstanceId = connection.consumer.blockId;
106
+ if (blockDetails[blockInstanceId]) {
107
+ blockDetails[blockInstanceId].connections.push(connection);
108
+ continue;
109
+ }
110
+ const instance = plan.spec.blocks.find((b) => b.id === blockInstanceId);
111
+ if (!instance) {
112
+ throw new Error(`Could not find instance ${blockInstanceId} in plan`);
113
+ }
114
+ const block = await this.getBlock(instance.block.ref);
115
+ if (!block) {
116
+ throw new Error(`Could not find block ${instance.block.ref} in plan`);
117
+ }
118
+ blockDetails[blockInstanceId] = {
119
+ instanceId: blockInstanceId,
120
+ connections: [connection],
121
+ block: block,
122
+ };
123
+ }
124
+ return Object.values(blockDetails);
125
+ }
126
+ async getBlock(ref) {
127
+ const definition = await definitionsManager_1.definitionsManager.getDefinition(ref);
128
+ if (!definition) {
129
+ throw new Error(`Could not find definition for ${ref}`);
130
+ }
131
+ return definition.definition;
132
+ }
133
+ async getPlan() {
134
+ const definition = await definitionsManager_1.definitionsManager.getDefinition(this.systemId);
135
+ if (!definition) {
136
+ throw new Error(`Could not find plan ${this.systemId}`);
137
+ }
138
+ return definition.definition;
139
+ }
140
+ }
141
+ exports.InternalConfigProvider = InternalConfigProvider;
142
+ async function createInternalConfigProvider(systemId, instanceId, info) {
143
+ const config = await configManager_1.configManager.getConfigForBlockInstance(systemId, instanceId);
144
+ return new InternalConfigProvider(systemId, instanceId, info, config);
145
+ }
146
+ exports.createInternalConfigProvider = createInternalConfigProvider;
@@ -2,17 +2,39 @@
2
2
  * Copyright 2023 Kapeta Inc.
3
3
  * SPDX-License-Identifier: BUSL-1.1
4
4
  */
5
- import { EntityList, LocalInstancePort } from '@kapeta/schemas';
6
- import { AnyMap } from '../types';
5
+ import { EntityList, LocalInstance, LocalInstancePort } from '@kapeta/schemas';
6
+ import { AnyMap, EnvironmentType } from '../types';
7
7
  export declare function getBlockInstanceContainerName(systemId: string, instanceId: string, blockType?: string): Promise<string>;
8
8
  export declare function toPortInfo(port: LocalInstancePort): LocalInstancePort | {
9
9
  port: number;
10
10
  type: string;
11
11
  };
12
+ export declare function getOperatorInstancePorts(systemId: string, operatorId: string, local: LocalInstance): Promise<{
13
+ portType: string;
14
+ port: number | undefined;
15
+ hostPort: any;
16
+ protocol: string | undefined;
17
+ }[]>;
18
+ /**
19
+ * Gets the hostname where all services are available - including the cluster service.
20
+ *
21
+ * For docker this is the internal docker host - otherwise it's the local machine
22
+ * Assumed to be the same address as the cluster service outside docker.
23
+ */
24
+ export declare function getRemoteHostForEnvironment(environment: EnvironmentType | undefined): string;
25
+ /**
26
+ * Get the bind address for the given environment.
27
+ *
28
+ * Outside of docker we bind to 127.0.0.1 - inside we bind to everything (0.0.0.0)
29
+ */
30
+ export declare function getBindAddressForEnvironment(environment: EnvironmentType | undefined, preferredHost?: string): string;
31
+ /**
32
+ * Get the docker host IP address for port binding.
33
+ */
34
+ export declare function getDockerHostIp(preferredHost?: string): string;
12
35
  export declare function getRemoteUrl(id: string, defautValue: string): any;
13
36
  export declare function readYML(path: string): any;
14
37
  export declare function isWindows(): boolean;
15
38
  export declare function isMac(): boolean;
16
39
  export declare function isLinux(): boolean;
17
- export declare function getBindHost(preferredHost?: string): string;
18
40
  export declare function getResolvedConfiguration(entities?: EntityList, config?: AnyMap, globalConfiguration?: AnyMap): AnyMap;
@@ -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.getResolvedConfiguration = exports.getBindHost = exports.isLinux = exports.isMac = exports.isWindows = exports.readYML = exports.getRemoteUrl = exports.toPortInfo = exports.getBlockInstanceContainerName = void 0;
10
+ exports.getResolvedConfiguration = exports.isLinux = exports.isMac = exports.isWindows = exports.readYML = exports.getRemoteUrl = exports.getDockerHostIp = exports.getBindAddressForEnvironment = exports.getRemoteHostForEnvironment = exports.getOperatorInstancePorts = exports.toPortInfo = exports.getBlockInstanceContainerName = void 0;
11
11
  const node_fs_1 = __importDefault(require("node:fs"));
12
12
  const yaml_1 = __importDefault(require("yaml"));
13
13
  const md5_1 = __importDefault(require("md5"));
@@ -18,6 +18,8 @@ const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-co
18
18
  const definitionsManager_1 = require("../definitionsManager");
19
19
  const nodejs_utils_1 = require("@kapeta/nodejs-utils");
20
20
  const assetManager_1 = require("../assetManager");
21
+ const serviceManager_1 = require("../serviceManager");
22
+ const clusterService_1 = require("../clusterService");
21
23
  async function getBlockInstanceContainerName(systemId, instanceId, blockType) {
22
24
  if (!blockType) {
23
25
  const instance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
@@ -51,6 +53,49 @@ function toPortInfo(port) {
51
53
  return port;
52
54
  }
53
55
  exports.toPortInfo = toPortInfo;
56
+ async function getOperatorInstancePorts(systemId, operatorId, local) {
57
+ const localPorts = local.ports ?? {};
58
+ const promises = Object.entries(localPorts).map(async ([portType, value]) => {
59
+ const portInfo = toPortInfo(value);
60
+ const hostPort = await serviceManager_1.serviceManager.ensureServicePort(systemId, operatorId, portType);
61
+ return {
62
+ portType,
63
+ port: portInfo.port,
64
+ hostPort,
65
+ protocol: portInfo.type,
66
+ };
67
+ });
68
+ return await Promise.all(promises);
69
+ }
70
+ exports.getOperatorInstancePorts = getOperatorInstancePorts;
71
+ /**
72
+ * Gets the hostname where all services are available - including the cluster service.
73
+ *
74
+ * For docker this is the internal docker host - otherwise it's the local machine
75
+ * Assumed to be the same address as the cluster service outside docker.
76
+ */
77
+ function getRemoteHostForEnvironment(environment) {
78
+ return environment === 'docker' ? types_1.DOCKER_HOST_INTERNAL : clusterService_1.clusterService.getClusterServiceHost();
79
+ }
80
+ exports.getRemoteHostForEnvironment = getRemoteHostForEnvironment;
81
+ /**
82
+ * Get the bind address for the given environment.
83
+ *
84
+ * Outside of docker we bind to 127.0.0.1 - inside we bind to everything (0.0.0.0)
85
+ */
86
+ function getBindAddressForEnvironment(environment, preferredHost = '127.0.0.1') {
87
+ return environment === 'docker' ? '0.0.0.0' : preferredHost;
88
+ }
89
+ exports.getBindAddressForEnvironment = getBindAddressForEnvironment;
90
+ /**
91
+ * Get the docker host IP address for port binding.
92
+ */
93
+ function getDockerHostIp(preferredHost = '127.0.0.1') {
94
+ // On Linux we need to bind to 0.0.0.0 to be able to connect to it from docker containers.
95
+ // TODO: This might pose a security risk - so we should authenticate all requests using a shared secret/nonce that we pass around.
96
+ return isLinux() ? '0.0.0.0' : preferredHost;
97
+ }
98
+ exports.getDockerHostIp = getDockerHostIp;
54
99
  function getRemoteUrl(id, defautValue) {
55
100
  const remoteConfig = local_cluster_config_1.default.getClusterConfig().remote;
56
101
  return remoteConfig?.[id] ?? defautValue;
@@ -78,12 +123,6 @@ function isLinux() {
78
123
  return !isWindows() && !isMac();
79
124
  }
80
125
  exports.isLinux = isLinux;
81
- function getBindHost(preferredHost = '127.0.0.1') {
82
- // On Linux we need to bind to 0.0.0.0 to be able to connect to it from docker containers.
83
- // TODO: This might pose a security risk - so we should authenticate all requests using a shared secret/nonce that we pass around.
84
- return isLinux() ? '0.0.0.0' : preferredHost;
85
- }
86
- exports.getBindHost = getBindHost;
87
126
  function getResolvedConfiguration(entities, config, globalConfiguration) {
88
127
  if (!entities || !globalConfiguration) {
89
128
  return config || {};
package/dist/esm/index.js CHANGED
@@ -210,7 +210,10 @@ exports.default = {
210
210
  }
211
211
  reject(err);
212
212
  });
213
- const bindHost = (0, utils_1.getBindHost)(host);
213
+ // On Linux we need to bind to 0.0.0.0 to be able to connect to it from docker containers.
214
+ // TODO: This might pose a security risk - so we should authenticate all requests using a
215
+ // shared secret/nonce that we pass around.
216
+ const bindHost = (0, utils_1.isLinux)() ? '0.0.0.0' : host;
214
217
  currentServer.listen(port, bindHost, async () => {
215
218
  try {
216
219
  const ensureCLITask = await (0, commandLineUtils_1.ensureCLI)();
@@ -136,7 +136,7 @@ router.get('/provides/:type', async (req, res) => {
136
136
  * assign port numbers to it etc.
137
137
  */
138
138
  router.get('/consumes/resource/:resourceType/:portType/:name', async (req, res) => {
139
- const operatorInfo = await operatorManager_1.operatorManager.getConsumerResourceInfo(req.kapeta.systemId, req.kapeta.instanceId, req.params.resourceType, req.params.portType, req.params.name, req.kapeta.environment);
139
+ const operatorInfo = await operatorManager_1.operatorManager.getConsumerResourceInfo(req.kapeta.systemId, req.kapeta.instanceId, req.params.resourceType, req.params.portType, req.params.name, req.kapeta.environment, req.query.ensure !== 'false');
140
140
  res.send(operatorInfo);
141
141
  });
142
142
  /**
@@ -154,7 +154,7 @@ router.get('/consumes/:resourceName/:type', (req, res) => {
154
154
  * If the remote service is not already running it will be started
155
155
  */
156
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);
157
+ const operatorInfo = await instanceManager_1.instanceManager.getInstanceOperator(req.kapeta.systemId, req.params.instanceId, req.kapeta.environment, req.query.ensure !== 'false');
158
158
  res.send(operatorInfo);
159
159
  });
160
160
  exports.default = router;
@@ -19,6 +19,7 @@ const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-co
19
19
  const node_uuid_1 = __importDefault(require("node-uuid"));
20
20
  const md5_1 = __importDefault(require("md5"));
21
21
  const utils_1 = require("./utils/utils");
22
+ const types_1 = require("./types");
22
23
  const nodejs_api_client_1 = require("@kapeta/nodejs-api-client");
23
24
  const taskManager_1 = require("./taskManager");
24
25
  const node_events_1 = require("node:events");
@@ -508,7 +509,8 @@ class ContainerManager {
508
509
  const newName = 'deleting-' + node_uuid_1.default.v4();
509
510
  // Rename the container first to avoid name conflicts if people start the same container
510
511
  await container.rename({ name: newName });
511
- await container.remove({ force: !!opts?.force });
512
+ const newContainer = this.docker().getContainer(newName);
513
+ await newContainer.remove({ force: !!opts?.force });
512
514
  }
513
515
  /**
514
516
  *
@@ -925,11 +927,11 @@ function getExtraHosts(dockerVersion) {
925
927
  const [major, minor] = dockerVersion.split('.');
926
928
  if (parseInt(major) >= 20 && parseInt(minor) >= 10) {
927
929
  // Docker 20.10+ on Linux supports adding host.docker.internal to point to host-gateway
928
- return ['host.docker.internal:host-gateway'];
930
+ return [`${types_1.DOCKER_HOST_INTERNAL}:host-gateway`];
929
931
  }
930
932
  // Docker versions lower than 20.10 needs an actual IP address. We use the default network bridge which
931
933
  // is always 172.17.0.1
932
- return ['host.docker.internal:172.17.0.1'];
934
+ return [`${types_1.DOCKER_HOST_INTERNAL}:172.17.0.1`];
933
935
  }
934
936
  return undefined;
935
937
  }
@@ -2,8 +2,9 @@
2
2
  * Copyright 2023 Kapeta Inc.
3
3
  * SPDX-License-Identifier: BUSL-1.1
4
4
  */
5
- import { EnvironmentType, InstanceInfo, LogEntry, OperatorInstanceInfo } from './types';
5
+ import { EnvironmentType, InstanceInfo, LogEntry } from './types';
6
6
  import { Task } from './taskManager';
7
+ import { InstanceOperator } from '@kapeta/sdk-config';
7
8
  export declare class InstanceManager {
8
9
  private _interval;
9
10
  private readonly _instances;
@@ -14,6 +15,7 @@ export declare class InstanceManager {
14
15
  getInstancesForPlan(systemId: string): Promise<InstanceInfo[]>;
15
16
  getInstance(systemId: string, instanceId: string): InstanceInfo | undefined;
16
17
  private exclusive;
18
+ private isLocked;
17
19
  getLogs(systemId: string, instanceId: string): Promise<LogEntry[]>;
18
20
  saveInternalInstance(instance: InstanceInfo): Promise<InstanceInfo>;
19
21
  /**
@@ -25,7 +27,7 @@ export declare class InstanceManager {
25
27
  markAsStopped(systemId: string, instanceId: string): Promise<void>;
26
28
  startAllForPlan(systemId: string): Promise<Task<InstanceInfo[]>>;
27
29
  stopAllForPlan(systemId: string): Task<void>;
28
- getInstanceOperator(systemId: string, instanceId: string, environment?: EnvironmentType): Promise<OperatorInstanceInfo>;
30
+ getInstanceOperator(systemId: string, instanceId: string, environment?: EnvironmentType, ensureContainer?: boolean): Promise<InstanceOperator<any, any>>;
29
31
  stop(systemId: string, instanceId: string): Promise<void>;
30
32
  private stopInner;
31
33
  start(systemId: string, instanceId: string, checkForSingleton?: boolean): Promise<InstanceInfo | Task<InstanceInfo>>;
@@ -79,6 +79,9 @@ class InstanceManager {
79
79
  //console.log(`Releasing lock for ${key}`, this.instanceLocks.isBusy(key));
80
80
  return result;
81
81
  }
82
+ isLocked(systemId, instanceId) {
83
+ return this.instanceLocks.isBusy(`${systemId}/${instanceId}`);
84
+ }
82
85
  async getLogs(systemId, instanceId) {
83
86
  const instance = this.getInstance(systemId, instanceId);
84
87
  if (!instance) {
@@ -268,7 +271,7 @@ class InstanceManager {
268
271
  name: `Stopping plan ${systemId}`,
269
272
  });
270
273
  }
271
- async getInstanceOperator(systemId, instanceId, environment) {
274
+ async getInstanceOperator(systemId, instanceId, environment, ensureContainer = true) {
272
275
  const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
273
276
  if (!blockInstance) {
274
277
  throw new Error(`Instance not found: ${systemId}/${instanceId}`);
@@ -279,30 +282,48 @@ class InstanceManager {
279
282
  throw new Error(`Block not found: ${blockRef}`);
280
283
  }
281
284
  const operatorDefinition = await definitionsManager_1.definitionsManager.getDefinition(block.kind);
282
- if (!operatorDefinition?.definition.spec.local) {
285
+ if (!operatorDefinition) {
286
+ throw new Error(`Operator not found: ${block.kind}`);
287
+ }
288
+ if (operatorDefinition.definition.kind !== types_1.KIND_BLOCK_TYPE_OPERATOR) {
289
+ throw new Error(`Block is not an operator: ${blockRef}`);
290
+ }
291
+ if (!operatorDefinition.definition.spec.local) {
283
292
  throw new Error(`Operator block has no local definition: ${blockRef}`);
284
293
  }
285
294
  const localConfig = operatorDefinition.definition.spec.local;
286
- let instance = await this.start(systemId, instanceId);
287
- if (instance instanceof taskManager_1.Task) {
288
- instance = await instance.wait();
289
- }
290
- const container = await containerManager_1.containerManager.get(instance.pid);
291
- if (!container) {
292
- throw new Error(`Container not found: ${instance.pid}`);
295
+ const ports = {};
296
+ if (ensureContainer) {
297
+ let instance = await this.start(systemId, instanceId);
298
+ if (instance instanceof taskManager_1.Task) {
299
+ instance = await instance.wait();
300
+ }
301
+ const container = await containerManager_1.containerManager.get(instance.pid);
302
+ if (!container) {
303
+ throw new Error(`Container not found: ${instance.pid}`);
304
+ }
305
+ const portInfo = await container.getPorts();
306
+ if (!portInfo) {
307
+ throw new Error(`No ports found for instance: ${instanceId}`);
308
+ }
309
+ Object.entries(portInfo).forEach(([key, value]) => {
310
+ ports[key] = {
311
+ protocol: value.protocol,
312
+ port: parseInt(value.hostPort),
313
+ };
314
+ });
293
315
  }
294
- const portInfo = await container.getPorts();
295
- if (!portInfo) {
296
- throw new Error(`No ports found for instance: ${instanceId}`);
316
+ else {
317
+ // If we're not ensuring the container is running we just get the ports from the local config
318
+ const instancePorts = await (0, utils_1.getOperatorInstancePorts)(systemId, instanceId, localConfig);
319
+ instancePorts.forEach((port) => {
320
+ ports[port.portType] = {
321
+ protocol: port.protocol,
322
+ port: port.hostPort,
323
+ };
324
+ });
297
325
  }
298
- const hostname = serviceManager_1.serviceManager.getLocalHost(environment);
299
- const ports = {};
300
- Object.entries(portInfo).forEach(([key, value]) => {
301
- ports[key] = {
302
- protocol: value.protocol,
303
- port: parseInt(value.hostPort),
304
- };
305
- });
326
+ const hostname = (0, utils_1.getRemoteHostForEnvironment)(environment);
306
327
  return {
307
328
  hostname,
308
329
  ports,
@@ -347,6 +368,7 @@ class InstanceManager {
347
368
  if (changeDesired && instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
348
369
  instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
349
370
  }
371
+ const wasFailed = instance.status === types_1.InstanceStatus.FAILED;
350
372
  instance.status = types_1.InstanceStatus.STOPPING;
351
373
  socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
352
374
  console.log('Stopping instance: %s::%s [desired: %s] [intentional: %s]', systemId, instanceId, instance.desiredStatus, changeDesired);
@@ -357,7 +379,12 @@ class InstanceManager {
357
379
  const container = await containerManager_1.containerManager.getContainerByName(containerName);
358
380
  if (container) {
359
381
  try {
360
- await container.stop();
382
+ if (wasFailed) {
383
+ await container.remove();
384
+ }
385
+ else {
386
+ await container.stop();
387
+ }
361
388
  instance.status = types_1.InstanceStatus.STOPPED;
362
389
  socketManager_1.socketManager.emitSystemEvent(systemId, socketManager_1.EVENT_STATUS_CHANGED, instance);
363
390
  this.save();
@@ -413,7 +440,7 @@ class InstanceManager {
413
440
  existingInstance = undefined;
414
441
  }
415
442
  }
416
- if (existingInstance?.pid) {
443
+ if (existingInstance && existingInstance.pid) {
417
444
  if (existingInstance.status === types_1.InstanceStatus.READY) {
418
445
  // Instance is already running
419
446
  return existingInstance;
@@ -474,8 +501,7 @@ class InstanceManager {
474
501
  return existingInstance;
475
502
  }
476
503
  }
477
- const instanceConfig = await configManager_1.configManager.getConfigForSection(systemId, instanceId);
478
- const resolvedConfig = (0, utils_1.getResolvedConfiguration)(blockSpec.configuration, instanceConfig, blockInstance.defaultConfiguration);
504
+ const resolvedConfig = await configManager_1.configManager.getConfigForBlockInstance(systemId, instanceId);
479
505
  const task = taskManager_1.taskManager.add(`instance:start:${systemId}:${instanceId}`, async () => {
480
506
  const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
481
507
  const startTime = Date.now();
@@ -708,14 +734,20 @@ class InstanceManager {
708
734
  return types_1.InstanceStatus.READY;
709
735
  }
710
736
  if (statusType === 'created') {
711
- if (state.ExitCode !== 0) {
712
- // Failed during creation
713
- return types_1.InstanceStatus.FAILED;
737
+ if (state.ExitCode !== undefined && state.ExitCode !== 0) {
738
+ // Failed during creation. Exit code is not always reliable though
739
+ if (state.Error) {
740
+ return types_1.InstanceStatus.FAILED;
741
+ }
742
+ else {
743
+ return types_1.InstanceStatus.STOPPED;
744
+ }
714
745
  }
715
746
  return types_1.InstanceStatus.STARTING;
716
747
  }
717
748
  if (statusType === 'exited' || statusType === 'dead') {
718
- if (state.ExitCode === 0) {
749
+ if (!state.Error) {
750
+ // Exit code is not always reliable - if there is no error we assume it's stopped
719
751
  return types_1.InstanceStatus.STOPPED;
720
752
  }
721
753
  return types_1.InstanceStatus.FAILED;
@@ -4,8 +4,9 @@
4
4
  */
5
5
  import { DefinitionInfo } from '@kapeta/local-cluster-config';
6
6
  import { ContainerInfo } from './containerManager';
7
- import { EnvironmentType, OperatorInfo } from './types';
7
+ import { AnyMap, EnvironmentType } from './types';
8
8
  import { LocalInstance } from '@kapeta/schemas';
9
+ import { ResourceInfo } from '@kapeta/sdk-config';
9
10
  declare class Operator {
10
11
  private readonly _data;
11
12
  constructor(data: DefinitionInfo);
@@ -25,7 +26,8 @@ declare class OperatorManager {
25
26
  /**
26
27
  * Get information about a specific consumed resource
27
28
  */
28
- getConsumerResourceInfo(systemId: string, fromServiceId: string, resourceType: string, portType: string, name: string, environment?: EnvironmentType): Promise<OperatorInfo>;
29
+ getConsumerResourceInfo(systemId: string, fromServiceId: string, resourceType: string, portType: string, name: string, environment?: EnvironmentType, ensureContainer?: boolean): Promise<ResourceInfo<any, any>>;
30
+ getOperatorPorts(systemId: string, kind: string, version: string): Promise<AnyMap>;
29
31
  /**
30
32
  * Ensure we have a running operator of given type
31
33
  *