@kapeta/local-cluster-service 0.10.1 → 0.11.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/src/containerManager.d.ts +6 -4
- package/dist/cjs/src/containerManager.js +100 -45
- package/dist/cjs/src/definitionsManager.d.ts +1 -0
- package/dist/cjs/src/definitionsManager.js +7 -0
- package/dist/cjs/src/instanceManager.d.ts +6 -2
- package/dist/cjs/src/instanceManager.js +240 -233
- package/dist/cjs/src/instances/routes.js +10 -4
- package/dist/cjs/src/operatorManager.js +8 -6
- package/dist/cjs/src/repositoryManager.js +4 -4
- package/dist/cjs/src/types.d.ts +0 -9
- package/dist/cjs/src/utils/BlockInstanceRunner.js +9 -64
- package/dist/cjs/src/utils/utils.d.ts +1 -1
- package/dist/cjs/src/utils/utils.js +3 -2
- package/dist/esm/src/containerManager.d.ts +6 -4
- package/dist/esm/src/containerManager.js +100 -45
- package/dist/esm/src/definitionsManager.d.ts +1 -0
- package/dist/esm/src/definitionsManager.js +7 -0
- package/dist/esm/src/instanceManager.d.ts +6 -2
- package/dist/esm/src/instanceManager.js +240 -233
- package/dist/esm/src/instances/routes.js +10 -4
- package/dist/esm/src/operatorManager.js +8 -6
- package/dist/esm/src/repositoryManager.js +4 -4
- package/dist/esm/src/types.d.ts +0 -9
- package/dist/esm/src/utils/BlockInstanceRunner.js +9 -64
- package/dist/esm/src/utils/utils.d.ts +1 -1
- package/dist/esm/src/utils/utils.js +3 -2
- package/package.json +3 -1
- package/src/containerManager.ts +126 -49
- package/src/definitionsManager.ts +8 -0
- package/src/instanceManager.ts +270 -255
- package/src/instances/routes.ts +9 -4
- package/src/operatorManager.ts +9 -8
- package/src/repositoryManager.ts +5 -5
- package/src/types.ts +0 -7
- package/src/utils/BlockInstanceRunner.ts +10 -66
- package/src/utils/LogData.ts +1 -0
- package/src/utils/utils.ts +3 -2
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.instanceManager = exports.InstanceManager = void 0;
|
7
7
|
const lodash_1 = __importDefault(require("lodash"));
|
8
8
|
const request_1 = __importDefault(require("request"));
|
9
|
+
const async_lock_1 = __importDefault(require("async-lock"));
|
9
10
|
const BlockInstanceRunner_1 = require("./utils/BlockInstanceRunner");
|
10
11
|
const storageService_1 = require("./storageService");
|
11
12
|
const socketManager_1 = require("./socketManager");
|
@@ -25,6 +26,7 @@ const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs -
|
|
25
26
|
class InstanceManager {
|
26
27
|
_interval = undefined;
|
27
28
|
_instances = [];
|
29
|
+
instanceLocks = new async_lock_1.default();
|
28
30
|
constructor() {
|
29
31
|
this._instances = storageService_1.storageService.section('instances', []);
|
30
32
|
// We need to wait a bit before running the first check
|
@@ -56,6 +58,36 @@ class InstanceManager {
|
|
56
58
|
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
57
59
|
return this._instances.find((i) => i.systemId === systemId && i.instanceId === instanceId);
|
58
60
|
}
|
61
|
+
async exclusive(systemId, instanceId, fn) {
|
62
|
+
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
63
|
+
const key = `${systemId}/${instanceId}`;
|
64
|
+
return this.instanceLocks.acquire(key, fn);
|
65
|
+
}
|
66
|
+
async getLogs(systemId, instanceId) {
|
67
|
+
const instance = this.getInstance(systemId, instanceId);
|
68
|
+
if (!instance) {
|
69
|
+
throw new Error(`Instance ${systemId}/${instanceId} not found`);
|
70
|
+
}
|
71
|
+
switch (instance.type) {
|
72
|
+
case types_1.InstanceType.DOCKER:
|
73
|
+
return await containerManager_1.containerManager.getLogs(instance);
|
74
|
+
case types_1.InstanceType.UNKNOWN:
|
75
|
+
return [{
|
76
|
+
level: 'INFO',
|
77
|
+
message: 'Instance is starting...',
|
78
|
+
time: Date.now(),
|
79
|
+
source: 'stdout',
|
80
|
+
}];
|
81
|
+
case types_1.InstanceType.LOCAL:
|
82
|
+
return [{
|
83
|
+
level: 'INFO',
|
84
|
+
message: 'Instance started outside Kapeta - logs not available...',
|
85
|
+
time: Date.now(),
|
86
|
+
source: 'stdout',
|
87
|
+
}];
|
88
|
+
}
|
89
|
+
return [];
|
90
|
+
}
|
59
91
|
async saveInternalInstance(instance) {
|
60
92
|
instance.systemId = (0, utils_1.normalizeKapetaUri)(instance.systemId);
|
61
93
|
if (instance.ref) {
|
@@ -86,58 +118,59 @@ class InstanceManager {
|
|
86
118
|
* which self-registers with the cluster service locally on startup.
|
87
119
|
*/
|
88
120
|
async registerInstanceFromSDK(systemId, instanceId, info) {
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
if (instance
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
if (info.owner === types_1.InstanceOwner.EXTERNAL) {
|
100
|
-
//If instance was started externally - then we want to replace the internal instance with that
|
101
|
-
if (instance.owner === types_1.InstanceOwner.INTERNAL &&
|
102
|
-
(instance.status === types_1.InstanceStatus.READY ||
|
103
|
-
instance.status === types_1.InstanceStatus.STARTING ||
|
104
|
-
instance.status === types_1.InstanceStatus.UNHEALTHY)) {
|
105
|
-
throw new Error(`Instance ${instanceId} is already running`);
|
121
|
+
return this.exclusive(systemId, instanceId, async () => {
|
122
|
+
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
123
|
+
let instance = this.getInstance(systemId, instanceId);
|
124
|
+
//Get target address
|
125
|
+
const address = await serviceManager_1.serviceManager.getProviderAddress(systemId, instanceId, info.portType ?? DEFAULT_HEALTH_PORT_TYPE);
|
126
|
+
const healthUrl = this.getHealthUrl(info, address);
|
127
|
+
if (instance) {
|
128
|
+
if (instance.status === types_1.InstanceStatus.STOPPING && instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
|
129
|
+
//If instance is stopping do not interfere
|
130
|
+
return;
|
106
131
|
}
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
132
|
+
if (info.owner === types_1.InstanceOwner.EXTERNAL) {
|
133
|
+
//If instance was started externally - then we want to replace the internal instance with that
|
134
|
+
if (instance.owner === types_1.InstanceOwner.INTERNAL &&
|
135
|
+
(instance.status === types_1.InstanceStatus.READY ||
|
136
|
+
instance.status === types_1.InstanceStatus.STARTING ||
|
137
|
+
instance.status === types_1.InstanceStatus.UNHEALTHY)) {
|
138
|
+
throw new Error(`Instance ${instanceId} is already running`);
|
139
|
+
}
|
140
|
+
instance.desiredStatus = info.desiredStatus;
|
141
|
+
instance.owner = info.owner;
|
142
|
+
instance.status = types_1.InstanceStatus.STARTING;
|
143
|
+
instance.startedAt = Date.now();
|
144
|
+
}
|
145
|
+
instance.pid = info.pid;
|
146
|
+
instance.address = address;
|
147
|
+
if (info.type) {
|
148
|
+
instance.type = info.type;
|
149
|
+
}
|
150
|
+
if (healthUrl) {
|
151
|
+
instance.health = healthUrl;
|
152
|
+
}
|
153
|
+
this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
117
154
|
}
|
118
|
-
|
119
|
-
instance
|
155
|
+
else {
|
156
|
+
//If instance was not found - then we're receiving an externally started instance
|
157
|
+
instance = {
|
158
|
+
...info,
|
159
|
+
systemId,
|
160
|
+
instanceId,
|
161
|
+
status: types_1.InstanceStatus.STARTING,
|
162
|
+
startedAt: Date.now(),
|
163
|
+
desiredStatus: types_1.DesiredInstanceStatus.EXTERNAL,
|
164
|
+
owner: types_1.InstanceOwner.EXTERNAL,
|
165
|
+
health: healthUrl,
|
166
|
+
address,
|
167
|
+
};
|
168
|
+
this._instances.push(instance);
|
169
|
+
this.emitSystemEvent(systemId, EVENT_INSTANCE_CREATED, instance);
|
120
170
|
}
|
121
|
-
this.
|
122
|
-
|
123
|
-
|
124
|
-
//If instance was not found - then we're receiving an externally started instance
|
125
|
-
instance = {
|
126
|
-
...info,
|
127
|
-
systemId,
|
128
|
-
instanceId,
|
129
|
-
status: types_1.InstanceStatus.STARTING,
|
130
|
-
startedAt: Date.now(),
|
131
|
-
desiredStatus: types_1.DesiredInstanceStatus.EXTERNAL,
|
132
|
-
owner: types_1.InstanceOwner.EXTERNAL,
|
133
|
-
health: healthUrl,
|
134
|
-
address,
|
135
|
-
};
|
136
|
-
this._instances.push(instance);
|
137
|
-
this.emitSystemEvent(systemId, EVENT_INSTANCE_CREATED, instance);
|
138
|
-
}
|
139
|
-
this.save();
|
140
|
-
return instance;
|
171
|
+
this.save();
|
172
|
+
return instance;
|
173
|
+
});
|
141
174
|
}
|
142
175
|
getHealthUrl(info, address) {
|
143
176
|
let healthUrl = null;
|
@@ -151,15 +184,17 @@ class InstanceManager {
|
|
151
184
|
return healthUrl;
|
152
185
|
}
|
153
186
|
markAsStopped(systemId, instanceId) {
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
instance.status
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
187
|
+
return this.exclusive(systemId, instanceId, async () => {
|
188
|
+
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
189
|
+
const instance = lodash_1.default.find(this._instances, { systemId, instanceId });
|
190
|
+
if (instance && instance.owner === types_1.InstanceOwner.EXTERNAL && instance.status !== types_1.InstanceStatus.STOPPED) {
|
191
|
+
instance.status = types_1.InstanceStatus.STOPPED;
|
192
|
+
instance.pid = null;
|
193
|
+
instance.health = null;
|
194
|
+
this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
195
|
+
this.save();
|
196
|
+
}
|
197
|
+
});
|
163
198
|
}
|
164
199
|
async startAllForPlan(systemId) {
|
165
200
|
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
@@ -188,54 +223,60 @@ class InstanceManager {
|
|
188
223
|
return settled.map((p) => (p.status === 'fulfilled' ? p.value : null)).filter((p) => !!p);
|
189
224
|
}
|
190
225
|
async stop(systemId, instanceId) {
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
instance.
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
226
|
+
return this.stopInner(systemId, instanceId, true);
|
227
|
+
}
|
228
|
+
async stopInner(systemId, instanceId, changeDesired = false) {
|
229
|
+
return this.exclusive(systemId, instanceId, async () => {
|
230
|
+
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
231
|
+
const instance = this.getInstance(systemId, instanceId);
|
232
|
+
if (!instance) {
|
233
|
+
return;
|
234
|
+
}
|
235
|
+
if (instance.status === types_1.InstanceStatus.STOPPED) {
|
236
|
+
return;
|
237
|
+
}
|
238
|
+
if (changeDesired &&
|
239
|
+
instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
|
240
|
+
instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
|
241
|
+
}
|
242
|
+
instance.status = types_1.InstanceStatus.STOPPING;
|
243
|
+
this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
244
|
+
console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
|
245
|
+
this.save();
|
246
|
+
try {
|
247
|
+
if (instance.type === 'docker') {
|
248
|
+
const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
|
249
|
+
const container = await containerManager_1.containerManager.getContainerByName(containerName);
|
250
|
+
if (container) {
|
251
|
+
try {
|
252
|
+
await container.stop();
|
253
|
+
instance.status = types_1.InstanceStatus.STOPPED;
|
254
|
+
this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
255
|
+
this.save();
|
256
|
+
}
|
257
|
+
catch (e) {
|
258
|
+
console.error('Failed to stop container', e);
|
259
|
+
}
|
216
260
|
}
|
217
|
-
|
218
|
-
console.
|
261
|
+
else {
|
262
|
+
console.warn('Container not found', containerName);
|
219
263
|
}
|
264
|
+
return;
|
220
265
|
}
|
221
|
-
|
222
|
-
|
266
|
+
if (!instance.pid) {
|
267
|
+
instance.status = types_1.InstanceStatus.STOPPED;
|
268
|
+
this.save();
|
269
|
+
return;
|
223
270
|
}
|
224
|
-
|
225
|
-
}
|
226
|
-
if (!instance.pid) {
|
271
|
+
process.kill(instance.pid, 'SIGTERM');
|
227
272
|
instance.status = types_1.InstanceStatus.STOPPED;
|
273
|
+
this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
228
274
|
this.save();
|
229
|
-
return;
|
230
275
|
}
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
}
|
236
|
-
catch (e) {
|
237
|
-
console.error('Failed to stop process', e);
|
238
|
-
}
|
276
|
+
catch (e) {
|
277
|
+
console.error('Failed to stop process', e);
|
278
|
+
}
|
279
|
+
});
|
239
280
|
}
|
240
281
|
async stopAllForPlan(systemId) {
|
241
282
|
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
@@ -243,143 +284,111 @@ class InstanceManager {
|
|
243
284
|
return this.stopInstances(instancesForPlan);
|
244
285
|
}
|
245
286
|
async start(systemId, instanceId) {
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
const blockInstance = plan.spec && plan.spec.blocks ? lodash_1.default.find(plan.spec.blocks, { id: instanceId }) : null;
|
252
|
-
if (!blockInstance) {
|
253
|
-
throw new Error('Block instance not found: ' + instanceId);
|
254
|
-
}
|
255
|
-
const blockRef = (0, utils_1.normalizeKapetaUri)(blockInstance.block.ref);
|
256
|
-
const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
|
257
|
-
if (!blockAsset) {
|
258
|
-
throw new Error('Block not found: ' + blockRef);
|
259
|
-
}
|
260
|
-
const existingInstance = this.getInstance(systemId, instanceId);
|
261
|
-
if (existingInstance) {
|
262
|
-
if (existingInstance.status === types_1.InstanceStatus.READY) {
|
263
|
-
// Instance is already running
|
264
|
-
return existingInstance;
|
287
|
+
return this.exclusive(systemId, instanceId, async () => {
|
288
|
+
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
289
|
+
const plan = await assetManager_1.assetManager.getPlan(systemId, true);
|
290
|
+
if (!plan) {
|
291
|
+
throw new Error('Plan not found: ' + systemId);
|
265
292
|
}
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
return existingInstance;
|
293
|
+
const blockInstance = plan.spec && plan.spec.blocks ? lodash_1.default.find(plan.spec.blocks, { id: instanceId }) : null;
|
294
|
+
if (!blockInstance) {
|
295
|
+
throw new Error('Block instance not found: ' + instanceId);
|
270
296
|
}
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
297
|
+
const blockRef = (0, utils_1.normalizeKapetaUri)(blockInstance.block.ref);
|
298
|
+
const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
|
299
|
+
if (!blockAsset) {
|
300
|
+
throw new Error('Block not found: ' + blockRef);
|
275
301
|
}
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
name: blockAsset.data.metadata.name,
|
282
|
-
desiredStatus: types_1.DesiredInstanceStatus.RUN,
|
283
|
-
owner: types_1.InstanceOwner.INTERNAL,
|
284
|
-
type: types_1.InstanceType.UNKNOWN,
|
285
|
-
status: types_1.InstanceStatus.STARTING,
|
286
|
-
startedAt: Date.now(),
|
287
|
-
};
|
288
|
-
console.log('Starting instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
|
289
|
-
// Save the instance before starting it, so that we can track the status
|
290
|
-
await this.saveInternalInstance(instance);
|
291
|
-
if (existingInstance) {
|
292
|
-
// Check if the instance is already running - but after we've commmuicated the desired status
|
293
|
-
const currentStatus = await this.requestInstanceStatus(existingInstance);
|
294
|
-
if (currentStatus === types_1.InstanceStatus.READY) {
|
295
|
-
// Instance is already running
|
296
|
-
return existingInstance;
|
297
|
-
}
|
298
|
-
}
|
299
|
-
const instanceConfig = await configManager_1.configManager.getConfigForSection(systemId, instanceId);
|
300
|
-
const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
|
301
|
-
const startTime = Date.now();
|
302
|
-
try {
|
303
|
-
const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
|
304
|
-
//emit stdout/stderr via sockets
|
305
|
-
processInfo.output.on('data', (data) => {
|
306
|
-
const payload = {
|
307
|
-
source: 'stdout',
|
308
|
-
level: 'INFO',
|
309
|
-
message: data.toString(),
|
310
|
-
time: Date.now(),
|
311
|
-
};
|
312
|
-
this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, payload);
|
313
|
-
});
|
314
|
-
processInfo.output.on('exit', (exitCode) => {
|
315
|
-
const timeRunning = Date.now() - startTime;
|
316
|
-
const instance = this.getInstance(systemId, instanceId);
|
317
|
-
if (instance?.status === types_1.InstanceStatus.READY) {
|
318
|
-
//It's already been running
|
319
|
-
return;
|
302
|
+
const existingInstance = this.getInstance(systemId, instanceId);
|
303
|
+
if (existingInstance) {
|
304
|
+
if (existingInstance.status === types_1.InstanceStatus.READY) {
|
305
|
+
// Instance is already running
|
306
|
+
return existingInstance;
|
320
307
|
}
|
321
|
-
if (
|
322
|
-
|
323
|
-
//
|
324
|
-
return;
|
308
|
+
if (existingInstance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
|
309
|
+
existingInstance.status === types_1.InstanceStatus.STARTING) {
|
310
|
+
// Internal instance is already starting - don't start it again
|
311
|
+
return existingInstance;
|
325
312
|
}
|
326
|
-
if (
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
this.save();
|
331
|
-
}
|
332
|
-
this.emitSystemEvent(systemId, EVENT_INSTANCE_EXITED, {
|
333
|
-
error: 'Failed to start instance',
|
334
|
-
status: EVENT_INSTANCE_EXITED,
|
335
|
-
instanceId: blockInstance.id,
|
336
|
-
});
|
313
|
+
if (existingInstance.owner === types_1.InstanceOwner.EXTERNAL &&
|
314
|
+
existingInstance.status === types_1.InstanceStatus.STARTING) {
|
315
|
+
// External instance is already starting - don't start it again
|
316
|
+
return existingInstance;
|
337
317
|
}
|
338
|
-
}
|
339
|
-
instance
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
{
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
const
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
318
|
+
}
|
319
|
+
let instance = {
|
320
|
+
systemId,
|
321
|
+
instanceId,
|
322
|
+
ref: blockRef,
|
323
|
+
name: blockAsset.data.metadata.name,
|
324
|
+
desiredStatus: types_1.DesiredInstanceStatus.RUN,
|
325
|
+
owner: types_1.InstanceOwner.INTERNAL,
|
326
|
+
type: existingInstance?.type ?? types_1.InstanceType.UNKNOWN,
|
327
|
+
status: types_1.InstanceStatus.STARTING,
|
328
|
+
startedAt: Date.now(),
|
329
|
+
};
|
330
|
+
console.log('Starting instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
|
331
|
+
// Save the instance before starting it, so that we can track the status
|
332
|
+
await this.saveInternalInstance(instance);
|
333
|
+
if (existingInstance) {
|
334
|
+
// Check if the instance is already running - but after we've commmuicated the desired status
|
335
|
+
const currentStatus = await this.requestInstanceStatus(existingInstance);
|
336
|
+
if (currentStatus === types_1.InstanceStatus.READY) {
|
337
|
+
// Instance is already running
|
338
|
+
return existingInstance;
|
339
|
+
}
|
340
|
+
}
|
341
|
+
const instanceConfig = await configManager_1.configManager.getConfigForSection(systemId, instanceId);
|
342
|
+
const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
|
343
|
+
const startTime = Date.now();
|
344
|
+
try {
|
345
|
+
const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
|
346
|
+
instance.status = types_1.InstanceStatus.READY;
|
347
|
+
return this.saveInternalInstance({
|
348
|
+
...instance,
|
349
|
+
type: processInfo.type,
|
350
|
+
pid: processInfo.pid ?? -1,
|
351
|
+
health: null,
|
352
|
+
portType: processInfo.portType,
|
353
|
+
status: types_1.InstanceStatus.READY,
|
354
|
+
});
|
355
|
+
}
|
356
|
+
catch (e) {
|
357
|
+
console.warn('Failed to start instance', e);
|
358
|
+
const logs = [
|
359
|
+
{
|
360
|
+
source: 'stdout',
|
361
|
+
level: 'ERROR',
|
362
|
+
message: e.message,
|
363
|
+
time: Date.now(),
|
364
|
+
},
|
365
|
+
];
|
366
|
+
const out = await this.saveInternalInstance({
|
367
|
+
...instance,
|
368
|
+
type: types_1.InstanceType.LOCAL,
|
369
|
+
pid: null,
|
370
|
+
health: null,
|
371
|
+
portType: DEFAULT_HEALTH_PORT_TYPE,
|
372
|
+
status: types_1.InstanceStatus.FAILED,
|
373
|
+
});
|
374
|
+
this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
|
375
|
+
this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
|
376
|
+
error: `Failed to start instance: ${e.message}`,
|
377
|
+
status: EVENT_INSTANCE_EXITED,
|
378
|
+
instanceId: blockInstance.id,
|
379
|
+
});
|
380
|
+
return out;
|
381
|
+
}
|
382
|
+
});
|
379
383
|
}
|
380
384
|
async restart(systemId, instanceId) {
|
381
385
|
systemId = (0, utils_1.normalizeKapetaUri)(systemId);
|
382
|
-
await this.
|
386
|
+
await this.stopInner(systemId, instanceId);
|
387
|
+
const existingInstance = this.getInstance(systemId, instanceId);
|
388
|
+
if (existingInstance?.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
|
389
|
+
// Internal instance was marked as stopped - abort restart
|
390
|
+
return existingInstance;
|
391
|
+
}
|
383
392
|
return this.start(systemId, instanceId);
|
384
393
|
}
|
385
394
|
async stopAll() {
|
@@ -393,9 +402,7 @@ class InstanceManager {
|
|
393
402
|
save() {
|
394
403
|
try {
|
395
404
|
storageService_1.storageService.put('instances', this._instances.map((instance) => {
|
396
|
-
|
397
|
-
delete copy.internal;
|
398
|
-
return copy;
|
405
|
+
return { ...instance };
|
399
406
|
}));
|
400
407
|
}
|
401
408
|
catch (e) {
|
@@ -408,7 +415,7 @@ class InstanceManager {
|
|
408
415
|
const all = [...this._instances];
|
409
416
|
while (all.length > 0) {
|
410
417
|
// Check a few instances at a time - docker doesn't like too many concurrent requests
|
411
|
-
const chunk = all.splice(0,
|
418
|
+
const chunk = all.splice(0, 30);
|
412
419
|
const promises = chunk.map(async (instance) => {
|
413
420
|
if (!instance.systemId) {
|
414
421
|
return;
|
@@ -468,7 +475,7 @@ class InstanceManager {
|
|
468
475
|
[types_1.InstanceStatus.READY, types_1.InstanceStatus.STARTING, types_1.InstanceStatus.UNHEALTHY].includes(newStatus)) {
|
469
476
|
//If the instance is running but we want it to stop, stop it
|
470
477
|
try {
|
471
|
-
await this.
|
478
|
+
await this.stopInner(instance.systemId, instance.instanceId);
|
472
479
|
}
|
473
480
|
catch (e) {
|
474
481
|
console.warn('Failed to stop instance', instance.systemId, instance.instanceId, e);
|
@@ -497,7 +504,7 @@ class InstanceManager {
|
|
497
504
|
}
|
498
505
|
async getExternalStatus(instance) {
|
499
506
|
if (instance.type === types_1.InstanceType.DOCKER) {
|
500
|
-
const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.instanceId);
|
507
|
+
const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
|
501
508
|
const container = await containerManager_1.containerManager.getContainerByName(containerName);
|
502
509
|
if (!container) {
|
503
510
|
// If the container doesn't exist, we consider the instance stopped
|
@@ -67,15 +67,21 @@ router.post('/:systemId/:instanceId/stop', async (req, res) => {
|
|
67
67
|
/**
|
68
68
|
* Get logs for instance in a plan
|
69
69
|
*/
|
70
|
-
router.get('/:systemId/:instanceId/logs', (req, res) => {
|
70
|
+
router.get('/:systemId/:instanceId/logs', async (req, res) => {
|
71
71
|
const instanceInfo = instanceManager_1.instanceManager.getInstance(req.params.systemId, req.params.instanceId);
|
72
72
|
if (!instanceInfo) {
|
73
73
|
res.status(404).send({ ok: false });
|
74
74
|
return;
|
75
75
|
}
|
76
|
-
|
77
|
-
logs
|
78
|
-
|
76
|
+
try {
|
77
|
+
const logs = await instanceManager_1.instanceManager.getLogs(req.params.systemId, req.params.instanceId);
|
78
|
+
res.status(200).send({
|
79
|
+
logs,
|
80
|
+
});
|
81
|
+
}
|
82
|
+
catch (e) {
|
83
|
+
res.status(500).send({ ok: false, error: e.message });
|
84
|
+
}
|
79
85
|
});
|
80
86
|
/**
|
81
87
|
* Get public address for instance in a plan if available
|
@@ -117,13 +117,11 @@ class OperatorManager {
|
|
117
117
|
const operatorData = operator.getData();
|
118
118
|
const portTypes = Object.keys(operatorData.ports);
|
119
119
|
portTypes.sort();
|
120
|
-
const containerBaseName = 'kapeta-resource';
|
121
|
-
const nameParts = [resourceType.toLowerCase()];
|
122
120
|
const ports = {};
|
123
121
|
for (let i = 0; i < portTypes.length; i++) {
|
124
122
|
const portType = portTypes[i];
|
125
123
|
let containerPortInfo = operatorData.ports[portType];
|
126
|
-
const hostPort = await serviceManager_1.serviceManager.ensureServicePort(resourceType, portType);
|
124
|
+
const hostPort = await serviceManager_1.serviceManager.ensureServicePort(systemId, resourceType, portType);
|
127
125
|
if (typeof containerPortInfo === 'number' || typeof containerPortInfo === 'string') {
|
128
126
|
containerPortInfo = { port: containerPortInfo, type: 'tcp' };
|
129
127
|
}
|
@@ -131,14 +129,18 @@ class OperatorManager {
|
|
131
129
|
containerPortInfo.type = 'tcp';
|
132
130
|
}
|
133
131
|
const portId = containerPortInfo.port + '/' + containerPortInfo.type;
|
134
|
-
nameParts.push(portType + '-' + portId + '-' + hostPort);
|
135
132
|
ports[portId] = {
|
136
133
|
type: portType,
|
137
134
|
hostPort,
|
138
135
|
};
|
139
136
|
}
|
140
|
-
const mounts = containerManager_1.containerManager.createMounts(resourceType, operatorData.mounts);
|
141
|
-
const
|
137
|
+
const mounts = await containerManager_1.containerManager.createMounts(systemId, resourceType, operatorData.mounts);
|
138
|
+
const nameParts = [
|
139
|
+
systemId,
|
140
|
+
resourceType.toLowerCase(),
|
141
|
+
version
|
142
|
+
];
|
143
|
+
const containerName = `kapeta-resource-${(0, md5_1.default)(nameParts.join('_'))}`;
|
142
144
|
const PortBindings = {};
|
143
145
|
const Env = [];
|
144
146
|
const Labels = {
|