@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 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.8",
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",
@@ -1,13 +1,14 @@
1
- const Path = require("node:path");
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("@kapeta/nodejs-utils");
10
- const repositoryManager = require("./repositoryManager");
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('Could not generate code for %s', yaml.kind ? yaml.kind : 'unknown yaml');
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(FS.readFileSync(filePath).toString())
155
- .map(doc => doc.toJSON());
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(assetInfo => `kapeta://${assetInfo.metadata.name}:${version}`);
161
-
162
- return this.getAssets().filter(a => refs.some(ref => compareRefs(ref, a.ref)));
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.path);
180
+ this.cache.flushAll();
181
+ await Actions.uninstall(progressListener, [asset.ref]);
172
182
  }
173
183
  }
174
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
  };