@kapeta/local-cluster-service 0.27.0 → 0.28.1
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/definitions.d.ts +2 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/src/api.js +2 -3
- package/dist/cjs/src/assetManager.d.ts +3 -0
- package/dist/cjs/src/assetManager.js +57 -1
- package/dist/cjs/src/definitionsManager.d.ts +1 -0
- package/dist/cjs/src/definitionsManager.js +13 -0
- package/dist/cjs/src/repositoryManager.d.ts +9 -0
- package/dist/cjs/src/repositoryManager.js +92 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/src/api.js +2 -3
- package/dist/esm/src/assetManager.d.ts +3 -0
- package/dist/esm/src/assetManager.js +57 -1
- package/dist/esm/src/definitionsManager.d.ts +1 -0
- package/dist/esm/src/definitionsManager.js +13 -0
- package/dist/esm/src/repositoryManager.d.ts +9 -0
- package/dist/esm/src/repositoryManager.js +92 -0
- package/index.ts +3 -0
- package/package.json +1 -1
- package/src/api.ts +2 -3
- package/src/assetManager.ts +66 -1
- package/src/definitionsManager.ts +18 -1
- package/src/repositoryManager.ts +114 -3
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.28.1](https://github.com/kapetacom/local-cluster-service/compare/v0.28.0...v0.28.1) (2023-12-12)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* return all imported asset types from import endpoint [KAP-2187] ([#103](https://github.com/kapetacom/local-cluster-service/issues/103)) ([02ac22c](https://github.com/kapetacom/local-cluster-service/commit/02ac22c4a54db05b0e80943daba0b5496e5bb319))
|
7
|
+
|
8
|
+
# [0.28.0](https://github.com/kapetacom/local-cluster-service/compare/v0.27.0...v0.28.0) (2023-11-15)
|
9
|
+
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* Auto-upgrade providers every 10th minute ([#100](https://github.com/kapetacom/local-cluster-service/issues/100)) ([2c35569](https://github.com/kapetacom/local-cluster-service/commit/2c35569587f456374529bb1f9a9e8f5c03be2189))
|
14
|
+
|
1
15
|
# [0.27.0](https://github.com/kapetacom/local-cluster-service/compare/v0.26.0...v0.27.0) (2023-11-13)
|
2
16
|
|
3
17
|
|
package/definitions.d.ts
CHANGED
@@ -13,12 +13,14 @@ declare module '@kapeta/nodejs-registry-utils' {
|
|
13
13
|
export interface AssetVersion {
|
14
14
|
content: Kind;
|
15
15
|
dependencies: Dependency[];
|
16
|
+
version: string;
|
16
17
|
}
|
17
18
|
|
18
19
|
export class RegistryService {
|
19
20
|
constructor(url: string);
|
20
21
|
|
21
22
|
getVersion(fullName: string, version: string): Promise<AssetVersion>;
|
23
|
+
getLatestVersion(name): Promise<AssetVersion>;
|
22
24
|
}
|
23
25
|
|
24
26
|
export const Config: any;
|
package/dist/cjs/index.js
CHANGED
@@ -58,6 +58,7 @@ const DefaultProviderInstaller_1 = require("./src/utils/DefaultProviderInstaller
|
|
58
58
|
const authManager_1 = require("./src/authManager");
|
59
59
|
const codeGeneratorManager_1 = require("./src/codeGeneratorManager");
|
60
60
|
const Sentry = __importStar(require("@sentry/node"));
|
61
|
+
const assetManager_1 = require("./src/assetManager");
|
61
62
|
Sentry.init({
|
62
63
|
dsn: 'https://0b7cc946d82c591473d6f95fff5e210b@o4505820837249024.ingest.sentry.io/4506212692000768',
|
63
64
|
enabled: process.env.NODE_ENV !== 'development',
|
@@ -231,6 +232,7 @@ exports.default = {
|
|
231
232
|
catch (e) {
|
232
233
|
console.error('Failed to install default providers.', e);
|
233
234
|
}
|
235
|
+
assetManager_1.assetManager.startUpgradeInterval();
|
234
236
|
resolve({ host, port, dockerStatus: containerManager_1.containerManager.isAlive() });
|
235
237
|
});
|
236
238
|
currentServer.host = host;
|
package/dist/cjs/src/api.js
CHANGED
@@ -10,13 +10,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
const express_promise_router_1 = __importDefault(require("express-promise-router"));
|
11
11
|
const cors_1 = require("./middleware/cors");
|
12
12
|
const nodejs_api_client_1 = require("@kapeta/nodejs-api-client");
|
13
|
-
const
|
13
|
+
const nodejs_registry_utils_1 = require("@kapeta/nodejs-registry-utils");
|
14
14
|
const { createAPIRoute } = require('@kapeta/web-microfrontend/server');
|
15
15
|
const packageJson = require('../package.json');
|
16
16
|
const router = (0, express_promise_router_1.default)();
|
17
|
-
const remoteServices = local_cluster_config_1.default.getClusterConfig().remoteServices ?? {};
|
18
17
|
router.use('/', cors_1.corsHandler);
|
19
|
-
router.use('/registry', createAPIRoute(
|
18
|
+
router.use('/registry', createAPIRoute(nodejs_registry_utils_1.Config.data?.registry?.url ?? 'https://registry.kapeta.com', {
|
20
19
|
nonce: false,
|
21
20
|
userAgent: `KapetaDesktopCluster/${packageJson.version}`,
|
22
21
|
tokenFetcher: () => {
|
@@ -16,6 +16,7 @@ export interface EnrichedAsset {
|
|
16
16
|
ymlPath: string;
|
17
17
|
}
|
18
18
|
declare class AssetManager {
|
19
|
+
startUpgradeInterval(): void;
|
19
20
|
/**
|
20
21
|
*
|
21
22
|
* @param {string[]} [assetKinds]
|
@@ -31,6 +32,8 @@ declare class AssetManager {
|
|
31
32
|
importFile(filePath: string): Promise<EnrichedAsset[]>;
|
32
33
|
unregisterAsset(ref: string): Promise<void>;
|
33
34
|
installAsset(ref: string, wait?: boolean): Promise<import("./taskManager").Task<void>[] | undefined>;
|
35
|
+
private cleanupUnusedProviders;
|
36
|
+
private upgradeAllProviders;
|
34
37
|
private maybeGenerateCode;
|
35
38
|
}
|
36
39
|
export declare const assetManager: AssetManager;
|
@@ -20,7 +20,9 @@ const definitionsManager_1 = require("./definitionsManager");
|
|
20
20
|
const taskManager_1 = require("./taskManager");
|
21
21
|
const cacheManager_1 = require("./cacheManager");
|
22
22
|
const node_uuid_1 = __importDefault(require("node-uuid"));
|
23
|
+
const node_os_1 = __importDefault(require("node:os"));
|
23
24
|
const CACHE_TTL = 60 * 60 * 1000; // 1 hour
|
25
|
+
const UPGRADE_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
|
24
26
|
const toKey = (ref) => `assetManager:asset:${ref}`;
|
25
27
|
function enrichAsset(asset) {
|
26
28
|
return {
|
@@ -47,6 +49,21 @@ function parseRef(ref) {
|
|
47
49
|
return [out[0].toLowerCase(), out[1].toLowerCase()];
|
48
50
|
}
|
49
51
|
class AssetManager {
|
52
|
+
startUpgradeInterval() {
|
53
|
+
console.debug('Checking for upgrades...');
|
54
|
+
this.upgradeAllProviders()
|
55
|
+
.then((task) => {
|
56
|
+
return task && task.wait();
|
57
|
+
})
|
58
|
+
.catch((e) => {
|
59
|
+
console.error('Failed to upgrade providers', e);
|
60
|
+
})
|
61
|
+
.finally(() => {
|
62
|
+
setTimeout(() => {
|
63
|
+
this.startUpgradeInterval();
|
64
|
+
}, UPGRADE_CHECK_INTERVAL);
|
65
|
+
});
|
66
|
+
}
|
50
67
|
/**
|
51
68
|
*
|
52
69
|
* @param {string[]} [assetKinds]
|
@@ -166,7 +183,8 @@ class AssetManager {
|
|
166
183
|
cacheManager_1.cacheManager.remove(key);
|
167
184
|
});
|
168
185
|
definitionsManager_1.definitionsManager.clearCache();
|
169
|
-
|
186
|
+
// Get all possible asset kinds, to make sure we return the result if possible
|
187
|
+
const assets = await this.getAssets([]);
|
170
188
|
return assets.filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
|
171
189
|
}
|
172
190
|
async unregisterAsset(ref) {
|
@@ -191,6 +209,44 @@ class AssetManager {
|
|
191
209
|
definitionsManager_1.definitionsManager.clearCache();
|
192
210
|
return await repositoryManager_1.repositoryManager.ensureAsset(uri.handle, uri.name, uri.version, wait);
|
193
211
|
}
|
212
|
+
async cleanupUnusedProviders() {
|
213
|
+
const unusedProviders = await repositoryManager_1.repositoryManager.getUnusedProviders();
|
214
|
+
if (unusedProviders.length < 1) {
|
215
|
+
return;
|
216
|
+
}
|
217
|
+
console.log('Cleaning up unused providers: ', unusedProviders);
|
218
|
+
await Promise.all(unusedProviders.map((ref) => {
|
219
|
+
return this.unregisterAsset(ref);
|
220
|
+
}));
|
221
|
+
}
|
222
|
+
async upgradeAllProviders() {
|
223
|
+
const providers = await definitionsManager_1.definitionsManager.getProviderDefinitions();
|
224
|
+
const names = providers.map((p) => p.definition.metadata.name);
|
225
|
+
const refs = await repositoryManager_1.repositoryManager.getUpdatableAssets(names);
|
226
|
+
if (refs.length < 1) {
|
227
|
+
await this.cleanupUnusedProviders();
|
228
|
+
return;
|
229
|
+
}
|
230
|
+
console.log('Installing updates', refs);
|
231
|
+
const updateAll = async () => {
|
232
|
+
try {
|
233
|
+
//We change to a temp dir to avoid issues with the current working directory
|
234
|
+
process.chdir(node_os_1.default.tmpdir());
|
235
|
+
await nodejs_registry_utils_1.Actions.install(new progressListener_1.ProgressListener(), refs, {});
|
236
|
+
await this.cleanupUnusedProviders();
|
237
|
+
}
|
238
|
+
catch (e) {
|
239
|
+
console.error(`Failed to update assets: ${refs.join(',')}`, e);
|
240
|
+
throw e;
|
241
|
+
}
|
242
|
+
cacheManager_1.cacheManager.flush();
|
243
|
+
definitionsManager_1.definitionsManager.clearCache();
|
244
|
+
};
|
245
|
+
return taskManager_1.taskManager.add(`asset:update`, updateAll, {
|
246
|
+
name: `Installing ${refs.length} updates`,
|
247
|
+
group: 'asset:update:check',
|
248
|
+
});
|
249
|
+
}
|
194
250
|
async maybeGenerateCode(ref, ymlPath, block) {
|
195
251
|
ref = (0, nodejs_utils_1.normalizeKapetaUri)(ref);
|
196
252
|
if (await codeGeneratorManager_1.codeGeneratorManager.canGenerateCode(block)) {
|
@@ -11,6 +11,7 @@ declare class DefinitionsManager {
|
|
11
11
|
exists(ref: string): Promise<boolean>;
|
12
12
|
getProviderDefinitions(): Promise<DefinitionInfo[]>;
|
13
13
|
getDefinition(ref: string): Promise<DefinitionInfo | undefined>;
|
14
|
+
getLatestDefinition(name: string): Promise<DefinitionInfo | undefined>;
|
14
15
|
getVersions(assetName: string): Promise<DefinitionInfo[]>;
|
15
16
|
clearCache(): void;
|
16
17
|
}
|
@@ -136,6 +136,19 @@ class DefinitionsManager {
|
|
136
136
|
return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
|
137
137
|
});
|
138
138
|
}
|
139
|
+
async getLatestDefinition(name) {
|
140
|
+
const definitions = await this.getDefinitions();
|
141
|
+
const allVersions = definitions.filter((d) => {
|
142
|
+
return d.version !== 'local' && d.definition.metadata.name === name;
|
143
|
+
});
|
144
|
+
if (allVersions.length === 0) {
|
145
|
+
return;
|
146
|
+
}
|
147
|
+
allVersions.sort((a, b) => {
|
148
|
+
return (0, nodejs_utils_1.parseVersion)(a.version).compareTo((0, nodejs_utils_1.parseVersion)(b.version)) * -1;
|
149
|
+
});
|
150
|
+
return allVersions[0];
|
151
|
+
}
|
139
152
|
async getVersions(assetName) {
|
140
153
|
const uri = (0, nodejs_utils_1.parseKapetaUri)(assetName);
|
141
154
|
const definitions = await this.getDefinitions();
|
@@ -19,6 +19,15 @@ declare class RepositoryManager extends EventEmitter {
|
|
19
19
|
setSourceOfChangeFor(file: string, source: SourceOfChange): Promise<void>;
|
20
20
|
clearSourceOfChangeFor(file: string): Promise<void>;
|
21
21
|
ensureDefaultProviders(): Promise<void>;
|
22
|
+
/**
|
23
|
+
* Will go through all available assets and get a list of
|
24
|
+
* providers that are not referenced anywhere.
|
25
|
+
*
|
26
|
+
* It will also make sure to not include the latest version of an asset.
|
27
|
+
*
|
28
|
+
*/
|
29
|
+
getUnusedProviders(): Promise<string[]>;
|
30
|
+
getUpdatableAssets(allNames: string[]): Promise<string[]>;
|
22
31
|
private scheduleInstallation;
|
23
32
|
ensureAsset(handle: string, name: string, version: string, wait?: boolean): Promise<undefined | Task[]>;
|
24
33
|
}
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.repositoryManager = void 0;
|
11
11
|
const node_os_1 = __importDefault(require("node:os"));
|
12
12
|
const socketManager_1 = require("./socketManager");
|
13
|
+
const schemas_1 = require("@kapeta/schemas");
|
13
14
|
const nodejs_registry_utils_1 = require("@kapeta/nodejs-registry-utils");
|
14
15
|
const definitionsManager_1 = require("./definitionsManager");
|
15
16
|
const taskManager_1 = require("./taskManager");
|
@@ -69,6 +70,97 @@ class RepositoryManager extends node_events_1.EventEmitter {
|
|
69
70
|
socketManager_1.socketManager.emitGlobal(EVENT_DEFAULT_PROVIDERS_END, {});
|
70
71
|
});
|
71
72
|
}
|
73
|
+
/**
|
74
|
+
* Will go through all available assets and get a list of
|
75
|
+
* providers that are not referenced anywhere.
|
76
|
+
*
|
77
|
+
* It will also make sure to not include the latest version of an asset.
|
78
|
+
*
|
79
|
+
*/
|
80
|
+
async getUnusedProviders() {
|
81
|
+
const allDefinitions = await definitionsManager_1.definitionsManager.getDefinitions();
|
82
|
+
const blocks = [];
|
83
|
+
const plans = [];
|
84
|
+
const providerMap = new Map();
|
85
|
+
const providerVersions = {};
|
86
|
+
const unusedProviders = new Set();
|
87
|
+
allDefinitions.forEach((d) => {
|
88
|
+
if (d.definition.kind === 'core/plan') {
|
89
|
+
plans.push(d);
|
90
|
+
return;
|
91
|
+
}
|
92
|
+
if (d.definition.kind.startsWith('core/')) {
|
93
|
+
const ref = (0, nodejs_utils_1.normalizeKapetaUri)(`${d.definition.metadata.name}:${d.version}`);
|
94
|
+
providerMap.set(ref, d);
|
95
|
+
if (!providerVersions[d.definition.metadata.name]) {
|
96
|
+
providerVersions[d.definition.metadata.name] = new Set();
|
97
|
+
}
|
98
|
+
providerVersions[d.definition.metadata.name].add(d.version);
|
99
|
+
unusedProviders.add(ref);
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
blocks.push(d);
|
103
|
+
});
|
104
|
+
const latestVersions = {};
|
105
|
+
Object.entries(providerVersions).forEach(([name, versions]) => {
|
106
|
+
const versionArray = Array.from(versions);
|
107
|
+
versionArray.sort((a, b) => {
|
108
|
+
return (0, nodejs_utils_1.parseVersion)(a).compareTo((0, nodejs_utils_1.parseVersion)(b)) * -1;
|
109
|
+
});
|
110
|
+
latestVersions[name] = versionArray[0];
|
111
|
+
});
|
112
|
+
function markDependencyAsUsed(dep) {
|
113
|
+
const uri = (0, nodejs_utils_1.parseKapetaUri)(dep.name);
|
114
|
+
const ref = uri.toNormalizedString();
|
115
|
+
if (unusedProviders.has(ref)) {
|
116
|
+
unusedProviders.delete(ref);
|
117
|
+
}
|
118
|
+
}
|
119
|
+
plans.forEach((plan) => {
|
120
|
+
const dependencies = (0, schemas_1.resolveDependencies)(plan.definition);
|
121
|
+
dependencies.forEach(markDependencyAsUsed);
|
122
|
+
});
|
123
|
+
blocks.forEach((block) => {
|
124
|
+
const blockTypeKind = (0, nodejs_utils_1.normalizeKapetaUri)(block.definition.kind);
|
125
|
+
unusedProviders.delete(blockTypeKind);
|
126
|
+
const blockTypeProvider = providerMap.get(blockTypeKind);
|
127
|
+
if (!blockTypeProvider) {
|
128
|
+
console.warn('No provider found for block type', block.definition.kind);
|
129
|
+
return;
|
130
|
+
}
|
131
|
+
const dependencies = (0, schemas_1.resolveDependencies)(block.definition, blockTypeProvider.definition);
|
132
|
+
dependencies.forEach(markDependencyAsUsed);
|
133
|
+
});
|
134
|
+
return Array.from(unusedProviders).filter((ref) => {
|
135
|
+
const uri = (0, nodejs_utils_1.parseKapetaUri)(ref);
|
136
|
+
if (uri.version == 'local') {
|
137
|
+
// Don't delete local assets
|
138
|
+
return false;
|
139
|
+
}
|
140
|
+
// Don't delete the latest version of an asset
|
141
|
+
return latestVersions[uri.fullName] !== uri.version;
|
142
|
+
});
|
143
|
+
}
|
144
|
+
async getUpdatableAssets(allNames) {
|
145
|
+
const names = Array.from(new Set(allNames));
|
146
|
+
const currentVersions = await Promise.all(names.map((name) => definitionsManager_1.definitionsManager.getLatestDefinition(name).catch(() => undefined)));
|
147
|
+
const latestVersions = await Promise.all(names.map((name) => this._registryService.getLatestVersion(name).catch(() => undefined)));
|
148
|
+
return names
|
149
|
+
.map((name, index) => {
|
150
|
+
const currentVersion = currentVersions[index];
|
151
|
+
const latestVersion = latestVersions[index];
|
152
|
+
if (!currentVersion || !latestVersion) {
|
153
|
+
// Shouldn't happen unless the registry is down or an asset was deleted
|
154
|
+
return undefined;
|
155
|
+
}
|
156
|
+
const ref = (0, nodejs_utils_1.normalizeKapetaUri)(`${name}:${latestVersion.version}`);
|
157
|
+
if (currentVersion.version === latestVersion.version) {
|
158
|
+
return undefined;
|
159
|
+
}
|
160
|
+
return ref;
|
161
|
+
})
|
162
|
+
.filter((ref) => !!ref);
|
163
|
+
}
|
72
164
|
async scheduleInstallation(refs) {
|
73
165
|
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
74
166
|
const createInstaller = (ref) => {
|
package/dist/esm/index.js
CHANGED
@@ -58,6 +58,7 @@ const DefaultProviderInstaller_1 = require("./src/utils/DefaultProviderInstaller
|
|
58
58
|
const authManager_1 = require("./src/authManager");
|
59
59
|
const codeGeneratorManager_1 = require("./src/codeGeneratorManager");
|
60
60
|
const Sentry = __importStar(require("@sentry/node"));
|
61
|
+
const assetManager_1 = require("./src/assetManager");
|
61
62
|
Sentry.init({
|
62
63
|
dsn: 'https://0b7cc946d82c591473d6f95fff5e210b@o4505820837249024.ingest.sentry.io/4506212692000768',
|
63
64
|
enabled: process.env.NODE_ENV !== 'development',
|
@@ -231,6 +232,7 @@ exports.default = {
|
|
231
232
|
catch (e) {
|
232
233
|
console.error('Failed to install default providers.', e);
|
233
234
|
}
|
235
|
+
assetManager_1.assetManager.startUpgradeInterval();
|
234
236
|
resolve({ host, port, dockerStatus: containerManager_1.containerManager.isAlive() });
|
235
237
|
});
|
236
238
|
currentServer.host = host;
|
package/dist/esm/src/api.js
CHANGED
@@ -10,13 +10,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
const express_promise_router_1 = __importDefault(require("express-promise-router"));
|
11
11
|
const cors_1 = require("./middleware/cors");
|
12
12
|
const nodejs_api_client_1 = require("@kapeta/nodejs-api-client");
|
13
|
-
const
|
13
|
+
const nodejs_registry_utils_1 = require("@kapeta/nodejs-registry-utils");
|
14
14
|
const { createAPIRoute } = require('@kapeta/web-microfrontend/server');
|
15
15
|
const packageJson = require('../package.json');
|
16
16
|
const router = (0, express_promise_router_1.default)();
|
17
|
-
const remoteServices = local_cluster_config_1.default.getClusterConfig().remoteServices ?? {};
|
18
17
|
router.use('/', cors_1.corsHandler);
|
19
|
-
router.use('/registry', createAPIRoute(
|
18
|
+
router.use('/registry', createAPIRoute(nodejs_registry_utils_1.Config.data?.registry?.url ?? 'https://registry.kapeta.com', {
|
20
19
|
nonce: false,
|
21
20
|
userAgent: `KapetaDesktopCluster/${packageJson.version}`,
|
22
21
|
tokenFetcher: () => {
|
@@ -16,6 +16,7 @@ export interface EnrichedAsset {
|
|
16
16
|
ymlPath: string;
|
17
17
|
}
|
18
18
|
declare class AssetManager {
|
19
|
+
startUpgradeInterval(): void;
|
19
20
|
/**
|
20
21
|
*
|
21
22
|
* @param {string[]} [assetKinds]
|
@@ -31,6 +32,8 @@ declare class AssetManager {
|
|
31
32
|
importFile(filePath: string): Promise<EnrichedAsset[]>;
|
32
33
|
unregisterAsset(ref: string): Promise<void>;
|
33
34
|
installAsset(ref: string, wait?: boolean): Promise<import("./taskManager").Task<void>[] | undefined>;
|
35
|
+
private cleanupUnusedProviders;
|
36
|
+
private upgradeAllProviders;
|
34
37
|
private maybeGenerateCode;
|
35
38
|
}
|
36
39
|
export declare const assetManager: AssetManager;
|
@@ -20,7 +20,9 @@ const definitionsManager_1 = require("./definitionsManager");
|
|
20
20
|
const taskManager_1 = require("./taskManager");
|
21
21
|
const cacheManager_1 = require("./cacheManager");
|
22
22
|
const node_uuid_1 = __importDefault(require("node-uuid"));
|
23
|
+
const node_os_1 = __importDefault(require("node:os"));
|
23
24
|
const CACHE_TTL = 60 * 60 * 1000; // 1 hour
|
25
|
+
const UPGRADE_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
|
24
26
|
const toKey = (ref) => `assetManager:asset:${ref}`;
|
25
27
|
function enrichAsset(asset) {
|
26
28
|
return {
|
@@ -47,6 +49,21 @@ function parseRef(ref) {
|
|
47
49
|
return [out[0].toLowerCase(), out[1].toLowerCase()];
|
48
50
|
}
|
49
51
|
class AssetManager {
|
52
|
+
startUpgradeInterval() {
|
53
|
+
console.debug('Checking for upgrades...');
|
54
|
+
this.upgradeAllProviders()
|
55
|
+
.then((task) => {
|
56
|
+
return task && task.wait();
|
57
|
+
})
|
58
|
+
.catch((e) => {
|
59
|
+
console.error('Failed to upgrade providers', e);
|
60
|
+
})
|
61
|
+
.finally(() => {
|
62
|
+
setTimeout(() => {
|
63
|
+
this.startUpgradeInterval();
|
64
|
+
}, UPGRADE_CHECK_INTERVAL);
|
65
|
+
});
|
66
|
+
}
|
50
67
|
/**
|
51
68
|
*
|
52
69
|
* @param {string[]} [assetKinds]
|
@@ -166,7 +183,8 @@ class AssetManager {
|
|
166
183
|
cacheManager_1.cacheManager.remove(key);
|
167
184
|
});
|
168
185
|
definitionsManager_1.definitionsManager.clearCache();
|
169
|
-
|
186
|
+
// Get all possible asset kinds, to make sure we return the result if possible
|
187
|
+
const assets = await this.getAssets([]);
|
170
188
|
return assets.filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
|
171
189
|
}
|
172
190
|
async unregisterAsset(ref) {
|
@@ -191,6 +209,44 @@ class AssetManager {
|
|
191
209
|
definitionsManager_1.definitionsManager.clearCache();
|
192
210
|
return await repositoryManager_1.repositoryManager.ensureAsset(uri.handle, uri.name, uri.version, wait);
|
193
211
|
}
|
212
|
+
async cleanupUnusedProviders() {
|
213
|
+
const unusedProviders = await repositoryManager_1.repositoryManager.getUnusedProviders();
|
214
|
+
if (unusedProviders.length < 1) {
|
215
|
+
return;
|
216
|
+
}
|
217
|
+
console.log('Cleaning up unused providers: ', unusedProviders);
|
218
|
+
await Promise.all(unusedProviders.map((ref) => {
|
219
|
+
return this.unregisterAsset(ref);
|
220
|
+
}));
|
221
|
+
}
|
222
|
+
async upgradeAllProviders() {
|
223
|
+
const providers = await definitionsManager_1.definitionsManager.getProviderDefinitions();
|
224
|
+
const names = providers.map((p) => p.definition.metadata.name);
|
225
|
+
const refs = await repositoryManager_1.repositoryManager.getUpdatableAssets(names);
|
226
|
+
if (refs.length < 1) {
|
227
|
+
await this.cleanupUnusedProviders();
|
228
|
+
return;
|
229
|
+
}
|
230
|
+
console.log('Installing updates', refs);
|
231
|
+
const updateAll = async () => {
|
232
|
+
try {
|
233
|
+
//We change to a temp dir to avoid issues with the current working directory
|
234
|
+
process.chdir(node_os_1.default.tmpdir());
|
235
|
+
await nodejs_registry_utils_1.Actions.install(new progressListener_1.ProgressListener(), refs, {});
|
236
|
+
await this.cleanupUnusedProviders();
|
237
|
+
}
|
238
|
+
catch (e) {
|
239
|
+
console.error(`Failed to update assets: ${refs.join(',')}`, e);
|
240
|
+
throw e;
|
241
|
+
}
|
242
|
+
cacheManager_1.cacheManager.flush();
|
243
|
+
definitionsManager_1.definitionsManager.clearCache();
|
244
|
+
};
|
245
|
+
return taskManager_1.taskManager.add(`asset:update`, updateAll, {
|
246
|
+
name: `Installing ${refs.length} updates`,
|
247
|
+
group: 'asset:update:check',
|
248
|
+
});
|
249
|
+
}
|
194
250
|
async maybeGenerateCode(ref, ymlPath, block) {
|
195
251
|
ref = (0, nodejs_utils_1.normalizeKapetaUri)(ref);
|
196
252
|
if (await codeGeneratorManager_1.codeGeneratorManager.canGenerateCode(block)) {
|
@@ -11,6 +11,7 @@ declare class DefinitionsManager {
|
|
11
11
|
exists(ref: string): Promise<boolean>;
|
12
12
|
getProviderDefinitions(): Promise<DefinitionInfo[]>;
|
13
13
|
getDefinition(ref: string): Promise<DefinitionInfo | undefined>;
|
14
|
+
getLatestDefinition(name: string): Promise<DefinitionInfo | undefined>;
|
14
15
|
getVersions(assetName: string): Promise<DefinitionInfo[]>;
|
15
16
|
clearCache(): void;
|
16
17
|
}
|
@@ -136,6 +136,19 @@ class DefinitionsManager {
|
|
136
136
|
return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
|
137
137
|
});
|
138
138
|
}
|
139
|
+
async getLatestDefinition(name) {
|
140
|
+
const definitions = await this.getDefinitions();
|
141
|
+
const allVersions = definitions.filter((d) => {
|
142
|
+
return d.version !== 'local' && d.definition.metadata.name === name;
|
143
|
+
});
|
144
|
+
if (allVersions.length === 0) {
|
145
|
+
return;
|
146
|
+
}
|
147
|
+
allVersions.sort((a, b) => {
|
148
|
+
return (0, nodejs_utils_1.parseVersion)(a.version).compareTo((0, nodejs_utils_1.parseVersion)(b.version)) * -1;
|
149
|
+
});
|
150
|
+
return allVersions[0];
|
151
|
+
}
|
139
152
|
async getVersions(assetName) {
|
140
153
|
const uri = (0, nodejs_utils_1.parseKapetaUri)(assetName);
|
141
154
|
const definitions = await this.getDefinitions();
|
@@ -19,6 +19,15 @@ declare class RepositoryManager extends EventEmitter {
|
|
19
19
|
setSourceOfChangeFor(file: string, source: SourceOfChange): Promise<void>;
|
20
20
|
clearSourceOfChangeFor(file: string): Promise<void>;
|
21
21
|
ensureDefaultProviders(): Promise<void>;
|
22
|
+
/**
|
23
|
+
* Will go through all available assets and get a list of
|
24
|
+
* providers that are not referenced anywhere.
|
25
|
+
*
|
26
|
+
* It will also make sure to not include the latest version of an asset.
|
27
|
+
*
|
28
|
+
*/
|
29
|
+
getUnusedProviders(): Promise<string[]>;
|
30
|
+
getUpdatableAssets(allNames: string[]): Promise<string[]>;
|
22
31
|
private scheduleInstallation;
|
23
32
|
ensureAsset(handle: string, name: string, version: string, wait?: boolean): Promise<undefined | Task[]>;
|
24
33
|
}
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.repositoryManager = void 0;
|
11
11
|
const node_os_1 = __importDefault(require("node:os"));
|
12
12
|
const socketManager_1 = require("./socketManager");
|
13
|
+
const schemas_1 = require("@kapeta/schemas");
|
13
14
|
const nodejs_registry_utils_1 = require("@kapeta/nodejs-registry-utils");
|
14
15
|
const definitionsManager_1 = require("./definitionsManager");
|
15
16
|
const taskManager_1 = require("./taskManager");
|
@@ -69,6 +70,97 @@ class RepositoryManager extends node_events_1.EventEmitter {
|
|
69
70
|
socketManager_1.socketManager.emitGlobal(EVENT_DEFAULT_PROVIDERS_END, {});
|
70
71
|
});
|
71
72
|
}
|
73
|
+
/**
|
74
|
+
* Will go through all available assets and get a list of
|
75
|
+
* providers that are not referenced anywhere.
|
76
|
+
*
|
77
|
+
* It will also make sure to not include the latest version of an asset.
|
78
|
+
*
|
79
|
+
*/
|
80
|
+
async getUnusedProviders() {
|
81
|
+
const allDefinitions = await definitionsManager_1.definitionsManager.getDefinitions();
|
82
|
+
const blocks = [];
|
83
|
+
const plans = [];
|
84
|
+
const providerMap = new Map();
|
85
|
+
const providerVersions = {};
|
86
|
+
const unusedProviders = new Set();
|
87
|
+
allDefinitions.forEach((d) => {
|
88
|
+
if (d.definition.kind === 'core/plan') {
|
89
|
+
plans.push(d);
|
90
|
+
return;
|
91
|
+
}
|
92
|
+
if (d.definition.kind.startsWith('core/')) {
|
93
|
+
const ref = (0, nodejs_utils_1.normalizeKapetaUri)(`${d.definition.metadata.name}:${d.version}`);
|
94
|
+
providerMap.set(ref, d);
|
95
|
+
if (!providerVersions[d.definition.metadata.name]) {
|
96
|
+
providerVersions[d.definition.metadata.name] = new Set();
|
97
|
+
}
|
98
|
+
providerVersions[d.definition.metadata.name].add(d.version);
|
99
|
+
unusedProviders.add(ref);
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
blocks.push(d);
|
103
|
+
});
|
104
|
+
const latestVersions = {};
|
105
|
+
Object.entries(providerVersions).forEach(([name, versions]) => {
|
106
|
+
const versionArray = Array.from(versions);
|
107
|
+
versionArray.sort((a, b) => {
|
108
|
+
return (0, nodejs_utils_1.parseVersion)(a).compareTo((0, nodejs_utils_1.parseVersion)(b)) * -1;
|
109
|
+
});
|
110
|
+
latestVersions[name] = versionArray[0];
|
111
|
+
});
|
112
|
+
function markDependencyAsUsed(dep) {
|
113
|
+
const uri = (0, nodejs_utils_1.parseKapetaUri)(dep.name);
|
114
|
+
const ref = uri.toNormalizedString();
|
115
|
+
if (unusedProviders.has(ref)) {
|
116
|
+
unusedProviders.delete(ref);
|
117
|
+
}
|
118
|
+
}
|
119
|
+
plans.forEach((plan) => {
|
120
|
+
const dependencies = (0, schemas_1.resolveDependencies)(plan.definition);
|
121
|
+
dependencies.forEach(markDependencyAsUsed);
|
122
|
+
});
|
123
|
+
blocks.forEach((block) => {
|
124
|
+
const blockTypeKind = (0, nodejs_utils_1.normalizeKapetaUri)(block.definition.kind);
|
125
|
+
unusedProviders.delete(blockTypeKind);
|
126
|
+
const blockTypeProvider = providerMap.get(blockTypeKind);
|
127
|
+
if (!blockTypeProvider) {
|
128
|
+
console.warn('No provider found for block type', block.definition.kind);
|
129
|
+
return;
|
130
|
+
}
|
131
|
+
const dependencies = (0, schemas_1.resolveDependencies)(block.definition, blockTypeProvider.definition);
|
132
|
+
dependencies.forEach(markDependencyAsUsed);
|
133
|
+
});
|
134
|
+
return Array.from(unusedProviders).filter((ref) => {
|
135
|
+
const uri = (0, nodejs_utils_1.parseKapetaUri)(ref);
|
136
|
+
if (uri.version == 'local') {
|
137
|
+
// Don't delete local assets
|
138
|
+
return false;
|
139
|
+
}
|
140
|
+
// Don't delete the latest version of an asset
|
141
|
+
return latestVersions[uri.fullName] !== uri.version;
|
142
|
+
});
|
143
|
+
}
|
144
|
+
async getUpdatableAssets(allNames) {
|
145
|
+
const names = Array.from(new Set(allNames));
|
146
|
+
const currentVersions = await Promise.all(names.map((name) => definitionsManager_1.definitionsManager.getLatestDefinition(name).catch(() => undefined)));
|
147
|
+
const latestVersions = await Promise.all(names.map((name) => this._registryService.getLatestVersion(name).catch(() => undefined)));
|
148
|
+
return names
|
149
|
+
.map((name, index) => {
|
150
|
+
const currentVersion = currentVersions[index];
|
151
|
+
const latestVersion = latestVersions[index];
|
152
|
+
if (!currentVersion || !latestVersion) {
|
153
|
+
// Shouldn't happen unless the registry is down or an asset was deleted
|
154
|
+
return undefined;
|
155
|
+
}
|
156
|
+
const ref = (0, nodejs_utils_1.normalizeKapetaUri)(`${name}:${latestVersion.version}`);
|
157
|
+
if (currentVersion.version === latestVersion.version) {
|
158
|
+
return undefined;
|
159
|
+
}
|
160
|
+
return ref;
|
161
|
+
})
|
162
|
+
.filter((ref) => !!ref);
|
163
|
+
}
|
72
164
|
async scheduleInstallation(refs) {
|
73
165
|
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
74
166
|
const createInstaller = (ref) => {
|
package/index.ts
CHANGED
@@ -32,6 +32,7 @@ import { defaultProviderInstaller } from './src/utils/DefaultProviderInstaller';
|
|
32
32
|
import { authManager } from './src/authManager';
|
33
33
|
import { codeGeneratorManager } from './src/codeGeneratorManager';
|
34
34
|
import * as Sentry from '@sentry/node';
|
35
|
+
import { assetManager } from './src/assetManager';
|
35
36
|
|
36
37
|
Sentry.init({
|
37
38
|
dsn: 'https://0b7cc946d82c591473d6f95fff5e210b@o4505820837249024.ingest.sentry.io/4506212692000768',
|
@@ -245,6 +246,8 @@ export default {
|
|
245
246
|
console.error('Failed to install default providers.', e);
|
246
247
|
}
|
247
248
|
|
249
|
+
assetManager.startUpgradeInterval();
|
250
|
+
|
248
251
|
resolve({ host, port, dockerStatus: containerManager.isAlive() });
|
249
252
|
});
|
250
253
|
currentServer.host = host;
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
@@ -6,18 +6,17 @@
|
|
6
6
|
import Router from 'express-promise-router';
|
7
7
|
import { corsHandler } from './middleware/cors';
|
8
8
|
import { KapetaAPI } from '@kapeta/nodejs-api-client';
|
9
|
-
import
|
9
|
+
import { Config } from '@kapeta/nodejs-registry-utils';
|
10
10
|
const { createAPIRoute } = require('@kapeta/web-microfrontend/server');
|
11
11
|
const packageJson = require('../package.json');
|
12
12
|
|
13
13
|
const router = Router();
|
14
14
|
|
15
|
-
const remoteServices = ClusterConfiguration.getClusterConfig().remoteServices ?? {};
|
16
15
|
router.use('/', corsHandler);
|
17
16
|
|
18
17
|
router.use(
|
19
18
|
'/registry',
|
20
|
-
createAPIRoute(
|
19
|
+
createAPIRoute(Config.data?.registry?.url ?? 'https://registry.kapeta.com', {
|
21
20
|
nonce: false,
|
22
21
|
userAgent: `KapetaDesktopCluster/${packageJson.version}`,
|
23
22
|
tokenFetcher: () => {
|
package/src/assetManager.ts
CHANGED
@@ -18,8 +18,10 @@ import { taskManager } from './taskManager';
|
|
18
18
|
import { SourceOfChange } from './types';
|
19
19
|
import { cacheManager } from './cacheManager';
|
20
20
|
import uuid from 'node-uuid';
|
21
|
+
import os from 'node:os';
|
21
22
|
|
22
23
|
const CACHE_TTL = 60 * 60 * 1000; // 1 hour
|
24
|
+
const UPGRADE_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
|
23
25
|
|
24
26
|
const toKey = (ref: string) => `assetManager:asset:${ref}`;
|
25
27
|
|
@@ -64,6 +66,22 @@ function parseRef(ref: string) {
|
|
64
66
|
}
|
65
67
|
|
66
68
|
class AssetManager {
|
69
|
+
public startUpgradeInterval() {
|
70
|
+
console.debug('Checking for upgrades...');
|
71
|
+
this.upgradeAllProviders()
|
72
|
+
.then((task) => {
|
73
|
+
return task && task.wait();
|
74
|
+
})
|
75
|
+
.catch((e) => {
|
76
|
+
console.error('Failed to upgrade providers', e);
|
77
|
+
})
|
78
|
+
.finally(() => {
|
79
|
+
setTimeout(() => {
|
80
|
+
this.startUpgradeInterval();
|
81
|
+
}, UPGRADE_CHECK_INTERVAL);
|
82
|
+
});
|
83
|
+
}
|
84
|
+
|
67
85
|
/**
|
68
86
|
*
|
69
87
|
* @param {string[]} [assetKinds]
|
@@ -226,7 +244,8 @@ class AssetManager {
|
|
226
244
|
|
227
245
|
definitionsManager.clearCache();
|
228
246
|
|
229
|
-
|
247
|
+
// Get all possible asset kinds, to make sure we return the result if possible
|
248
|
+
const assets = await this.getAssets([]);
|
230
249
|
|
231
250
|
return assets.filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
|
232
251
|
}
|
@@ -258,6 +277,52 @@ class AssetManager {
|
|
258
277
|
return await repositoryManager.ensureAsset(uri.handle, uri.name, uri.version, wait);
|
259
278
|
}
|
260
279
|
|
280
|
+
private async cleanupUnusedProviders(): Promise<void> {
|
281
|
+
const unusedProviders = await repositoryManager.getUnusedProviders();
|
282
|
+
if (unusedProviders.length < 1) {
|
283
|
+
return;
|
284
|
+
}
|
285
|
+
|
286
|
+
console.log('Cleaning up unused providers: ', unusedProviders);
|
287
|
+
await Promise.all(
|
288
|
+
unusedProviders.map((ref) => {
|
289
|
+
return this.unregisterAsset(ref);
|
290
|
+
})
|
291
|
+
);
|
292
|
+
}
|
293
|
+
|
294
|
+
private async upgradeAllProviders() {
|
295
|
+
const providers = await definitionsManager.getProviderDefinitions();
|
296
|
+
const names = providers.map((p) => p.definition.metadata.name);
|
297
|
+
|
298
|
+
const refs = await repositoryManager.getUpdatableAssets(names);
|
299
|
+
|
300
|
+
if (refs.length < 1) {
|
301
|
+
await this.cleanupUnusedProviders();
|
302
|
+
return;
|
303
|
+
}
|
304
|
+
|
305
|
+
console.log('Installing updates', refs);
|
306
|
+
const updateAll = async () => {
|
307
|
+
try {
|
308
|
+
//We change to a temp dir to avoid issues with the current working directory
|
309
|
+
process.chdir(os.tmpdir());
|
310
|
+
await Actions.install(new ProgressListener(), refs, {});
|
311
|
+
await this.cleanupUnusedProviders();
|
312
|
+
} catch (e) {
|
313
|
+
console.error(`Failed to update assets: ${refs.join(',')}`, e);
|
314
|
+
throw e;
|
315
|
+
}
|
316
|
+
cacheManager.flush();
|
317
|
+
definitionsManager.clearCache();
|
318
|
+
};
|
319
|
+
|
320
|
+
return taskManager.add(`asset:update`, updateAll, {
|
321
|
+
name: `Installing ${refs.length} updates`,
|
322
|
+
group: 'asset:update:check',
|
323
|
+
});
|
324
|
+
}
|
325
|
+
|
261
326
|
private async maybeGenerateCode(ref: string, ymlPath: string, block: Definition) {
|
262
327
|
ref = normalizeKapetaUri(ref);
|
263
328
|
if (await codeGeneratorManager.canGenerateCode(block)) {
|
@@ -4,7 +4,7 @@
|
|
4
4
|
*/
|
5
5
|
|
6
6
|
import ClusterConfiguration, { DefinitionInfo } from '@kapeta/local-cluster-config';
|
7
|
-
import { parseKapetaUri, normalizeKapetaUri } from '@kapeta/nodejs-utils';
|
7
|
+
import { parseKapetaUri, normalizeKapetaUri, parseVersion } from '@kapeta/nodejs-utils';
|
8
8
|
import { cacheManager, doCached } from './cacheManager';
|
9
9
|
import { KapetaAPI } from '@kapeta/nodejs-api-client';
|
10
10
|
import { Plan } from '@kapeta/schemas';
|
@@ -170,6 +170,23 @@ class DefinitionsManager {
|
|
170
170
|
});
|
171
171
|
}
|
172
172
|
|
173
|
+
public async getLatestDefinition(name: string) {
|
174
|
+
const definitions = await this.getDefinitions();
|
175
|
+
const allVersions = definitions.filter((d) => {
|
176
|
+
return d.version !== 'local' && d.definition.metadata.name === name;
|
177
|
+
});
|
178
|
+
|
179
|
+
if (allVersions.length === 0) {
|
180
|
+
return;
|
181
|
+
}
|
182
|
+
|
183
|
+
allVersions.sort((a, b) => {
|
184
|
+
return parseVersion(a.version).compareTo(parseVersion(b.version)) * -1;
|
185
|
+
});
|
186
|
+
|
187
|
+
return allVersions[0];
|
188
|
+
}
|
189
|
+
|
173
190
|
public async getVersions(assetName: string) {
|
174
191
|
const uri = parseKapetaUri(assetName);
|
175
192
|
const definitions = await this.getDefinitions();
|
package/src/repositoryManager.ts
CHANGED
@@ -5,16 +5,17 @@
|
|
5
5
|
|
6
6
|
import os from 'node:os';
|
7
7
|
import { socketManager } from './socketManager';
|
8
|
-
import { Dependency } from '@kapeta/schemas';
|
9
|
-
import { Actions, Config, RegistryService } from '@kapeta/nodejs-registry-utils';
|
8
|
+
import { DependencyReference, Dependency, resolveDependencies } from '@kapeta/schemas';
|
9
|
+
import { Actions, AssetVersion, Config, RegistryService } from '@kapeta/nodejs-registry-utils';
|
10
10
|
import { definitionsManager } from './definitionsManager';
|
11
11
|
import { Task, taskManager } from './taskManager';
|
12
|
-
import { normalizeKapetaUri } from '@kapeta/nodejs-utils';
|
12
|
+
import { normalizeKapetaUri, parseKapetaUri, parseVersion } from '@kapeta/nodejs-utils';
|
13
13
|
import { ProgressListener } from './progressListener';
|
14
14
|
import { RepositoryWatcher } from './RepositoryWatcher';
|
15
15
|
import { SourceOfChange } from './types';
|
16
16
|
import { cacheManager } from './cacheManager';
|
17
17
|
import { EventEmitter } from 'node:events';
|
18
|
+
import { DefinitionInfo } from '@kapeta/local-cluster-config';
|
18
19
|
|
19
20
|
const EVENT_DEFAULT_PROVIDERS_START = 'default-providers-start';
|
20
21
|
const EVENT_DEFAULT_PROVIDERS_END = 'default-providers-end';
|
@@ -77,6 +78,116 @@ class RepositoryManager extends EventEmitter {
|
|
77
78
|
});
|
78
79
|
}
|
79
80
|
|
81
|
+
/**
|
82
|
+
* Will go through all available assets and get a list of
|
83
|
+
* providers that are not referenced anywhere.
|
84
|
+
*
|
85
|
+
* It will also make sure to not include the latest version of an asset.
|
86
|
+
*
|
87
|
+
*/
|
88
|
+
public async getUnusedProviders(): Promise<string[]> {
|
89
|
+
const allDefinitions: DefinitionInfo[] = await definitionsManager.getDefinitions();
|
90
|
+
const blocks: DefinitionInfo[] = [];
|
91
|
+
const plans: DefinitionInfo[] = [];
|
92
|
+
const providerMap = new Map<string, DefinitionInfo>();
|
93
|
+
const providerVersions: { [name: string]: Set<string> } = {};
|
94
|
+
const unusedProviders = new Set<string>();
|
95
|
+
allDefinitions.forEach((d) => {
|
96
|
+
if (d.definition.kind === 'core/plan') {
|
97
|
+
plans.push(d);
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
|
101
|
+
if (d.definition.kind.startsWith('core/')) {
|
102
|
+
const ref = normalizeKapetaUri(`${d.definition.metadata.name}:${d.version}`);
|
103
|
+
providerMap.set(ref, d);
|
104
|
+
if (!providerVersions[d.definition.metadata.name]) {
|
105
|
+
providerVersions[d.definition.metadata.name] = new Set<string>();
|
106
|
+
}
|
107
|
+
providerVersions[d.definition.metadata.name].add(d.version);
|
108
|
+
unusedProviders.add(ref);
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
blocks.push(d);
|
112
|
+
});
|
113
|
+
|
114
|
+
const latestVersions: { [name: string]: string } = {};
|
115
|
+
Object.entries(providerVersions).forEach(([name, versions]) => {
|
116
|
+
const versionArray = Array.from(versions);
|
117
|
+
versionArray.sort((a, b) => {
|
118
|
+
return parseVersion(a).compareTo(parseVersion(b)) * -1;
|
119
|
+
});
|
120
|
+
latestVersions[name] = versionArray[0];
|
121
|
+
});
|
122
|
+
|
123
|
+
function markDependencyAsUsed(dep: DependencyReference) {
|
124
|
+
const uri = parseKapetaUri(dep.name);
|
125
|
+
const ref = uri.toNormalizedString();
|
126
|
+
if (unusedProviders.has(ref)) {
|
127
|
+
unusedProviders.delete(ref);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
plans.forEach((plan) => {
|
132
|
+
const dependencies = resolveDependencies(plan.definition);
|
133
|
+
dependencies.forEach(markDependencyAsUsed);
|
134
|
+
});
|
135
|
+
|
136
|
+
blocks.forEach((block) => {
|
137
|
+
const blockTypeKind = normalizeKapetaUri(block.definition.kind);
|
138
|
+
unusedProviders.delete(blockTypeKind);
|
139
|
+
const blockTypeProvider = providerMap.get(blockTypeKind);
|
140
|
+
if (!blockTypeProvider) {
|
141
|
+
console.warn('No provider found for block type', block.definition.kind);
|
142
|
+
return;
|
143
|
+
}
|
144
|
+
const dependencies = resolveDependencies(block.definition, blockTypeProvider.definition);
|
145
|
+
dependencies.forEach(markDependencyAsUsed);
|
146
|
+
});
|
147
|
+
|
148
|
+
return Array.from(unusedProviders).filter((ref) => {
|
149
|
+
const uri = parseKapetaUri(ref);
|
150
|
+
if (uri.version == 'local') {
|
151
|
+
// Don't delete local assets
|
152
|
+
return false;
|
153
|
+
}
|
154
|
+
|
155
|
+
// Don't delete the latest version of an asset
|
156
|
+
return latestVersions[uri.fullName] !== uri.version;
|
157
|
+
});
|
158
|
+
}
|
159
|
+
|
160
|
+
public async getUpdatableAssets(allNames: string[]): Promise<string[]> {
|
161
|
+
const names = Array.from(new Set<string>(allNames));
|
162
|
+
|
163
|
+
const currentVersions = await Promise.all(
|
164
|
+
names.map((name) => definitionsManager.getLatestDefinition(name).catch(() => undefined))
|
165
|
+
);
|
166
|
+
|
167
|
+
const latestVersions = await Promise.all(
|
168
|
+
names.map((name) => this._registryService.getLatestVersion(name).catch(() => undefined))
|
169
|
+
);
|
170
|
+
|
171
|
+
return names
|
172
|
+
.map((name, index) => {
|
173
|
+
const currentVersion: DefinitionInfo | undefined = currentVersions[index];
|
174
|
+
const latestVersion: AssetVersion | undefined = latestVersions[index];
|
175
|
+
if (!currentVersion || !latestVersion) {
|
176
|
+
// Shouldn't happen unless the registry is down or an asset was deleted
|
177
|
+
return undefined;
|
178
|
+
}
|
179
|
+
|
180
|
+
const ref = normalizeKapetaUri(`${name}:${latestVersion.version}`);
|
181
|
+
|
182
|
+
if (currentVersion.version === latestVersion.version) {
|
183
|
+
return undefined;
|
184
|
+
}
|
185
|
+
|
186
|
+
return ref;
|
187
|
+
})
|
188
|
+
.filter((ref) => !!ref) as string[];
|
189
|
+
}
|
190
|
+
|
80
191
|
private async scheduleInstallation(refs: string[]): Promise<Task[]> {
|
81
192
|
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
82
193
|
const createInstaller = (ref: string) => {
|