@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.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/src/config/routes.js +2 -2
- package/dist/cjs/src/containerManager.js +5 -3
- package/dist/cjs/src/instanceManager.d.ts +4 -2
- package/dist/cjs/src/instanceManager.js +60 -28
- package/dist/cjs/src/operatorManager.d.ts +4 -2
- package/dist/cjs/src/operatorManager.js +32 -23
- package/dist/cjs/src/serviceManager.d.ts +0 -1
- package/dist/cjs/src/serviceManager.js +2 -8
- package/dist/cjs/src/types.d.ts +1 -29
- package/dist/cjs/src/types.js +2 -1
- package/dist/cjs/src/utils/BlockInstanceRunner.js +30 -30
- package/dist/cjs/src/utils/InternalConfigProvider.d.ts +38 -0
- package/dist/cjs/src/utils/InternalConfigProvider.js +146 -0
- package/dist/cjs/src/utils/utils.d.ts +25 -3
- package/dist/cjs/src/utils/utils.js +46 -7
- package/dist/esm/index.js +4 -1
- package/dist/esm/src/config/routes.js +2 -2
- package/dist/esm/src/containerManager.js +5 -3
- package/dist/esm/src/instanceManager.d.ts +4 -2
- package/dist/esm/src/instanceManager.js +60 -28
- package/dist/esm/src/operatorManager.d.ts +4 -2
- package/dist/esm/src/operatorManager.js +32 -23
- package/dist/esm/src/serviceManager.d.ts +0 -1
- package/dist/esm/src/serviceManager.js +2 -8
- package/dist/esm/src/types.d.ts +1 -29
- package/dist/esm/src/types.js +2 -1
- package/dist/esm/src/utils/BlockInstanceRunner.js +30 -30
- package/dist/esm/src/utils/InternalConfigProvider.d.ts +38 -0
- package/dist/esm/src/utils/InternalConfigProvider.js +146 -0
- package/dist/esm/src/utils/utils.d.ts +25 -3
- package/dist/esm/src/utils/utils.js +46 -7
- package/index.ts +5 -2
- package/package.json +6 -6
- package/src/config/routes.ts +4 -2
- package/src/containerManager.ts +6 -4
- package/src/instanceManager.ts +74 -38
- package/src/operatorManager.ts +46 -37
- package/src/serviceManager.ts +3 -11
- package/src/types.ts +2 -31
- package/src/utils/BlockInstanceRunner.ts +48 -38
- package/src/utils/InternalConfigProvider.ts +214 -0
- 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.
|
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
|
-
|
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
|
-
|
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 [
|
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 [
|
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
|
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<
|
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
|
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
|
-
|
287
|
-
if (
|
288
|
-
instance = await
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
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
|
-
|
295
|
-
|
296
|
-
|
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 =
|
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
|
-
|
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
|
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
|
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
|
-
|
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.
|
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 {
|
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<
|
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
|
*
|