@kapeta/local-cluster-service 0.34.1 → 0.35.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.
- package/.github/workflows/check-license.yml +0 -1
- package/CHANGELOG.md +14 -0
- package/dist/cjs/src/config/routes.js +9 -0
- package/dist/cjs/src/containerManager.d.ts +2 -8
- package/dist/cjs/src/containerManager.js +4 -4
- package/dist/cjs/src/instanceManager.d.ts +2 -1
- package/dist/cjs/src/instanceManager.js +43 -1
- package/dist/cjs/src/operatorManager.d.ts +2 -2
- package/dist/cjs/src/operatorManager.js +2 -7
- package/dist/cjs/src/proxy/types/rest.js +2 -1
- package/dist/cjs/src/proxy/types/web.js +2 -1
- package/dist/cjs/src/serviceManager.d.ts +1 -0
- package/dist/cjs/src/serviceManager.js +9 -9
- package/dist/cjs/src/types.d.ts +39 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.js +16 -12
- package/dist/cjs/src/utils/utils.d.ts +5 -1
- package/dist/cjs/src/utils/utils.js +11 -1
- package/dist/esm/src/config/routes.js +9 -0
- package/dist/esm/src/containerManager.d.ts +2 -8
- package/dist/esm/src/containerManager.js +4 -4
- package/dist/esm/src/instanceManager.d.ts +2 -1
- package/dist/esm/src/instanceManager.js +43 -1
- package/dist/esm/src/operatorManager.d.ts +2 -2
- package/dist/esm/src/operatorManager.js +2 -7
- package/dist/esm/src/proxy/types/rest.js +2 -1
- package/dist/esm/src/proxy/types/web.js +2 -1
- package/dist/esm/src/serviceManager.d.ts +1 -0
- package/dist/esm/src/serviceManager.js +9 -9
- package/dist/esm/src/types.d.ts +39 -0
- package/dist/esm/src/utils/BlockInstanceRunner.js +16 -12
- package/dist/esm/src/utils/utils.d.ts +5 -1
- package/dist/esm/src/utils/utils.js +11 -1
- package/package.json +6 -5
- package/src/config/routes.ts +15 -0
- package/src/containerManager.ts +5 -12
- package/src/instanceManager.ts +72 -4
- package/src/operatorManager.ts +5 -13
- package/src/proxy/types/rest.ts +2 -1
- package/src/proxy/types/web.ts +3 -2
- package/src/serviceManager.ts +11 -8
- package/src/types.ts +35 -0
- package/src/utils/BlockInstanceRunner.ts +21 -14
- package/src/utils/utils.ts +13 -2
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# [0.35.0](https://github.com/kapetacom/local-cluster-service/compare/v0.34.2...v0.35.0) (2024-01-31)
|
2
|
+
|
3
|
+
|
4
|
+
### Features
|
5
|
+
|
6
|
+
* Adds support for getting instance operator info ([#122](https://github.com/kapetacom/local-cluster-service/issues/122)) ([8fa18ac](https://github.com/kapetacom/local-cluster-service/commit/8fa18ac58226ce9776f79a64d3a99cf56456e834))
|
7
|
+
|
8
|
+
## [0.34.2](https://github.com/kapetacom/local-cluster-service/compare/v0.34.1...v0.34.2) (2024-01-22)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* Forward query params correctly ([#121](https://github.com/kapetacom/local-cluster-service/issues/121)) ([1bbf6f0](https://github.com/kapetacom/local-cluster-service/commit/1bbf6f0cf0fee218c441f3b556683873e208c951))
|
14
|
+
|
1
15
|
## [0.34.1](https://github.com/kapetacom/local-cluster-service/compare/v0.34.0...v0.34.1) (2024-01-22)
|
2
16
|
|
3
17
|
|
@@ -148,4 +148,13 @@ router.get('/consumes/resource/:resourceType/:portType/:name', async (req, res)
|
|
148
148
|
router.get('/consumes/:resourceName/:type', (req, res) => {
|
149
149
|
res.send(serviceManager_1.serviceManager.getConsumerAddress(req.kapeta.systemId, req.kapeta.instanceId, req.params.resourceName, req.params.type, req.kapeta.environment));
|
150
150
|
});
|
151
|
+
/**
|
152
|
+
* Used by services to information about a block operator
|
153
|
+
*
|
154
|
+
* If the remote service is not already running it will be started
|
155
|
+
*/
|
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);
|
158
|
+
res.send(operatorInfo);
|
159
|
+
});
|
151
160
|
exports.default = router;
|
@@ -5,7 +5,7 @@
|
|
5
5
|
/// <reference types="node" />
|
6
6
|
import FSExtra from 'fs-extra';
|
7
7
|
import Docker from 'dockerode';
|
8
|
-
import { InstanceInfo, LogEntry } from './types';
|
8
|
+
import { Health, InstanceInfo, LogEntry } from './types';
|
9
9
|
type StringMap = {
|
10
10
|
[key: string]: string;
|
11
11
|
};
|
@@ -26,12 +26,6 @@ export interface DockerMounts {
|
|
26
26
|
}
|
27
27
|
export type DockerContainerStatus = 'created' | 'running' | 'paused' | 'restarting' | 'removing' | 'exited' | 'dead';
|
28
28
|
export type DockerContainerHealth = 'starting' | 'healthy' | 'unhealthy' | 'none';
|
29
|
-
interface Health {
|
30
|
-
cmd: string;
|
31
|
-
interval?: number;
|
32
|
-
timeout?: number;
|
33
|
-
retries?: number;
|
34
|
-
}
|
35
29
|
export declare const CONTAINER_LABEL_PORT_PREFIX = "kapeta_port-";
|
36
30
|
export declare const COMPOSE_LABEL_PROJECT = "com.docker.compose.project";
|
37
31
|
export declare const COMPOSE_LABEL_SERVICE = "com.docker.compose.service";
|
@@ -50,7 +44,7 @@ declare class ContainerManager {
|
|
50
44
|
isAlive(): boolean;
|
51
45
|
getMountPoint(systemId: string, ref: string, mountName: string): string;
|
52
46
|
createMounts(systemId: string, kind: string, mountOpts: StringMap | null | undefined): Promise<StringMap>;
|
53
|
-
createVolumes(systemId: string,
|
47
|
+
createVolumes(systemId: string, serviceId: string, mountOpts: StringMap | null | undefined): Promise<DockerMounts[]>;
|
54
48
|
ping(): Promise<void>;
|
55
49
|
docker(): Docker;
|
56
50
|
getContainerByName(containerName: string): Promise<ContainerInfo | undefined>;
|
@@ -168,12 +168,12 @@ class ContainerManager {
|
|
168
168
|
}
|
169
169
|
return mounts;
|
170
170
|
}
|
171
|
-
async createVolumes(systemId,
|
171
|
+
async createVolumes(systemId, serviceId, mountOpts) {
|
172
172
|
const Mounts = [];
|
173
173
|
if (mountOpts) {
|
174
174
|
const mountOptList = Object.entries(mountOpts);
|
175
175
|
for (const [mountName, containerPath] of mountOptList) {
|
176
|
-
const volumeName = `${systemId}_${
|
176
|
+
const volumeName = `${systemId}_${serviceId}_${mountName}`.replace(/[^a-z0-9]/gi, '_');
|
177
177
|
Mounts.push({
|
178
178
|
Target: containerPath,
|
179
179
|
Source: volumeName,
|
@@ -182,7 +182,7 @@ class ContainerManager {
|
|
182
182
|
Consistency: 'consistent',
|
183
183
|
Labels: {
|
184
184
|
[exports.COMPOSE_LABEL_PROJECT]: systemId.replace(/[^a-z0-9]/gi, '_'),
|
185
|
-
[exports.COMPOSE_LABEL_SERVICE]:
|
185
|
+
[exports.COMPOSE_LABEL_SERVICE]: serviceId.replace(/[^a-z0-9]/gi, '_'),
|
186
186
|
},
|
187
187
|
});
|
188
188
|
}
|
@@ -791,7 +791,7 @@ class ContainerInfo {
|
|
791
791
|
if (!name.startsWith(exports.CONTAINER_LABEL_PORT_PREFIX)) {
|
792
792
|
return;
|
793
793
|
}
|
794
|
-
const hostPort = name.
|
794
|
+
const hostPort = name.substring(exports.CONTAINER_LABEL_PORT_PREFIX.length);
|
795
795
|
portTypes[hostPort] = portType;
|
796
796
|
});
|
797
797
|
lodash_1.default.forEach(inspectResult.HostConfig.PortBindings, (portBindings, containerPortSpec) => {
|
@@ -2,7 +2,7 @@
|
|
2
2
|
* Copyright 2023 Kapeta Inc.
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
|
-
import { InstanceInfo, LogEntry } from './types';
|
5
|
+
import { EnvironmentType, InstanceInfo, LogEntry, OperatorInstanceInfo } from './types';
|
6
6
|
import { Task } from './taskManager';
|
7
7
|
export declare class InstanceManager {
|
8
8
|
private _interval;
|
@@ -27,6 +27,7 @@ export declare class InstanceManager {
|
|
27
27
|
stop(systemId: string, instanceId: string): Promise<void>;
|
28
28
|
private stopInner;
|
29
29
|
stopAllForPlan(systemId: string): Task<void>;
|
30
|
+
getInstanceOperator(systemId: string, instanceId: string, environment?: EnvironmentType): Promise<OperatorInstanceInfo>;
|
30
31
|
start(systemId: string, instanceId: string): Promise<InstanceInfo | Task<InstanceInfo>>;
|
31
32
|
/**
|
32
33
|
* Stops an instance but does not remove it from the list of active instances
|
@@ -323,6 +323,48 @@ class InstanceManager {
|
|
323
323
|
name: `Stopping plan ${systemId}`,
|
324
324
|
});
|
325
325
|
}
|
326
|
+
async getInstanceOperator(systemId, instanceId, environment) {
|
327
|
+
const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
|
328
|
+
if (!blockInstance) {
|
329
|
+
throw new Error(`Instance not found: ${systemId}/${instanceId}`);
|
330
|
+
}
|
331
|
+
const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockInstance.block.ref);
|
332
|
+
const block = await assetManager_1.assetManager.getAsset(blockRef, true);
|
333
|
+
if (!block) {
|
334
|
+
throw new Error(`Block not found: ${blockRef}`);
|
335
|
+
}
|
336
|
+
const operatorDefinition = await definitionsManager_1.definitionsManager.getDefinition(block.kind);
|
337
|
+
if (!operatorDefinition?.definition.spec.local) {
|
338
|
+
throw new Error(`Operator block has no local definition: ${blockRef}`);
|
339
|
+
}
|
340
|
+
const localConfig = operatorDefinition.definition.spec.local;
|
341
|
+
let instance = await this.start(systemId, instanceId);
|
342
|
+
if (instance instanceof taskManager_1.Task) {
|
343
|
+
instance = await instance.wait();
|
344
|
+
}
|
345
|
+
const container = await containerManager_1.containerManager.get(instance.pid);
|
346
|
+
if (!container) {
|
347
|
+
throw new Error(`Container not found: ${instance.pid}`);
|
348
|
+
}
|
349
|
+
const portInfo = await container.getPorts();
|
350
|
+
if (!portInfo) {
|
351
|
+
throw new Error(`No ports found for instance: ${instanceId}`);
|
352
|
+
}
|
353
|
+
const hostname = serviceManager_1.serviceManager.getLocalHost(environment);
|
354
|
+
const ports = {};
|
355
|
+
Object.entries(portInfo).forEach(([key, value]) => {
|
356
|
+
ports[key] = {
|
357
|
+
protocol: value.protocol,
|
358
|
+
port: parseInt(value.hostPort),
|
359
|
+
};
|
360
|
+
});
|
361
|
+
return {
|
362
|
+
hostname,
|
363
|
+
ports,
|
364
|
+
credentials: localConfig.credentials,
|
365
|
+
options: localConfig.options,
|
366
|
+
};
|
367
|
+
}
|
326
368
|
async start(systemId, instanceId) {
|
327
369
|
return this.exclusive(systemId, instanceId, async () => {
|
328
370
|
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|
@@ -333,7 +375,7 @@ class InstanceManager {
|
|
333
375
|
throw new Error('Block not found: ' + blockRef);
|
334
376
|
}
|
335
377
|
const existingInstance = this.getInstance(systemId, instanceId);
|
336
|
-
if (existingInstance) {
|
378
|
+
if (existingInstance && existingInstance.pid) {
|
337
379
|
if (existingInstance.status === types_1.InstanceStatus.READY) {
|
338
380
|
// Instance is already running
|
339
381
|
return existingInstance;
|
@@ -4,12 +4,12 @@
|
|
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 { EnvironmentType, LocalImageOptions, OperatorInfo } from './types';
|
8
8
|
export declare const KIND_OPERATOR = "core/resource-type-operator";
|
9
9
|
declare class Operator {
|
10
10
|
private readonly _data;
|
11
11
|
constructor(data: DefinitionInfo);
|
12
|
-
getLocalData():
|
12
|
+
getLocalData(): LocalImageOptions;
|
13
13
|
getDefinitionInfo(): DefinitionInfo;
|
14
14
|
getCredentials(): any;
|
15
15
|
}
|
@@ -137,13 +137,8 @@ class OperatorManager {
|
|
137
137
|
const portType = portTypes[i];
|
138
138
|
let containerPortInfo = operatorData.ports[portType];
|
139
139
|
const hostPort = await serviceManager_1.serviceManager.ensureServicePort(systemId, resourceType, portType);
|
140
|
-
|
141
|
-
|
142
|
-
}
|
143
|
-
if (!containerPortInfo.type) {
|
144
|
-
containerPortInfo.type = 'tcp';
|
145
|
-
}
|
146
|
-
const portId = containerPortInfo.port + '/' + containerPortInfo.type;
|
140
|
+
const portInfo = (0, utils_1.toPortInfo)(containerPortInfo);
|
141
|
+
const portId = portInfo.port + '/' + portInfo.type;
|
147
142
|
ports[portId] = {
|
148
143
|
type: portType,
|
149
144
|
hostPort,
|
@@ -14,6 +14,7 @@ const path_1 = __importDefault(require("path"));
|
|
14
14
|
const pathTemplateParser_1 = require("../../utils/pathTemplateParser");
|
15
15
|
const networkManager_1 = require("../../networkManager");
|
16
16
|
const socketManager_1 = require("../../socketManager");
|
17
|
+
const qs_1 = require("qs");
|
17
18
|
function getRestMethodId(restResource, httpMethod, httpPath) {
|
18
19
|
return lodash_1.default.findKey(restResource.spec.methods, (method) => {
|
19
20
|
let methodType = method.method ? method.method.toUpperCase() : 'GET';
|
@@ -77,7 +78,7 @@ function proxyRestRequest(req, res, opts) {
|
|
77
78
|
providerPath = '/' + providerPath;
|
78
79
|
}
|
79
80
|
if (!lodash_1.default.isEmpty(req.query)) {
|
80
|
-
providerPath += '?' +
|
81
|
+
providerPath += '?' + (0, qs_1.stringify)(req.query, { arrayFormat: 'repeat' });
|
81
82
|
}
|
82
83
|
const requestHeaders = lodash_1.default.clone(req.headers);
|
83
84
|
delete requestHeaders['content-length'];
|
@@ -12,6 +12,7 @@ const request_1 = __importDefault(require("request"));
|
|
12
12
|
const lodash_1 = __importDefault(require("lodash"));
|
13
13
|
const networkManager_1 = require("../../networkManager");
|
14
14
|
const socketManager_1 = require("../../socketManager");
|
15
|
+
const qs_1 = require("qs");
|
15
16
|
function proxyHttpRequest(req, res, opts) {
|
16
17
|
const requestHeaders = lodash_1.default.clone(req.headers);
|
17
18
|
delete requestHeaders['content-length'];
|
@@ -26,7 +27,7 @@ function proxyHttpRequest(req, res, opts) {
|
|
26
27
|
path = path.replace(sourceBasePath, targetBasePath);
|
27
28
|
}
|
28
29
|
if (!lodash_1.default.isEmpty(req.query)) {
|
29
|
-
path += '?' +
|
30
|
+
path += '?' + (0, qs_1.stringify)(req.query, { arrayFormat: 'repeat' });
|
30
31
|
}
|
31
32
|
console.log('Proxy request to provider: %s => %s%s [http]', opts.consumerPath, opts.address, path);
|
32
33
|
const reqOpts = {
|
@@ -9,6 +9,7 @@ export declare const HTTP_PORTS: string[];
|
|
9
9
|
declare class ServiceManager {
|
10
10
|
private _systems;
|
11
11
|
constructor();
|
12
|
+
getLocalHost(environmentType?: EnvironmentType): string;
|
12
13
|
_forLocal(port: string | number, path?: string, environmentType?: EnvironmentType): string;
|
13
14
|
_ensureSystem(systemId: string): any;
|
14
15
|
_ensureService(systemId: string, serviceId: string): any;
|
@@ -31,22 +31,22 @@ class ServiceManager {
|
|
31
31
|
});
|
32
32
|
});
|
33
33
|
}
|
34
|
-
|
35
|
-
if (!path) {
|
36
|
-
path = '';
|
37
|
-
}
|
38
|
-
let host;
|
34
|
+
getLocalHost(environmentType) {
|
39
35
|
if (environmentType === 'docker') {
|
40
36
|
//We're inside a docker container, so we can use this special host name to access the host machine
|
41
|
-
|
37
|
+
return 'host.docker.internal';
|
42
38
|
}
|
43
|
-
|
44
|
-
|
39
|
+
return clusterService_1.clusterService.getClusterServiceHost();
|
40
|
+
}
|
41
|
+
_forLocal(port, path, environmentType) {
|
42
|
+
if (!path) {
|
43
|
+
path = '';
|
45
44
|
}
|
45
|
+
const hostname = this.getLocalHost(environmentType);
|
46
46
|
if (path.startsWith('/')) {
|
47
47
|
path = path.substring(1);
|
48
48
|
}
|
49
|
-
return `http://${
|
49
|
+
return `http://${hostname}:${port}/${path}`;
|
50
50
|
}
|
51
51
|
_ensureSystem(systemId) {
|
52
52
|
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|
package/dist/cjs/src/types.d.ts
CHANGED
@@ -56,6 +56,30 @@ export type ProcessInfo = {
|
|
56
56
|
pid?: number | string | null;
|
57
57
|
portType?: string;
|
58
58
|
};
|
59
|
+
export interface Health {
|
60
|
+
cmd: string;
|
61
|
+
interval?: number;
|
62
|
+
timeout?: number;
|
63
|
+
retries?: number;
|
64
|
+
}
|
65
|
+
export type PortInfo = {
|
66
|
+
port: number;
|
67
|
+
type: 'tcp' | 'udp';
|
68
|
+
} | number | string;
|
69
|
+
export type LocalImageOptions<Credentials = AnyMap, Options = AnyMap> = {
|
70
|
+
image: string;
|
71
|
+
ports: {
|
72
|
+
[key: string]: PortInfo;
|
73
|
+
};
|
74
|
+
credentials?: Credentials;
|
75
|
+
options?: Options;
|
76
|
+
cmd?: string;
|
77
|
+
env?: AnyMap;
|
78
|
+
health?: Health;
|
79
|
+
mounts?: {
|
80
|
+
[key: string]: string;
|
81
|
+
};
|
82
|
+
};
|
59
83
|
export type InstanceInfo = {
|
60
84
|
systemId: string;
|
61
85
|
instanceId: string;
|
@@ -73,6 +97,21 @@ export type InstanceInfo = {
|
|
73
97
|
portType?: string;
|
74
98
|
};
|
75
99
|
export type ProxyRequestHandler = (req: StringBodyRequest, res: express.Response, info: ProxyRequestInfo) => void;
|
100
|
+
export interface OperatorInstancePort {
|
101
|
+
protocol: string;
|
102
|
+
port: number;
|
103
|
+
}
|
104
|
+
export interface OperatorInstanceInfo {
|
105
|
+
hostname: string;
|
106
|
+
ports: {
|
107
|
+
[portType: string]: OperatorInstancePort;
|
108
|
+
};
|
109
|
+
path?: string;
|
110
|
+
query?: string;
|
111
|
+
hash?: string;
|
112
|
+
options?: AnyMap;
|
113
|
+
credentials?: AnyMap;
|
114
|
+
}
|
76
115
|
export interface OperatorInfo {
|
77
116
|
host: string;
|
78
117
|
port: string;
|
@@ -288,7 +288,8 @@ class BlockInstanceRunner {
|
|
288
288
|
if (!spec?.local?.image) {
|
289
289
|
throw new Error(`Provider did not have local image: ${providerRef}`);
|
290
290
|
}
|
291
|
-
const
|
291
|
+
const local = spec.local;
|
292
|
+
const dockerImage = local.image;
|
292
293
|
//We only want 1 operator per operator type - across all local systems
|
293
294
|
const containerName = (0, utils_1.getBlockInstanceContainerName)(this._systemId, blockInstance.id);
|
294
295
|
const logs = new LogData_1.LogData();
|
@@ -298,11 +299,13 @@ class BlockInstanceRunner {
|
|
298
299
|
const PortBindings = {};
|
299
300
|
let HealthCheck = undefined;
|
300
301
|
let Mounts = [];
|
301
|
-
const localPorts =
|
302
|
+
const localPorts = local.ports ?? {};
|
303
|
+
const labels = {};
|
302
304
|
const promises = Object.entries(localPorts).map(async ([portType, value]) => {
|
303
|
-
const
|
305
|
+
const portInfo = (0, utils_1.toPortInfo)(value);
|
306
|
+
const dockerPort = `${portInfo.port}/${portInfo.type}`;
|
304
307
|
ExposedPorts[dockerPort] = {};
|
305
|
-
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] =
|
308
|
+
addonEnv[`KAPETA_LOCAL_SERVER_PORT_${portType.toUpperCase()}`] = `${portInfo.port}`;
|
306
309
|
const publicPort = await serviceManager_1.serviceManager.ensureServicePort(this._systemId, blockInstance.id, portType);
|
307
310
|
PortBindings[dockerPort] = [
|
308
311
|
{
|
@@ -310,19 +313,19 @@ class BlockInstanceRunner {
|
|
310
313
|
HostPort: `${publicPort}`,
|
311
314
|
},
|
312
315
|
];
|
316
|
+
labels[containerManager_1.CONTAINER_LABEL_PORT_PREFIX + publicPort] = portType;
|
313
317
|
});
|
314
318
|
await Promise.all(promises);
|
315
|
-
if (
|
316
|
-
Object.entries(
|
319
|
+
if (local.env) {
|
320
|
+
Object.entries(local.env).forEach(([key, value]) => {
|
317
321
|
addonEnv[key] = value;
|
318
322
|
});
|
319
323
|
}
|
320
|
-
if (
|
321
|
-
|
322
|
-
Mounts = containerManager_1.containerManager.toDockerMounts(mounts);
|
324
|
+
if (local.mounts) {
|
325
|
+
Mounts = await containerManager_1.containerManager.createVolumes(this._systemId, blockUri.id, local.mounts);
|
323
326
|
}
|
324
|
-
if (
|
325
|
-
HealthCheck = containerManager_1.containerManager.toDockerHealth(
|
327
|
+
if (local.health) {
|
328
|
+
HealthCheck = containerManager_1.containerManager.toDockerHealth(local.health);
|
326
329
|
}
|
327
330
|
// For windows we need to default to root
|
328
331
|
const innerHome = process.platform === 'win32' ? '/root/.kapeta' : local_cluster_config_1.default.getKapetaBasedir();
|
@@ -342,6 +345,7 @@ class BlockInstanceRunner {
|
|
342
345
|
Mounts,
|
343
346
|
},
|
344
347
|
Labels: {
|
348
|
+
...labels,
|
345
349
|
instance: blockInstance.id,
|
346
350
|
[containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
347
351
|
[containerManager_1.COMPOSE_LABEL_SERVICE]: blockUri.id.replace(/[^a-z0-9]/gi, '_'),
|
@@ -356,7 +360,7 @@ class BlockInstanceRunner {
|
|
356
360
|
}).map(([key, value]) => `${key}=${value}`),
|
357
361
|
],
|
358
362
|
});
|
359
|
-
const portTypes =
|
363
|
+
const portTypes = local.ports ? Object.keys(local.ports) : [];
|
360
364
|
if (portTypes.length > 0) {
|
361
365
|
out.portType = portTypes[0];
|
362
366
|
}
|
@@ -3,8 +3,12 @@
|
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
5
|
import { EntityList } from '@kapeta/schemas';
|
6
|
-
import { AnyMap } from '../types';
|
6
|
+
import { AnyMap, PortInfo } from '../types';
|
7
7
|
export declare function getBlockInstanceContainerName(systemId: string, instanceId: string): string;
|
8
|
+
export declare function toPortInfo(port: PortInfo): {
|
9
|
+
port: number;
|
10
|
+
type: string;
|
11
|
+
};
|
8
12
|
export declare function getRemoteUrl(id: string, defautValue: string): any;
|
9
13
|
export declare function readYML(path: string): any;
|
10
14
|
export declare function isWindows(): boolean;
|
@@ -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.getBlockInstanceContainerName = void 0;
|
10
|
+
exports.getResolvedConfiguration = exports.getBindHost = exports.isLinux = exports.isMac = exports.isWindows = exports.readYML = exports.getRemoteUrl = 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"));
|
@@ -17,6 +17,16 @@ function getBlockInstanceContainerName(systemId, instanceId) {
|
|
17
17
|
return `kapeta-block-instance-${(0, md5_1.default)(systemId + instanceId)}`;
|
18
18
|
}
|
19
19
|
exports.getBlockInstanceContainerName = getBlockInstanceContainerName;
|
20
|
+
function toPortInfo(port) {
|
21
|
+
if (typeof port === 'number' || typeof port === 'string') {
|
22
|
+
return { port: parseInt(`${port}`), type: 'tcp' };
|
23
|
+
}
|
24
|
+
if (!port.type) {
|
25
|
+
port.type = 'tcp';
|
26
|
+
}
|
27
|
+
return port;
|
28
|
+
}
|
29
|
+
exports.toPortInfo = toPortInfo;
|
20
30
|
function getRemoteUrl(id, defautValue) {
|
21
31
|
const remoteConfig = local_cluster_config_1.default.getClusterConfig().remote;
|
22
32
|
return remoteConfig?.[id] ?? defautValue;
|
@@ -148,4 +148,13 @@ router.get('/consumes/resource/:resourceType/:portType/:name', async (req, res)
|
|
148
148
|
router.get('/consumes/:resourceName/:type', (req, res) => {
|
149
149
|
res.send(serviceManager_1.serviceManager.getConsumerAddress(req.kapeta.systemId, req.kapeta.instanceId, req.params.resourceName, req.params.type, req.kapeta.environment));
|
150
150
|
});
|
151
|
+
/**
|
152
|
+
* Used by services to information about a block operator
|
153
|
+
*
|
154
|
+
* If the remote service is not already running it will be started
|
155
|
+
*/
|
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);
|
158
|
+
res.send(operatorInfo);
|
159
|
+
});
|
151
160
|
exports.default = router;
|
@@ -5,7 +5,7 @@
|
|
5
5
|
/// <reference types="node" />
|
6
6
|
import FSExtra from 'fs-extra';
|
7
7
|
import Docker from 'dockerode';
|
8
|
-
import { InstanceInfo, LogEntry } from './types';
|
8
|
+
import { Health, InstanceInfo, LogEntry } from './types';
|
9
9
|
type StringMap = {
|
10
10
|
[key: string]: string;
|
11
11
|
};
|
@@ -26,12 +26,6 @@ export interface DockerMounts {
|
|
26
26
|
}
|
27
27
|
export type DockerContainerStatus = 'created' | 'running' | 'paused' | 'restarting' | 'removing' | 'exited' | 'dead';
|
28
28
|
export type DockerContainerHealth = 'starting' | 'healthy' | 'unhealthy' | 'none';
|
29
|
-
interface Health {
|
30
|
-
cmd: string;
|
31
|
-
interval?: number;
|
32
|
-
timeout?: number;
|
33
|
-
retries?: number;
|
34
|
-
}
|
35
29
|
export declare const CONTAINER_LABEL_PORT_PREFIX = "kapeta_port-";
|
36
30
|
export declare const COMPOSE_LABEL_PROJECT = "com.docker.compose.project";
|
37
31
|
export declare const COMPOSE_LABEL_SERVICE = "com.docker.compose.service";
|
@@ -50,7 +44,7 @@ declare class ContainerManager {
|
|
50
44
|
isAlive(): boolean;
|
51
45
|
getMountPoint(systemId: string, ref: string, mountName: string): string;
|
52
46
|
createMounts(systemId: string, kind: string, mountOpts: StringMap | null | undefined): Promise<StringMap>;
|
53
|
-
createVolumes(systemId: string,
|
47
|
+
createVolumes(systemId: string, serviceId: string, mountOpts: StringMap | null | undefined): Promise<DockerMounts[]>;
|
54
48
|
ping(): Promise<void>;
|
55
49
|
docker(): Docker;
|
56
50
|
getContainerByName(containerName: string): Promise<ContainerInfo | undefined>;
|
@@ -168,12 +168,12 @@ class ContainerManager {
|
|
168
168
|
}
|
169
169
|
return mounts;
|
170
170
|
}
|
171
|
-
async createVolumes(systemId,
|
171
|
+
async createVolumes(systemId, serviceId, mountOpts) {
|
172
172
|
const Mounts = [];
|
173
173
|
if (mountOpts) {
|
174
174
|
const mountOptList = Object.entries(mountOpts);
|
175
175
|
for (const [mountName, containerPath] of mountOptList) {
|
176
|
-
const volumeName = `${systemId}_${
|
176
|
+
const volumeName = `${systemId}_${serviceId}_${mountName}`.replace(/[^a-z0-9]/gi, '_');
|
177
177
|
Mounts.push({
|
178
178
|
Target: containerPath,
|
179
179
|
Source: volumeName,
|
@@ -182,7 +182,7 @@ class ContainerManager {
|
|
182
182
|
Consistency: 'consistent',
|
183
183
|
Labels: {
|
184
184
|
[exports.COMPOSE_LABEL_PROJECT]: systemId.replace(/[^a-z0-9]/gi, '_'),
|
185
|
-
[exports.COMPOSE_LABEL_SERVICE]:
|
185
|
+
[exports.COMPOSE_LABEL_SERVICE]: serviceId.replace(/[^a-z0-9]/gi, '_'),
|
186
186
|
},
|
187
187
|
});
|
188
188
|
}
|
@@ -791,7 +791,7 @@ class ContainerInfo {
|
|
791
791
|
if (!name.startsWith(exports.CONTAINER_LABEL_PORT_PREFIX)) {
|
792
792
|
return;
|
793
793
|
}
|
794
|
-
const hostPort = name.
|
794
|
+
const hostPort = name.substring(exports.CONTAINER_LABEL_PORT_PREFIX.length);
|
795
795
|
portTypes[hostPort] = portType;
|
796
796
|
});
|
797
797
|
lodash_1.default.forEach(inspectResult.HostConfig.PortBindings, (portBindings, containerPortSpec) => {
|
@@ -2,7 +2,7 @@
|
|
2
2
|
* Copyright 2023 Kapeta Inc.
|
3
3
|
* SPDX-License-Identifier: BUSL-1.1
|
4
4
|
*/
|
5
|
-
import { InstanceInfo, LogEntry } from './types';
|
5
|
+
import { EnvironmentType, InstanceInfo, LogEntry, OperatorInstanceInfo } from './types';
|
6
6
|
import { Task } from './taskManager';
|
7
7
|
export declare class InstanceManager {
|
8
8
|
private _interval;
|
@@ -27,6 +27,7 @@ export declare class InstanceManager {
|
|
27
27
|
stop(systemId: string, instanceId: string): Promise<void>;
|
28
28
|
private stopInner;
|
29
29
|
stopAllForPlan(systemId: string): Task<void>;
|
30
|
+
getInstanceOperator(systemId: string, instanceId: string, environment?: EnvironmentType): Promise<OperatorInstanceInfo>;
|
30
31
|
start(systemId: string, instanceId: string): Promise<InstanceInfo | Task<InstanceInfo>>;
|
31
32
|
/**
|
32
33
|
* Stops an instance but does not remove it from the list of active instances
|
@@ -323,6 +323,48 @@ class InstanceManager {
|
|
323
323
|
name: `Stopping plan ${systemId}`,
|
324
324
|
});
|
325
325
|
}
|
326
|
+
async getInstanceOperator(systemId, instanceId, environment) {
|
327
|
+
const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
|
328
|
+
if (!blockInstance) {
|
329
|
+
throw new Error(`Instance not found: ${systemId}/${instanceId}`);
|
330
|
+
}
|
331
|
+
const blockRef = (0, nodejs_utils_1.normalizeKapetaUri)(blockInstance.block.ref);
|
332
|
+
const block = await assetManager_1.assetManager.getAsset(blockRef, true);
|
333
|
+
if (!block) {
|
334
|
+
throw new Error(`Block not found: ${blockRef}`);
|
335
|
+
}
|
336
|
+
const operatorDefinition = await definitionsManager_1.definitionsManager.getDefinition(block.kind);
|
337
|
+
if (!operatorDefinition?.definition.spec.local) {
|
338
|
+
throw new Error(`Operator block has no local definition: ${blockRef}`);
|
339
|
+
}
|
340
|
+
const localConfig = operatorDefinition.definition.spec.local;
|
341
|
+
let instance = await this.start(systemId, instanceId);
|
342
|
+
if (instance instanceof taskManager_1.Task) {
|
343
|
+
instance = await instance.wait();
|
344
|
+
}
|
345
|
+
const container = await containerManager_1.containerManager.get(instance.pid);
|
346
|
+
if (!container) {
|
347
|
+
throw new Error(`Container not found: ${instance.pid}`);
|
348
|
+
}
|
349
|
+
const portInfo = await container.getPorts();
|
350
|
+
if (!portInfo) {
|
351
|
+
throw new Error(`No ports found for instance: ${instanceId}`);
|
352
|
+
}
|
353
|
+
const hostname = serviceManager_1.serviceManager.getLocalHost(environment);
|
354
|
+
const ports = {};
|
355
|
+
Object.entries(portInfo).forEach(([key, value]) => {
|
356
|
+
ports[key] = {
|
357
|
+
protocol: value.protocol,
|
358
|
+
port: parseInt(value.hostPort),
|
359
|
+
};
|
360
|
+
});
|
361
|
+
return {
|
362
|
+
hostname,
|
363
|
+
ports,
|
364
|
+
credentials: localConfig.credentials,
|
365
|
+
options: localConfig.options,
|
366
|
+
};
|
367
|
+
}
|
326
368
|
async start(systemId, instanceId) {
|
327
369
|
return this.exclusive(systemId, instanceId, async () => {
|
328
370
|
systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
|
@@ -333,7 +375,7 @@ class InstanceManager {
|
|
333
375
|
throw new Error('Block not found: ' + blockRef);
|
334
376
|
}
|
335
377
|
const existingInstance = this.getInstance(systemId, instanceId);
|
336
|
-
if (existingInstance) {
|
378
|
+
if (existingInstance && existingInstance.pid) {
|
337
379
|
if (existingInstance.status === types_1.InstanceStatus.READY) {
|
338
380
|
// Instance is already running
|
339
381
|
return existingInstance;
|
@@ -4,12 +4,12 @@
|
|
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 { EnvironmentType, LocalImageOptions, OperatorInfo } from './types';
|
8
8
|
export declare const KIND_OPERATOR = "core/resource-type-operator";
|
9
9
|
declare class Operator {
|
10
10
|
private readonly _data;
|
11
11
|
constructor(data: DefinitionInfo);
|
12
|
-
getLocalData():
|
12
|
+
getLocalData(): LocalImageOptions;
|
13
13
|
getDefinitionInfo(): DefinitionInfo;
|
14
14
|
getCredentials(): any;
|
15
15
|
}
|
@@ -137,13 +137,8 @@ class OperatorManager {
|
|
137
137
|
const portType = portTypes[i];
|
138
138
|
let containerPortInfo = operatorData.ports[portType];
|
139
139
|
const hostPort = await serviceManager_1.serviceManager.ensureServicePort(systemId, resourceType, portType);
|
140
|
-
|
141
|
-
|
142
|
-
}
|
143
|
-
if (!containerPortInfo.type) {
|
144
|
-
containerPortInfo.type = 'tcp';
|
145
|
-
}
|
146
|
-
const portId = containerPortInfo.port + '/' + containerPortInfo.type;
|
140
|
+
const portInfo = (0, utils_1.toPortInfo)(containerPortInfo);
|
141
|
+
const portId = portInfo.port + '/' + portInfo.type;
|
147
142
|
ports[portId] = {
|
148
143
|
type: portType,
|
149
144
|
hostPort,
|