@kapeta/local-cluster-service 0.14.4 → 0.15.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/dist/cjs/index.js +18 -1
- package/dist/cjs/src/api.js +1 -1
- package/dist/cjs/src/definitionsManager.js +3 -0
- package/dist/cjs/src/progressListener.d.ts +1 -0
- package/dist/cjs/src/progressListener.js +39 -26
- package/dist/cjs/src/repositoryManager.d.ts +1 -0
- package/dist/cjs/src/repositoryManager.js +28 -7
- package/dist/cjs/src/taskManager.js +4 -0
- package/dist/cjs/src/utils/commandLineUtils.d.ts +2 -0
- package/dist/cjs/src/utils/commandLineUtils.js +23 -0
- package/dist/esm/index.js +18 -1
- package/dist/esm/src/api.js +3 -3
- package/dist/esm/src/assetManager.js +1 -1
- package/dist/esm/src/definitionsManager.js +3 -0
- package/dist/esm/src/progressListener.d.ts +1 -0
- package/dist/esm/src/progressListener.js +39 -26
- package/dist/esm/src/repositoryManager.d.ts +1 -0
- package/dist/esm/src/repositoryManager.js +28 -7
- package/dist/esm/src/taskManager.js +4 -0
- package/dist/esm/src/utils/commandLineUtils.d.ts +2 -0
- package/dist/esm/src/utils/commandLineUtils.js +18 -0
- package/index.ts +18 -7
- package/package.json +2 -2
- package/src/api.ts +18 -15
- package/src/assetManager.ts +11 -7
- package/src/containerManager.ts +8 -7
- package/src/definitionsManager.ts +3 -0
- package/src/progressListener.ts +43 -28
- package/src/repositoryManager.ts +29 -7
- package/src/taskManager.ts +5 -0
- package/src/utils/commandLineUtils.ts +26 -0
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.15.1](https://github.com/kapetacom/local-cluster-service/compare/v0.15.0...v0.15.1) (2023-08-09)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Move providers into TS file - compile doesnt copy json ([1e2681a](https://github.com/kapetacom/local-cluster-service/commit/1e2681ab85592366e6eb99aab72a514b4a1503c4))
|
7
|
+
|
8
|
+
# [0.15.0](https://github.com/kapetacom/local-cluster-service/compare/v0.14.4...v0.15.0) (2023-08-09)
|
9
|
+
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* auto-install core providers and cli when starting ([6495dce](https://github.com/kapetacom/local-cluster-service/commit/6495dcea33218fb214ee9df682ef327b91ebf817))
|
14
|
+
|
1
15
|
## [0.14.4](https://github.com/kapetacom/local-cluster-service/compare/v0.14.3...v0.14.4) (2023-08-09)
|
2
16
|
|
3
17
|
|
package/dist/cjs/index.js
CHANGED
@@ -24,6 +24,8 @@ const routes_10 = __importDefault(require("./src/tasks/routes"));
|
|
24
24
|
const api_1 = __importDefault(require("./src/api"));
|
25
25
|
const utils_1 = require("./src/utils/utils");
|
26
26
|
const request_1 = __importDefault(require("request"));
|
27
|
+
const repositoryManager_1 = require("./src/repositoryManager");
|
28
|
+
const commandLineUtils_1 = require("./src/utils/commandLineUtils");
|
27
29
|
let currentServer = null;
|
28
30
|
function createServer() {
|
29
31
|
const app = (0, express_1.default)();
|
@@ -144,7 +146,22 @@ exports.default = {
|
|
144
146
|
reject(err);
|
145
147
|
});
|
146
148
|
const bindHost = (0, utils_1.getBindHost)(host);
|
147
|
-
currentServer.listen(port, bindHost, () =>
|
149
|
+
currentServer.listen(port, bindHost, () => {
|
150
|
+
try {
|
151
|
+
(0, commandLineUtils_1.ensureCLI)().catch((e) => console.error('Failed to install CLI.', e));
|
152
|
+
}
|
153
|
+
catch (e) {
|
154
|
+
console.error('Failed to install CLI.', e);
|
155
|
+
}
|
156
|
+
try {
|
157
|
+
// Start installation process for all default providers
|
158
|
+
repositoryManager_1.repositoryManager.ensureDefaultProviders();
|
159
|
+
}
|
160
|
+
catch (e) {
|
161
|
+
console.error('Failed to install default providers.', e);
|
162
|
+
}
|
163
|
+
resolve({ host, port, dockerStatus: containerManager_1.containerManager.isAlive() });
|
164
|
+
});
|
148
165
|
currentServer.host = host;
|
149
166
|
currentServer.port = port;
|
150
167
|
});
|
package/dist/cjs/src/api.js
CHANGED
@@ -47,6 +47,9 @@ class DefinitionsManager {
|
|
47
47
|
getDefinition(ref) {
|
48
48
|
const uri = (0, nodejs_utils_1.parseKapetaUri)(ref);
|
49
49
|
return this.getDefinitions().find((d) => {
|
50
|
+
if (!uri.version) {
|
51
|
+
return d.definition.metadata.name === uri.fullName;
|
52
|
+
}
|
50
53
|
return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
|
51
54
|
});
|
52
55
|
}
|
@@ -6,6 +6,7 @@ declare class ProgressListener {
|
|
6
6
|
run(command: string, directory?: string): Promise<{
|
7
7
|
exit: number;
|
8
8
|
signal: NodeJS.Signals | null;
|
9
|
+
output: string;
|
9
10
|
}>;
|
10
11
|
progress(label: string, callback: () => void | Promise<void>): Promise<void>;
|
11
12
|
check(message: string, ok: boolean | Promise<boolean> | (() => Promise<boolean>)): Promise<void>;
|
@@ -13,38 +13,51 @@ class ProgressListener {
|
|
13
13
|
type: 'info',
|
14
14
|
message: `Running command "${command}"`,
|
15
15
|
});
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
16
|
+
const firstCommand = command.split(' ')[0];
|
17
|
+
return new Promise(async (resolve, reject) => {
|
18
|
+
try {
|
19
|
+
const chunks = [];
|
20
|
+
const child = (0, nodejs_process_1.spawn)(command, [], {
|
21
|
+
cwd: directory ? directory : process.cwd(),
|
22
|
+
detached: true,
|
23
|
+
shell: true,
|
24
|
+
});
|
25
|
+
child.onData((data) => {
|
26
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: data.line });
|
27
|
+
});
|
28
|
+
if (child.process.stdout) {
|
29
|
+
child.process.stdout.on('data', (data) => {
|
30
|
+
chunks.push(data);
|
30
31
|
});
|
31
|
-
reject(new Error(`Command "${command}" exited with code ${exit}`));
|
32
32
|
}
|
33
|
-
|
33
|
+
child.process.on('exit', (exit, signal) => {
|
34
|
+
if (exit !== 0) {
|
35
|
+
this.socketManager.emit(`install`, 'install:log', {
|
36
|
+
type: 'info',
|
37
|
+
message: `"${command}" failed: "${exit}"`,
|
38
|
+
});
|
39
|
+
reject(new Error(`Command "${command}" exited with code ${exit}`));
|
40
|
+
}
|
41
|
+
else {
|
42
|
+
this.socketManager.emit(`install`, 'install:log', {
|
43
|
+
type: 'info',
|
44
|
+
message: `Command OK: "${command}"`,
|
45
|
+
});
|
46
|
+
resolve({ exit, signal, output: Buffer.concat(chunks).toString() });
|
47
|
+
}
|
48
|
+
});
|
49
|
+
child.process.on('error', (err) => {
|
34
50
|
this.socketManager.emit(`install`, 'install:log', {
|
35
51
|
type: 'info',
|
36
|
-
message: `
|
52
|
+
message: `"${command}" failed: "${err.message}"`,
|
37
53
|
});
|
38
|
-
|
39
|
-
}
|
40
|
-
});
|
41
|
-
child.process.on('error', (err) => {
|
42
|
-
this.socketManager.emit(`install`, 'install:log', {
|
43
|
-
type: 'info',
|
44
|
-
message: `"${command}" failed: "${err.message}"`,
|
54
|
+
reject(err);
|
45
55
|
});
|
46
|
-
|
47
|
-
}
|
56
|
+
await child.wait();
|
57
|
+
}
|
58
|
+
catch (e) {
|
59
|
+
reject(e);
|
60
|
+
}
|
48
61
|
});
|
49
62
|
}
|
50
63
|
async progress(label, callback) {
|
@@ -8,6 +8,7 @@ declare class RepositoryManager {
|
|
8
8
|
setChangeEventsEnabled(enabled: boolean): void;
|
9
9
|
listenForChanges(): void;
|
10
10
|
stopListening(): void;
|
11
|
+
ensureDefaultProviders(): void;
|
11
12
|
private _install;
|
12
13
|
ensureAsset(handle: string, name: string, version: string, wait?: boolean): Promise<undefined | Task[]>;
|
13
14
|
}
|
@@ -18,6 +18,20 @@ const definitionsManager_1 = require("./definitionsManager");
|
|
18
18
|
const taskManager_1 = require("./taskManager");
|
19
19
|
const utils_1 = require("./utils/utils");
|
20
20
|
const assetManager_1 = require("./assetManager");
|
21
|
+
const DEFAULT_PROVIDERS = [
|
22
|
+
'kapeta/block-type-service',
|
23
|
+
'kapeta/block-type-frontend',
|
24
|
+
'kapeta/block-type-gateway-http',
|
25
|
+
'kapeta/resource-type-rest-api',
|
26
|
+
'kapeta/resource-type-rest-client',
|
27
|
+
'kapeta/resource-type-web-page',
|
28
|
+
'kapeta/resource-type-web-fragment',
|
29
|
+
'kapeta/resource-type-mongodb',
|
30
|
+
'kapeta/resource-type-postgresql',
|
31
|
+
'kapeta/language-target-react-ts',
|
32
|
+
'kapeta/language-target-nodejs',
|
33
|
+
'kapeta/language-target-java-spring-boot',
|
34
|
+
];
|
21
35
|
const INSTALL_ATTEMPTED = {};
|
22
36
|
class RepositoryManager {
|
23
37
|
changeEventsEnabled;
|
@@ -107,7 +121,10 @@ class RepositoryManager {
|
|
107
121
|
this.watcher();
|
108
122
|
this.watcher = undefined;
|
109
123
|
}
|
110
|
-
|
124
|
+
ensureDefaultProviders() {
|
125
|
+
this._install(DEFAULT_PROVIDERS);
|
126
|
+
}
|
127
|
+
_install(refs) {
|
111
128
|
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
112
129
|
const createInstaller = (ref) => {
|
113
130
|
return async () => {
|
@@ -117,7 +134,7 @@ class RepositoryManager {
|
|
117
134
|
if (definitionsManager_1.definitionsManager.exists(ref)) {
|
118
135
|
return;
|
119
136
|
}
|
120
|
-
console.log(`Installing asset: ${ref}`);
|
137
|
+
//console.log(`Installing asset: ${ref}`);
|
121
138
|
INSTALL_ATTEMPTED[ref] = true;
|
122
139
|
//Auto-install missing asset
|
123
140
|
try {
|
@@ -127,12 +144,16 @@ class RepositoryManager {
|
|
127
144
|
this.setChangeEventsEnabled(false);
|
128
145
|
await nodejs_registry_utils_1.Actions.install(progressListener_1.progressListener, [ref], {});
|
129
146
|
}
|
147
|
+
catch (e) {
|
148
|
+
console.error(`Failed to install asset: ${ref}`, e);
|
149
|
+
throw e;
|
150
|
+
}
|
130
151
|
finally {
|
131
152
|
this.setChangeEventsEnabled(true);
|
132
153
|
}
|
133
154
|
definitionsManager_1.definitionsManager.clearCache();
|
134
155
|
assetManager_1.assetManager.clearCache();
|
135
|
-
console.log(`Asset installed: ${ref}`);
|
156
|
+
//console.log(`Asset installed: ${ref}`);
|
136
157
|
};
|
137
158
|
};
|
138
159
|
const tasks = [];
|
@@ -150,7 +171,7 @@ class RepositoryManager {
|
|
150
171
|
}
|
151
172
|
const task = taskManager_1.taskManager.add(`asset:install:${ref}`, createInstaller(ref), {
|
152
173
|
name: `Installing ${ref}`,
|
153
|
-
group: 'asset:install:',
|
174
|
+
group: 'asset:install:', //Group prevents multiple tasks from running at the same time
|
154
175
|
});
|
155
176
|
tasks.push(task);
|
156
177
|
}
|
@@ -189,17 +210,17 @@ class RepositoryManager {
|
|
189
210
|
this._cache[ref] = true;
|
190
211
|
let tasks = undefined;
|
191
212
|
if (!installedAsset) {
|
192
|
-
tasks =
|
213
|
+
tasks = this._install([ref]);
|
193
214
|
}
|
194
215
|
else {
|
195
216
|
//Ensure dependencies are installed
|
196
217
|
const refs = assetVersion.dependencies.map((dep) => dep.name);
|
197
218
|
if (refs.length > 0) {
|
198
|
-
tasks =
|
219
|
+
tasks = this._install(refs);
|
199
220
|
}
|
200
221
|
}
|
201
222
|
if (tasks && wait) {
|
202
|
-
await Promise.all(tasks.map((t) => t.
|
223
|
+
await Promise.all(tasks.map((t) => t.wait()));
|
203
224
|
}
|
204
225
|
return tasks;
|
205
226
|
}
|
@@ -65,6 +65,8 @@ function createFuture() {
|
|
65
65
|
resolve = res;
|
66
66
|
reject = rej;
|
67
67
|
});
|
68
|
+
// Ignore unhandled promise rejections
|
69
|
+
promise.catch(() => { });
|
68
70
|
return {
|
69
71
|
promise,
|
70
72
|
resolve,
|
@@ -133,6 +135,7 @@ class TaskManager {
|
|
133
135
|
return;
|
134
136
|
}
|
135
137
|
}
|
138
|
+
const startTime = Date.now();
|
136
139
|
try {
|
137
140
|
task.status = TaskStatus.RUNNING;
|
138
141
|
task.emitUpdate();
|
@@ -149,6 +152,7 @@ class TaskManager {
|
|
149
152
|
}
|
150
153
|
finally {
|
151
154
|
this.remove(task.id);
|
155
|
+
console.log(`Task ${task.id} completed in ${Date.now() - startTime}ms`);
|
152
156
|
}
|
153
157
|
if (task.metadata.group) {
|
154
158
|
const nextTaskInGroup = this._tasks.find((t) => t.id !== task.id && t.metadata.group === task.metadata.group && t.status === TaskStatus.PENDING);
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.ensureCLI = exports.hasCLI = void 0;
|
4
|
+
const nodejs_process_1 = require("@kapeta/nodejs-process");
|
5
|
+
const taskManager_1 = require("../taskManager");
|
6
|
+
async function hasCLI() {
|
7
|
+
return (0, nodejs_process_1.hasApp)('kap');
|
8
|
+
}
|
9
|
+
exports.hasCLI = hasCLI;
|
10
|
+
async function ensureCLI() {
|
11
|
+
if (await hasCLI()) {
|
12
|
+
return null;
|
13
|
+
}
|
14
|
+
return taskManager_1.taskManager.add(`cli:install`, () => {
|
15
|
+
const process = (0, nodejs_process_1.spawn)('npm', ['install', '-g', '@kapeta/kap'], {
|
16
|
+
shell: true,
|
17
|
+
});
|
18
|
+
return process.wait();
|
19
|
+
}, {
|
20
|
+
name: `Installing Kapeta CLI`,
|
21
|
+
});
|
22
|
+
}
|
23
|
+
exports.ensureCLI = ensureCLI;
|
package/dist/esm/index.js
CHANGED
@@ -19,6 +19,8 @@ import TaskRoutes from './src/tasks/routes';
|
|
19
19
|
import APIRoutes from './src/api';
|
20
20
|
import { getBindHost } from './src/utils/utils';
|
21
21
|
import request from 'request';
|
22
|
+
import { repositoryManager } from './src/repositoryManager';
|
23
|
+
import { ensureCLI } from './src/utils/commandLineUtils';
|
22
24
|
let currentServer = null;
|
23
25
|
function createServer() {
|
24
26
|
const app = express();
|
@@ -139,7 +141,22 @@ export default {
|
|
139
141
|
reject(err);
|
140
142
|
});
|
141
143
|
const bindHost = getBindHost(host);
|
142
|
-
currentServer.listen(port, bindHost, () =>
|
144
|
+
currentServer.listen(port, bindHost, () => {
|
145
|
+
try {
|
146
|
+
ensureCLI().catch((e) => console.error('Failed to install CLI.', e));
|
147
|
+
}
|
148
|
+
catch (e) {
|
149
|
+
console.error('Failed to install CLI.', e);
|
150
|
+
}
|
151
|
+
try {
|
152
|
+
// Start installation process for all default providers
|
153
|
+
repositoryManager.ensureDefaultProviders();
|
154
|
+
}
|
155
|
+
catch (e) {
|
156
|
+
console.error('Failed to install default providers.', e);
|
157
|
+
}
|
158
|
+
resolve({ host, port, dockerStatus: containerManager.isAlive() });
|
159
|
+
});
|
143
160
|
currentServer.host = host;
|
144
161
|
currentServer.port = port;
|
145
162
|
});
|
package/dist/esm/src/api.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import Router from 'express-promise-router';
|
2
|
-
import { corsHandler } from
|
3
|
-
import { KapetaAPI } from
|
2
|
+
import { corsHandler } from './middleware/cors';
|
3
|
+
import { KapetaAPI } from '@kapeta/nodejs-api-client';
|
4
4
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
5
5
|
const { createAPIRoute } = require('@kapeta/web-microfrontend/server');
|
6
6
|
const packageJson = require('../package.json');
|
@@ -16,6 +16,6 @@ router.use('/registry', createAPIRoute(remoteServices.registry ?? 'https://regis
|
|
16
16
|
return api.getAccessToken();
|
17
17
|
}
|
18
18
|
return null;
|
19
|
-
}
|
19
|
+
},
|
20
20
|
}));
|
21
21
|
export default router;
|
@@ -10,7 +10,7 @@ import { repositoryManager } from './repositoryManager';
|
|
10
10
|
import { Actions } from '@kapeta/nodejs-registry-utils';
|
11
11
|
import { definitionsManager } from './definitionsManager';
|
12
12
|
import { normalizeKapetaUri } from './utils/utils';
|
13
|
-
import { taskManager } from
|
13
|
+
import { taskManager } from './taskManager';
|
14
14
|
function enrichAsset(asset) {
|
15
15
|
return {
|
16
16
|
ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
|
@@ -41,6 +41,9 @@ class DefinitionsManager {
|
|
41
41
|
getDefinition(ref) {
|
42
42
|
const uri = parseKapetaUri(ref);
|
43
43
|
return this.getDefinitions().find((d) => {
|
44
|
+
if (!uri.version) {
|
45
|
+
return d.definition.metadata.name === uri.fullName;
|
46
|
+
}
|
44
47
|
return parseKapetaUri(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
|
45
48
|
});
|
46
49
|
}
|
@@ -6,6 +6,7 @@ declare class ProgressListener {
|
|
6
6
|
run(command: string, directory?: string): Promise<{
|
7
7
|
exit: number;
|
8
8
|
signal: NodeJS.Signals | null;
|
9
|
+
output: string;
|
9
10
|
}>;
|
10
11
|
progress(label: string, callback: () => void | Promise<void>): Promise<void>;
|
11
12
|
check(message: string, ok: boolean | Promise<boolean> | (() => Promise<boolean>)): Promise<void>;
|
@@ -10,38 +10,51 @@ class ProgressListener {
|
|
10
10
|
type: 'info',
|
11
11
|
message: `Running command "${command}"`,
|
12
12
|
});
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
13
|
+
const firstCommand = command.split(' ')[0];
|
14
|
+
return new Promise(async (resolve, reject) => {
|
15
|
+
try {
|
16
|
+
const chunks = [];
|
17
|
+
const child = spawn(command, [], {
|
18
|
+
cwd: directory ? directory : process.cwd(),
|
19
|
+
detached: true,
|
20
|
+
shell: true,
|
21
|
+
});
|
22
|
+
child.onData((data) => {
|
23
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: data.line });
|
24
|
+
});
|
25
|
+
if (child.process.stdout) {
|
26
|
+
child.process.stdout.on('data', (data) => {
|
27
|
+
chunks.push(data);
|
27
28
|
});
|
28
|
-
reject(new Error(`Command "${command}" exited with code ${exit}`));
|
29
29
|
}
|
30
|
-
|
30
|
+
child.process.on('exit', (exit, signal) => {
|
31
|
+
if (exit !== 0) {
|
32
|
+
this.socketManager.emit(`install`, 'install:log', {
|
33
|
+
type: 'info',
|
34
|
+
message: `"${command}" failed: "${exit}"`,
|
35
|
+
});
|
36
|
+
reject(new Error(`Command "${command}" exited with code ${exit}`));
|
37
|
+
}
|
38
|
+
else {
|
39
|
+
this.socketManager.emit(`install`, 'install:log', {
|
40
|
+
type: 'info',
|
41
|
+
message: `Command OK: "${command}"`,
|
42
|
+
});
|
43
|
+
resolve({ exit, signal, output: Buffer.concat(chunks).toString() });
|
44
|
+
}
|
45
|
+
});
|
46
|
+
child.process.on('error', (err) => {
|
31
47
|
this.socketManager.emit(`install`, 'install:log', {
|
32
48
|
type: 'info',
|
33
|
-
message: `
|
49
|
+
message: `"${command}" failed: "${err.message}"`,
|
34
50
|
});
|
35
|
-
|
36
|
-
}
|
37
|
-
});
|
38
|
-
child.process.on('error', (err) => {
|
39
|
-
this.socketManager.emit(`install`, 'install:log', {
|
40
|
-
type: 'info',
|
41
|
-
message: `"${command}" failed: "${err.message}"`,
|
51
|
+
reject(err);
|
42
52
|
});
|
43
|
-
|
44
|
-
}
|
53
|
+
await child.wait();
|
54
|
+
}
|
55
|
+
catch (e) {
|
56
|
+
reject(e);
|
57
|
+
}
|
45
58
|
});
|
46
59
|
}
|
47
60
|
async progress(label, callback) {
|
@@ -8,6 +8,7 @@ declare class RepositoryManager {
|
|
8
8
|
setChangeEventsEnabled(enabled: boolean): void;
|
9
9
|
listenForChanges(): void;
|
10
10
|
stopListening(): void;
|
11
|
+
ensureDefaultProviders(): void;
|
11
12
|
private _install;
|
12
13
|
ensureAsset(handle: string, name: string, version: string, wait?: boolean): Promise<undefined | Task[]>;
|
13
14
|
}
|
@@ -12,6 +12,20 @@ import { definitionsManager } from './definitionsManager';
|
|
12
12
|
import { taskManager } from './taskManager';
|
13
13
|
import { normalizeKapetaUri } from './utils/utils';
|
14
14
|
import { assetManager } from './assetManager';
|
15
|
+
const DEFAULT_PROVIDERS = [
|
16
|
+
'kapeta/block-type-service',
|
17
|
+
'kapeta/block-type-frontend',
|
18
|
+
'kapeta/block-type-gateway-http',
|
19
|
+
'kapeta/resource-type-rest-api',
|
20
|
+
'kapeta/resource-type-rest-client',
|
21
|
+
'kapeta/resource-type-web-page',
|
22
|
+
'kapeta/resource-type-web-fragment',
|
23
|
+
'kapeta/resource-type-mongodb',
|
24
|
+
'kapeta/resource-type-postgresql',
|
25
|
+
'kapeta/language-target-react-ts',
|
26
|
+
'kapeta/language-target-nodejs',
|
27
|
+
'kapeta/language-target-java-spring-boot',
|
28
|
+
];
|
15
29
|
const INSTALL_ATTEMPTED = {};
|
16
30
|
class RepositoryManager {
|
17
31
|
changeEventsEnabled;
|
@@ -101,7 +115,10 @@ class RepositoryManager {
|
|
101
115
|
this.watcher();
|
102
116
|
this.watcher = undefined;
|
103
117
|
}
|
104
|
-
|
118
|
+
ensureDefaultProviders() {
|
119
|
+
this._install(DEFAULT_PROVIDERS);
|
120
|
+
}
|
121
|
+
_install(refs) {
|
105
122
|
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
106
123
|
const createInstaller = (ref) => {
|
107
124
|
return async () => {
|
@@ -111,7 +128,7 @@ class RepositoryManager {
|
|
111
128
|
if (definitionsManager.exists(ref)) {
|
112
129
|
return;
|
113
130
|
}
|
114
|
-
console.log(`Installing asset: ${ref}`);
|
131
|
+
//console.log(`Installing asset: ${ref}`);
|
115
132
|
INSTALL_ATTEMPTED[ref] = true;
|
116
133
|
//Auto-install missing asset
|
117
134
|
try {
|
@@ -121,12 +138,16 @@ class RepositoryManager {
|
|
121
138
|
this.setChangeEventsEnabled(false);
|
122
139
|
await Actions.install(progressListener, [ref], {});
|
123
140
|
}
|
141
|
+
catch (e) {
|
142
|
+
console.error(`Failed to install asset: ${ref}`, e);
|
143
|
+
throw e;
|
144
|
+
}
|
124
145
|
finally {
|
125
146
|
this.setChangeEventsEnabled(true);
|
126
147
|
}
|
127
148
|
definitionsManager.clearCache();
|
128
149
|
assetManager.clearCache();
|
129
|
-
console.log(`Asset installed: ${ref}`);
|
150
|
+
//console.log(`Asset installed: ${ref}`);
|
130
151
|
};
|
131
152
|
};
|
132
153
|
const tasks = [];
|
@@ -144,7 +165,7 @@ class RepositoryManager {
|
|
144
165
|
}
|
145
166
|
const task = taskManager.add(`asset:install:${ref}`, createInstaller(ref), {
|
146
167
|
name: `Installing ${ref}`,
|
147
|
-
group: 'asset:install:',
|
168
|
+
group: 'asset:install:', //Group prevents multiple tasks from running at the same time
|
148
169
|
});
|
149
170
|
tasks.push(task);
|
150
171
|
}
|
@@ -183,17 +204,17 @@ class RepositoryManager {
|
|
183
204
|
this._cache[ref] = true;
|
184
205
|
let tasks = undefined;
|
185
206
|
if (!installedAsset) {
|
186
|
-
tasks =
|
207
|
+
tasks = this._install([ref]);
|
187
208
|
}
|
188
209
|
else {
|
189
210
|
//Ensure dependencies are installed
|
190
211
|
const refs = assetVersion.dependencies.map((dep) => dep.name);
|
191
212
|
if (refs.length > 0) {
|
192
|
-
tasks =
|
213
|
+
tasks = this._install(refs);
|
193
214
|
}
|
194
215
|
}
|
195
216
|
if (tasks && wait) {
|
196
|
-
await Promise.all(tasks.map((t) => t.
|
217
|
+
await Promise.all(tasks.map((t) => t.wait()));
|
197
218
|
}
|
198
219
|
return tasks;
|
199
220
|
}
|
@@ -61,6 +61,8 @@ function createFuture() {
|
|
61
61
|
resolve = res;
|
62
62
|
reject = rej;
|
63
63
|
});
|
64
|
+
// Ignore unhandled promise rejections
|
65
|
+
promise.catch(() => { });
|
64
66
|
return {
|
65
67
|
promise,
|
66
68
|
resolve,
|
@@ -129,6 +131,7 @@ class TaskManager {
|
|
129
131
|
return;
|
130
132
|
}
|
131
133
|
}
|
134
|
+
const startTime = Date.now();
|
132
135
|
try {
|
133
136
|
task.status = TaskStatus.RUNNING;
|
134
137
|
task.emitUpdate();
|
@@ -145,6 +148,7 @@ class TaskManager {
|
|
145
148
|
}
|
146
149
|
finally {
|
147
150
|
this.remove(task.id);
|
151
|
+
console.log(`Task ${task.id} completed in ${Date.now() - startTime}ms`);
|
148
152
|
}
|
149
153
|
if (task.metadata.group) {
|
150
154
|
const nextTaskInGroup = this._tasks.find((t) => t.id !== task.id && t.metadata.group === task.metadata.group && t.status === TaskStatus.PENDING);
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { spawn, hasApp } from '@kapeta/nodejs-process';
|
2
|
+
import { taskManager } from '../taskManager';
|
3
|
+
export async function hasCLI() {
|
4
|
+
return hasApp('kap');
|
5
|
+
}
|
6
|
+
export async function ensureCLI() {
|
7
|
+
if (await hasCLI()) {
|
8
|
+
return null;
|
9
|
+
}
|
10
|
+
return taskManager.add(`cli:install`, () => {
|
11
|
+
const process = spawn('npm', ['install', '-g', '@kapeta/kap'], {
|
12
|
+
shell: true,
|
13
|
+
});
|
14
|
+
return process.wait();
|
15
|
+
}, {
|
16
|
+
name: `Installing Kapeta CLI`,
|
17
|
+
});
|
18
|
+
}
|
package/index.ts
CHANGED
@@ -20,10 +20,8 @@ import TaskRoutes from './src/tasks/routes';
|
|
20
20
|
import APIRoutes from './src/api';
|
21
21
|
import { getBindHost } from './src/utils/utils';
|
22
22
|
import request from 'request';
|
23
|
-
import
|
24
|
-
import {
|
25
|
-
import {corsHandler} from "./src/middleware/cors";
|
26
|
-
|
23
|
+
import { repositoryManager } from './src/repositoryManager';
|
24
|
+
import { ensureCLI } from './src/utils/commandLineUtils';
|
27
25
|
|
28
26
|
export type LocalClusterService = HTTP.Server & { host?: string; port?: number };
|
29
27
|
|
@@ -175,9 +173,22 @@ export default {
|
|
175
173
|
|
176
174
|
const bindHost = getBindHost(host);
|
177
175
|
|
178
|
-
currentServer.listen(port, bindHost, () =>
|
179
|
-
|
180
|
-
|
176
|
+
currentServer.listen(port, bindHost, () => {
|
177
|
+
try {
|
178
|
+
ensureCLI().catch((e: any) => console.error('Failed to install CLI.', e));
|
179
|
+
} catch (e: any) {
|
180
|
+
console.error('Failed to install CLI.', e);
|
181
|
+
}
|
182
|
+
|
183
|
+
try {
|
184
|
+
// Start installation process for all default providers
|
185
|
+
repositoryManager.ensureDefaultProviders();
|
186
|
+
} catch (e: any) {
|
187
|
+
console.error('Failed to install default providers.', e);
|
188
|
+
}
|
189
|
+
|
190
|
+
resolve({ host, port, dockerStatus: containerManager.isAlive() });
|
191
|
+
});
|
181
192
|
currentServer.host = host;
|
182
193
|
currentServer.port = port;
|
183
194
|
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@kapeta/local-cluster-service",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.15.1",
|
4
4
|
"description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
|
5
5
|
"type": "commonjs",
|
6
6
|
"exports": {
|
@@ -46,7 +46,7 @@
|
|
46
46
|
"@kapeta/codegen": "<2",
|
47
47
|
"@kapeta/local-cluster-config": ">= 0.2.3 <2",
|
48
48
|
"@kapeta/nodejs-api-client": "<2",
|
49
|
-
"@kapeta/nodejs-process": "
|
49
|
+
"@kapeta/nodejs-process": "<2",
|
50
50
|
"@kapeta/nodejs-registry-utils": "<2",
|
51
51
|
"@kapeta/nodejs-utils": "<2",
|
52
52
|
"@kapeta/schemas": "^0.0.58",
|
package/src/api.ts
CHANGED
@@ -1,25 +1,28 @@
|
|
1
1
|
import Router from 'express-promise-router';
|
2
|
-
import {corsHandler} from
|
3
|
-
import {KapetaAPI} from
|
2
|
+
import { corsHandler } from './middleware/cors';
|
3
|
+
import { KapetaAPI } from '@kapeta/nodejs-api-client';
|
4
4
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
5
|
-
const { createAPIRoute }
|
5
|
+
const { createAPIRoute } = require('@kapeta/web-microfrontend/server');
|
6
6
|
const packageJson = require('../package.json');
|
7
7
|
|
8
8
|
const router = Router();
|
9
9
|
|
10
10
|
const remoteServices = ClusterConfiguration.getClusterConfig().remoteServices ?? {};
|
11
|
-
router.use('/', corsHandler)
|
11
|
+
router.use('/', corsHandler);
|
12
12
|
|
13
|
-
router.use(
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
13
|
+
router.use(
|
14
|
+
'/registry',
|
15
|
+
createAPIRoute(remoteServices.registry ?? 'https://registry.kapeta.com', {
|
16
|
+
nonce: false,
|
17
|
+
userAgent: `KapetaDesktopCluster/${packageJson.version}`,
|
18
|
+
tokenFetcher: () => {
|
19
|
+
const api = new KapetaAPI();
|
20
|
+
if (api.hasToken()) {
|
21
|
+
return api.getAccessToken();
|
22
|
+
}
|
23
|
+
return null;
|
24
|
+
},
|
25
|
+
})
|
26
|
+
);
|
24
27
|
|
25
28
|
export default router;
|
package/src/assetManager.ts
CHANGED
@@ -12,7 +12,7 @@ import { BlockDefinition } from '@kapeta/schemas';
|
|
12
12
|
import { Actions } from '@kapeta/nodejs-registry-utils';
|
13
13
|
import { definitionsManager } from './definitionsManager';
|
14
14
|
import { normalizeKapetaUri } from './utils/utils';
|
15
|
-
import {taskManager} from
|
15
|
+
import { taskManager } from './taskManager';
|
16
16
|
|
17
17
|
export interface EnrichedAsset {
|
18
18
|
ref: string;
|
@@ -180,15 +180,19 @@ class AssetManager {
|
|
180
180
|
this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
|
181
181
|
}
|
182
182
|
|
183
|
-
private maybeGenerateCode(ref:string, ymlPath:string, block: BlockDefinition) {
|
183
|
+
private maybeGenerateCode(ref: string, ymlPath: string, block: BlockDefinition) {
|
184
184
|
ref = normalizeKapetaUri(ref);
|
185
185
|
if (codeGeneratorManager.canGenerateCode(block)) {
|
186
186
|
const assetTitle = block.metadata.title ? block.metadata.title : parseKapetaUri(block.metadata.name).name;
|
187
|
-
taskManager.add(
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
187
|
+
taskManager.add(
|
188
|
+
`codegen:${ref}`,
|
189
|
+
async () => {
|
190
|
+
await codeGeneratorManager.generate(ymlPath, block);
|
191
|
+
},
|
192
|
+
{
|
193
|
+
name: `Generating code for ${assetTitle}`,
|
194
|
+
}
|
195
|
+
);
|
192
196
|
return true;
|
193
197
|
}
|
194
198
|
return false;
|
package/src/containerManager.ts
CHANGED
@@ -252,13 +252,14 @@ class ContainerManager {
|
|
252
252
|
const api = new KapetaAPI();
|
253
253
|
const accessToken = api.hasToken() ? await api.getAccessToken() : null;
|
254
254
|
|
255
|
-
const auth =
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
255
|
+
const auth =
|
256
|
+
accessToken && image.startsWith('docker.kapeta.com/')
|
257
|
+
? {
|
258
|
+
username: 'kapeta',
|
259
|
+
password: accessToken,
|
260
|
+
serveraddress: 'docker.kapeta.com',
|
261
|
+
}
|
262
|
+
: {};
|
262
263
|
|
263
264
|
const stream = (await this.docker().image.create(auth, {
|
264
265
|
fromImage: imageName,
|
@@ -58,6 +58,9 @@ class DefinitionsManager {
|
|
58
58
|
public getDefinition(ref: string) {
|
59
59
|
const uri = parseKapetaUri(ref);
|
60
60
|
return this.getDefinitions().find((d) => {
|
61
|
+
if (!uri.version) {
|
62
|
+
return d.definition.metadata.name === uri.fullName;
|
63
|
+
}
|
61
64
|
return parseKapetaUri(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
|
62
65
|
});
|
63
66
|
}
|
package/src/progressListener.ts
CHANGED
@@ -7,46 +7,61 @@ class ProgressListener {
|
|
7
7
|
this.socketManager = socketManager;
|
8
8
|
}
|
9
9
|
|
10
|
-
run(command: string, directory?: string): Promise<{ exit: number; signal: NodeJS.Signals | null }> {
|
10
|
+
run(command: string, directory?: string): Promise<{ exit: number; signal: NodeJS.Signals | null; output: string }> {
|
11
11
|
this.socketManager.emit(`install`, 'install:log', {
|
12
12
|
type: 'info',
|
13
13
|
message: `Running command "${command}"`,
|
14
14
|
});
|
15
15
|
|
16
|
-
|
17
|
-
const child = spawn(command, [],{
|
18
|
-
cwd: directory ? directory : process.cwd(),
|
19
|
-
detached: true,
|
20
|
-
shell: true,
|
21
|
-
});
|
16
|
+
const firstCommand = command.split(' ')[0];
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
return new Promise(async (resolve, reject) => {
|
19
|
+
try {
|
20
|
+
const chunks: Buffer[] = [];
|
21
|
+
const child = spawn(command, [], {
|
22
|
+
cwd: directory ? directory : process.cwd(),
|
23
|
+
detached: true,
|
24
|
+
shell: true,
|
25
|
+
});
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
child.onData((data) => {
|
28
|
+
this.socketManager.emit(`install`, 'install:log', { type: 'info', message: data.line });
|
29
|
+
});
|
30
|
+
|
31
|
+
if (child.process.stdout) {
|
32
|
+
child.process.stdout.on('data', (data) => {
|
33
|
+
chunks.push(data);
|
32
34
|
});
|
33
|
-
|
34
|
-
|
35
|
+
}
|
36
|
+
|
37
|
+
child.process.on('exit', (exit, signal) => {
|
38
|
+
if (exit !== 0) {
|
39
|
+
this.socketManager.emit(`install`, 'install:log', {
|
40
|
+
type: 'info',
|
41
|
+
message: `"${command}" failed: "${exit}"`,
|
42
|
+
});
|
43
|
+
reject(new Error(`Command "${command}" exited with code ${exit}`));
|
44
|
+
} else {
|
45
|
+
this.socketManager.emit(`install`, 'install:log', {
|
46
|
+
type: 'info',
|
47
|
+
message: `Command OK: "${command}"`,
|
48
|
+
});
|
49
|
+
resolve({ exit, signal, output: Buffer.concat(chunks).toString() });
|
50
|
+
}
|
51
|
+
});
|
52
|
+
|
53
|
+
child.process.on('error', (err) => {
|
35
54
|
this.socketManager.emit(`install`, 'install:log', {
|
36
55
|
type: 'info',
|
37
|
-
message: `
|
56
|
+
message: `"${command}" failed: "${err.message}"`,
|
38
57
|
});
|
39
|
-
|
40
|
-
}
|
41
|
-
});
|
42
|
-
|
43
|
-
child.process.on('error', (err) => {
|
44
|
-
this.socketManager.emit(`install`, 'install:log', {
|
45
|
-
type: 'info',
|
46
|
-
message: `"${command}" failed: "${err.message}"`,
|
58
|
+
reject(err);
|
47
59
|
});
|
48
|
-
|
49
|
-
|
60
|
+
|
61
|
+
await child.wait();
|
62
|
+
} catch (e) {
|
63
|
+
reject(e);
|
64
|
+
}
|
50
65
|
});
|
51
66
|
}
|
52
67
|
|
package/src/repositoryManager.ts
CHANGED
@@ -14,6 +14,21 @@ import { Task, taskManager } from './taskManager';
|
|
14
14
|
import { normalizeKapetaUri } from './utils/utils';
|
15
15
|
import { assetManager } from './assetManager';
|
16
16
|
|
17
|
+
const DEFAULT_PROVIDERS = [
|
18
|
+
'kapeta/block-type-service',
|
19
|
+
'kapeta/block-type-frontend',
|
20
|
+
'kapeta/block-type-gateway-http',
|
21
|
+
'kapeta/resource-type-rest-api',
|
22
|
+
'kapeta/resource-type-rest-client',
|
23
|
+
'kapeta/resource-type-web-page',
|
24
|
+
'kapeta/resource-type-web-fragment',
|
25
|
+
'kapeta/resource-type-mongodb',
|
26
|
+
'kapeta/resource-type-postgresql',
|
27
|
+
'kapeta/language-target-react-ts',
|
28
|
+
'kapeta/language-target-nodejs',
|
29
|
+
'kapeta/language-target-java-spring-boot',
|
30
|
+
];
|
31
|
+
|
17
32
|
const INSTALL_ATTEMPTED: { [p: string]: boolean } = {};
|
18
33
|
|
19
34
|
class RepositoryManager {
|
@@ -114,7 +129,11 @@ class RepositoryManager {
|
|
114
129
|
this.watcher = undefined;
|
115
130
|
}
|
116
131
|
|
117
|
-
|
132
|
+
public ensureDefaultProviders(): void {
|
133
|
+
this._install(DEFAULT_PROVIDERS);
|
134
|
+
}
|
135
|
+
|
136
|
+
private _install(refs: string[]): Task[] {
|
118
137
|
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
119
138
|
const createInstaller = (ref: string) => {
|
120
139
|
return async () => {
|
@@ -125,7 +144,7 @@ class RepositoryManager {
|
|
125
144
|
if (definitionsManager.exists(ref)) {
|
126
145
|
return;
|
127
146
|
}
|
128
|
-
console.log(`Installing asset: ${ref}`);
|
147
|
+
//console.log(`Installing asset: ${ref}`);
|
129
148
|
INSTALL_ATTEMPTED[ref] = true;
|
130
149
|
//Auto-install missing asset
|
131
150
|
try {
|
@@ -134,12 +153,15 @@ class RepositoryManager {
|
|
134
153
|
//Disable change events while installing
|
135
154
|
this.setChangeEventsEnabled(false);
|
136
155
|
await Actions.install(progressListener, [ref], {});
|
156
|
+
} catch (e) {
|
157
|
+
console.error(`Failed to install asset: ${ref}`, e);
|
158
|
+
throw e;
|
137
159
|
} finally {
|
138
160
|
this.setChangeEventsEnabled(true);
|
139
161
|
}
|
140
162
|
definitionsManager.clearCache();
|
141
163
|
assetManager.clearCache();
|
142
|
-
console.log(`Asset installed: ${ref}`);
|
164
|
+
//console.log(`Asset installed: ${ref}`);
|
143
165
|
};
|
144
166
|
};
|
145
167
|
|
@@ -162,7 +184,7 @@ class RepositoryManager {
|
|
162
184
|
|
163
185
|
const task = taskManager.add(`asset:install:${ref}`, createInstaller(ref), {
|
164
186
|
name: `Installing ${ref}`,
|
165
|
-
group: 'asset:install:',
|
187
|
+
group: 'asset:install:', //Group prevents multiple tasks from running at the same time
|
166
188
|
});
|
167
189
|
|
168
190
|
tasks.push(task);
|
@@ -216,17 +238,17 @@ class RepositoryManager {
|
|
216
238
|
this._cache[ref] = true;
|
217
239
|
let tasks: Task[] | undefined = undefined;
|
218
240
|
if (!installedAsset) {
|
219
|
-
tasks =
|
241
|
+
tasks = this._install([ref]);
|
220
242
|
} else {
|
221
243
|
//Ensure dependencies are installed
|
222
244
|
const refs = assetVersion.dependencies.map((dep: Dependency) => dep.name);
|
223
245
|
if (refs.length > 0) {
|
224
|
-
tasks =
|
246
|
+
tasks = this._install(refs);
|
225
247
|
}
|
226
248
|
}
|
227
249
|
|
228
250
|
if (tasks && wait) {
|
229
|
-
await Promise.all(tasks.map((t) => t.
|
251
|
+
await Promise.all(tasks.map((t) => t.wait()));
|
230
252
|
}
|
231
253
|
|
232
254
|
return tasks;
|
package/src/taskManager.ts
CHANGED
@@ -106,6 +106,9 @@ function createFuture<T>(): Future<T> {
|
|
106
106
|
reject = rej;
|
107
107
|
});
|
108
108
|
|
109
|
+
// Ignore unhandled promise rejections
|
110
|
+
promise.catch(() => {});
|
111
|
+
|
109
112
|
return {
|
110
113
|
promise,
|
111
114
|
resolve,
|
@@ -193,6 +196,7 @@ class TaskManager {
|
|
193
196
|
}
|
194
197
|
}
|
195
198
|
|
199
|
+
const startTime = Date.now();
|
196
200
|
try {
|
197
201
|
task.status = TaskStatus.RUNNING;
|
198
202
|
task.emitUpdate();
|
@@ -207,6 +211,7 @@ class TaskManager {
|
|
207
211
|
task.emitUpdate();
|
208
212
|
} finally {
|
209
213
|
this.remove(task.id);
|
214
|
+
console.log(`Task ${task.id} completed in ${Date.now() - startTime}ms`);
|
210
215
|
}
|
211
216
|
|
212
217
|
if (task.metadata.group) {
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { spawn, hasApp } from '@kapeta/nodejs-process';
|
2
|
+
import { taskManager } from '../taskManager';
|
3
|
+
|
4
|
+
export async function hasCLI() {
|
5
|
+
return hasApp('kap');
|
6
|
+
}
|
7
|
+
|
8
|
+
export async function ensureCLI() {
|
9
|
+
if (await hasCLI()) {
|
10
|
+
return null;
|
11
|
+
}
|
12
|
+
|
13
|
+
return taskManager.add(
|
14
|
+
`cli:install`,
|
15
|
+
() => {
|
16
|
+
const process = spawn('npm', ['install', '-g', '@kapeta/kap'], {
|
17
|
+
shell: true,
|
18
|
+
});
|
19
|
+
|
20
|
+
return process.wait();
|
21
|
+
},
|
22
|
+
{
|
23
|
+
name: `Installing Kapeta CLI`,
|
24
|
+
}
|
25
|
+
);
|
26
|
+
}
|