@kapeta/local-cluster-service 0.5.8 → 0.5.10
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/package.json +2 -1
- package/src/assetManager.js +47 -37
- package/src/assets/routes.js +1 -1
- package/src/instanceManager.js +3 -3
- package/src/proxy/routes.js +7 -8
- package/src/proxy/types/rest.js +4 -4
- package/src/proxy/types/web.js +15 -28
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.5.10](https://github.com/kapetacom/local-cluster-service/compare/v0.5.9...v0.5.10) (2023-06-19)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* delete asset via ref ([6b5a968](https://github.com/kapetacom/local-cluster-service/commit/6b5a968a029753baea3a651a0538468086cda665))
|
7
|
+
|
8
|
+
## [0.5.9](https://github.com/kapetacom/local-cluster-service/compare/v0.5.8...v0.5.9) (2023-06-18)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* Resolves various issues when proxiying HTTP requests ([#34](https://github.com/kapetacom/local-cluster-service/issues/34)) ([cb9a472](https://github.com/kapetacom/local-cluster-service/commit/cb9a472e560412a85cea20a1dc9083826797ac95))
|
14
|
+
|
1
15
|
## [0.5.8](https://github.com/kapetacom/local-cluster-service/compare/v0.5.7...v0.5.8) (2023-06-18)
|
2
16
|
|
3
17
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@kapeta/local-cluster-service",
|
3
|
-
"version": "0.5.
|
3
|
+
"version": "0.5.10",
|
4
4
|
"description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
|
5
5
|
"main": "index.js",
|
6
6
|
"repository": {
|
@@ -34,6 +34,7 @@
|
|
34
34
|
"lodash": "^4.17.15",
|
35
35
|
"md5": "2.2.1",
|
36
36
|
"mkdirp": "0.5.1",
|
37
|
+
"node-cache": "^5.1.2",
|
37
38
|
"node-docker-api": "1.1.22",
|
38
39
|
"node-uuid": "^1.4.8",
|
39
40
|
"request": "2.88.2",
|
package/src/assetManager.js
CHANGED
@@ -1,13 +1,14 @@
|
|
1
|
-
const Path = require(
|
1
|
+
const Path = require('node:path');
|
2
2
|
const FS = require('node:fs');
|
3
3
|
const FSExtra = require('fs-extra');
|
4
4
|
const YAML = require('yaml');
|
5
5
|
const ClusterConfiguration = require('@kapeta/local-cluster-config').default;
|
6
|
-
const {Actions} = require('@kapeta/nodejs-registry-utils');
|
6
|
+
const { Actions } = require('@kapeta/nodejs-registry-utils');
|
7
7
|
const codeGeneratorManager = require('./codeGeneratorManager');
|
8
8
|
const progressListener = require('./progressListener');
|
9
|
-
const {parseKapetaUri} = require(
|
10
|
-
const repositoryManager = require(
|
9
|
+
const { parseKapetaUri } = require('@kapeta/nodejs-utils');
|
10
|
+
const repositoryManager = require('./repositoryManager');
|
11
|
+
const NodeCache = require('node-cache');
|
11
12
|
|
12
13
|
function enrichAsset(asset) {
|
13
14
|
return {
|
@@ -18,8 +19,8 @@ function enrichAsset(asset) {
|
|
18
19
|
kind: asset.definition.kind,
|
19
20
|
data: asset.definition,
|
20
21
|
path: asset.path,
|
21
|
-
ymlPath: asset.ymlPath
|
22
|
-
}
|
22
|
+
ymlPath: asset.ymlPath,
|
23
|
+
};
|
23
24
|
}
|
24
25
|
|
25
26
|
function compareRefs(a, b) {
|
@@ -29,23 +30,20 @@ function compareRefs(a, b) {
|
|
29
30
|
return aProtocol === bProtocol && aId === bId;
|
30
31
|
}
|
31
32
|
function parseRef(ref) {
|
32
|
-
let out = ref.split(/:\/\//,2);
|
33
|
+
let out = ref.split(/:\/\//, 2);
|
33
34
|
|
34
35
|
if (out.length === 1) {
|
35
|
-
return [
|
36
|
-
'kapeta',
|
37
|
-
ref.toLowerCase()
|
38
|
-
]
|
36
|
+
return ['kapeta', ref.toLowerCase()];
|
39
37
|
}
|
40
|
-
return [
|
41
|
-
out[0].toLowerCase(),
|
42
|
-
out[1].toLowerCase()
|
43
|
-
];
|
38
|
+
return [out[0].toLowerCase(), out[1].toLowerCase()];
|
44
39
|
}
|
45
40
|
|
46
41
|
class AssetManager {
|
47
|
-
|
48
|
-
|
42
|
+
constructor() {
|
43
|
+
this.cache = new NodeCache({
|
44
|
+
stdTTL: 60 * 60, // 1 hour
|
45
|
+
});
|
46
|
+
}
|
49
47
|
|
50
48
|
/**
|
51
49
|
*
|
@@ -56,10 +54,10 @@ class AssetManager {
|
|
56
54
|
if (!assetKinds) {
|
57
55
|
const blockTypeProviders = ClusterConfiguration.getDefinitions([
|
58
56
|
'core/block-type',
|
59
|
-
'core/block-type-operator'
|
57
|
+
'core/block-type-operator',
|
60
58
|
]);
|
61
|
-
assetKinds = blockTypeProviders.map(p => {
|
62
|
-
return `${p.definition.metadata.name}:${p.version}
|
59
|
+
assetKinds = blockTypeProviders.map((p) => {
|
60
|
+
return `${p.definition.metadata.name}:${p.version}`;
|
63
61
|
});
|
64
62
|
assetKinds.push('core/plan');
|
65
63
|
}
|
@@ -73,8 +71,8 @@ class AssetManager {
|
|
73
71
|
return this.getAssets(['core/plan']);
|
74
72
|
}
|
75
73
|
|
76
|
-
async getPlan(ref) {
|
77
|
-
const asset = await this.getAsset(ref);
|
74
|
+
async getPlan(ref, noCache = false) {
|
75
|
+
const asset = await this.getAsset(ref, noCache);
|
78
76
|
|
79
77
|
if ('core/plan' !== asset.kind) {
|
80
78
|
throw new Error('Asset was not a plan: ' + ref);
|
@@ -83,18 +81,22 @@ class AssetManager {
|
|
83
81
|
return asset.data;
|
84
82
|
}
|
85
83
|
|
86
|
-
async getAsset(ref) {
|
84
|
+
async getAsset(ref, noCache = false) {
|
85
|
+
const cacheKey = `getAsset:${ref}`;
|
86
|
+
if (noCache !== true && this.cache.has(cacheKey)) {
|
87
|
+
return this.cache.get(cacheKey);
|
88
|
+
}
|
87
89
|
const uri = parseKapetaUri(ref);
|
88
90
|
await repositoryManager.ensureAsset(uri.handle, uri.name, uri.version);
|
89
91
|
|
90
92
|
let asset = ClusterConfiguration.getDefinitions()
|
91
93
|
.map(enrichAsset)
|
92
|
-
.find(a => parseKapetaUri(a.ref).equals(uri));
|
94
|
+
.find((a) => parseKapetaUri(a.ref).equals(uri));
|
93
95
|
|
94
96
|
if (!asset) {
|
95
97
|
throw new Error('Asset not found: ' + ref);
|
96
98
|
}
|
97
|
-
|
99
|
+
this.cache.set(cacheKey, asset);
|
98
100
|
return asset;
|
99
101
|
}
|
100
102
|
|
@@ -115,12 +117,12 @@ class AssetManager {
|
|
115
117
|
if (codeGeneratorManager.canGenerateCode(yaml)) {
|
116
118
|
await codeGeneratorManager.generate(path, yaml);
|
117
119
|
}
|
118
|
-
|
120
|
+
this.cache.flushAll();
|
119
121
|
return asset;
|
120
122
|
}
|
121
123
|
|
122
124
|
async updateAsset(ref, yaml) {
|
123
|
-
const asset = await this.getAsset(ref);
|
125
|
+
const asset = await this.getAsset(ref, true);
|
124
126
|
if (!asset) {
|
125
127
|
throw new Error('Attempted to update unknown asset: ' + ref);
|
126
128
|
}
|
@@ -134,11 +136,14 @@ class AssetManager {
|
|
134
136
|
}
|
135
137
|
|
136
138
|
FS.writeFileSync(asset.ymlPath, YAML.stringify(yaml));
|
137
|
-
|
139
|
+
this.cache.flushAll();
|
138
140
|
if (codeGeneratorManager.canGenerateCode(yaml)) {
|
139
141
|
await codeGeneratorManager.generate(asset.ymlPath, yaml);
|
140
142
|
} else {
|
141
|
-
console.log(
|
143
|
+
console.log(
|
144
|
+
'Could not generate code for %s',
|
145
|
+
yaml.kind ? yaml.kind : 'unknown yaml'
|
146
|
+
);
|
142
147
|
}
|
143
148
|
}
|
144
149
|
|
@@ -151,24 +156,29 @@ class AssetManager {
|
|
151
156
|
throw new Error('File not found: ' + filePath);
|
152
157
|
}
|
153
158
|
|
154
|
-
const assetInfos = YAML.parseAllDocuments(
|
155
|
-
.
|
159
|
+
const assetInfos = YAML.parseAllDocuments(
|
160
|
+
FS.readFileSync(filePath).toString()
|
161
|
+
).map((doc) => doc.toJSON());
|
156
162
|
|
157
163
|
await Actions.link(progressListener, Path.dirname(filePath));
|
158
164
|
|
159
165
|
const version = 'local';
|
160
|
-
const refs = assetInfos.map(
|
161
|
-
|
162
|
-
|
166
|
+
const refs = assetInfos.map(
|
167
|
+
(assetInfo) => `kapeta://${assetInfo.metadata.name}:${version}`
|
168
|
+
);
|
169
|
+
this.cache.flushAll();
|
170
|
+
return this.getAssets().filter((a) =>
|
171
|
+
refs.some((ref) => compareRefs(ref, a.ref))
|
172
|
+
);
|
163
173
|
}
|
164
174
|
|
165
175
|
async unregisterAsset(ref) {
|
166
|
-
const asset = await this.getAsset(ref);
|
176
|
+
const asset = await this.getAsset(ref, true);
|
167
177
|
if (!asset) {
|
168
178
|
throw new Error('Asset does not exists: ' + ref);
|
169
179
|
}
|
170
|
-
|
171
|
-
await Actions.uninstall(progressListener, asset.
|
180
|
+
this.cache.flushAll();
|
181
|
+
await Actions.uninstall(progressListener, [asset.ref]);
|
172
182
|
}
|
173
183
|
}
|
174
184
|
|
package/src/assets/routes.js
CHANGED
package/src/instanceManager.js
CHANGED
@@ -243,7 +243,7 @@ class InstanceManager {
|
|
243
243
|
async createProcessesForPlan(planRef) {
|
244
244
|
await this.stopAllForPlan(planRef);
|
245
245
|
|
246
|
-
const plan = await assetManager.getPlan(planRef);
|
246
|
+
const plan = await assetManager.getPlan(planRef, true);
|
247
247
|
if (!plan) {
|
248
248
|
throw new Error('Plan not found: ' + planRef);
|
249
249
|
}
|
@@ -332,7 +332,7 @@ class InstanceManager {
|
|
332
332
|
* @return {Promise<PromiseInfo>}
|
333
333
|
*/
|
334
334
|
async createProcess(planRef, instanceId) {
|
335
|
-
const plan = await assetManager.getPlan(planRef);
|
335
|
+
const plan = await assetManager.getPlan(planRef, true);
|
336
336
|
if (!plan) {
|
337
337
|
throw new Error('Plan not found: ' + planRef);
|
338
338
|
}
|
@@ -344,7 +344,7 @@ class InstanceManager {
|
|
344
344
|
|
345
345
|
const blockRef = blockInstance.block.ref;
|
346
346
|
|
347
|
-
const blockAsset = await assetManager.getAsset(blockRef);
|
347
|
+
const blockAsset = await assetManager.getAsset(blockRef, true);
|
348
348
|
const instanceConfig = await configManager.getConfigForSection(planRef, instanceId);
|
349
349
|
|
350
350
|
if (!blockAsset) {
|
package/src/proxy/routes.js
CHANGED
@@ -34,7 +34,6 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
34
34
|
|
35
35
|
const plan = await assetManager.getPlan(req.params.systemId);
|
36
36
|
|
37
|
-
|
38
37
|
// We can find the connection by the consumer information alone since
|
39
38
|
// only 1 provider can be connected to a consumer resource at a time
|
40
39
|
const connection = _.find(plan.spec.connections, (connection) => {
|
@@ -72,13 +71,6 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
72
71
|
req.params.type
|
73
72
|
);
|
74
73
|
|
75
|
-
/*
|
76
|
-
Get the path the consumer requested.
|
77
|
-
Note that this might not match the path the destination is expecting so we need to identify the method
|
78
|
-
that is being called and identify the destination path from the connection.
|
79
|
-
*/
|
80
|
-
const consumerPath = req.originalUrl.substring(basePath.length - 1);
|
81
|
-
|
82
74
|
const fromBlockInstance = _.find(plan.spec.blocks, (blockInstance) => {
|
83
75
|
return blockInstance.id.toLowerCase() === connection.provider.blockId.toLowerCase();
|
84
76
|
});
|
@@ -109,6 +101,13 @@ router.all('/:systemId/:consumerInstanceId/:consumerResourceName/:type/*', async
|
|
109
101
|
address = address.substring(0, address.length - 1);
|
110
102
|
}
|
111
103
|
|
104
|
+
/*
|
105
|
+
Get the path the consumer requested.
|
106
|
+
Note that this might not match the path the destination is expecting so we need to identify the method
|
107
|
+
that is being called and identify the destination path from the connection.
|
108
|
+
*/
|
109
|
+
const consumerPath = req.originalUrl.substring(basePath.length - 1);
|
110
|
+
|
112
111
|
typeHandler(req, res, {
|
113
112
|
consumerPath,
|
114
113
|
address,
|
package/src/proxy/types/rest.js
CHANGED
@@ -11,7 +11,7 @@ function getRestMethodId(restResource, httpMethod, httpPath) {
|
|
11
11
|
return _.findKey(restResource.spec.methods, (method) => {
|
12
12
|
let methodType = method.method ? method.method.toUpperCase() : 'GET';
|
13
13
|
|
14
|
-
if (methodType !== httpMethod.toUpperCase()) {
|
14
|
+
if (methodType.toUpperCase() !== httpMethod.toUpperCase()) {
|
15
15
|
return false;
|
16
16
|
}
|
17
17
|
|
@@ -153,10 +153,10 @@ module.exports = function proxyRestRequest(req, res, opts) {
|
|
153
153
|
socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
|
154
154
|
|
155
155
|
if (responseBody) {
|
156
|
-
res.
|
157
|
-
} else {
|
158
|
-
res.end();
|
156
|
+
res.write(responseBody);
|
159
157
|
}
|
158
|
+
|
159
|
+
res.end();
|
160
160
|
});
|
161
161
|
|
162
162
|
};
|
package/src/proxy/types/web.js
CHANGED
@@ -10,9 +10,7 @@ const socketManager = require('../../socketManager');
|
|
10
10
|
* @param res {Response}
|
11
11
|
* @param opts {ProxyRequestInfo}
|
12
12
|
*/
|
13
|
-
module.exports = function
|
14
|
-
|
15
|
-
console.log('Proxy request to provider: %s => %s [web]', opts.consumerPath, opts.address);
|
13
|
+
module.exports = function proxyWebRequest(req, res, opts) {
|
16
14
|
|
17
15
|
const requestHeaders = _.clone(req.headers);
|
18
16
|
|
@@ -22,13 +20,14 @@ module.exports = function proxyRestRequest(req, res, opts) {
|
|
22
20
|
delete requestHeaders['host'];
|
23
21
|
delete requestHeaders['origin'];
|
24
22
|
|
25
|
-
const sourceBasePath = opts.
|
26
|
-
const targetBasePath = opts.
|
23
|
+
const sourceBasePath = opts.consumerResource.spec.path;
|
24
|
+
const targetBasePath = opts.providerResource.spec.path;
|
27
25
|
let path = opts.consumerPath;
|
28
26
|
if (opts.consumerPath.startsWith(sourceBasePath)) {
|
29
27
|
path = path.replace(sourceBasePath, targetBasePath);
|
30
28
|
}
|
31
29
|
|
30
|
+
console.log('Proxy request to provider: %s => %s%s [web]', opts.consumerPath, opts.address, path);
|
32
31
|
|
33
32
|
const reqOpts = {
|
34
33
|
method: req.method,
|
@@ -44,38 +43,26 @@ module.exports = function proxyRestRequest(req, res, opts) {
|
|
44
43
|
);
|
45
44
|
|
46
45
|
socketManager.emit(traffic.connectionId, 'traffic_start', traffic);
|
46
|
+
const proxyReq = request(reqOpts);
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
proxyReq.on('error', function(err) {
|
49
|
+
traffic.asError(err);
|
50
|
+
socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
|
51
|
+
if (!res.headersSent) {
|
53
52
|
res.status(500).send({error: '' + err});
|
54
|
-
return;
|
55
53
|
}
|
54
|
+
});
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
delete responseHeaders['content-length'];
|
60
|
-
delete responseHeaders['content-encoding'];
|
61
|
-
delete responseHeaders['connection'];
|
62
|
-
|
63
|
-
res.set(responseHeaders);
|
64
|
-
res.status(response.statusCode);
|
65
|
-
|
56
|
+
proxyReq.on('response', function(response) {
|
57
|
+
//TODO: Include the response body in the traffic object when it is not a stream
|
66
58
|
traffic.withResponse({
|
67
59
|
code: response.statusCode,
|
68
|
-
headers: response.headers
|
69
|
-
body: responseBody
|
60
|
+
headers: response.headers
|
70
61
|
});
|
71
62
|
|
72
63
|
socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
|
73
|
-
|
74
|
-
if (responseBody) {
|
75
|
-
res.send(responseBody);
|
76
|
-
} else {
|
77
|
-
res.end();
|
78
|
-
}
|
79
64
|
});
|
80
65
|
|
66
|
+
//We need to pipe the proxy response to the client response to handle sockets and event streams
|
67
|
+
proxyReq.pipe(res);
|
81
68
|
};
|