@kapeta/local-cluster-service 0.0.60
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/pr-check.yml +18 -0
- package/.github/workflows/publish.yml +20 -0
- package/.vscode/launch.json +17 -0
- package/LICENSE +21 -0
- package/README.md +32 -0
- package/definitions.d.ts +42 -0
- package/index.js +127 -0
- package/package.json +45 -0
- package/src/assetManager.js +249 -0
- package/src/assets/routes.js +133 -0
- package/src/clusterService.js +134 -0
- package/src/codeGeneratorManager.js +40 -0
- package/src/config/routes.js +118 -0
- package/src/configManager.js +128 -0
- package/src/containerManager.js +279 -0
- package/src/filesystem/routes.js +74 -0
- package/src/filesystemManager.js +89 -0
- package/src/identities/routes.js +19 -0
- package/src/instanceManager.js +416 -0
- package/src/instances/routes.js +117 -0
- package/src/middleware/cors.js +7 -0
- package/src/middleware/kapeta.js +20 -0
- package/src/middleware/stringBody.js +11 -0
- package/src/networkManager.js +120 -0
- package/src/operatorManager.js +180 -0
- package/src/providerManager.js +84 -0
- package/src/providers/routes.js +26 -0
- package/src/proxy/routes.js +125 -0
- package/src/proxy/types/rest.js +163 -0
- package/src/proxy/types/web.js +83 -0
- package/src/serviceManager.js +117 -0
- package/src/socketManager.js +50 -0
- package/src/storageService.js +87 -0
- package/src/traffic/routes.js +18 -0
- package/src/utils/pathTemplateParser.js +116 -0
- package/start.js +7 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
const clusterService = require('./clusterService');
|
2
|
+
const storageService = require('./storageService');
|
3
|
+
const _ = require('lodash');
|
4
|
+
|
5
|
+
const DEFAULT_PORT_TYPE = 'rest';
|
6
|
+
|
7
|
+
class ServiceManager {
|
8
|
+
|
9
|
+
constructor() {
|
10
|
+
this._systems = storageService.get("services");
|
11
|
+
if (!this._systems) {
|
12
|
+
this._systems = {};
|
13
|
+
}
|
14
|
+
|
15
|
+
_.forEach(this._systems, (system) => {
|
16
|
+
_.forEach(system, (services) => {
|
17
|
+
_.forEach(services, (portInfo) => {
|
18
|
+
clusterService.reservePort(portInfo.port);
|
19
|
+
});
|
20
|
+
});
|
21
|
+
});
|
22
|
+
}
|
23
|
+
|
24
|
+
_forLocal(port, path) {
|
25
|
+
if (!path) {
|
26
|
+
path = '';
|
27
|
+
}
|
28
|
+
const host = clusterService.getClusterServiceHost();
|
29
|
+
|
30
|
+
if (path.startsWith('/')) {
|
31
|
+
path = path.substr(1);
|
32
|
+
}
|
33
|
+
return `http://${host}:${port}/${path}`;
|
34
|
+
}
|
35
|
+
|
36
|
+
_ensureSystem(systemId) {
|
37
|
+
if (!this._systems[systemId]) {
|
38
|
+
this._systems[systemId] = {};
|
39
|
+
}
|
40
|
+
|
41
|
+
return this._systems[systemId];
|
42
|
+
}
|
43
|
+
|
44
|
+
_ensureService(systemId, serviceId) {
|
45
|
+
|
46
|
+
const system = this._ensureSystem(systemId);
|
47
|
+
|
48
|
+
if (!system[serviceId]) {
|
49
|
+
system[serviceId] = {};
|
50
|
+
}
|
51
|
+
|
52
|
+
return system[serviceId];
|
53
|
+
}
|
54
|
+
|
55
|
+
async ensureServicePort(systemId, blockInstanceId, portType) {
|
56
|
+
if (!portType) {
|
57
|
+
portType = DEFAULT_PORT_TYPE;
|
58
|
+
}
|
59
|
+
|
60
|
+
const service = this._ensureService(systemId, blockInstanceId);
|
61
|
+
|
62
|
+
if (!service[portType]) {
|
63
|
+
const port = await clusterService.getNextAvailablePort();
|
64
|
+
service[portType] = {port};
|
65
|
+
this._save();
|
66
|
+
}
|
67
|
+
|
68
|
+
const portTypeSection = service[portType];
|
69
|
+
|
70
|
+
|
71
|
+
return portTypeSection.port;
|
72
|
+
}
|
73
|
+
|
74
|
+
_save() {
|
75
|
+
storageService.put("services", this._systems);
|
76
|
+
}
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Gets the consumable address of a service block resource
|
80
|
+
*
|
81
|
+
* This returns a local proxy path to allow traffic inspection and control.
|
82
|
+
*
|
83
|
+
* @param {string} systemId
|
84
|
+
* @param {string} consumerInstanceId
|
85
|
+
* @param {string} consumerResourceName
|
86
|
+
* @param {string} portType
|
87
|
+
* @return {string}
|
88
|
+
*/
|
89
|
+
getConsumerAddress(systemId, consumerInstanceId, consumerResourceName, portType) {
|
90
|
+
const port = clusterService.getClusterServicePort();
|
91
|
+
const path = clusterService.getProxyPath(systemId, consumerInstanceId, consumerResourceName, portType);
|
92
|
+
return this._forLocal(port, path);
|
93
|
+
}
|
94
|
+
|
95
|
+
/**
|
96
|
+
* Gets the direct address of a service block
|
97
|
+
*
|
98
|
+
* This returns the actual endpoint address of a service that we're talking to.
|
99
|
+
* For local services this address will be on localhost - for remote services it will
|
100
|
+
* be their remotely available address.
|
101
|
+
*
|
102
|
+
* @param {string} systemId
|
103
|
+
* @param {string} providerInstanceId
|
104
|
+
* @param {string} portType
|
105
|
+
* @return {Promise<string>}
|
106
|
+
*/
|
107
|
+
async getProviderAddress(systemId, providerInstanceId, portType) {
|
108
|
+
const port = await this.ensureServicePort(systemId, providerInstanceId, portType);
|
109
|
+
return this._forLocal(port)
|
110
|
+
}
|
111
|
+
|
112
|
+
getServices() {
|
113
|
+
return this._systems;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
module.exports = new ServiceManager();
|
@@ -0,0 +1,50 @@
|
|
1
|
+
const _ = require('lodash');
|
2
|
+
|
3
|
+
class SocketManager {
|
4
|
+
|
5
|
+
constructor() {
|
6
|
+
this._io = null;
|
7
|
+
this._sockets = [];
|
8
|
+
return this;
|
9
|
+
}
|
10
|
+
|
11
|
+
setIo(io) {
|
12
|
+
console.log("socket ready");
|
13
|
+
this._io = io;
|
14
|
+
|
15
|
+
this._bindIO();
|
16
|
+
}
|
17
|
+
|
18
|
+
emit(context, type, payload) {
|
19
|
+
this._io.to(context).emit(type, {context, payload});
|
20
|
+
}
|
21
|
+
|
22
|
+
_bindIO() {
|
23
|
+
this._io.on('connection', (socket) => this._handleSocketCreated(socket))
|
24
|
+
}
|
25
|
+
|
26
|
+
_handleSocketCreated(socket) {
|
27
|
+
this._bindSocket(socket);
|
28
|
+
this._sockets.push(socket);
|
29
|
+
}
|
30
|
+
|
31
|
+
_bindSocket(socket) {
|
32
|
+
socket.on('disconnect', () => this._handleSocketDestroyed(socket))
|
33
|
+
socket.on('join', (id) => {
|
34
|
+
console.log("socket joined ", id);
|
35
|
+
socket.join(id);
|
36
|
+
})
|
37
|
+
socket.on('leave', (id) => {
|
38
|
+
console.log("socket left ", id);
|
39
|
+
socket.leave(id);
|
40
|
+
})
|
41
|
+
}
|
42
|
+
|
43
|
+
_handleSocketDestroyed(socket) {
|
44
|
+
_.pull(this._sockets, socket);
|
45
|
+
}
|
46
|
+
|
47
|
+
}
|
48
|
+
|
49
|
+
|
50
|
+
module.exports = new SocketManager();
|
@@ -0,0 +1,87 @@
|
|
1
|
+
const _ = require('lodash');
|
2
|
+
const FS = require('fs');
|
3
|
+
const mkdirp = require('mkdirp');
|
4
|
+
const YAML = require('yaml');
|
5
|
+
const ClusterConfiguration = require('@kapeta/local-cluster-config');
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Class that handles reading and writing from local configuration file.
|
9
|
+
*/
|
10
|
+
class StorageService {
|
11
|
+
|
12
|
+
constructor() {
|
13
|
+
this._data = this._readConfig();
|
14
|
+
}
|
15
|
+
|
16
|
+
getKapetaBasedir() {
|
17
|
+
return ClusterConfiguration.getKapetaBasedir();
|
18
|
+
}
|
19
|
+
|
20
|
+
_readConfig() {
|
21
|
+
return ClusterConfiguration.getClusterConfig();
|
22
|
+
}
|
23
|
+
|
24
|
+
_writeConfig() {
|
25
|
+
const configFile = ClusterConfiguration.getClusterConfigFile();
|
26
|
+
|
27
|
+
mkdirp.sync(this.getKapetaBasedir());
|
28
|
+
|
29
|
+
FS.writeFileSync(configFile, YAML.stringify(this._data));
|
30
|
+
}
|
31
|
+
|
32
|
+
section(section, defaultValue) {
|
33
|
+
if (!defaultValue) {
|
34
|
+
defaultValue = {};
|
35
|
+
}
|
36
|
+
if (!this._data[section]) {
|
37
|
+
this._data[section] = defaultValue;
|
38
|
+
this._writeConfig();
|
39
|
+
}
|
40
|
+
|
41
|
+
return this._data[section];
|
42
|
+
}
|
43
|
+
|
44
|
+
put(section, property, value) {
|
45
|
+
if (!_.isString(property)) {
|
46
|
+
this._data[section] = property;
|
47
|
+
this._writeConfig();
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
|
51
|
+
this.section(section)[property] = value;
|
52
|
+
this._writeConfig();
|
53
|
+
}
|
54
|
+
|
55
|
+
get(section, property) {
|
56
|
+
if (!property) {
|
57
|
+
return this.section(section);
|
58
|
+
}
|
59
|
+
|
60
|
+
return this.section(section)[property];
|
61
|
+
}
|
62
|
+
|
63
|
+
contains(section, property) {
|
64
|
+
if (!this._data[section]) {
|
65
|
+
return false;
|
66
|
+
}
|
67
|
+
|
68
|
+
return this._data[section].hasOwnProperty(property);
|
69
|
+
}
|
70
|
+
|
71
|
+
ensure(section, property, value) {
|
72
|
+
if (this.contains(section, property)) {
|
73
|
+
return this.get(section, property);
|
74
|
+
}
|
75
|
+
|
76
|
+
let out = value;
|
77
|
+
if (typeof value === 'function') {
|
78
|
+
out = value();
|
79
|
+
}
|
80
|
+
|
81
|
+
this.put(section, property, out);
|
82
|
+
|
83
|
+
return out;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
module.exports = new StorageService();
|
@@ -0,0 +1,18 @@
|
|
1
|
+
const Router = require('express-promise-router').default;
|
2
|
+
const router = new Router();
|
3
|
+
const networkManager = require('../networkManager');
|
4
|
+
|
5
|
+
router.get('/:systemId/target/:connectionId/', (req, res) => {
|
6
|
+
res.send(networkManager.getTrafficForConnection(req.params.systemId, req.params.connectionId));
|
7
|
+
});
|
8
|
+
|
9
|
+
router.get('/:systemId/source/:blockInstanceId/', (req, res) => {
|
10
|
+
res.send(networkManager.getTrafficForSource(req.params.systemId, req.params.blockInstanceId));
|
11
|
+
});
|
12
|
+
|
13
|
+
router.get('/:systemId/target/:blockInstanceId/', (req, res) => {
|
14
|
+
res.send(networkManager.getTrafficForTarget(req.params.systemId, req.params.blockInstanceId));
|
15
|
+
});
|
16
|
+
|
17
|
+
|
18
|
+
module.exports = router;
|
@@ -0,0 +1,116 @@
|
|
1
|
+
|
2
|
+
const TYPE_VARIABLE = 'variable';
|
3
|
+
const TYPE_PATH = 'path';
|
4
|
+
|
5
|
+
class PathTemplate {
|
6
|
+
constructor(pathTemplate) {
|
7
|
+
if (!pathTemplate.startsWith('/')) {
|
8
|
+
pathTemplate = '/' + pathTemplate;
|
9
|
+
}
|
10
|
+
this._path = pathTemplate;
|
11
|
+
|
12
|
+
this._parts = pathTemplate.split(/{/g).map((part) => {
|
13
|
+
if (part.endsWith('}')) {
|
14
|
+
let regex,
|
15
|
+
value = part.substr(0, part.length - 1);
|
16
|
+
|
17
|
+
[value, regex] = value.split(/:/, 2);
|
18
|
+
|
19
|
+
if (regex) {
|
20
|
+
regex = new RegExp('^' + regex);
|
21
|
+
} else {
|
22
|
+
regex = /^[^\/]+/
|
23
|
+
}
|
24
|
+
|
25
|
+
return {
|
26
|
+
type: TYPE_VARIABLE,
|
27
|
+
value,
|
28
|
+
regex
|
29
|
+
};
|
30
|
+
}
|
31
|
+
|
32
|
+
return {
|
33
|
+
type: TYPE_PATH,
|
34
|
+
value: part
|
35
|
+
};
|
36
|
+
});
|
37
|
+
|
38
|
+
|
39
|
+
}
|
40
|
+
|
41
|
+
get path() {
|
42
|
+
return this._path;
|
43
|
+
}
|
44
|
+
|
45
|
+
matches(path) {
|
46
|
+
return this.parse(path) !== null;
|
47
|
+
}
|
48
|
+
|
49
|
+
parse(path) {
|
50
|
+
if (!path.startsWith('/')) {
|
51
|
+
path = '/' + path;
|
52
|
+
}
|
53
|
+
|
54
|
+
const values = {};
|
55
|
+
|
56
|
+
for(let i = 0 ; i < this._parts.length; i++) {
|
57
|
+
const part = this._parts[i];
|
58
|
+
switch (part.type) {
|
59
|
+
case TYPE_PATH:
|
60
|
+
if (!path.startsWith(part.value)) {
|
61
|
+
return null;
|
62
|
+
}
|
63
|
+
|
64
|
+
path = path.substr(part.value.length);
|
65
|
+
break;
|
66
|
+
case TYPE_VARIABLE:
|
67
|
+
if (!part.regex.test(path)) {
|
68
|
+
return null;
|
69
|
+
}
|
70
|
+
|
71
|
+
const newPath = path.replace(part.regex,'');
|
72
|
+
const value = path.substr(0, path.length - newPath.length);
|
73
|
+
values[part.value] = value;
|
74
|
+
path = newPath;
|
75
|
+
break;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
if (path && path !== '/') {
|
80
|
+
//We did not match all of it
|
81
|
+
return null;
|
82
|
+
}
|
83
|
+
|
84
|
+
return values;
|
85
|
+
}
|
86
|
+
|
87
|
+
create(variables) {
|
88
|
+
return this._parts.map((part) => {
|
89
|
+
switch (part.type) {
|
90
|
+
case TYPE_PATH:
|
91
|
+
return part.value;
|
92
|
+
case TYPE_VARIABLE:
|
93
|
+
if (variables[part.value] === undefined ||
|
94
|
+
variables[part.value] === null ) {
|
95
|
+
return ''
|
96
|
+
}
|
97
|
+
|
98
|
+
return variables[part.value];
|
99
|
+
}
|
100
|
+
}).join('');
|
101
|
+
}
|
102
|
+
|
103
|
+
toString() {
|
104
|
+
return 'tmpl: ' + this.path
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Parses a path into a RESTPath
|
110
|
+
* @param {string} path
|
111
|
+
*/
|
112
|
+
function pathTemplateParser(path) {
|
113
|
+
return new PathTemplate(path);
|
114
|
+
}
|
115
|
+
|
116
|
+
module.exports = pathTemplateParser;
|
package/start.js
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
const localClusterService = require('./index.js');
|
2
|
+
|
3
|
+
localClusterService.start()
|
4
|
+
.then(({host,port}) => console.log('Listening on port %s:%s', host, port))
|
5
|
+
.catch(e => {
|
6
|
+
console.error('Failed to start local cluster due to an error:\n\t - %s', e.toString())
|
7
|
+
});
|