@kapeta/local-cluster-service 0.15.2 → 0.16.0
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/src/RepositoryWatcher.d.ts +22 -0
- package/dist/cjs/src/RepositoryWatcher.js +273 -0
- package/dist/cjs/src/assetManager.js +33 -20
- package/dist/cjs/src/containerManager.js +0 -1
- package/dist/cjs/src/identities/routes.js +2 -1
- package/dist/cjs/src/instanceManager.d.ts +0 -2
- package/dist/cjs/src/instanceManager.js +16 -35
- package/dist/cjs/src/progressListener.d.ts +5 -6
- package/dist/cjs/src/progressListener.js +54 -36
- package/dist/cjs/src/repositoryManager.d.ts +4 -4
- package/dist/cjs/src/repositoryManager.js +20 -93
- package/dist/cjs/src/socketManager.d.ts +18 -6
- package/dist/cjs/src/socketManager.js +35 -1
- package/dist/esm/src/RepositoryWatcher.d.ts +22 -0
- package/dist/esm/src/RepositoryWatcher.js +266 -0
- package/dist/esm/src/assetManager.js +35 -22
- package/dist/esm/src/containerManager.js +0 -1
- package/dist/esm/src/identities/routes.js +2 -1
- package/dist/esm/src/instanceManager.d.ts +0 -2
- package/dist/esm/src/instanceManager.js +16 -35
- package/dist/esm/src/progressListener.d.ts +5 -6
- package/dist/esm/src/progressListener.js +53 -36
- package/dist/esm/src/repositoryManager.d.ts +4 -4
- package/dist/esm/src/repositoryManager.js +20 -93
- package/dist/esm/src/socketManager.d.ts +18 -6
- package/dist/esm/src/socketManager.js +34 -0
- package/package.json +2 -2
- package/src/RepositoryWatcher.ts +304 -0
- package/src/assetManager.ts +39 -25
- package/src/containerManager.ts +0 -5
- package/src/identities/routes.ts +2 -1
- package/src/instanceManager.ts +22 -35
- package/src/progressListener.ts +59 -39
- package/src/repositoryManager.ts +26 -100
- package/src/socketManager.ts +44 -5
@@ -0,0 +1,266 @@
|
|
1
|
+
import chokidar from 'chokidar';
|
2
|
+
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
3
|
+
import FS from 'fs-extra';
|
4
|
+
import Path from 'node:path';
|
5
|
+
import YAML from 'yaml';
|
6
|
+
import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
7
|
+
import _ from 'lodash';
|
8
|
+
import { socketManager } from './socketManager';
|
9
|
+
import { definitionsManager } from './definitionsManager';
|
10
|
+
import { assetManager } from './assetManager';
|
11
|
+
function clearAllCaches() {
|
12
|
+
definitionsManager.clearCache();
|
13
|
+
assetManager.clearCache();
|
14
|
+
}
|
15
|
+
const KAPETA_YML_RX = /^kapeta.ya?ml$/;
|
16
|
+
export class RepositoryWatcher {
|
17
|
+
watcher;
|
18
|
+
disabled = false;
|
19
|
+
baseDir;
|
20
|
+
allDefinitions = [];
|
21
|
+
symbolicLinks = {};
|
22
|
+
ignoredFiles = new Set();
|
23
|
+
constructor() {
|
24
|
+
this.baseDir = ClusterConfiguration.getRepositoryBasedir();
|
25
|
+
}
|
26
|
+
setDisabled(disabled) {
|
27
|
+
this.disabled = disabled;
|
28
|
+
}
|
29
|
+
watch() {
|
30
|
+
if (!FS.existsSync(this.baseDir)) {
|
31
|
+
FS.mkdirpSync(this.baseDir);
|
32
|
+
}
|
33
|
+
this.allDefinitions = ClusterConfiguration.getDefinitions();
|
34
|
+
try {
|
35
|
+
this.watcher = chokidar.watch(this.baseDir, {
|
36
|
+
followSymlinks: false,
|
37
|
+
ignorePermissionErrors: true,
|
38
|
+
disableGlobbing: true,
|
39
|
+
persistent: true,
|
40
|
+
depth: 2,
|
41
|
+
ignored: (path) => this.ignoreFile(path),
|
42
|
+
});
|
43
|
+
this.watcher.on('all', this.handleFileChange.bind(this));
|
44
|
+
this.watcher.on('error', (error) => {
|
45
|
+
console.log('Error watching repository', error);
|
46
|
+
});
|
47
|
+
this.watcher.on('ready', () => {
|
48
|
+
console.log('Watching local repository for provider changes: %s', this.baseDir);
|
49
|
+
});
|
50
|
+
}
|
51
|
+
catch (e) {
|
52
|
+
// Fallback to run without watch mode due to potential platform issues.
|
53
|
+
// https://nodejs.org/docs/latest/api/fs.html#caveats
|
54
|
+
console.log('Unable to watch for changes. Changes to assets will not update automatically.', e);
|
55
|
+
return;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
async ignoreChangesFor(file) {
|
59
|
+
this.ignoredFiles.add(file);
|
60
|
+
const realPath = await FS.realpath(file);
|
61
|
+
if (realPath !== file) {
|
62
|
+
this.ignoredFiles.add(realPath);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
async resumeChangedFor(file) {
|
66
|
+
this.ignoredFiles.delete(file);
|
67
|
+
const realPath = await FS.realpath(file);
|
68
|
+
if (realPath !== file) {
|
69
|
+
this.ignoredFiles.delete(realPath);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
async unwatch() {
|
73
|
+
if (!this.watcher) {
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
this.symbolicLinks = {};
|
77
|
+
await this.watcher.close();
|
78
|
+
this.watcher = undefined;
|
79
|
+
}
|
80
|
+
async getAssetIdentity(path) {
|
81
|
+
const baseName = Path.basename(path);
|
82
|
+
let handle, name, version;
|
83
|
+
if (path.startsWith(this.baseDir)) {
|
84
|
+
const relativePath = Path.relative(this.baseDir, path);
|
85
|
+
// Inside the repo we can use the path to determine the handle, name and version
|
86
|
+
[handle, name, version] = relativePath.split(/\//g);
|
87
|
+
if (!handle || !name || !version) {
|
88
|
+
// Do nothing with this
|
89
|
+
return;
|
90
|
+
}
|
91
|
+
return {
|
92
|
+
handle,
|
93
|
+
name,
|
94
|
+
version,
|
95
|
+
};
|
96
|
+
}
|
97
|
+
if (!KAPETA_YML_RX.test(baseName)) {
|
98
|
+
// Do nothing with this
|
99
|
+
return;
|
100
|
+
}
|
101
|
+
// Outside the repo we need to use the file content to determine the handle, name
|
102
|
+
// Version is always 'local'
|
103
|
+
version = 'local';
|
104
|
+
try {
|
105
|
+
const definition = YAML.parse((await FS.readFile(path)).toString());
|
106
|
+
const uri = parseKapetaUri(definition.metadata.name);
|
107
|
+
handle = uri.handle;
|
108
|
+
name = uri.name;
|
109
|
+
return {
|
110
|
+
handle,
|
111
|
+
name,
|
112
|
+
version,
|
113
|
+
};
|
114
|
+
}
|
115
|
+
catch (e) {
|
116
|
+
// Ignore issues in the YML file
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
async handleFileChange(eventName, path) {
|
121
|
+
if (!path) {
|
122
|
+
return;
|
123
|
+
}
|
124
|
+
if (this.ignoredFiles.has(path)) {
|
125
|
+
return;
|
126
|
+
}
|
127
|
+
//console.log('File changed', eventName, path);
|
128
|
+
const assetIdentity = await this.getAssetIdentity(path);
|
129
|
+
if (!assetIdentity) {
|
130
|
+
return;
|
131
|
+
}
|
132
|
+
if (this.disabled) {
|
133
|
+
return;
|
134
|
+
}
|
135
|
+
// If this is false it's because we're watching a symlink target
|
136
|
+
const withinRepo = path.startsWith(this.baseDir);
|
137
|
+
if (withinRepo && assetIdentity.version === 'local' && path.endsWith('/local')) {
|
138
|
+
// This is likely a symlink target
|
139
|
+
if (eventName === 'add') {
|
140
|
+
//console.log('Checking if we should add symlink target', handle, name, version, path);
|
141
|
+
await this.addSymlinkTarget(path);
|
142
|
+
}
|
143
|
+
if (eventName === 'unlink') {
|
144
|
+
await this.removeSymlinkTarget(path);
|
145
|
+
}
|
146
|
+
if (eventName === 'change') {
|
147
|
+
await this.updateSymlinkTarget(path);
|
148
|
+
}
|
149
|
+
}
|
150
|
+
await this.checkForChange(assetIdentity);
|
151
|
+
}
|
152
|
+
async checkForChange(assetIdentity) {
|
153
|
+
const ymlPath = Path.join(this.baseDir, assetIdentity.handle, assetIdentity.name, assetIdentity.version, 'kapeta.yml');
|
154
|
+
const newDefinitions = ClusterConfiguration.getDefinitions();
|
155
|
+
const newDefinition = newDefinitions.find((d) => d.ymlPath === ymlPath);
|
156
|
+
let currentDefinition = this.allDefinitions.find((d) => d.ymlPath === ymlPath);
|
157
|
+
const ymlExists = await this.exists(ymlPath);
|
158
|
+
let type;
|
159
|
+
if (ymlExists) {
|
160
|
+
if (currentDefinition) {
|
161
|
+
if (newDefinition && _.isEqual(currentDefinition, newDefinition)) {
|
162
|
+
//Definition was not changed
|
163
|
+
return;
|
164
|
+
}
|
165
|
+
type = 'updated';
|
166
|
+
}
|
167
|
+
else if (newDefinition) {
|
168
|
+
type = 'added';
|
169
|
+
currentDefinition = newDefinition;
|
170
|
+
}
|
171
|
+
else {
|
172
|
+
//Other definition was added / updated - ignore
|
173
|
+
return;
|
174
|
+
}
|
175
|
+
}
|
176
|
+
else {
|
177
|
+
if (currentDefinition) {
|
178
|
+
const ref = parseKapetaUri(`${currentDefinition.definition.metadata.name}:${currentDefinition.version}`).id;
|
179
|
+
//Something was removed
|
180
|
+
type = 'removed';
|
181
|
+
}
|
182
|
+
else {
|
183
|
+
//Other definition was removed - ignore
|
184
|
+
return;
|
185
|
+
}
|
186
|
+
}
|
187
|
+
const payload = {
|
188
|
+
type,
|
189
|
+
definition: newDefinition?.definition ?? currentDefinition?.definition,
|
190
|
+
asset: assetIdentity,
|
191
|
+
};
|
192
|
+
this.allDefinitions = newDefinitions;
|
193
|
+
//console.log('Asset changed', payload);
|
194
|
+
socketManager.emitGlobal('asset-change', payload);
|
195
|
+
clearAllCaches();
|
196
|
+
}
|
197
|
+
async exists(path) {
|
198
|
+
try {
|
199
|
+
await FS.access(path);
|
200
|
+
return true;
|
201
|
+
}
|
202
|
+
catch (e) {
|
203
|
+
return false;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
async removeSymlinkTarget(path) {
|
207
|
+
if (this.symbolicLinks[path]) {
|
208
|
+
//console.log('Unwatching symlink target %s => %s', path, this.symbolicLinks[path]);
|
209
|
+
this.watcher?.unwatch(this.symbolicLinks[path]);
|
210
|
+
delete this.symbolicLinks[path];
|
211
|
+
}
|
212
|
+
}
|
213
|
+
async updateSymlinkTarget(path) {
|
214
|
+
if (this.symbolicLinks[path]) {
|
215
|
+
//console.log('Updating symlink target %s => %s', path, this.symbolicLinks[path]);
|
216
|
+
this.watcher?.unwatch(this.symbolicLinks[path]);
|
217
|
+
delete this.symbolicLinks[path];
|
218
|
+
await this.addSymlinkTarget(path);
|
219
|
+
}
|
220
|
+
}
|
221
|
+
async addSymlinkTarget(path) {
|
222
|
+
try {
|
223
|
+
// Make sure we're not watching the symlink target
|
224
|
+
await this.removeSymlinkTarget(path);
|
225
|
+
const stat = await FS.lstat(path);
|
226
|
+
if (stat.isSymbolicLink()) {
|
227
|
+
const realPath = `${await FS.realpath(path)}/kapeta.yml`;
|
228
|
+
if (await this.exists(realPath)) {
|
229
|
+
//console.log('Watching symlink target %s => %s', path, realPath);
|
230
|
+
this.watcher?.add(realPath);
|
231
|
+
this.symbolicLinks[path] = realPath;
|
232
|
+
}
|
233
|
+
}
|
234
|
+
}
|
235
|
+
catch (e) {
|
236
|
+
// Ignore
|
237
|
+
console.warn('Failed to check local symlink target', e);
|
238
|
+
}
|
239
|
+
}
|
240
|
+
ignoreFile(path) {
|
241
|
+
if (!path.startsWith(this.baseDir)) {
|
242
|
+
return false;
|
243
|
+
}
|
244
|
+
if (path.includes('/node_modules/')) {
|
245
|
+
return true;
|
246
|
+
}
|
247
|
+
const filename = Path.basename(path);
|
248
|
+
if (filename.startsWith('.')) {
|
249
|
+
return true;
|
250
|
+
}
|
251
|
+
const relativePath = Path.relative(this.baseDir, path).split(Path.sep);
|
252
|
+
try {
|
253
|
+
if (FS.statSync(path).isDirectory()) {
|
254
|
+
if (relativePath.length > 3) {
|
255
|
+
return true;
|
256
|
+
}
|
257
|
+
return false;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
catch (e) {
|
261
|
+
// Didn't exist - dont ignore
|
262
|
+
return false;
|
263
|
+
}
|
264
|
+
return !/^kapeta\.ya?ml$/.test(filename);
|
265
|
+
}
|
266
|
+
}
|
@@ -1,16 +1,19 @@
|
|
1
1
|
import Path from 'node:path';
|
2
|
-
import FS from '
|
3
|
-
import FSExtra from 'fs-extra';
|
2
|
+
import FS from 'fs-extra';
|
4
3
|
import YAML from 'yaml';
|
5
4
|
import NodeCache from 'node-cache';
|
6
5
|
import { codeGeneratorManager } from './codeGeneratorManager';
|
7
|
-
import {
|
6
|
+
import { ProgressListener } from './progressListener';
|
8
7
|
import { parseKapetaUri } from '@kapeta/nodejs-utils';
|
9
8
|
import { repositoryManager } from './repositoryManager';
|
10
9
|
import { Actions } from '@kapeta/nodejs-registry-utils';
|
11
10
|
import { definitionsManager } from './definitionsManager';
|
12
11
|
import { normalizeKapetaUri } from './utils/utils';
|
13
12
|
import { taskManager } from './taskManager';
|
13
|
+
function clearAllCaches() {
|
14
|
+
definitionsManager.clearCache();
|
15
|
+
assetManager.clearCache();
|
16
|
+
}
|
14
17
|
function enrichAsset(asset) {
|
15
18
|
return {
|
16
19
|
ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
|
@@ -97,19 +100,16 @@ class AssetManager {
|
|
97
100
|
return asset;
|
98
101
|
}
|
99
102
|
async createAsset(path, yaml) {
|
100
|
-
if (FS.
|
103
|
+
if (await FS.pathExists(path)) {
|
101
104
|
throw new Error('File already exists: ' + path);
|
102
105
|
}
|
103
106
|
const dirName = Path.dirname(path);
|
104
|
-
if (!FS.
|
105
|
-
|
107
|
+
if (!(await FS.pathExists(dirName))) {
|
108
|
+
await FS.mkdirp(dirName);
|
106
109
|
}
|
107
|
-
|
108
|
-
FS.writeFileSync(path, YAML.stringify(yaml));
|
110
|
+
await FS.writeFile(path, YAML.stringify(yaml));
|
109
111
|
const asset = await this.importFile(path);
|
110
|
-
|
111
|
-
this.cache.flushAll();
|
112
|
-
definitionsManager.clearCache();
|
112
|
+
clearAllCaches();
|
113
113
|
const ref = `kapeta://${yaml.metadata.name}:local`;
|
114
114
|
this.maybeGenerateCode(ref, path, yaml);
|
115
115
|
return asset;
|
@@ -125,11 +125,23 @@ class AssetManager {
|
|
125
125
|
if (!asset.ymlPath) {
|
126
126
|
throw new Error('Attempted to update corrupted asset: ' + ref);
|
127
127
|
}
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
128
|
+
const path = asset.ymlPath;
|
129
|
+
try {
|
130
|
+
await repositoryManager.ignoreChangesFor(path);
|
131
|
+
await FS.writeFile(asset.ymlPath, YAML.stringify(yaml));
|
132
|
+
console.log('Wrote to ' + asset.ymlPath);
|
133
|
+
clearAllCaches();
|
134
|
+
this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
|
135
|
+
}
|
136
|
+
finally {
|
137
|
+
//We need to wait a bit for the disk to settle before we can resume watching
|
138
|
+
setTimeout(async () => {
|
139
|
+
try {
|
140
|
+
await repositoryManager.resumeChangedFor(path);
|
141
|
+
}
|
142
|
+
catch (e) { }
|
143
|
+
}, 500);
|
144
|
+
}
|
133
145
|
}
|
134
146
|
maybeGenerateCode(ref, ymlPath, block) {
|
135
147
|
ref = normalizeKapetaUri(ref);
|
@@ -148,14 +160,15 @@ class AssetManager {
|
|
148
160
|
if (filePath.startsWith('file://')) {
|
149
161
|
filePath = filePath.substring('file://'.length);
|
150
162
|
}
|
151
|
-
if (!FS.
|
163
|
+
if (!(await FS.pathExists(filePath))) {
|
152
164
|
throw new Error('File not found: ' + filePath);
|
153
165
|
}
|
154
|
-
const
|
155
|
-
|
166
|
+
const content = await FS.readFile(filePath);
|
167
|
+
const assetInfos = YAML.parseAllDocuments(content.toString()).map((doc) => doc.toJSON());
|
168
|
+
await Actions.link(new ProgressListener(), Path.dirname(filePath));
|
156
169
|
const version = 'local';
|
157
170
|
const refs = assetInfos.map((assetInfo) => `kapeta://${assetInfo.metadata.name}:${version}`);
|
158
|
-
|
171
|
+
clearAllCaches();
|
159
172
|
return this.getAssets().filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
|
160
173
|
}
|
161
174
|
async unregisterAsset(ref) {
|
@@ -163,8 +176,8 @@ class AssetManager {
|
|
163
176
|
if (!asset) {
|
164
177
|
throw new Error('Asset does not exists: ' + ref);
|
165
178
|
}
|
166
|
-
|
167
|
-
await Actions.uninstall(
|
179
|
+
clearAllCaches();
|
180
|
+
await Actions.uninstall(new ProgressListener(), [asset.ref]);
|
168
181
|
}
|
169
182
|
async installAsset(ref) {
|
170
183
|
const asset = await this.getAsset(ref, true, false);
|
@@ -11,7 +11,6 @@ import md5 from 'md5';
|
|
11
11
|
import { getBlockInstanceContainerName } from './utils/utils';
|
12
12
|
import { KapetaAPI } from '@kapeta/nodejs-api-client';
|
13
13
|
import { taskManager } from './taskManager';
|
14
|
-
const EVENT_IMAGE_PULL = 'docker-image-pull';
|
15
14
|
export const CONTAINER_LABEL_PORT_PREFIX = 'kapeta_port-';
|
16
15
|
const NANO_SECOND = 1000000;
|
17
16
|
const HEALTH_CHECK_INTERVAL = 3000;
|
@@ -2,10 +2,10 @@ import Router from 'express-promise-router';
|
|
2
2
|
import { KapetaAPI } from '@kapeta/nodejs-api-client';
|
3
3
|
import { corsHandler } from '../middleware/cors';
|
4
4
|
const router = Router();
|
5
|
-
const api = new KapetaAPI();
|
6
5
|
router.use('/', corsHandler);
|
7
6
|
router.get('/current', async (req, res) => {
|
8
7
|
try {
|
8
|
+
const api = new KapetaAPI();
|
9
9
|
if (api.hasToken()) {
|
10
10
|
res.send(await api.getCurrentIdentity());
|
11
11
|
}
|
@@ -19,6 +19,7 @@ router.get('/current', async (req, res) => {
|
|
19
19
|
});
|
20
20
|
router.get('/:identityId/memberships', async (req, res) => {
|
21
21
|
try {
|
22
|
+
const api = new KapetaAPI();
|
22
23
|
if (api.hasToken()) {
|
23
24
|
res.send(await api.getMemberships(req.params.identityId));
|
24
25
|
}
|
@@ -3,7 +3,7 @@ import request from 'request';
|
|
3
3
|
import AsyncLock from 'async-lock';
|
4
4
|
import { BlockInstanceRunner } from './utils/BlockInstanceRunner';
|
5
5
|
import { storageService } from './storageService';
|
6
|
-
import { socketManager } from './socketManager';
|
6
|
+
import { EVENT_INSTANCE_CREATED, EVENT_INSTANCE_EXITED, EVENT_STATUS_CHANGED, socketManager, } from './socketManager';
|
7
7
|
import { serviceManager } from './serviceManager';
|
8
8
|
import { assetManager } from './assetManager';
|
9
9
|
import { containerManager, HEALTH_CHECK_TIMEOUT } from './containerManager';
|
@@ -16,10 +16,6 @@ import { definitionsManager } from './definitionsManager';
|
|
16
16
|
import { Task, taskManager } from './taskManager';
|
17
17
|
const CHECK_INTERVAL = 5000;
|
18
18
|
const DEFAULT_HEALTH_PORT_TYPE = 'rest';
|
19
|
-
const EVENT_STATUS_CHANGED = 'status-changed';
|
20
|
-
const EVENT_INSTANCE_CREATED = 'instance-created';
|
21
|
-
const EVENT_INSTANCE_EXITED = 'instance-exited';
|
22
|
-
const EVENT_INSTANCE_LOG = 'instance-log';
|
23
19
|
const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs - it failed
|
24
20
|
export class InstanceManager {
|
25
21
|
_interval = undefined;
|
@@ -55,7 +51,10 @@ export class InstanceManager {
|
|
55
51
|
return [];
|
56
52
|
}
|
57
53
|
const plan = planInfo.definition;
|
58
|
-
|
54
|
+
if (!plan?.spec?.blocks) {
|
55
|
+
return [];
|
56
|
+
}
|
57
|
+
const instanceIds = plan.spec?.blocks?.map((block) => block.id) || [];
|
59
58
|
return this._instances.filter((instance) => instance.systemId === systemId && instanceIds.includes(instance.instanceId));
|
60
59
|
}
|
61
60
|
getInstance(systemId, instanceId) {
|
@@ -115,11 +114,11 @@ export class InstanceManager {
|
|
115
114
|
if (existingInstance) {
|
116
115
|
const ix = this._instances.indexOf(existingInstance);
|
117
116
|
this._instances.splice(ix, 1, instance);
|
118
|
-
|
117
|
+
socketManager.emitSystemEvent(instance.systemId, EVENT_STATUS_CHANGED, instance);
|
119
118
|
}
|
120
119
|
else {
|
121
120
|
this._instances.push(instance);
|
122
|
-
|
121
|
+
socketManager.emitSystemEvent(instance.systemId, EVENT_INSTANCE_CREATED, instance);
|
123
122
|
}
|
124
123
|
this.save();
|
125
124
|
return instance;
|
@@ -162,7 +161,7 @@ export class InstanceManager {
|
|
162
161
|
if (healthUrl) {
|
163
162
|
instance.health = healthUrl;
|
164
163
|
}
|
165
|
-
|
164
|
+
socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
166
165
|
}
|
167
166
|
else {
|
168
167
|
//If instance was not found - then we're receiving an externally started instance
|
@@ -178,7 +177,7 @@ export class InstanceManager {
|
|
178
177
|
address,
|
179
178
|
};
|
180
179
|
this._instances.push(instance);
|
181
|
-
|
180
|
+
socketManager.emitSystemEvent(systemId, EVENT_INSTANCE_CREATED, instance);
|
182
181
|
}
|
183
182
|
this.save();
|
184
183
|
return instance;
|
@@ -203,7 +202,7 @@ export class InstanceManager {
|
|
203
202
|
instance.status = InstanceStatus.STOPPED;
|
204
203
|
instance.pid = null;
|
205
204
|
instance.health = null;
|
206
|
-
|
205
|
+
socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
207
206
|
this.save();
|
208
207
|
}
|
209
208
|
});
|
@@ -261,7 +260,7 @@ export class InstanceManager {
|
|
261
260
|
instance.desiredStatus = DesiredInstanceStatus.STOP;
|
262
261
|
}
|
263
262
|
instance.status = InstanceStatus.STOPPING;
|
264
|
-
|
263
|
+
socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
265
264
|
console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
|
266
265
|
this.save();
|
267
266
|
try {
|
@@ -272,7 +271,7 @@ export class InstanceManager {
|
|
272
271
|
try {
|
273
272
|
await container.stop();
|
274
273
|
instance.status = InstanceStatus.STOPPED;
|
275
|
-
|
274
|
+
socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
276
275
|
this.save();
|
277
276
|
}
|
278
277
|
catch (e) {
|
@@ -291,7 +290,7 @@ export class InstanceManager {
|
|
291
290
|
}
|
292
291
|
process.kill(instance.pid, 'SIGTERM');
|
293
292
|
instance.status = InstanceStatus.STOPPED;
|
294
|
-
|
293
|
+
socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
295
294
|
this.save();
|
296
295
|
}
|
297
296
|
catch (e) {
|
@@ -416,8 +415,8 @@ export class InstanceManager {
|
|
416
415
|
status: InstanceStatus.FAILED,
|
417
416
|
errorMessage: e.message ?? 'Failed to start - Check logs for details.',
|
418
417
|
});
|
419
|
-
|
420
|
-
|
418
|
+
socketManager.emitInstanceLog(systemId, instanceId, logs[0]);
|
419
|
+
socketManager.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
|
421
420
|
error: `Failed to start instance: ${e.message}`,
|
422
421
|
status: EVENT_INSTANCE_EXITED,
|
423
422
|
instanceId: blockInstance.id,
|
@@ -506,7 +505,7 @@ export class InstanceManager {
|
|
506
505
|
const oldStatus = instance.status;
|
507
506
|
instance.status = newStatus;
|
508
507
|
console.log('Instance status changed: %s %s: %s -> %s', instance.systemId, instance.instanceId, oldStatus, instance.status);
|
509
|
-
|
508
|
+
socketManager.emitSystemEvent(instance.systemId, EVENT_STATUS_CHANGED, instance);
|
510
509
|
changed = true;
|
511
510
|
}
|
512
511
|
}
|
@@ -638,24 +637,6 @@ export class InstanceManager {
|
|
638
637
|
});
|
639
638
|
});
|
640
639
|
}
|
641
|
-
emitSystemEvent(systemId, type, payload) {
|
642
|
-
systemId = normalizeKapetaUri(systemId);
|
643
|
-
try {
|
644
|
-
socketManager.emit(`${systemId}/instances`, type, payload);
|
645
|
-
}
|
646
|
-
catch (e) {
|
647
|
-
console.warn('Failed to emit instance event: %s', e.message);
|
648
|
-
}
|
649
|
-
}
|
650
|
-
emitInstanceEvent(systemId, instanceId, type, payload) {
|
651
|
-
systemId = normalizeKapetaUri(systemId);
|
652
|
-
try {
|
653
|
-
socketManager.emit(`${systemId}/instances/${instanceId}`, type, payload);
|
654
|
-
}
|
655
|
-
catch (e) {
|
656
|
-
console.warn('Failed to emit instance event: %s', e.message);
|
657
|
-
}
|
658
|
-
}
|
659
640
|
}
|
660
641
|
export const instanceManager = new InstanceManager();
|
661
642
|
process.on('exit', async () => {
|
@@ -1,8 +1,9 @@
|
|
1
1
|
/// <reference types="node" />
|
2
|
-
|
3
|
-
|
4
|
-
private
|
5
|
-
constructor(
|
2
|
+
export declare class ProgressListener {
|
3
|
+
private readonly systemId;
|
4
|
+
private readonly instanceId;
|
5
|
+
constructor(systemId?: string, instanceId?: string);
|
6
|
+
private emitLog;
|
6
7
|
run(command: string, directory?: string): Promise<{
|
7
8
|
exit: number;
|
8
9
|
signal: NodeJS.Signals | null;
|
@@ -17,5 +18,3 @@ declare class ProgressListener {
|
|
17
18
|
info(msg: string, ...args: any[]): void;
|
18
19
|
debug(msg: string, ...args: any[]): void;
|
19
20
|
}
|
20
|
-
export declare const progressListener: ProgressListener;
|
21
|
-
export {};
|