@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.
Files changed (38) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/src/containerManager.d.ts +6 -4
  3. package/dist/cjs/src/containerManager.js +100 -45
  4. package/dist/cjs/src/definitionsManager.d.ts +1 -0
  5. package/dist/cjs/src/definitionsManager.js +7 -0
  6. package/dist/cjs/src/instanceManager.d.ts +6 -2
  7. package/dist/cjs/src/instanceManager.js +240 -233
  8. package/dist/cjs/src/instances/routes.js +10 -4
  9. package/dist/cjs/src/operatorManager.js +8 -6
  10. package/dist/cjs/src/repositoryManager.js +4 -4
  11. package/dist/cjs/src/types.d.ts +0 -9
  12. package/dist/cjs/src/utils/BlockInstanceRunner.js +9 -64
  13. package/dist/cjs/src/utils/utils.d.ts +1 -1
  14. package/dist/cjs/src/utils/utils.js +3 -2
  15. package/dist/esm/src/containerManager.d.ts +6 -4
  16. package/dist/esm/src/containerManager.js +100 -45
  17. package/dist/esm/src/definitionsManager.d.ts +1 -0
  18. package/dist/esm/src/definitionsManager.js +7 -0
  19. package/dist/esm/src/instanceManager.d.ts +6 -2
  20. package/dist/esm/src/instanceManager.js +240 -233
  21. package/dist/esm/src/instances/routes.js +10 -4
  22. package/dist/esm/src/operatorManager.js +8 -6
  23. package/dist/esm/src/repositoryManager.js +4 -4
  24. package/dist/esm/src/types.d.ts +0 -9
  25. package/dist/esm/src/utils/BlockInstanceRunner.js +9 -64
  26. package/dist/esm/src/utils/utils.d.ts +1 -1
  27. package/dist/esm/src/utils/utils.js +3 -2
  28. package/package.json +3 -1
  29. package/src/containerManager.ts +126 -49
  30. package/src/definitionsManager.ts +8 -0
  31. package/src/instanceManager.ts +270 -255
  32. package/src/instances/routes.ts +9 -4
  33. package/src/operatorManager.ts +9 -8
  34. package/src/repositoryManager.ts +5 -5
  35. package/src/types.ts +0 -7
  36. package/src/utils/BlockInstanceRunner.ts +10 -66
  37. package/src/utils/LogData.ts +1 -0
  38. 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
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
90
- let instance = this.getInstance(systemId, instanceId);
91
- //Get target address
92
- const address = await serviceManager_1.serviceManager.getProviderAddress(systemId, instanceId, info.portType ?? DEFAULT_HEALTH_PORT_TYPE);
93
- const healthUrl = this.getHealthUrl(info, address);
94
- if (instance) {
95
- if (instance.status === types_1.InstanceStatus.STOPPING && instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
96
- //If instance is stopping do not interfere
97
- return;
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
- instance.desiredStatus = info.desiredStatus;
108
- instance.owner = info.owner;
109
- instance.internal = undefined;
110
- instance.status = types_1.InstanceStatus.STARTING;
111
- instance.startedAt = Date.now();
112
- }
113
- instance.pid = info.pid;
114
- instance.address = address;
115
- if (info.type) {
116
- instance.type = info.type;
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
- if (healthUrl) {
119
- instance.health = healthUrl;
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.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
122
- }
123
- else {
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
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
155
- const instance = lodash_1.default.find(this._instances, { systemId, instanceId });
156
- if (instance && instance.owner === types_1.InstanceOwner.EXTERNAL && instance.status !== types_1.InstanceStatus.STOPPED) {
157
- instance.status = types_1.InstanceStatus.STOPPED;
158
- instance.pid = null;
159
- instance.health = null;
160
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
161
- this.save();
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
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
192
- const instance = this.getInstance(systemId, instanceId);
193
- if (!instance) {
194
- return;
195
- }
196
- if (instance.status === types_1.InstanceStatus.STOPPED) {
197
- return;
198
- }
199
- if (instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
200
- instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
201
- }
202
- instance.status = types_1.InstanceStatus.STOPPING;
203
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
204
- console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
205
- this.save();
206
- try {
207
- if (instance.type === 'docker') {
208
- const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.instanceId);
209
- const container = await containerManager_1.containerManager.getContainerByName(containerName);
210
- if (container) {
211
- try {
212
- await container.stop();
213
- instance.status = types_1.InstanceStatus.STOPPED;
214
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
215
- this.save();
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
- catch (e) {
218
- console.error('Failed to stop container', e);
261
+ else {
262
+ console.warn('Container not found', containerName);
219
263
  }
264
+ return;
220
265
  }
221
- else {
222
- console.warn('Container not found', containerName);
266
+ if (!instance.pid) {
267
+ instance.status = types_1.InstanceStatus.STOPPED;
268
+ this.save();
269
+ return;
223
270
  }
224
- return;
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
- process.kill(instance.pid, 'SIGTERM');
232
- instance.status = types_1.InstanceStatus.STOPPED;
233
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
234
- this.save();
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
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
247
- const plan = await assetManager_1.assetManager.getPlan(systemId, true);
248
- if (!plan) {
249
- throw new Error('Plan not found: ' + systemId);
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
- if (existingInstance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
267
- existingInstance.status === types_1.InstanceStatus.STARTING) {
268
- // Internal instance is already starting - don't start it again
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
- if (existingInstance.owner === types_1.InstanceOwner.EXTERNAL &&
272
- existingInstance.status === types_1.InstanceStatus.STARTING) {
273
- // External instance is already starting - don't start it again
274
- return existingInstance;
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
- let instance = {
278
- systemId,
279
- instanceId,
280
- ref: blockRef,
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 (exitCode === 143 || exitCode === 137) {
322
- //Process got SIGTERM (143) or SIGKILL (137)
323
- //TODO: Windows?
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 (exitCode !== 0 || timeRunning < MIN_TIME_RUNNING) {
327
- const instance = this.getInstance(systemId, instanceId);
328
- if (instance) {
329
- instance.status = types_1.InstanceStatus.FAILED;
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.status = types_1.InstanceStatus.READY;
340
- return this.saveInternalInstance({
341
- ...instance,
342
- type: processInfo.type,
343
- pid: processInfo.pid ?? -1,
344
- health: null,
345
- portType: processInfo.portType,
346
- status: types_1.InstanceStatus.READY,
347
- internal: {
348
- logs: processInfo.logs,
349
- output: processInfo.output,
350
- },
351
- });
352
- }
353
- catch (e) {
354
- console.warn('Failed to start instance', e);
355
- const logs = [
356
- {
357
- source: 'stdout',
358
- level: 'ERROR',
359
- message: e.message,
360
- time: Date.now(),
361
- },
362
- ];
363
- const out = await this.saveInternalInstance({
364
- ...instance,
365
- type: types_1.InstanceType.LOCAL,
366
- pid: null,
367
- health: null,
368
- portType: DEFAULT_HEALTH_PORT_TYPE,
369
- status: types_1.InstanceStatus.FAILED,
370
- });
371
- this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
372
- this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
373
- error: `Failed to start instance: ${e.message}`,
374
- status: EVENT_INSTANCE_EXITED,
375
- instanceId: blockInstance.id,
376
- });
377
- return out;
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.stop(systemId, instanceId);
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
- const copy = { ...instance };
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, 20);
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.stop(instance.systemId, instance.instanceId);
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
- res.status(202).send({
77
- logs: instanceInfo.internal?.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 containerName = containerBaseName + '-' + (0, md5_1.default)(nameParts.join('_'));
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 = {