@kapeta/local-cluster-service 0.6.0 → 0.7.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/.eslintrc.cjs +17 -0
- package/.github/workflows/main.yml +22 -22
- package/.prettierignore +4 -0
- package/.vscode/launch.json +2 -4
- package/CHANGELOG.md +14 -0
- package/definitions.d.ts +17 -35
- package/dist/cjs/index.d.ts +27 -0
- package/dist/cjs/index.js +126 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/src/assetManager.d.ts +31 -0
- package/dist/cjs/src/assetManager.js +153 -0
- package/dist/cjs/src/assets/routes.d.ts +3 -0
- package/dist/cjs/src/assets/routes.js +117 -0
- package/dist/cjs/src/clusterService.d.ts +40 -0
- package/dist/cjs/src/clusterService.js +114 -0
- package/dist/cjs/src/codeGeneratorManager.d.ts +8 -0
- package/dist/cjs/src/codeGeneratorManager.js +53 -0
- package/dist/cjs/src/config/routes.d.ts +3 -0
- package/dist/cjs/src/config/routes.js +126 -0
- package/dist/cjs/src/configManager.d.ts +36 -0
- package/dist/cjs/src/configManager.js +110 -0
- package/dist/cjs/src/containerManager.d.ts +89 -0
- package/dist/cjs/src/containerManager.js +365 -0
- package/dist/cjs/src/filesystem/routes.d.ts +3 -0
- package/dist/cjs/src/filesystem/routes.js +69 -0
- package/dist/cjs/src/filesystemManager.d.ts +15 -0
- package/dist/cjs/src/filesystemManager.js +87 -0
- package/dist/cjs/src/identities/routes.d.ts +3 -0
- package/dist/cjs/src/identities/routes.js +18 -0
- package/dist/cjs/src/instanceManager.d.ts +56 -0
- package/dist/cjs/src/instanceManager.js +424 -0
- package/dist/cjs/src/instances/routes.d.ts +3 -0
- package/dist/cjs/src/instances/routes.js +134 -0
- package/dist/cjs/src/middleware/cors.d.ts +2 -0
- package/dist/cjs/src/middleware/cors.js +10 -0
- package/dist/cjs/src/middleware/kapeta.d.ts +11 -0
- package/dist/cjs/src/middleware/kapeta.js +17 -0
- package/dist/cjs/src/middleware/stringBody.d.ts +5 -0
- package/dist/cjs/src/middleware/stringBody.js +14 -0
- package/dist/cjs/src/networkManager.d.ts +32 -0
- package/dist/cjs/src/networkManager.js +109 -0
- package/dist/cjs/src/operatorManager.d.ts +36 -0
- package/dist/cjs/src/operatorManager.js +165 -0
- package/dist/cjs/src/progressListener.d.ts +20 -0
- package/dist/cjs/src/progressListener.js +91 -0
- package/dist/cjs/src/providerManager.d.ts +9 -0
- package/dist/cjs/src/providerManager.js +51 -0
- package/dist/cjs/src/providers/routes.d.ts +3 -0
- package/dist/cjs/src/providers/routes.js +42 -0
- package/dist/cjs/src/proxy/routes.d.ts +3 -0
- package/dist/cjs/src/proxy/routes.js +111 -0
- package/dist/cjs/src/proxy/types/rest.d.ts +4 -0
- package/dist/cjs/src/proxy/types/rest.js +114 -0
- package/dist/cjs/src/proxy/types/web.d.ts +4 -0
- package/dist/cjs/src/proxy/types/web.js +53 -0
- package/dist/cjs/src/repositoryManager.d.ts +17 -0
- package/dist/cjs/src/repositoryManager.js +215 -0
- package/dist/cjs/src/serviceManager.d.ts +29 -0
- package/dist/cjs/src/serviceManager.js +99 -0
- package/dist/cjs/src/socketManager.d.ts +14 -0
- package/dist/cjs/src/socketManager.js +53 -0
- package/dist/cjs/src/storageService.d.ts +17 -0
- package/dist/cjs/src/storageService.js +74 -0
- package/dist/cjs/src/traffic/routes.d.ts +3 -0
- package/dist/cjs/src/traffic/routes.js +18 -0
- package/dist/cjs/src/types.d.ts +88 -0
- package/dist/cjs/src/types.js +2 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +29 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.js +468 -0
- package/dist/cjs/src/utils/LogData.d.ts +19 -0
- package/dist/cjs/src/utils/LogData.js +43 -0
- package/dist/cjs/src/utils/pathTemplateParser.d.ts +26 -0
- package/dist/cjs/src/utils/pathTemplateParser.js +121 -0
- package/dist/cjs/src/utils/utils.d.ts +1 -0
- package/dist/cjs/src/utils/utils.js +18 -0
- package/dist/cjs/start.d.ts +1 -0
- package/dist/cjs/start.js +12 -0
- package/dist/esm/index.d.ts +27 -0
- package/dist/esm/index.js +121 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/src/assetManager.d.ts +31 -0
- package/{src → dist/esm/src}/assetManager.js +22 -60
- package/dist/esm/src/assets/routes.d.ts +3 -0
- package/{src → dist/esm/src}/assets/routes.js +21 -36
- package/dist/esm/src/clusterService.d.ts +40 -0
- package/{src → dist/esm/src}/clusterService.js +14 -37
- package/dist/esm/src/codeGeneratorManager.d.ts +8 -0
- package/{src → dist/esm/src}/codeGeneratorManager.js +15 -24
- package/dist/esm/src/config/routes.d.ts +3 -0
- package/dist/esm/src/config/routes.js +121 -0
- package/dist/esm/src/configManager.d.ts +36 -0
- package/{src → dist/esm/src}/configManager.js +11 -40
- package/dist/esm/src/containerManager.d.ts +89 -0
- package/{src → dist/esm/src}/containerManager.js +81 -182
- package/dist/esm/src/filesystem/routes.d.ts +3 -0
- package/dist/esm/src/filesystem/routes.js +64 -0
- package/dist/esm/src/filesystemManager.d.ts +15 -0
- package/{src → dist/esm/src}/filesystemManager.js +20 -28
- package/dist/esm/src/identities/routes.d.ts +3 -0
- package/dist/esm/src/identities/routes.js +13 -0
- package/dist/esm/src/instanceManager.d.ts +56 -0
- package/{src → dist/esm/src}/instanceManager.js +94 -175
- package/dist/esm/src/instances/routes.d.ts +3 -0
- package/{src → dist/esm/src}/instances/routes.js +31 -70
- package/dist/esm/src/middleware/cors.d.ts +2 -0
- package/{src → dist/esm/src}/middleware/cors.js +2 -3
- package/dist/esm/src/middleware/kapeta.d.ts +11 -0
- package/{src → dist/esm/src}/middleware/kapeta.js +3 -7
- package/dist/esm/src/middleware/stringBody.d.ts +5 -0
- package/{src → dist/esm/src}/middleware/stringBody.js +2 -3
- package/dist/esm/src/networkManager.d.ts +32 -0
- package/{src → dist/esm/src}/networkManager.js +16 -33
- package/dist/esm/src/operatorManager.d.ts +36 -0
- package/{src → dist/esm/src}/operatorManager.js +35 -91
- package/dist/esm/src/progressListener.d.ts +20 -0
- package/dist/esm/src/progressListener.js +88 -0
- package/dist/esm/src/providerManager.d.ts +9 -0
- package/dist/esm/src/providerManager.js +45 -0
- package/dist/esm/src/providers/routes.d.ts +3 -0
- package/{src → dist/esm/src}/providers/routes.js +10 -16
- package/dist/esm/src/proxy/routes.d.ts +3 -0
- package/dist/esm/src/proxy/routes.js +106 -0
- package/dist/esm/src/proxy/types/rest.d.ts +4 -0
- package/dist/esm/src/proxy/types/rest.js +107 -0
- package/dist/esm/src/proxy/types/web.d.ts +4 -0
- package/{src → dist/esm/src}/proxy/types/web.js +13 -35
- package/dist/esm/src/repositoryManager.d.ts +17 -0
- package/dist/esm/src/repositoryManager.js +209 -0
- package/dist/esm/src/serviceManager.d.ts +29 -0
- package/{src → dist/esm/src}/serviceManager.js +12 -42
- package/dist/esm/src/socketManager.d.ts +14 -0
- package/{src → dist/esm/src}/socketManager.js +19 -23
- package/dist/esm/src/storageService.d.ts +17 -0
- package/{src → dist/esm/src}/storageService.js +8 -27
- package/dist/esm/src/traffic/routes.d.ts +3 -0
- package/{src → dist/esm/src}/traffic/routes.js +4 -9
- package/dist/esm/src/types.d.ts +88 -0
- package/dist/esm/src/types.js +1 -0
- package/dist/esm/src/utils/BlockInstanceRunner.d.ts +29 -0
- package/{src → dist/esm/src}/utils/BlockInstanceRunner.js +137 -256
- package/dist/esm/src/utils/LogData.d.ts +19 -0
- package/{src → dist/esm/src}/utils/LogData.js +11 -22
- package/dist/esm/src/utils/pathTemplateParser.d.ts +26 -0
- package/{src → dist/esm/src}/utils/pathTemplateParser.js +21 -40
- package/dist/esm/src/utils/utils.d.ts +1 -0
- package/dist/esm/src/utils/utils.js +11 -0
- package/dist/esm/start.d.ts +1 -0
- package/dist/esm/start.js +7 -0
- package/index.ts +147 -0
- package/package.json +106 -74
- package/src/assetManager.ts +191 -0
- package/src/assets/routes.ts +132 -0
- package/src/clusterService.ts +134 -0
- package/src/codeGeneratorManager.ts +57 -0
- package/src/config/routes.ts +159 -0
- package/src/configManager.ts +148 -0
- package/src/containerManager.ts +466 -0
- package/src/filesystem/routes.ts +74 -0
- package/src/filesystemManager.ts +93 -0
- package/src/identities/routes.ts +20 -0
- package/src/instanceManager.ts +503 -0
- package/src/instances/routes.ts +164 -0
- package/src/middleware/cors.ts +9 -0
- package/src/middleware/kapeta.ts +27 -0
- package/src/middleware/stringBody.ts +16 -0
- package/src/networkManager.ts +137 -0
- package/src/operatorManager.ts +221 -0
- package/src/progressListener.ts +102 -0
- package/src/{providerManager.js → providerManager.ts} +15 -31
- package/src/providers/routes.ts +46 -0
- package/src/proxy/routes.ts +148 -0
- package/src/proxy/types/{rest.js → rest.ts} +30 -30
- package/src/proxy/types/web.ts +60 -0
- package/src/{repositoryManager.js → repositoryManager.ts} +45 -73
- package/src/serviceManager.ts +120 -0
- package/src/socketManager.ts +57 -0
- package/src/storageService.ts +88 -0
- package/src/traffic/routes.ts +18 -0
- package/src/types.ts +97 -0
- package/src/utils/BlockInstanceRunner.ts +555 -0
- package/src/utils/LogData.ts +47 -0
- package/src/utils/pathTemplateParser.ts +138 -0
- package/src/utils/utils.ts +12 -0
- package/start.ts +8 -0
- package/tsconfig.json +13 -0
- package/index.js +0 -127
- package/src/config/routes.js +0 -160
- package/src/filesystem/routes.js +0 -74
- package/src/identities/routes.js +0 -19
- package/src/progressListener.js +0 -82
- package/src/proxy/routes.js +0 -126
- package/src/utils/utils.js +0 -13
- package/start.js +0 -7
@@ -0,0 +1,164 @@
|
|
1
|
+
import Router from 'express-promise-router';
|
2
|
+
import { instanceManager } from '../instanceManager';
|
3
|
+
import { serviceManager } from '../serviceManager';
|
4
|
+
import { corsHandler } from '../middleware/cors';
|
5
|
+
import { NextFunction, Request, Response } from 'express';
|
6
|
+
import { kapetaHeaders, KapetaRequest } from '../middleware/kapeta';
|
7
|
+
import { stringBody } from '../middleware/stringBody';
|
8
|
+
import { EnvironmentType, KapetaBodyRequest } from '../types';
|
9
|
+
|
10
|
+
const router = Router();
|
11
|
+
router.use('/', corsHandler);
|
12
|
+
router.use('/', kapetaHeaders);
|
13
|
+
/**
|
14
|
+
* Get all instances
|
15
|
+
*/
|
16
|
+
router.get('/', (req: Request, res: Response) => {
|
17
|
+
res.send(instanceManager.getInstances());
|
18
|
+
});
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Get all instances
|
22
|
+
*/
|
23
|
+
router.get('/:systemId/instances', (req: Request, res: Response) => {
|
24
|
+
res.send(instanceManager.getInstancesForPlan(req.params.systemId));
|
25
|
+
});
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Start all instances in a plan
|
29
|
+
*/
|
30
|
+
router.post('/:systemId/start', async (req: Request, res: Response) => {
|
31
|
+
const processes = await instanceManager.createProcessesForPlan(req.params.systemId);
|
32
|
+
|
33
|
+
res.status(202).send({
|
34
|
+
ok: true,
|
35
|
+
processes: processes.map((p) => {
|
36
|
+
return { pid: p.pid, type: p.type };
|
37
|
+
}),
|
38
|
+
});
|
39
|
+
});
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Stop all instances in plan
|
43
|
+
*/
|
44
|
+
router.post('/:systemId/stop', async (req: Request, res: Response) => {
|
45
|
+
await instanceManager.stopAllForPlan(req.params.systemId);
|
46
|
+
|
47
|
+
res.status(202).send({
|
48
|
+
ok: true,
|
49
|
+
});
|
50
|
+
});
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Start single instance in a plan
|
54
|
+
*/
|
55
|
+
router.post('/:systemId/:instanceId/start', async (req: Request, res: Response) => {
|
56
|
+
const process = await instanceManager.createProcess(req.params.systemId, req.params.instanceId);
|
57
|
+
|
58
|
+
res.status(202).send({
|
59
|
+
ok: true,
|
60
|
+
pid: process.pid,
|
61
|
+
type: process.type,
|
62
|
+
});
|
63
|
+
});
|
64
|
+
|
65
|
+
/**
|
66
|
+
* Stop single instance in a plan
|
67
|
+
*/
|
68
|
+
router.post('/:systemId/:instanceId/stop', async (req: Request, res: Response) => {
|
69
|
+
await instanceManager.stopProcess(req.params.systemId, req.params.instanceId);
|
70
|
+
|
71
|
+
res.status(202).send({ ok: true });
|
72
|
+
});
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Get logs for instance in a plan
|
76
|
+
*/
|
77
|
+
router.get('/:systemId/:instanceId/logs', (req: Request, res: Response) => {
|
78
|
+
const processInfo = instanceManager.getProcessForInstance(req.params.systemId, req.params.instanceId);
|
79
|
+
if (!processInfo) {
|
80
|
+
res.status(404).send({ ok: false });
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
|
84
|
+
res.status(202).send({
|
85
|
+
logs: processInfo.logs(),
|
86
|
+
});
|
87
|
+
});
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Get public address for instance in a plan if available
|
91
|
+
*/
|
92
|
+
router.get('/:systemId/:instanceId/address/public', (req: Request, res: Response) => {
|
93
|
+
const instance = instanceManager.getInstance(req.params.systemId, req.params.instanceId);
|
94
|
+
if (!instance) {
|
95
|
+
res.status(404).send({ ok: false });
|
96
|
+
return;
|
97
|
+
}
|
98
|
+
|
99
|
+
if (!instance.address) {
|
100
|
+
res.status(400).send({ error: `Instance does not have an address. Make sure it's running.` });
|
101
|
+
return;
|
102
|
+
}
|
103
|
+
|
104
|
+
res.status(200).send(instance.address);
|
105
|
+
});
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Get public address for particular resource on instance in a plan if available
|
109
|
+
*/
|
110
|
+
router.get(
|
111
|
+
'/:systemId/:instanceId/provider/:portType/:resourceName/address/public',
|
112
|
+
(req: KapetaRequest, res: Response) => {
|
113
|
+
res.send(
|
114
|
+
serviceManager.getConsumerAddress(
|
115
|
+
req.params.systemId,
|
116
|
+
req.params.instanceId,
|
117
|
+
req.params.resourceName,
|
118
|
+
req.params.portType,
|
119
|
+
req.kapeta?.environment
|
120
|
+
)
|
121
|
+
);
|
122
|
+
}
|
123
|
+
);
|
124
|
+
|
125
|
+
router.use('/', stringBody);
|
126
|
+
router.use('/', (req: KapetaBodyRequest, res: Response, next: NextFunction) => {
|
127
|
+
if (!req.kapeta!.blockRef) {
|
128
|
+
res.status(400).send({ error: 'Missing X-Kapeta-Block header.' });
|
129
|
+
return;
|
130
|
+
}
|
131
|
+
next();
|
132
|
+
});
|
133
|
+
|
134
|
+
/**
|
135
|
+
* Updates the full configuration for a given service.
|
136
|
+
*/
|
137
|
+
router.put('/', async (req: KapetaBodyRequest, res: Response) => {
|
138
|
+
let instance = req.stringBody ? JSON.parse(req.stringBody) : null;
|
139
|
+
if (req.kapeta!.environment === 'docker') {
|
140
|
+
//A bit hacky but we want to avoid overwriting the docker PID with a process PID
|
141
|
+
const oldInstance = instanceManager.getInstance(req.kapeta!.systemId, req.kapeta!.instanceId);
|
142
|
+
if (oldInstance) {
|
143
|
+
instance.pid = oldInstance.pid;
|
144
|
+
}
|
145
|
+
instance.type = 'docker';
|
146
|
+
} else if (req.kapeta!.environment === 'process') {
|
147
|
+
instance.type = 'process';
|
148
|
+
}
|
149
|
+
|
150
|
+
await instanceManager.registerInstance(req.kapeta!.systemId, req.kapeta!.instanceId, instance);
|
151
|
+
|
152
|
+
res.status(202).send({ ok: true });
|
153
|
+
});
|
154
|
+
|
155
|
+
/**
|
156
|
+
* Delete instance
|
157
|
+
*/
|
158
|
+
router.delete('/', async (req: KapetaRequest, res: Response) => {
|
159
|
+
await instanceManager.setInstanceAsStopped(req.kapeta!.systemId, req.kapeta!.instanceId);
|
160
|
+
|
161
|
+
res.status(202).send({ ok: true });
|
162
|
+
});
|
163
|
+
|
164
|
+
export default router;
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
2
|
+
|
3
|
+
export function corsHandler(req: Request, res: Response, next: NextFunction) {
|
4
|
+
res.set('Access-Control-Allow-Origin', req.headers.origin);
|
5
|
+
res.set('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, HEAD, PATCH');
|
6
|
+
res.set('Access-Control-Allow-Headers', '*');
|
7
|
+
|
8
|
+
next();
|
9
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
2
|
+
import { EnvironmentType } from '../types';
|
3
|
+
|
4
|
+
export interface KapetaRequest extends Request {
|
5
|
+
kapeta?: {
|
6
|
+
blockRef: string;
|
7
|
+
instanceId: string;
|
8
|
+
systemId: string;
|
9
|
+
environment?: EnvironmentType;
|
10
|
+
};
|
11
|
+
}
|
12
|
+
|
13
|
+
export function kapetaHeaders(req: KapetaRequest, res: Response, next: NextFunction) {
|
14
|
+
let blockRef: string = req.headers['x-kapeta-block'] as string;
|
15
|
+
let systemId: string = req.headers['x-kapeta-system'] as string;
|
16
|
+
let instanceId: string = req.headers['x-kapeta-instance'] as string;
|
17
|
+
let environment: string = req.headers['x-kapeta-environment'] as string;
|
18
|
+
|
19
|
+
req.kapeta = {
|
20
|
+
blockRef,
|
21
|
+
instanceId,
|
22
|
+
systemId,
|
23
|
+
environment: environment ? (environment as EnvironmentType) : undefined,
|
24
|
+
};
|
25
|
+
|
26
|
+
next();
|
27
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
2
|
+
|
3
|
+
export type StringBodyRequest = Request<any> & {
|
4
|
+
stringBody?: string;
|
5
|
+
};
|
6
|
+
|
7
|
+
export function stringBody(req: StringBodyRequest, res: Response, next: NextFunction) {
|
8
|
+
// push the data to body
|
9
|
+
const body: Buffer[] = [];
|
10
|
+
req.on('data', (chunk) => {
|
11
|
+
body.push(chunk);
|
12
|
+
}).on('end', () => {
|
13
|
+
req.stringBody = Buffer.concat(body).toString();
|
14
|
+
next();
|
15
|
+
});
|
16
|
+
}
|
@@ -0,0 +1,137 @@
|
|
1
|
+
import uuid from 'node-uuid';
|
2
|
+
import { Connection, SimpleRequest, SimpleResponse } from './types';
|
3
|
+
import express from 'express';
|
4
|
+
|
5
|
+
class NetworkManager {
|
6
|
+
private _connections: { [systemId: string]: { [connectionId: string]: Traffic[] } };
|
7
|
+
private _sources: { [systemId: string]: { [instanceId: string]: Traffic[] } };
|
8
|
+
private _targets: { [systemId: string]: { [instanceId: string]: Traffic[] } };
|
9
|
+
|
10
|
+
static toConnectionId(connection: Connection) {
|
11
|
+
return [
|
12
|
+
connection.provider.blockId,
|
13
|
+
connection.provider.resourceName,
|
14
|
+
connection.consumer.blockId,
|
15
|
+
connection.consumer.resourceName,
|
16
|
+
].join('_');
|
17
|
+
}
|
18
|
+
|
19
|
+
constructor() {
|
20
|
+
this._connections = {};
|
21
|
+
this._sources = {};
|
22
|
+
this._targets = {};
|
23
|
+
}
|
24
|
+
|
25
|
+
_ensureSystem(systemId: string) {
|
26
|
+
if (!this._connections[systemId]) {
|
27
|
+
this._connections[systemId] = {};
|
28
|
+
}
|
29
|
+
|
30
|
+
if (!this._sources[systemId]) {
|
31
|
+
this._sources[systemId] = {};
|
32
|
+
}
|
33
|
+
|
34
|
+
if (!this._targets[systemId]) {
|
35
|
+
this._targets[systemId] = {};
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
_ensureConnection(systemId: string, connectionId: string) {
|
40
|
+
this._ensureSystem(systemId);
|
41
|
+
|
42
|
+
if (!this._connections[systemId][connectionId]) {
|
43
|
+
this._connections[systemId][connectionId] = [];
|
44
|
+
}
|
45
|
+
|
46
|
+
return this._connections[systemId][connectionId];
|
47
|
+
}
|
48
|
+
|
49
|
+
_ensureSource(systemId: string, sourceBlockInstanceId: string) {
|
50
|
+
this._ensureSystem(systemId);
|
51
|
+
|
52
|
+
if (!this._sources[systemId][sourceBlockInstanceId]) {
|
53
|
+
this._sources[systemId][sourceBlockInstanceId] = [];
|
54
|
+
}
|
55
|
+
|
56
|
+
return this._sources[systemId][sourceBlockInstanceId];
|
57
|
+
}
|
58
|
+
|
59
|
+
_ensureTarget(systemId: string, targetBlockInstanceId: string) {
|
60
|
+
this._ensureSystem(systemId);
|
61
|
+
|
62
|
+
if (!this._targets[systemId][targetBlockInstanceId]) {
|
63
|
+
this._targets[systemId][targetBlockInstanceId] = [];
|
64
|
+
}
|
65
|
+
|
66
|
+
return this._targets[systemId][targetBlockInstanceId];
|
67
|
+
}
|
68
|
+
|
69
|
+
addRequest(
|
70
|
+
systemId: string,
|
71
|
+
connection: Connection,
|
72
|
+
request: SimpleRequest,
|
73
|
+
consumerMethodId?: string,
|
74
|
+
providerMethodId?: string
|
75
|
+
) {
|
76
|
+
const traffic = new Traffic(connection, request, consumerMethodId, providerMethodId);
|
77
|
+
|
78
|
+
this._ensureConnection(systemId, traffic.connectionId).push(traffic);
|
79
|
+
this._ensureSource(systemId, connection.provider.blockId).push(traffic);
|
80
|
+
this._ensureTarget(systemId, connection.consumer.blockId).push(traffic);
|
81
|
+
|
82
|
+
return traffic;
|
83
|
+
}
|
84
|
+
|
85
|
+
getTrafficForConnection(systemId: string, connectionId: string) {
|
86
|
+
return this._ensureConnection(systemId, connectionId);
|
87
|
+
}
|
88
|
+
|
89
|
+
getTrafficForSource(systemId: string, blockInstanceId: string) {
|
90
|
+
return this._ensureSource(systemId, blockInstanceId);
|
91
|
+
}
|
92
|
+
|
93
|
+
getTrafficForTarget(systemId: string, blockInstanceId: string) {
|
94
|
+
return this._ensureTarget(systemId, blockInstanceId);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
class Traffic {
|
99
|
+
public readonly id: string;
|
100
|
+
public readonly connectionId: string;
|
101
|
+
public readonly consumerMethodId: string | undefined;
|
102
|
+
public readonly providerMethodId: string | undefined;
|
103
|
+
public readonly created: number;
|
104
|
+
public readonly request: SimpleRequest;
|
105
|
+
public ended: null | number;
|
106
|
+
public error: null | string;
|
107
|
+
public response: SimpleResponse | null;
|
108
|
+
|
109
|
+
constructor(connection: Connection, request: SimpleRequest, consumerMethodId?: string, providerMethodId?: string) {
|
110
|
+
this.id = uuid.v4();
|
111
|
+
this.connectionId = NetworkManager.toConnectionId(connection);
|
112
|
+
this.consumerMethodId = consumerMethodId;
|
113
|
+
this.providerMethodId = providerMethodId;
|
114
|
+
this.request = request;
|
115
|
+
this.response = null;
|
116
|
+
this.error = null;
|
117
|
+
this.ended = null;
|
118
|
+
this.created = new Date().getTime();
|
119
|
+
}
|
120
|
+
|
121
|
+
asError(err: any) {
|
122
|
+
this.ended = new Date().getTime();
|
123
|
+
this.response = {
|
124
|
+
code: 0,
|
125
|
+
headers: {},
|
126
|
+
body: null,
|
127
|
+
};
|
128
|
+
this.error = err + '';
|
129
|
+
}
|
130
|
+
|
131
|
+
withResponse(response: SimpleResponse) {
|
132
|
+
this.ended = new Date().getTime();
|
133
|
+
this.response = response;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
export const networkManager = new NetworkManager();
|
@@ -0,0 +1,221 @@
|
|
1
|
+
import ClusterConfiguration, { DefinitionInfo } from '@kapeta/local-cluster-config';
|
2
|
+
import _ from 'lodash';
|
3
|
+
import Path from 'path';
|
4
|
+
import md5 from 'md5';
|
5
|
+
import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
6
|
+
import { serviceManager } from './serviceManager';
|
7
|
+
import { storageService } from './storageService';
|
8
|
+
import { ContainerInfo, containerManager } from './containerManager';
|
9
|
+
import FSExtra from 'fs-extra';
|
10
|
+
import { AnyMap, EnvironmentType, OperatorInfo } from './types';
|
11
|
+
import { BlockInstance, BlockResource, Resource } from '@kapeta/schemas';
|
12
|
+
|
13
|
+
const KIND_OPERATOR = 'core/resource-type-operator';
|
14
|
+
|
15
|
+
class Operator {
|
16
|
+
private _data: any;
|
17
|
+
constructor(data: any) {
|
18
|
+
this._data = data;
|
19
|
+
}
|
20
|
+
|
21
|
+
getData() {
|
22
|
+
return this._data;
|
23
|
+
}
|
24
|
+
|
25
|
+
getCredentials() {
|
26
|
+
return this._data.credentials;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
class OperatorManager {
|
31
|
+
private _mountDir: string;
|
32
|
+
|
33
|
+
constructor() {
|
34
|
+
this._mountDir = Path.join(storageService.getKapetaBasedir(), 'mounts');
|
35
|
+
|
36
|
+
FSExtra.mkdirpSync(this._mountDir);
|
37
|
+
}
|
38
|
+
|
39
|
+
_getMountPoint(operatorType: string, mountName: string) {
|
40
|
+
return Path.join(this._mountDir, operatorType, mountName);
|
41
|
+
}
|
42
|
+
|
43
|
+
/**
|
44
|
+
* Get operator definition for resource type
|
45
|
+
*
|
46
|
+
* @param {string} resourceType
|
47
|
+
* @param {string} version
|
48
|
+
* @return {Operator}
|
49
|
+
*/
|
50
|
+
getOperator(resourceType: string, version: string) {
|
51
|
+
const operators = ClusterConfiguration.getDefinitions(KIND_OPERATOR);
|
52
|
+
|
53
|
+
const operator: DefinitionInfo | undefined = operators.find(
|
54
|
+
(operator) =>
|
55
|
+
operator.definition &&
|
56
|
+
operator.definition.metadata &&
|
57
|
+
operator.definition.metadata.name &&
|
58
|
+
operator.definition.metadata.name.toLowerCase() === resourceType.toLowerCase() &&
|
59
|
+
operator.version === version
|
60
|
+
);
|
61
|
+
|
62
|
+
if (!operator) {
|
63
|
+
throw new Error(`Unknown resource type: ${resourceType}:${version}`);
|
64
|
+
}
|
65
|
+
|
66
|
+
if (!operator.definition.spec || !operator.definition.spec.local) {
|
67
|
+
throw new Error(`Operator missing local definition: ${resourceType}:${version}`);
|
68
|
+
}
|
69
|
+
|
70
|
+
return new Operator(operator.definition.spec.local);
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Get information about a specific consumed resource
|
75
|
+
*/
|
76
|
+
async getConsumerResourceInfo(
|
77
|
+
systemId: string,
|
78
|
+
fromServiceId: string,
|
79
|
+
resourceType: string,
|
80
|
+
portType: string,
|
81
|
+
name: string,
|
82
|
+
environment?: EnvironmentType
|
83
|
+
): Promise<OperatorInfo> {
|
84
|
+
const plans = ClusterConfiguration.getDefinitions('core/plan');
|
85
|
+
|
86
|
+
const planUri = parseKapetaUri(systemId);
|
87
|
+
const currentPlan = plans.find(
|
88
|
+
(plan) => plan.definition.metadata.name === planUri.fullName && plan.version === planUri.version
|
89
|
+
);
|
90
|
+
if (!currentPlan) {
|
91
|
+
throw new Error(`Unknown plan: ${systemId}`);
|
92
|
+
}
|
93
|
+
|
94
|
+
const currentInstance = currentPlan.definition.spec.blocks?.find(
|
95
|
+
(instance: BlockInstance) => instance.id === fromServiceId
|
96
|
+
);
|
97
|
+
if (!currentInstance) {
|
98
|
+
throw new Error(`Unknown instance: ${fromServiceId} in plan ${systemId}`);
|
99
|
+
}
|
100
|
+
|
101
|
+
const blockUri = parseKapetaUri(currentInstance.block.ref);
|
102
|
+
const blockDefinition = ClusterConfiguration.getDefinitions().find(
|
103
|
+
(definition) =>
|
104
|
+
definition.version === blockUri.version && definition.definition.metadata.name === blockUri.fullName
|
105
|
+
);
|
106
|
+
|
107
|
+
if (!blockDefinition) {
|
108
|
+
throw new Error(`Unknown block: ${currentInstance.block.ref} in plan ${systemId}`);
|
109
|
+
}
|
110
|
+
|
111
|
+
const blockResource = blockDefinition.definition.spec?.consumers?.find(
|
112
|
+
(resource: Resource) => resource.metadata.name === name
|
113
|
+
);
|
114
|
+
if (!blockResource) {
|
115
|
+
throw new Error(`Unknown resource: ${name} in block ${currentInstance.block.ref} in plan ${systemId}`);
|
116
|
+
}
|
117
|
+
|
118
|
+
const kindUri = parseKapetaUri(blockResource.kind);
|
119
|
+
const operator = this.getOperator(resourceType, kindUri.version);
|
120
|
+
const credentials = operator.getCredentials();
|
121
|
+
const container = await this.ensureResource(systemId, resourceType, kindUri.version);
|
122
|
+
const portInfo = await container.getPort(portType);
|
123
|
+
|
124
|
+
if (!portInfo) {
|
125
|
+
throw new Error('Unknown resource port type : ' + resourceType + '#' + portType);
|
126
|
+
}
|
127
|
+
|
128
|
+
const dbName = name + '_' + fromServiceId.replace(/[^a-z0-9]/gi, '');
|
129
|
+
|
130
|
+
return {
|
131
|
+
host: environment === 'docker' ? 'host.docker.internal' : '127.0.0.1',
|
132
|
+
port: portInfo.hostPort,
|
133
|
+
type: portType,
|
134
|
+
protocol: portInfo.protocol,
|
135
|
+
options: {
|
136
|
+
dbName,
|
137
|
+
},
|
138
|
+
credentials,
|
139
|
+
};
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Ensure we have a running operator of given type
|
144
|
+
*
|
145
|
+
* @param {string} systemId
|
146
|
+
* @param {string} resourceType
|
147
|
+
* @param {string} version
|
148
|
+
* @return {Promise<ContainerInfo>}
|
149
|
+
*/
|
150
|
+
async ensureResource(systemId: string, resourceType: string, version: string): Promise<ContainerInfo> {
|
151
|
+
const operator = this.getOperator(resourceType, version);
|
152
|
+
|
153
|
+
const operatorData = operator.getData();
|
154
|
+
|
155
|
+
const portTypes = Object.keys(operatorData.ports);
|
156
|
+
|
157
|
+
portTypes.sort();
|
158
|
+
|
159
|
+
const containerBaseName = 'kapeta-resource';
|
160
|
+
|
161
|
+
const nameParts = [resourceType.toLowerCase()];
|
162
|
+
|
163
|
+
const ports: AnyMap = {};
|
164
|
+
|
165
|
+
for (let i = 0; i < portTypes.length; i++) {
|
166
|
+
const portType = portTypes[i];
|
167
|
+
let containerPortInfo = operatorData.ports[portType];
|
168
|
+
const hostPort = await serviceManager.ensureServicePort(resourceType, portType);
|
169
|
+
|
170
|
+
if (typeof containerPortInfo === 'number' || typeof containerPortInfo === 'string') {
|
171
|
+
containerPortInfo = { port: containerPortInfo, type: 'tcp' };
|
172
|
+
}
|
173
|
+
|
174
|
+
if (!containerPortInfo.type) {
|
175
|
+
containerPortInfo.type = 'tcp';
|
176
|
+
}
|
177
|
+
|
178
|
+
const portId = containerPortInfo.port + '/' + containerPortInfo.type;
|
179
|
+
nameParts.push(portType + '-' + portId + '-' + hostPort);
|
180
|
+
|
181
|
+
ports[portId] = {
|
182
|
+
type: portType,
|
183
|
+
hostPort,
|
184
|
+
};
|
185
|
+
}
|
186
|
+
|
187
|
+
const mounts = containerManager.createMounts(resourceType, operatorData.mounts);
|
188
|
+
|
189
|
+
const containerName = containerBaseName + '-' + md5(nameParts.join('_'));
|
190
|
+
let container = await containerManager.get(containerName);
|
191
|
+
|
192
|
+
const isRunning = container ? await container.isRunning() : false;
|
193
|
+
if (container && !isRunning) {
|
194
|
+
await container.start();
|
195
|
+
}
|
196
|
+
|
197
|
+
if (!container) {
|
198
|
+
container = await containerManager.run(operatorData.image, containerName, {
|
199
|
+
mounts,
|
200
|
+
ports,
|
201
|
+
health: operatorData.health,
|
202
|
+
env: operatorData.env,
|
203
|
+
cmd: operatorData.cmd,
|
204
|
+
});
|
205
|
+
}
|
206
|
+
|
207
|
+
try {
|
208
|
+
if (operatorData.health) {
|
209
|
+
await containerManager.waitForHealthy(container.native);
|
210
|
+
} else {
|
211
|
+
await containerManager.waitForReady(container.native);
|
212
|
+
}
|
213
|
+
} catch (e: any) {
|
214
|
+
console.error(e.message);
|
215
|
+
}
|
216
|
+
|
217
|
+
return container;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
export const operatorManager = new OperatorManager();
|
@@ -0,0 +1,102 @@
|
|
1
|
+
import { spawn } from 'child_process';
|
2
|
+
import { SocketManager, socketManager } from './socketManager';
|
3
|
+
class ProgressListener {
|
4
|
+
private socketManager: SocketManager;
|
5
|
+
|
6
|
+
constructor(socketManager: SocketManager) {
|
7
|
+
this.socketManager = socketManager;
|
8
|
+
}
|
9
|
+
|
10
|
+
run(command: string, directory?: string): Promise<{ exit: number; signal: NodeJS.Signals | null }> {
|
11
|
+
this.socketManager.emit(`install`, 'install:log', {
|
12
|
+
type: 'info',
|
13
|
+
message: `Running command "${command}"`,
|
14
|
+
});
|
15
|
+
|
16
|
+
return new Promise((resolve, reject) => {
|
17
|
+
const child = spawn(command, {
|
18
|
+
cwd: directory ? directory : process.cwd(),
|
19
|
+
detached: true,
|
20
|
+
shell: true,
|
21
|
+
});
|
22
|
+
|
23
|
+
child.stdout.on('data', (data) => {
|
24
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: data.toString() });
|
25
|
+
});
|
26
|
+
|
27
|
+
child.stderr.on('data', (data) => {
|
28
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: data.toString() });
|
29
|
+
});
|
30
|
+
|
31
|
+
child.on('exit', (exit, signal) => {
|
32
|
+
if (exit !== 0) {
|
33
|
+
this.socketManager.emit(`install`, 'install:log', {
|
34
|
+
type: 'info',
|
35
|
+
message: `"${command}" failed: "${exit}"`,
|
36
|
+
});
|
37
|
+
reject(new Error(`Command "${command}" exited with code ${exit}`));
|
38
|
+
} else {
|
39
|
+
this.socketManager.emit(`install`, 'install:log', {
|
40
|
+
type: 'info',
|
41
|
+
message: `Command OK: "${command}"`,
|
42
|
+
});
|
43
|
+
resolve({ exit, signal });
|
44
|
+
}
|
45
|
+
});
|
46
|
+
|
47
|
+
child.on('error', (err) => {
|
48
|
+
this.socketManager.emit(`install`, 'install:log', {
|
49
|
+
type: 'info',
|
50
|
+
message: `"${command}" failed: "${err.message}"`,
|
51
|
+
});
|
52
|
+
reject(err);
|
53
|
+
});
|
54
|
+
});
|
55
|
+
}
|
56
|
+
|
57
|
+
async progress(label: string, callback: () => void | Promise<void>) {
|
58
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: `${label}: started` });
|
59
|
+
try {
|
60
|
+
const result = await callback();
|
61
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: `${label}: done` });
|
62
|
+
return result;
|
63
|
+
} catch (e: any) {
|
64
|
+
this.socketManager.emit(`install`, 'install:log', {
|
65
|
+
type: 'info',
|
66
|
+
message: `${label}: failed. ${e.message}`,
|
67
|
+
});
|
68
|
+
throw e;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
async check(message: string, ok: boolean | Promise<boolean> | (() => Promise<boolean>)) {
|
73
|
+
const wasOk = await ok;
|
74
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: `${message}: ${wasOk}` });
|
75
|
+
}
|
76
|
+
|
77
|
+
start(label: string) {
|
78
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: label });
|
79
|
+
}
|
80
|
+
|
81
|
+
showValue(label: string, value: any) {
|
82
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: `${label}: ${value}` });
|
83
|
+
}
|
84
|
+
|
85
|
+
error(msg: string, ...args: any[]) {
|
86
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'error', message: msg });
|
87
|
+
}
|
88
|
+
|
89
|
+
warn(msg: string, ...args: any[]) {
|
90
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'warn', message: msg });
|
91
|
+
}
|
92
|
+
|
93
|
+
info(msg: string, ...args: any[]) {
|
94
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: msg });
|
95
|
+
}
|
96
|
+
|
97
|
+
debug(msg: string, ...args: any[]) {
|
98
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'debug', message: msg });
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
export const progressListener = new ProgressListener(socketManager);
|