@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 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.8",
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",
@@ -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
- const asset = await this.getAsset(ref);
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
  }
@@ -41,7 +41,7 @@ router.get('/read', async (req, res) => {
41
41
  }
42
42
 
43
43
  try {
44
- res.send(await assetManager.getAsset(req.query.ref));
44
+ res.send(await assetManager.getAsset(req.query.ref, true));
45
45
  } catch(err) {
46
46
  res.status(400).send({error: err.message});
47
47
  }
@@ -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) {
@@ -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,
@@ -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.send(responseBody);
157
- } else {
158
- res.end();
156
+ res.write(responseBody);
159
157
  }
158
+
159
+ res.end();
160
160
  });
161
161
 
162
162
  };
@@ -10,9 +10,7 @@ const socketManager = require('../../socketManager');
10
10
  * @param res {Response}
11
11
  * @param opts {ProxyRequestInfo}
12
12
  */
13
- module.exports = function proxyRestRequest(req, res, opts) {
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.providerResource.spec.path;
26
- const targetBasePath = opts.consumerResource.spec.path;
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
- request(reqOpts, function(err, response, responseBody) {
49
- if (err) {
50
- traffic.asError(err);
51
- socketManager.emit(traffic.connectionId, 'traffic_end', traffic);
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
- const responseHeaders = _.clone(response.headers);
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
  };