@kapeta/local-cluster-service 0.16.1 → 0.16.3

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.16.3](https://github.com/kapetacom/local-cluster-service/compare/v0.16.2...v0.16.3) (2023-08-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Add route for getting status for a single instance ([00159e2](https://github.com/kapetacom/local-cluster-service/commit/00159e255015b519d741c6528a6c9ed3a586dc25))
7
+
8
+ ## [0.16.2](https://github.com/kapetacom/local-cluster-service/compare/v0.16.1...v0.16.2) (2023-08-12)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Be smarter about caching ([086ffe4](https://github.com/kapetacom/local-cluster-service/commit/086ffe416a044a8d9dc5e33aa879960d3e3c2b1a))
14
+
1
15
  ## [0.16.1](https://github.com/kapetacom/local-cluster-service/compare/v0.16.0...v0.16.1) (2023-08-11)
2
16
 
3
17
 
@@ -58,16 +58,26 @@ class RepositoryWatcher {
58
58
  }
59
59
  async setSourceOfChangeFor(file, source) {
60
60
  this.sourceOfChange.set(file, source);
61
- const realPath = await fs_extra_1.default.realpath(file);
62
- if (realPath !== file) {
63
- this.sourceOfChange.set(realPath, source);
61
+ try {
62
+ const realPath = await fs_extra_1.default.realpath(file);
63
+ if (realPath !== file) {
64
+ this.sourceOfChange.set(realPath, source);
65
+ }
66
+ }
67
+ catch (e) {
68
+ // Ignore
64
69
  }
65
70
  }
66
71
  async clearSourceOfChangeFor(file) {
67
72
  this.sourceOfChange.delete(file);
68
- const realPath = await fs_extra_1.default.realpath(file);
69
- if (realPath !== file) {
70
- this.sourceOfChange.delete(realPath);
73
+ try {
74
+ const realPath = await fs_extra_1.default.realpath(file);
75
+ if (realPath !== file) {
76
+ this.sourceOfChange.delete(realPath);
77
+ }
78
+ }
79
+ catch (e) {
80
+ // Ignore
71
81
  }
72
82
  }
73
83
  async unwatch() {
@@ -17,6 +17,7 @@ const utils_1 = require("./utils/utils");
17
17
  const taskManager_1 = require("./taskManager");
18
18
  const cacheManager_1 = require("./cacheManager");
19
19
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
20
+ const toKey = (ref) => `assetManager:asset:${ref}`;
20
21
  function enrichAsset(asset) {
21
22
  return {
22
23
  ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
@@ -73,7 +74,7 @@ class AssetManager {
73
74
  }
74
75
  async getAsset(ref, noCache = false, autoFetch = true) {
75
76
  ref = (0, utils_1.normalizeKapetaUri)(ref);
76
- const cacheKey = `getAsset:${ref}`;
77
+ const cacheKey = toKey(ref);
77
78
  if (!noCache && cacheManager_1.cacheManager.has(cacheKey)) {
78
79
  return cacheManager_1.cacheManager.get(cacheKey);
79
80
  }
@@ -104,12 +105,19 @@ class AssetManager {
104
105
  await repositoryManager_1.repositoryManager.setSourceOfChangeFor(path, sourceOfChange);
105
106
  await fs_extra_1.default.writeFile(path, yaml_1.default.stringify(yaml));
106
107
  const asset = await this.importFile(path);
107
- cacheManager_1.cacheManager.flush();
108
+ asset.forEach((a) => {
109
+ const ref = (0, utils_1.normalizeKapetaUri)(a.ref);
110
+ const key = toKey(ref);
111
+ cacheManager_1.cacheManager.set(key, a, CACHE_TTL);
112
+ });
113
+ definitionsManager_1.definitionsManager.clearCache();
114
+ console.log(`Created asset at: ${path}`);
108
115
  const ref = `kapeta://${yaml.metadata.name}:local`;
109
116
  this.maybeGenerateCode(ref, path, yaml);
110
117
  return asset;
111
118
  }
112
119
  async updateAsset(ref, yaml, sourceOfChange = 'filesystem') {
120
+ ref = (0, utils_1.normalizeKapetaUri)(ref);
113
121
  const asset = await this.getAsset(ref, true, false);
114
122
  if (!asset) {
115
123
  throw new Error('Attempted to update unknown asset: ' + ref);
@@ -122,8 +130,9 @@ class AssetManager {
122
130
  }
123
131
  await repositoryManager_1.repositoryManager.setSourceOfChangeFor(asset.ymlPath, sourceOfChange);
124
132
  await fs_extra_1.default.writeFile(asset.ymlPath, yaml_1.default.stringify(yaml));
125
- console.log('Wrote to ' + asset.ymlPath);
126
- cacheManager_1.cacheManager.flush();
133
+ console.log(`Updated asset at: ${asset.ymlPath}`);
134
+ cacheManager_1.cacheManager.remove(toKey(ref));
135
+ definitionsManager_1.definitionsManager.clearCache();
127
136
  this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
128
137
  }
129
138
  async importFile(filePath) {
@@ -137,8 +146,12 @@ class AssetManager {
137
146
  const assetInfos = yaml_1.default.parseAllDocuments(content.toString()).map((doc) => doc.toJSON());
138
147
  await nodejs_registry_utils_1.Actions.link(new progressListener_1.ProgressListener(), node_path_1.default.dirname(filePath));
139
148
  const version = 'local';
140
- const refs = assetInfos.map((assetInfo) => `kapeta://${assetInfo.metadata.name}:${version}`);
141
- cacheManager_1.cacheManager.flush();
149
+ const refs = assetInfos.map((assetInfo) => (0, utils_1.normalizeKapetaUri)(`kapeta://${assetInfo.metadata.name}:${version}`));
150
+ refs.forEach((ref) => {
151
+ const key = toKey(ref);
152
+ cacheManager_1.cacheManager.remove(key);
153
+ });
154
+ definitionsManager_1.definitionsManager.clearCache();
142
155
  return this.getAssets().filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
143
156
  }
144
157
  async unregisterAsset(ref) {
@@ -146,7 +159,9 @@ class AssetManager {
146
159
  if (!asset) {
147
160
  throw new Error('Asset does not exists: ' + ref);
148
161
  }
149
- cacheManager_1.cacheManager.flush();
162
+ const key = toKey(ref);
163
+ cacheManager_1.cacheManager.remove(key);
164
+ definitionsManager_1.definitionsManager.clearCache();
150
165
  await nodejs_registry_utils_1.Actions.uninstall(new progressListener_1.ProgressListener(), [asset.ref]);
151
166
  }
152
167
  async installAsset(ref) {
@@ -156,6 +171,9 @@ class AssetManager {
156
171
  }
157
172
  const uri = (0, nodejs_utils_1.parseKapetaUri)(ref);
158
173
  console.log('Installing %s', ref);
174
+ const key = toKey(ref);
175
+ cacheManager_1.cacheManager.remove(key);
176
+ definitionsManager_1.definitionsManager.clearCache();
159
177
  return await repositoryManager_1.repositoryManager.ensureAsset(uri.handle, uri.name, uri.version, false);
160
178
  }
161
179
  maybeGenerateCode(ref, ymlPath, block) {
@@ -2,7 +2,7 @@ export interface CacheEntry<T = any> {
2
2
  expires: number;
3
3
  data: T;
4
4
  }
5
- declare class CacheManager {
5
+ export declare class CacheManager {
6
6
  private cache;
7
7
  flush(): void;
8
8
  doCached<T>(key: string, getter: () => T, ttl?: number): T;
@@ -10,7 +10,7 @@ declare class CacheManager {
10
10
  set<T>(key: string, data: T, ttl?: number): void;
11
11
  has(key: string): boolean;
12
12
  remove(key: string): number;
13
+ removePrefix(key: string): void;
13
14
  }
14
15
  export declare const cacheManager: CacheManager;
15
16
  export declare const doCached: <T>(key: string, getter: () => T, ttl?: number) => T;
16
- export {};
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.doCached = exports.cacheManager = void 0;
6
+ exports.doCached = exports.cacheManager = exports.CacheManager = void 0;
7
7
  const node_cache_1 = __importDefault(require("node-cache"));
8
8
  const DEFAULT_CACHE_TTL = 60 * 1000; // 1 min
9
9
  class CacheManager {
@@ -32,7 +32,16 @@ class CacheManager {
32
32
  remove(key) {
33
33
  return this.cache.del(key);
34
34
  }
35
+ removePrefix(key) {
36
+ const keys = this.cache.keys();
37
+ for (const k of keys) {
38
+ if (k.startsWith(key)) {
39
+ this.remove(k);
40
+ }
41
+ }
42
+ }
35
43
  }
44
+ exports.CacheManager = CacheManager;
36
45
  exports.cacheManager = new CacheManager();
37
46
  const doCached = (key, getter, ttl = DEFAULT_CACHE_TTL) => exports.cacheManager.doCached(key, getter, ttl);
38
47
  exports.doCached = doCached;
@@ -6,6 +6,7 @@ declare class DefinitionsManager {
6
6
  exists(ref: string): boolean;
7
7
  getProviderDefinitions(): DefinitionInfo[];
8
8
  getDefinition(ref: string): DefinitionInfo | undefined;
9
+ clearCache(): void;
9
10
  }
10
11
  export declare const definitionsManager: DefinitionsManager;
11
12
  export {};
@@ -18,7 +18,7 @@ class DefinitionsManager {
18
18
  return 'none';
19
19
  }
20
20
  getFullKey(kindFilter) {
21
- return `DefinitionsManager:${this.getHash(kindFilter)}`;
21
+ return `definitionsManager:${this.getHash(kindFilter)}`;
22
22
  }
23
23
  getDefinitions(kindFilter) {
24
24
  const key = this.getFullKey(kindFilter);
@@ -39,5 +39,8 @@ class DefinitionsManager {
39
39
  return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
40
40
  });
41
41
  }
42
+ clearCache() {
43
+ cacheManager_1.cacheManager.removePrefix('definitionsManager:');
44
+ }
42
45
  }
43
46
  exports.definitionsManager = new DefinitionsManager();
@@ -26,6 +26,12 @@ router.get('/', (req, res) => {
26
26
  router.get('/:systemId/instances', (req, res) => {
27
27
  res.send(instanceManager_1.instanceManager.getInstancesForPlan(req.params.systemId));
28
28
  });
29
+ /**
30
+ * Get single instance in a plan
31
+ */
32
+ router.get('/:systemId/instances/:instanceId', (req, res) => {
33
+ res.send(instanceManager_1.instanceManager.getInstance(req.params.systemId, req.params.instanceId));
34
+ });
29
35
  /**
30
36
  * Start all instances in a plan
31
37
  */
@@ -52,16 +52,26 @@ export class RepositoryWatcher {
52
52
  }
53
53
  async setSourceOfChangeFor(file, source) {
54
54
  this.sourceOfChange.set(file, source);
55
- const realPath = await FS.realpath(file);
56
- if (realPath !== file) {
57
- this.sourceOfChange.set(realPath, source);
55
+ try {
56
+ const realPath = await FS.realpath(file);
57
+ if (realPath !== file) {
58
+ this.sourceOfChange.set(realPath, source);
59
+ }
60
+ }
61
+ catch (e) {
62
+ // Ignore
58
63
  }
59
64
  }
60
65
  async clearSourceOfChangeFor(file) {
61
66
  this.sourceOfChange.delete(file);
62
- const realPath = await FS.realpath(file);
63
- if (realPath !== file) {
64
- this.sourceOfChange.delete(realPath);
67
+ try {
68
+ const realPath = await FS.realpath(file);
69
+ if (realPath !== file) {
70
+ this.sourceOfChange.delete(realPath);
71
+ }
72
+ }
73
+ catch (e) {
74
+ // Ignore
65
75
  }
66
76
  }
67
77
  async unwatch() {
@@ -11,6 +11,7 @@ import { normalizeKapetaUri } from './utils/utils';
11
11
  import { taskManager } from './taskManager';
12
12
  import { cacheManager } from './cacheManager';
13
13
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
14
+ const toKey = (ref) => `assetManager:asset:${ref}`;
14
15
  function enrichAsset(asset) {
15
16
  return {
16
17
  ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
@@ -67,7 +68,7 @@ class AssetManager {
67
68
  }
68
69
  async getAsset(ref, noCache = false, autoFetch = true) {
69
70
  ref = normalizeKapetaUri(ref);
70
- const cacheKey = `getAsset:${ref}`;
71
+ const cacheKey = toKey(ref);
71
72
  if (!noCache && cacheManager.has(cacheKey)) {
72
73
  return cacheManager.get(cacheKey);
73
74
  }
@@ -98,12 +99,19 @@ class AssetManager {
98
99
  await repositoryManager.setSourceOfChangeFor(path, sourceOfChange);
99
100
  await FS.writeFile(path, YAML.stringify(yaml));
100
101
  const asset = await this.importFile(path);
101
- cacheManager.flush();
102
+ asset.forEach((a) => {
103
+ const ref = normalizeKapetaUri(a.ref);
104
+ const key = toKey(ref);
105
+ cacheManager.set(key, a, CACHE_TTL);
106
+ });
107
+ definitionsManager.clearCache();
108
+ console.log(`Created asset at: ${path}`);
102
109
  const ref = `kapeta://${yaml.metadata.name}:local`;
103
110
  this.maybeGenerateCode(ref, path, yaml);
104
111
  return asset;
105
112
  }
106
113
  async updateAsset(ref, yaml, sourceOfChange = 'filesystem') {
114
+ ref = normalizeKapetaUri(ref);
107
115
  const asset = await this.getAsset(ref, true, false);
108
116
  if (!asset) {
109
117
  throw new Error('Attempted to update unknown asset: ' + ref);
@@ -116,8 +124,9 @@ class AssetManager {
116
124
  }
117
125
  await repositoryManager.setSourceOfChangeFor(asset.ymlPath, sourceOfChange);
118
126
  await FS.writeFile(asset.ymlPath, YAML.stringify(yaml));
119
- console.log('Wrote to ' + asset.ymlPath);
120
- cacheManager.flush();
127
+ console.log(`Updated asset at: ${asset.ymlPath}`);
128
+ cacheManager.remove(toKey(ref));
129
+ definitionsManager.clearCache();
121
130
  this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
122
131
  }
123
132
  async importFile(filePath) {
@@ -131,8 +140,12 @@ class AssetManager {
131
140
  const assetInfos = YAML.parseAllDocuments(content.toString()).map((doc) => doc.toJSON());
132
141
  await Actions.link(new ProgressListener(), Path.dirname(filePath));
133
142
  const version = 'local';
134
- const refs = assetInfos.map((assetInfo) => `kapeta://${assetInfo.metadata.name}:${version}`);
135
- cacheManager.flush();
143
+ const refs = assetInfos.map((assetInfo) => normalizeKapetaUri(`kapeta://${assetInfo.metadata.name}:${version}`));
144
+ refs.forEach((ref) => {
145
+ const key = toKey(ref);
146
+ cacheManager.remove(key);
147
+ });
148
+ definitionsManager.clearCache();
136
149
  return this.getAssets().filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
137
150
  }
138
151
  async unregisterAsset(ref) {
@@ -140,7 +153,9 @@ class AssetManager {
140
153
  if (!asset) {
141
154
  throw new Error('Asset does not exists: ' + ref);
142
155
  }
143
- cacheManager.flush();
156
+ const key = toKey(ref);
157
+ cacheManager.remove(key);
158
+ definitionsManager.clearCache();
144
159
  await Actions.uninstall(new ProgressListener(), [asset.ref]);
145
160
  }
146
161
  async installAsset(ref) {
@@ -150,6 +165,9 @@ class AssetManager {
150
165
  }
151
166
  const uri = parseKapetaUri(ref);
152
167
  console.log('Installing %s', ref);
168
+ const key = toKey(ref);
169
+ cacheManager.remove(key);
170
+ definitionsManager.clearCache();
153
171
  return await repositoryManager.ensureAsset(uri.handle, uri.name, uri.version, false);
154
172
  }
155
173
  maybeGenerateCode(ref, ymlPath, block) {
@@ -2,7 +2,7 @@ export interface CacheEntry<T = any> {
2
2
  expires: number;
3
3
  data: T;
4
4
  }
5
- declare class CacheManager {
5
+ export declare class CacheManager {
6
6
  private cache;
7
7
  flush(): void;
8
8
  doCached<T>(key: string, getter: () => T, ttl?: number): T;
@@ -10,7 +10,7 @@ declare class CacheManager {
10
10
  set<T>(key: string, data: T, ttl?: number): void;
11
11
  has(key: string): boolean;
12
12
  remove(key: string): number;
13
+ removePrefix(key: string): void;
13
14
  }
14
15
  export declare const cacheManager: CacheManager;
15
16
  export declare const doCached: <T>(key: string, getter: () => T, ttl?: number) => T;
16
- export {};
@@ -1,6 +1,6 @@
1
1
  import NodeCache from 'node-cache';
2
2
  const DEFAULT_CACHE_TTL = 60 * 1000; // 1 min
3
- class CacheManager {
3
+ export class CacheManager {
4
4
  cache = new NodeCache();
5
5
  flush() {
6
6
  this.cache.flushAll();
@@ -26,6 +26,14 @@ class CacheManager {
26
26
  remove(key) {
27
27
  return this.cache.del(key);
28
28
  }
29
+ removePrefix(key) {
30
+ const keys = this.cache.keys();
31
+ for (const k of keys) {
32
+ if (k.startsWith(key)) {
33
+ this.remove(k);
34
+ }
35
+ }
36
+ }
29
37
  }
30
38
  export const cacheManager = new CacheManager();
31
39
  export const doCached = (key, getter, ttl = DEFAULT_CACHE_TTL) => cacheManager.doCached(key, getter, ttl);
@@ -6,6 +6,7 @@ declare class DefinitionsManager {
6
6
  exists(ref: string): boolean;
7
7
  getProviderDefinitions(): DefinitionInfo[];
8
8
  getDefinition(ref: string): DefinitionInfo | undefined;
9
+ clearCache(): void;
9
10
  }
10
11
  export declare const definitionsManager: DefinitionsManager;
11
12
  export {};
@@ -1,6 +1,6 @@
1
1
  import ClusterConfiguration from '@kapeta/local-cluster-config';
2
2
  import { parseKapetaUri } from '@kapeta/nodejs-utils';
3
- import { doCached } from './cacheManager';
3
+ import { cacheManager, doCached } from './cacheManager';
4
4
  class DefinitionsManager {
5
5
  getHash(kindFilter) {
6
6
  if (kindFilter) {
@@ -12,7 +12,7 @@ class DefinitionsManager {
12
12
  return 'none';
13
13
  }
14
14
  getFullKey(kindFilter) {
15
- return `DefinitionsManager:${this.getHash(kindFilter)}`;
15
+ return `definitionsManager:${this.getHash(kindFilter)}`;
16
16
  }
17
17
  getDefinitions(kindFilter) {
18
18
  const key = this.getFullKey(kindFilter);
@@ -33,5 +33,8 @@ class DefinitionsManager {
33
33
  return parseKapetaUri(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
34
34
  });
35
35
  }
36
+ clearCache() {
37
+ cacheManager.removePrefix('definitionsManager:');
38
+ }
36
39
  }
37
40
  export const definitionsManager = new DefinitionsManager();
@@ -21,6 +21,12 @@ router.get('/', (req, res) => {
21
21
  router.get('/:systemId/instances', (req, res) => {
22
22
  res.send(instanceManager.getInstancesForPlan(req.params.systemId));
23
23
  });
24
+ /**
25
+ * Get single instance in a plan
26
+ */
27
+ router.get('/:systemId/instances/:instanceId', (req, res) => {
28
+ res.send(instanceManager.getInstance(req.params.systemId, req.params.instanceId));
29
+ });
24
30
  /**
25
31
  * Start all instances in a plan
26
32
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.16.1",
3
+ "version": "0.16.3",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -6,8 +6,6 @@ import YAML from 'yaml';
6
6
  import { parseKapetaUri } from '@kapeta/nodejs-utils';
7
7
  import _ from 'lodash';
8
8
  import { socketManager } from './socketManager';
9
- import { definitionsManager } from './definitionsManager';
10
- import { assetManager } from './assetManager';
11
9
  import { SourceOfChange, WatchEventName } from './types';
12
10
  import { cacheManager } from './cacheManager';
13
11
 
@@ -64,17 +62,25 @@ export class RepositoryWatcher {
64
62
 
65
63
  async setSourceOfChangeFor(file: string, source: SourceOfChange) {
66
64
  this.sourceOfChange.set(file, source);
67
- const realPath = await FS.realpath(file);
68
- if (realPath !== file) {
69
- this.sourceOfChange.set(realPath, source);
65
+ try {
66
+ const realPath = await FS.realpath(file);
67
+ if (realPath !== file) {
68
+ this.sourceOfChange.set(realPath, source);
69
+ }
70
+ } catch (e) {
71
+ // Ignore
70
72
  }
71
73
  }
72
74
 
73
75
  async clearSourceOfChangeFor(file: string) {
74
76
  this.sourceOfChange.delete(file);
75
- const realPath = await FS.realpath(file);
76
- if (realPath !== file) {
77
- this.sourceOfChange.delete(realPath);
77
+ try {
78
+ const realPath = await FS.realpath(file);
79
+ if (realPath !== file) {
80
+ this.sourceOfChange.delete(realPath);
81
+ }
82
+ } catch (e) {
83
+ // Ignore
78
84
  }
79
85
  }
80
86
 
@@ -16,6 +16,8 @@ import { cacheManager } from './cacheManager';
16
16
 
17
17
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
18
18
 
19
+ const toKey = (ref: string) => `assetManager:asset:${ref}`;
20
+
19
21
  export interface EnrichedAsset {
20
22
  ref: string;
21
23
  editable: boolean;
@@ -99,7 +101,7 @@ class AssetManager {
99
101
  autoFetch: boolean = true
100
102
  ): Promise<EnrichedAsset | undefined> {
101
103
  ref = normalizeKapetaUri(ref);
102
- const cacheKey = `getAsset:${ref}`;
104
+ const cacheKey = toKey(ref);
103
105
  if (!noCache && cacheManager.has(cacheKey)) {
104
106
  return cacheManager.get(cacheKey);
105
107
  }
@@ -138,8 +140,14 @@ class AssetManager {
138
140
  await repositoryManager.setSourceOfChangeFor(path, sourceOfChange);
139
141
  await FS.writeFile(path, YAML.stringify(yaml));
140
142
  const asset = await this.importFile(path);
143
+ asset.forEach((a) => {
144
+ const ref = normalizeKapetaUri(a.ref);
145
+ const key = toKey(ref);
146
+ cacheManager.set(key, a, CACHE_TTL);
147
+ });
141
148
 
142
- cacheManager.flush();
149
+ definitionsManager.clearCache();
150
+ console.log(`Created asset at: ${path}`);
143
151
 
144
152
  const ref = `kapeta://${yaml.metadata.name}:local`;
145
153
 
@@ -149,6 +157,7 @@ class AssetManager {
149
157
  }
150
158
 
151
159
  async updateAsset(ref: string, yaml: BlockDefinition, sourceOfChange: SourceOfChange = 'filesystem') {
160
+ ref = normalizeKapetaUri(ref);
152
161
  const asset = await this.getAsset(ref, true, false);
153
162
  if (!asset) {
154
163
  throw new Error('Attempted to update unknown asset: ' + ref);
@@ -164,9 +173,10 @@ class AssetManager {
164
173
 
165
174
  await repositoryManager.setSourceOfChangeFor(asset.ymlPath, sourceOfChange);
166
175
  await FS.writeFile(asset.ymlPath, YAML.stringify(yaml));
167
- console.log('Wrote to ' + asset.ymlPath);
176
+ console.log(`Updated asset at: ${asset.ymlPath}`);
168
177
 
169
- cacheManager.flush();
178
+ cacheManager.remove(toKey(ref));
179
+ definitionsManager.clearCache();
170
180
 
171
181
  this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
172
182
  }
@@ -186,9 +196,15 @@ class AssetManager {
186
196
  await Actions.link(new ProgressListener(), Path.dirname(filePath));
187
197
 
188
198
  const version = 'local';
189
- const refs = assetInfos.map((assetInfo) => `kapeta://${assetInfo.metadata.name}:${version}`);
199
+ const refs = assetInfos.map((assetInfo) =>
200
+ normalizeKapetaUri(`kapeta://${assetInfo.metadata.name}:${version}`)
201
+ );
202
+ refs.forEach((ref) => {
203
+ const key = toKey(ref);
204
+ cacheManager.remove(key);
205
+ });
190
206
 
191
- cacheManager.flush();
207
+ definitionsManager.clearCache();
192
208
 
193
209
  return this.getAssets().filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
194
210
  }
@@ -199,7 +215,9 @@ class AssetManager {
199
215
  throw new Error('Asset does not exists: ' + ref);
200
216
  }
201
217
 
202
- cacheManager.flush();
218
+ const key = toKey(ref);
219
+ cacheManager.remove(key);
220
+ definitionsManager.clearCache();
203
221
 
204
222
  await Actions.uninstall(new ProgressListener(), [asset.ref]);
205
223
  }
@@ -211,6 +229,9 @@ class AssetManager {
211
229
  }
212
230
  const uri = parseKapetaUri(ref);
213
231
  console.log('Installing %s', ref);
232
+ const key = toKey(ref);
233
+ cacheManager.remove(key);
234
+ definitionsManager.clearCache();
214
235
 
215
236
  return await repositoryManager.ensureAsset(uri.handle, uri.name, uri.version, false);
216
237
  }
@@ -5,7 +5,7 @@ export interface CacheEntry<T = any> {
5
5
  expires: number;
6
6
  data: T;
7
7
  }
8
- class CacheManager {
8
+ export class CacheManager {
9
9
  private cache: NodeCache = new NodeCache();
10
10
 
11
11
  public flush() {
@@ -37,6 +37,15 @@ class CacheManager {
37
37
  public remove(key: string) {
38
38
  return this.cache.del(key);
39
39
  }
40
+
41
+ public removePrefix(key: string) {
42
+ const keys = this.cache.keys();
43
+ for (const k of keys) {
44
+ if (k.startsWith(key)) {
45
+ this.remove(k);
46
+ }
47
+ }
48
+ }
40
49
  }
41
50
 
42
51
  export const cacheManager = new CacheManager();
@@ -1,6 +1,6 @@
1
1
  import ClusterConfiguration, { DefinitionInfo } from '@kapeta/local-cluster-config';
2
2
  import { parseKapetaUri } from '@kapeta/nodejs-utils';
3
- import { doCached } from './cacheManager';
3
+ import { cacheManager, doCached } from './cacheManager';
4
4
 
5
5
  class DefinitionsManager {
6
6
  private getHash(kindFilter?: string | string[]) {
@@ -14,7 +14,7 @@ class DefinitionsManager {
14
14
  }
15
15
 
16
16
  private getFullKey(kindFilter?: string | string[]) {
17
- return `DefinitionsManager:${this.getHash(kindFilter)}`;
17
+ return `definitionsManager:${this.getHash(kindFilter)}`;
18
18
  }
19
19
 
20
20
  public getDefinitions(kindFilter?: string | string[]): DefinitionInfo[] {
@@ -40,6 +40,10 @@ class DefinitionsManager {
40
40
  return parseKapetaUri(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
41
41
  });
42
42
  }
43
+
44
+ public clearCache() {
45
+ cacheManager.removePrefix('definitionsManager:');
46
+ }
43
47
  }
44
48
 
45
49
  export const definitionsManager = new DefinitionsManager();
@@ -25,6 +25,13 @@ router.get('/:systemId/instances', (req: Request, res: Response) => {
25
25
  res.send(instanceManager.getInstancesForPlan(req.params.systemId));
26
26
  });
27
27
 
28
+ /**
29
+ * Get single instance in a plan
30
+ */
31
+ router.get('/:systemId/instances/:instanceId', (req: Request, res: Response) => {
32
+ res.send(instanceManager.getInstance(req.params.systemId, req.params.instanceId));
33
+ });
34
+
28
35
  /**
29
36
  * Start all instances in a plan
30
37
  */