@kapeta/local-cluster-service 0.5.8 → 0.5.9
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 +7 -0
- package/package.json +2 -1
- package/src/assetManager.js +21 -10
- 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,10 @@
|
|
1
|
+
## [0.5.9](https://github.com/kapetacom/local-cluster-service/compare/v0.5.8...v0.5.9) (2023-06-18)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* 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))
|
7
|
+
|
1
8
|
## [0.5.8](https://github.com/kapetacom/local-cluster-service/compare/v0.5.7...v0.5.8) (2023-06-18)
|
2
9
|
|
3
10
|
|
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.9",
|
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
@@ -8,6 +8,7 @@ const codeGeneratorManager = require('./codeGeneratorManager');
|
|
8
8
|
const progressListener = require('./progressListener');
|
9
9
|
const {parseKapetaUri} = require("@kapeta/nodejs-utils");
|
10
10
|
const repositoryManager = require("./repositoryManager");
|
11
|
+
const NodeCache = require("node-cache");
|
11
12
|
|
12
13
|
function enrichAsset(asset) {
|
13
14
|
return {
|
@@ -45,6 +46,11 @@ function parseRef(ref) {
|
|
45
46
|
|
46
47
|
class AssetManager {
|
47
48
|
|
49
|
+
constructor() {
|
50
|
+
this.cache = new NodeCache({
|
51
|
+
stdTTL: 60 * 60, // 1 hour
|
52
|
+
})
|
53
|
+
}
|
48
54
|
|
49
55
|
|
50
56
|
/**
|
@@ -73,8 +79,9 @@ class AssetManager {
|
|
73
79
|
return this.getAssets(['core/plan']);
|
74
80
|
}
|
75
81
|
|
76
|
-
async getPlan(ref) {
|
77
|
-
|
82
|
+
async getPlan(ref, noCache = false) {
|
83
|
+
|
84
|
+
const asset = await this.getAsset(ref, noCache);
|
78
85
|
|
79
86
|
if ('core/plan' !== asset.kind) {
|
80
87
|
throw new Error('Asset was not a plan: ' + ref);
|
@@ -83,7 +90,11 @@ class AssetManager {
|
|
83
90
|
return asset.data;
|
84
91
|
}
|
85
92
|
|
86
|
-
async getAsset(ref) {
|
93
|
+
async getAsset(ref, noCache = false) {
|
94
|
+
const cacheKey = `getAsset:${ref}`;
|
95
|
+
if (noCache !== true && this.cache.has(cacheKey)) {
|
96
|
+
return this.cache.get(cacheKey);
|
97
|
+
}
|
87
98
|
const uri = parseKapetaUri(ref);
|
88
99
|
await repositoryManager.ensureAsset(uri.handle, uri.name, uri.version);
|
89
100
|
|
@@ -94,7 +105,7 @@ class AssetManager {
|
|
94
105
|
if (!asset) {
|
95
106
|
throw new Error('Asset not found: ' + ref);
|
96
107
|
}
|
97
|
-
|
108
|
+
this.cache.set(cacheKey, asset);
|
98
109
|
return asset;
|
99
110
|
}
|
100
111
|
|
@@ -115,12 +126,12 @@ class AssetManager {
|
|
115
126
|
if (codeGeneratorManager.canGenerateCode(yaml)) {
|
116
127
|
await codeGeneratorManager.generate(path, yaml);
|
117
128
|
}
|
118
|
-
|
129
|
+
this.cache.flushAll();
|
119
130
|
return asset;
|
120
131
|
}
|
121
132
|
|
122
133
|
async updateAsset(ref, yaml) {
|
123
|
-
const asset = await this.getAsset(ref);
|
134
|
+
const asset = await this.getAsset(ref, true);
|
124
135
|
if (!asset) {
|
125
136
|
throw new Error('Attempted to update unknown asset: ' + ref);
|
126
137
|
}
|
@@ -134,7 +145,7 @@ class AssetManager {
|
|
134
145
|
}
|
135
146
|
|
136
147
|
FS.writeFileSync(asset.ymlPath, YAML.stringify(yaml));
|
137
|
-
|
148
|
+
this.cache.flushAll();
|
138
149
|
if (codeGeneratorManager.canGenerateCode(yaml)) {
|
139
150
|
await codeGeneratorManager.generate(asset.ymlPath, yaml);
|
140
151
|
} else {
|
@@ -158,16 +169,16 @@ class AssetManager {
|
|
158
169
|
|
159
170
|
const version = 'local';
|
160
171
|
const refs = assetInfos.map(assetInfo => `kapeta://${assetInfo.metadata.name}:${version}`);
|
161
|
-
|
172
|
+
this.cache.flushAll();
|
162
173
|
return this.getAssets().filter(a => refs.some(ref => compareRefs(ref, a.ref)));
|
163
174
|
}
|
164
175
|
|
165
176
|
async unregisterAsset(ref) {
|
166
|
-
const asset = await this.getAsset(ref);
|
177
|
+
const asset = await this.getAsset(ref, true);
|
167
178
|
if (!asset) {
|
168
179
|
throw new Error('Asset does not exists: ' + ref);
|
169
180
|
}
|
170
|
-
|
181
|
+
this.cache.flushAll();
|
171
182
|
await Actions.uninstall(progressListener, asset.path);
|
172
183
|
}
|
173
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
|
};
|