@kapeta/local-cluster-service 0.0.76 → 0.1.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 +4 -4
- package/index.js +2 -2
- package/package.json +2 -3
- package/src/assetManager.js +4 -2
- package/src/containerManager.js +288 -243
- package/src/instanceManager.js +33 -9
- package/src/instances/routes.js +10 -0
- package/src/networkManager.js +6 -6
- package/src/proxy/routes.js +16 -14
- package/src/proxy/types/rest.js +5 -6
- package/src/proxy/types/web.js +3 -5
- package/src/repositoryManager.js +16 -5
- package/src/serviceManager.js +0 -1
- package/src/utils/BlockInstanceRunner.js +435 -0
- package/src/utils/LogData.js +50 -0
- package/src/utils/utils.js +13 -0
package/src/containerManager.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
const {
|
2
|
-
const _ = require("lodash");
|
3
|
-
const os = require("os");
|
1
|
+
const {Docker} = require("node-docker-api");
|
4
2
|
const path = require("path");
|
5
|
-
|
3
|
+
const _ = require('lodash');
|
4
|
+
const FS = require("node:fs");
|
5
|
+
const os = require("os");
|
6
6
|
const LABEL_PORT_PREFIX = "kapeta_port-";
|
7
7
|
|
8
8
|
const NANO_SECOND = 1000000;
|
@@ -10,301 +10,346 @@ const HEALTH_CHECK_INTERVAL = 1000;
|
|
10
10
|
const HEALTH_CHECK_MAX = 20;
|
11
11
|
|
12
12
|
const promisifyStream = (stream) =>
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
new Promise((resolve, reject) => {
|
14
|
+
stream.on("data", (d) => console.log(d.toString()));
|
15
|
+
stream.on("end", resolve);
|
16
|
+
stream.on("error", reject);
|
17
|
+
});
|
18
18
|
|
19
19
|
class ContainerManager {
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
}
|
24
|
-
|
25
|
-
async initialize() {
|
26
|
-
// try
|
27
|
-
const connectOptions = [
|
28
|
-
// use defaults: DOCKER_HOST etc from env, if available
|
29
|
-
undefined,
|
30
|
-
// default linux
|
31
|
-
{ socketPath: "/var/run/docker.sock" },
|
32
|
-
// default macOS
|
33
|
-
{ socketPath: path.join(os.homedir(), ".docker/run/docker.sock") },
|
34
|
-
// Default http
|
35
|
-
{ protocol: "http", host: "localhost", port: 2375 },
|
36
|
-
{ protocol: "https", host: "localhost", port: 2376 },
|
37
|
-
];
|
38
|
-
for (const opts of connectOptions) {
|
39
|
-
try {
|
40
|
-
const client = new Docker(opts);
|
41
|
-
await client.ping();
|
42
|
-
this._docker = client;
|
43
|
-
return;
|
44
|
-
} catch (err) {
|
45
|
-
// silently ignore bad configs
|
46
|
-
}
|
20
|
+
constructor() {
|
21
|
+
this._docker = null;
|
22
|
+
this._alive = false;
|
47
23
|
}
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
async ping() {
|
52
|
-
await this._docker.ping();
|
53
|
-
this._alive = true;
|
54
|
-
}
|
55
|
-
|
56
|
-
async pull(image) {
|
57
|
-
let [imageName, tag] = image.split(/:/);
|
58
|
-
if (!tag) {
|
59
|
-
tag = "latest";
|
24
|
+
|
25
|
+
isAlive() {
|
26
|
+
return this._alive;
|
60
27
|
}
|
61
28
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
29
|
+
async initialize() {
|
30
|
+
// try
|
31
|
+
const connectOptions = [
|
32
|
+
// use defaults: DOCKER_HOST etc from env, if available
|
33
|
+
undefined,
|
34
|
+
// default linux
|
35
|
+
{socketPath: "/var/run/docker.sock"},
|
36
|
+
// default macOS
|
37
|
+
{socketPath: path.join(os.homedir(), ".docker/run/docker.sock")},
|
38
|
+
// Default http
|
39
|
+
{protocol: "http", host: "localhost", port: 2375},
|
40
|
+
{protocol: "https", host: "localhost", port: 2376},
|
41
|
+
];
|
42
|
+
for (const opts of connectOptions) {
|
43
|
+
try {
|
44
|
+
const client = new Docker(opts);
|
45
|
+
await client.ping();
|
46
|
+
this._docker = client;
|
47
|
+
return;
|
48
|
+
} catch (err) {
|
49
|
+
// silently ignore bad configs
|
50
|
+
}
|
68
51
|
}
|
69
|
-
|
70
|
-
|
71
|
-
}
|
72
|
-
|
73
|
-
/**
|
74
|
-
*
|
75
|
-
* @param {string} image
|
76
|
-
* @param {string} name
|
77
|
-
* @param {{ports:{},mounts:{},env:{}}} opts
|
78
|
-
* @return {Promise<ContainerInfo>}
|
79
|
-
*/
|
80
|
-
async run(image, name, opts) {
|
81
|
-
const Mounts = [];
|
82
|
-
const PortBindings = {};
|
83
|
-
const Env = [];
|
84
|
-
const Labels = {
|
85
|
-
kapeta: "true",
|
86
|
-
};
|
87
|
-
|
88
|
-
console.log("Pulling image: %s", image);
|
89
|
-
|
90
|
-
await this.pull(image);
|
91
|
-
|
92
|
-
console.log("Image pulled: %s", image);
|
93
|
-
|
94
|
-
_.forEach(opts.ports, (portInfo, containerPort) => {
|
95
|
-
PortBindings["" + containerPort] = [
|
96
|
-
{
|
97
|
-
HostPort: "" + portInfo.hostPort,
|
98
|
-
},
|
99
|
-
];
|
100
|
-
|
101
|
-
Labels[LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
|
102
|
-
});
|
52
|
+
throw new Error("Unable to connect to docker");
|
53
|
+
}
|
103
54
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
Type: "bind",
|
109
|
-
ReadOnly: false,
|
110
|
-
Consistency: "consistent",
|
111
|
-
});
|
112
|
-
});
|
55
|
+
async ping() {
|
56
|
+
await this._docker.ping();
|
57
|
+
this._alive = true;
|
58
|
+
}
|
113
59
|
|
114
|
-
|
115
|
-
|
116
|
-
|
60
|
+
async ping() {
|
61
|
+
|
62
|
+
try {
|
63
|
+
const pingResult = await this._docker.ping();
|
64
|
+
if (pingResult !== 'OK') {
|
65
|
+
throw new Error(`Ping failed: ${pingResult}`);
|
66
|
+
}
|
67
|
+
} catch (e) {
|
68
|
+
throw new Error(`Docker not running. Please start the docker daemon before running this command. Error: ${e.message}`);
|
69
|
+
}
|
117
70
|
|
118
|
-
|
119
|
-
|
120
|
-
if (opts.health) {
|
121
|
-
HealthCheck = {
|
122
|
-
Test: ["CMD-SHELL", opts.health.cmd],
|
123
|
-
Interval: opts.health.interval
|
124
|
-
? opts.health.interval * NANO_SECOND
|
125
|
-
: 5000 * NANO_SECOND,
|
126
|
-
Timeout: opts.health.timeout
|
127
|
-
? opts.health.timeout * NANO_SECOND
|
128
|
-
: 15000 * NANO_SECOND,
|
129
|
-
Retries: opts.health.retries || 10,
|
130
|
-
};
|
131
|
-
|
132
|
-
console.log("Adding health check", HealthCheck);
|
71
|
+
this._alive = true;
|
133
72
|
}
|
134
73
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
HealthCheck,
|
141
|
-
HostConfig: {
|
142
|
-
PortBindings,
|
143
|
-
Mounts,
|
144
|
-
},
|
145
|
-
});
|
74
|
+
async ensureAlive() {
|
75
|
+
if (!this._alive) {
|
76
|
+
await this.ping();
|
77
|
+
}
|
78
|
+
}
|
146
79
|
|
147
|
-
|
80
|
+
async docker() {
|
81
|
+
await this.ensureAlive();
|
82
|
+
return this._docker;
|
83
|
+
}
|
148
84
|
|
149
|
-
|
150
|
-
|
85
|
+
async getContainerByName(containerName) {
|
86
|
+
const containers = await this._docker.container.list({all: true});
|
87
|
+
return containers.find(container => {
|
88
|
+
return container.data.Names.indexOf(`/${containerName}`) > -1;
|
89
|
+
});
|
151
90
|
}
|
152
91
|
|
153
|
-
|
154
|
-
|
92
|
+
async pull(image) {
|
93
|
+
let [imageName, tag] = image.split(/:/);
|
94
|
+
if (!tag) {
|
95
|
+
tag = 'latest';
|
96
|
+
}
|
155
97
|
|
156
|
-
|
157
|
-
|
158
|
-
|
98
|
+
await this._docker.image
|
99
|
+
.create(
|
100
|
+
{},
|
101
|
+
{
|
102
|
+
fromImage: imageName,
|
103
|
+
tag: tag,
|
104
|
+
}
|
105
|
+
)
|
106
|
+
.then((stream) => promisifyStream(stream));
|
159
107
|
}
|
160
108
|
|
161
|
-
|
162
|
-
|
109
|
+
/**
|
110
|
+
*
|
111
|
+
* @param {string} image
|
112
|
+
* @param {string} name
|
113
|
+
* @param {{ports:{},mounts:{},env:{}}} opts
|
114
|
+
* @return {Promise<ContainerInfo>}
|
115
|
+
*/
|
116
|
+
async run(image, name, opts) {
|
117
|
+
const Mounts = [];
|
118
|
+
const PortBindings = {};
|
119
|
+
const Env = [];
|
120
|
+
const Labels = {
|
121
|
+
kapeta: "true",
|
122
|
+
};
|
123
|
+
|
124
|
+
console.log("Pulling image: %s", image);
|
125
|
+
|
126
|
+
await this.pull(image);
|
127
|
+
|
128
|
+
console.log("Image pulled: %s", image);
|
129
|
+
|
130
|
+
_.forEach(opts.ports, (portInfo, containerPort) => {
|
131
|
+
PortBindings['' + containerPort] = [
|
132
|
+
{
|
133
|
+
HostPort: '' + portInfo.hostPort,
|
134
|
+
HostIp: '127.0.0.1'
|
135
|
+
}
|
136
|
+
];
|
137
|
+
|
138
|
+
Labels[LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
|
139
|
+
});
|
140
|
+
|
141
|
+
_.forEach(opts.mounts, (Source, Target) => {
|
142
|
+
Mounts.push({
|
143
|
+
Target,
|
144
|
+
Source,
|
145
|
+
Type: "bind",
|
146
|
+
ReadOnly: false,
|
147
|
+
Consistency: "consistent",
|
148
|
+
});
|
149
|
+
});
|
150
|
+
|
151
|
+
_.forEach(opts.env, (value, name) => {
|
152
|
+
Env.push(name + "=" + value);
|
153
|
+
});
|
154
|
+
|
155
|
+
let HealthCheck = undefined;
|
156
|
+
|
157
|
+
if (opts.health) {
|
158
|
+
HealthCheck = {
|
159
|
+
Test: ["CMD-SHELL", opts.health.cmd],
|
160
|
+
Interval: opts.health.interval
|
161
|
+
? opts.health.interval * NANO_SECOND
|
162
|
+
: 5000 * NANO_SECOND,
|
163
|
+
Timeout: opts.health.timeout
|
164
|
+
? opts.health.timeout * NANO_SECOND
|
165
|
+
: 15000 * NANO_SECOND,
|
166
|
+
Retries: opts.health.retries || 10,
|
167
|
+
};
|
168
|
+
|
169
|
+
console.log("Adding health check", HealthCheck);
|
170
|
+
}
|
171
|
+
|
172
|
+
|
173
|
+
const dockerContainer = await this.startContainer({
|
174
|
+
name: name,
|
175
|
+
Image: image,
|
176
|
+
Labels,
|
177
|
+
Env,
|
178
|
+
HealthCheck,
|
179
|
+
HostConfig: {
|
180
|
+
PortBindings,
|
181
|
+
Mounts
|
182
|
+
}
|
183
|
+
});
|
184
|
+
|
185
|
+
if (opts.health) {
|
186
|
+
await this._waitForHealthy(dockerContainer);
|
187
|
+
}
|
188
|
+
|
189
|
+
return new ContainerInfo(dockerContainer);
|
163
190
|
}
|
164
191
|
|
165
|
-
|
166
|
-
|
167
|
-
|
192
|
+
async startContainer(opts) {
|
193
|
+
const dockerContainer = await this._docker.container.create(opts);
|
194
|
+
|
195
|
+
await dockerContainer.start();
|
196
|
+
|
197
|
+
return dockerContainer;
|
168
198
|
}
|
169
199
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
try {
|
193
|
-
dockerContainer = await this._docker.container.get(name);
|
194
|
-
await dockerContainer.status();
|
195
|
-
} catch (err) {
|
196
|
-
//Ignore
|
197
|
-
console.log("Container not available - creating it: %s", name);
|
198
|
-
dockerContainer = null;
|
200
|
+
|
201
|
+
async _waitForHealthy(container, attempt) {
|
202
|
+
if (!attempt) {
|
203
|
+
attempt = 0;
|
204
|
+
}
|
205
|
+
|
206
|
+
if (attempt >= HEALTH_CHECK_MAX) {
|
207
|
+
throw new Error("Operator did not become healthy within the timeout");
|
208
|
+
}
|
209
|
+
|
210
|
+
if (await this._isHealthy(container)) {
|
211
|
+
console.log("Container became healthy");
|
212
|
+
return;
|
213
|
+
}
|
214
|
+
|
215
|
+
return new Promise((resolve) => {
|
216
|
+
setTimeout(async () => {
|
217
|
+
await this._waitForHealthy(container, attempt + 1);
|
218
|
+
resolve();
|
219
|
+
}, HEALTH_CHECK_INTERVAL);
|
220
|
+
});
|
199
221
|
}
|
200
222
|
|
201
|
-
|
202
|
-
|
223
|
+
async _isHealthy(container) {
|
224
|
+
const info = await container.status();
|
225
|
+
|
226
|
+
return info?.data?.State?.Health?.Status === "healthy";
|
203
227
|
}
|
204
228
|
|
205
|
-
|
206
|
-
|
229
|
+
/**
|
230
|
+
*
|
231
|
+
* @param name
|
232
|
+
* @return {Promise<ContainerInfo>}
|
233
|
+
*/
|
234
|
+
async get(name) {
|
235
|
+
let dockerContainer = null;
|
236
|
+
|
237
|
+
try {
|
238
|
+
dockerContainer = await this._docker.container.get(name);
|
239
|
+
await dockerContainer.status();
|
240
|
+
} catch (err) {
|
241
|
+
//Ignore
|
242
|
+
console.log("Container not available - creating it: %s", name);
|
243
|
+
dockerContainer = null;
|
244
|
+
}
|
245
|
+
|
246
|
+
if (!dockerContainer) {
|
247
|
+
return null;
|
248
|
+
}
|
249
|
+
|
250
|
+
return new ContainerInfo(dockerContainer);
|
251
|
+
}
|
207
252
|
}
|
208
253
|
|
209
254
|
class ContainerInfo {
|
210
|
-
/**
|
211
|
-
*
|
212
|
-
* @param {Container} dockerContainer
|
213
|
-
*/
|
214
|
-
constructor(dockerContainer) {
|
215
255
|
/**
|
216
256
|
*
|
217
|
-
* @
|
218
|
-
* @private
|
257
|
+
* @param {Container} dockerContainer
|
219
258
|
*/
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
return false;
|
259
|
+
constructor(dockerContainer) {
|
260
|
+
/**
|
261
|
+
*
|
262
|
+
* @type {Container}
|
263
|
+
* @private
|
264
|
+
*/
|
265
|
+
this._container = dockerContainer;
|
228
266
|
}
|
229
267
|
|
230
|
-
|
231
|
-
|
268
|
+
async isRunning() {
|
269
|
+
const inspectResult = await this.getStatus();
|
232
270
|
|
233
|
-
|
234
|
-
|
235
|
-
|
271
|
+
if (!inspectResult || !inspectResult.State) {
|
272
|
+
return false;
|
273
|
+
}
|
236
274
|
|
237
|
-
|
238
|
-
|
239
|
-
}
|
275
|
+
return inspectResult.State.Running || inspectResult.State.Restarting;
|
276
|
+
}
|
240
277
|
|
241
|
-
|
242
|
-
|
243
|
-
|
278
|
+
async start() {
|
279
|
+
await this._container.start();
|
280
|
+
}
|
244
281
|
|
245
|
-
|
246
|
-
|
247
|
-
|
282
|
+
async restart() {
|
283
|
+
await this._container.restart();
|
284
|
+
}
|
248
285
|
|
249
|
-
|
250
|
-
|
286
|
+
async stop() {
|
287
|
+
await this._container.stop();
|
288
|
+
}
|
251
289
|
|
252
|
-
|
253
|
-
|
290
|
+
async remove(opts) {
|
291
|
+
await this._container.delete({force: !!opts.force});
|
254
292
|
}
|
255
293
|
|
256
|
-
|
257
|
-
|
294
|
+
async getPort(type) {
|
295
|
+
const ports = await this.getPorts();
|
258
296
|
|
259
|
-
|
260
|
-
|
297
|
+
if (ports[type]) {
|
298
|
+
return ports[type];
|
299
|
+
}
|
261
300
|
|
262
|
-
|
263
|
-
|
301
|
+
return null;
|
302
|
+
}
|
264
303
|
|
265
|
-
|
266
|
-
|
304
|
+
async getStatus() {
|
305
|
+
const result = await this._container.status();
|
267
306
|
|
268
|
-
|
269
|
-
!inspectResult ||
|
270
|
-
!inspectResult.Config ||
|
271
|
-
!inspectResult.Config.Labels
|
272
|
-
) {
|
273
|
-
return false;
|
307
|
+
return result ? result.data : null;
|
274
308
|
}
|
275
309
|
|
276
|
-
|
277
|
-
|
310
|
+
async getPorts() {
|
311
|
+
const inspectResult = await this.getStatus();
|
278
312
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
313
|
+
if (
|
314
|
+
!inspectResult ||
|
315
|
+
!inspectResult.Config ||
|
316
|
+
!inspectResult.Config.Labels
|
317
|
+
) {
|
318
|
+
return false;
|
319
|
+
}
|
283
320
|
|
284
|
-
|
321
|
+
const portTypes = {};
|
322
|
+
const ports = {};
|
285
323
|
|
286
|
-
|
287
|
-
|
324
|
+
_.forEach(inspectResult.Config.Labels, (portType, name) => {
|
325
|
+
if (!name.startsWith(LABEL_PORT_PREFIX)) {
|
326
|
+
return;
|
327
|
+
}
|
288
328
|
|
289
|
-
|
290
|
-
inspectResult.HostConfig.PortBindings,
|
291
|
-
(portBindings, containerPortSpec) => {
|
292
|
-
let [containerPort, protocol] = containerPortSpec.split(/\//);
|
329
|
+
const hostPort = name.substr(LABEL_PORT_PREFIX.length);
|
293
330
|
|
294
|
-
|
331
|
+
portTypes[hostPort] = portType;
|
332
|
+
});
|
295
333
|
|
296
|
-
|
334
|
+
_.forEach(
|
335
|
+
inspectResult.HostConfig.PortBindings,
|
336
|
+
(portBindings, containerPortSpec) => {
|
337
|
+
let [containerPort, protocol] = containerPortSpec.split(/\//);
|
297
338
|
|
298
|
-
|
299
|
-
containerPort,
|
300
|
-
protocol,
|
301
|
-
hostPort,
|
302
|
-
};
|
303
|
-
}
|
304
|
-
);
|
339
|
+
const hostPort = portBindings[0].HostPort;
|
305
340
|
|
306
|
-
|
307
|
-
|
341
|
+
const portType = portTypes[hostPort];
|
342
|
+
|
343
|
+
ports[portType] = {
|
344
|
+
containerPort,
|
345
|
+
protocol,
|
346
|
+
hostPort,
|
347
|
+
};
|
348
|
+
}
|
349
|
+
);
|
350
|
+
|
351
|
+
return ports;
|
352
|
+
}
|
308
353
|
}
|
309
354
|
|
310
355
|
module.exports = new ContainerManager();
|
package/src/instanceManager.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
const _ = require('lodash');
|
2
2
|
const request = require('request');
|
3
|
-
|
4
|
-
const
|
3
|
+
const EventEmitter = require("events");
|
4
|
+
const BlockInstanceRunner = require('./utils/BlockInstanceRunner');
|
5
5
|
|
6
6
|
const storageService = require('./storageService');
|
7
7
|
const socketManager = require('./socketManager');
|
@@ -68,6 +68,10 @@ class InstanceManager {
|
|
68
68
|
|
69
69
|
if (instance.status !== newStatus) {
|
70
70
|
instance.status = newStatus;
|
71
|
+
console.log(
|
72
|
+
'Instance status changed: %s %s -> %s',
|
73
|
+
instance.systemId, instance.instanceId, instance.status
|
74
|
+
)
|
71
75
|
this._emit(instance.systemId, EVENT_STATUS_CHANGED, instance);
|
72
76
|
changed = true;
|
73
77
|
}
|
@@ -85,7 +89,11 @@ class InstanceManager {
|
|
85
89
|
|
86
90
|
if (instance.type === 'docker') {
|
87
91
|
const container = await containerManager.get(instance.pid);
|
88
|
-
|
92
|
+
if (!container) {
|
93
|
+
console.warn('Container not found: %s', instance.pid);
|
94
|
+
return false;
|
95
|
+
}
|
96
|
+
return await container.isRunning();
|
89
97
|
}
|
90
98
|
|
91
99
|
//Otherwise its just a normal process.
|
@@ -185,8 +193,12 @@ class InstanceManager {
|
|
185
193
|
if (instance) {
|
186
194
|
instance.status = STATUS_STARTING;
|
187
195
|
instance.pid = info.pid;
|
188
|
-
|
189
|
-
|
196
|
+
if (info.type) {
|
197
|
+
instance.type = info.type;
|
198
|
+
}
|
199
|
+
if (healthUrl) {
|
200
|
+
instance.health = healthUrl;
|
201
|
+
}
|
190
202
|
this._emit(systemId, EVENT_STATUS_CHANGED, instance);
|
191
203
|
} else {
|
192
204
|
instance = {
|
@@ -268,7 +280,9 @@ class InstanceManager {
|
|
268
280
|
try {
|
269
281
|
if (instance.type === 'docker') {
|
270
282
|
const container = await containerManager.get(instance.pid);
|
271
|
-
|
283
|
+
if (container) {
|
284
|
+
await container.stop();
|
285
|
+
}
|
272
286
|
return;
|
273
287
|
}
|
274
288
|
process.kill(instance.pid, 'SIGTERM');
|
@@ -383,7 +397,8 @@ class InstanceManager {
|
|
383
397
|
message: e.message,
|
384
398
|
time: Date.now()
|
385
399
|
}
|
386
|
-
]
|
400
|
+
];
|
401
|
+
|
387
402
|
await this.registerInstance(planRef, instanceId, {
|
388
403
|
type: 'local',
|
389
404
|
pid: null,
|
@@ -401,7 +416,12 @@ class InstanceManager {
|
|
401
416
|
return this._processes[planRef][instanceId] = {
|
402
417
|
pid: -1,
|
403
418
|
type,
|
404
|
-
logs: () => logs
|
419
|
+
logs: () => logs,
|
420
|
+
stop: () => Promise.resolve(),
|
421
|
+
ref: blockRef,
|
422
|
+
id: instanceId,
|
423
|
+
name: blockInstance.name,
|
424
|
+
output: new EventEmitter()
|
405
425
|
};
|
406
426
|
}
|
407
427
|
|
@@ -427,7 +447,11 @@ class InstanceManager {
|
|
427
447
|
}
|
428
448
|
|
429
449
|
if (this._processes[planRef][instanceId]) {
|
430
|
-
|
450
|
+
try {
|
451
|
+
await this._processes[planRef][instanceId].stop();
|
452
|
+
} catch (e) {
|
453
|
+
console.error('Failed to stop process for instance: %s -> %s', planRef, instanceId, e);
|
454
|
+
}
|
431
455
|
delete this._processes[planRef][instanceId];
|
432
456
|
}
|
433
457
|
}
|