@kapeta/local-cluster-service 0.11.0 → 0.12.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/definitions.d.ts +7 -0
  3. package/dist/cjs/src/config/routes.js +1 -1
  4. package/dist/cjs/src/containerManager.d.ts +2 -1
  5. package/dist/cjs/src/containerManager.js +125 -21
  6. package/dist/cjs/src/definitionsManager.d.ts +1 -0
  7. package/dist/cjs/src/definitionsManager.js +7 -4
  8. package/dist/cjs/src/instanceManager.d.ts +12 -2
  9. package/dist/cjs/src/instanceManager.js +253 -200
  10. package/dist/cjs/src/operatorManager.d.ts +2 -0
  11. package/dist/cjs/src/operatorManager.js +69 -67
  12. package/dist/cjs/src/socketManager.d.ts +1 -0
  13. package/dist/cjs/src/socketManager.js +3 -0
  14. package/dist/cjs/src/types.d.ts +1 -0
  15. package/dist/cjs/src/utils/BlockInstanceRunner.js +2 -2
  16. package/dist/esm/src/config/routes.js +1 -1
  17. package/dist/esm/src/containerManager.d.ts +2 -1
  18. package/dist/esm/src/containerManager.js +126 -22
  19. package/dist/esm/src/definitionsManager.d.ts +1 -0
  20. package/dist/esm/src/definitionsManager.js +8 -5
  21. package/dist/esm/src/instanceManager.d.ts +12 -2
  22. package/dist/esm/src/instanceManager.js +253 -200
  23. package/dist/esm/src/operatorManager.d.ts +2 -0
  24. package/dist/esm/src/operatorManager.js +67 -65
  25. package/dist/esm/src/socketManager.d.ts +1 -0
  26. package/dist/esm/src/socketManager.js +3 -0
  27. package/dist/esm/src/types.d.ts +1 -0
  28. package/dist/esm/src/utils/BlockInstanceRunner.js +2 -2
  29. package/dist/esm/src/utils/utils.js +1 -1
  30. package/package.json +3 -1
  31. package/src/config/routes.ts +1 -1
  32. package/src/containerManager.ts +178 -43
  33. package/src/definitionsManager.ts +9 -5
  34. package/src/instanceManager.ts +288 -228
  35. package/src/instances/routes.ts +1 -1
  36. package/src/operatorManager.ts +72 -70
  37. package/src/socketManager.ts +4 -0
  38. package/src/types.ts +1 -1
  39. package/src/utils/BlockInstanceRunner.ts +12 -22
  40. package/src/utils/utils.ts +2 -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");
@@ -15,6 +16,9 @@ const containerManager_1 = require("./containerManager");
15
16
  const configManager_1 = require("./configManager");
16
17
  const types_1 = require("./types");
17
18
  const utils_1 = require("./utils/utils");
19
+ const operatorManager_1 = require("./operatorManager");
20
+ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
21
+ const definitionsManager_1 = require("./definitionsManager");
18
22
  const CHECK_INTERVAL = 5000;
19
23
  const DEFAULT_HEALTH_PORT_TYPE = 'rest';
20
24
  const EVENT_STATUS_CHANGED = 'status-changed';
@@ -25,6 +29,7 @@ const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs -
25
29
  class InstanceManager {
26
30
  _interval = undefined;
27
31
  _instances = [];
32
+ instanceLocks = new async_lock_1.default();
28
33
  constructor() {
29
34
  this._instances = storageService_1.storageService.section('instances', []);
30
35
  // We need to wait a bit before running the first check
@@ -56,6 +61,14 @@ class InstanceManager {
56
61
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
57
62
  return this._instances.find((i) => i.systemId === systemId && i.instanceId === instanceId);
58
63
  }
64
+ async exclusive(systemId, instanceId, fn) {
65
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
66
+ const key = `${systemId}/${instanceId}`;
67
+ //console.log(`Acquiring lock for ${key}`, this.instanceLocks.isBusy(key));
68
+ const result = await this.instanceLocks.acquire(key, fn);
69
+ //console.log(`Releasing lock for ${key}`, this.instanceLocks.isBusy(key));
70
+ return result;
71
+ }
59
72
  async getLogs(systemId, instanceId) {
60
73
  const instance = this.getInstance(systemId, instanceId);
61
74
  if (!instance) {
@@ -65,19 +78,23 @@ class InstanceManager {
65
78
  case types_1.InstanceType.DOCKER:
66
79
  return await containerManager_1.containerManager.getLogs(instance);
67
80
  case types_1.InstanceType.UNKNOWN:
68
- return [{
81
+ return [
82
+ {
69
83
  level: 'INFO',
70
84
  message: 'Instance is starting...',
71
85
  time: Date.now(),
72
86
  source: 'stdout',
73
- }];
87
+ },
88
+ ];
74
89
  case types_1.InstanceType.LOCAL:
75
- return [{
90
+ return [
91
+ {
76
92
  level: 'INFO',
77
93
  message: 'Instance started outside Kapeta - logs not available...',
78
94
  time: Date.now(),
79
95
  source: 'stdout',
80
- }];
96
+ },
97
+ ];
81
98
  }
82
99
  return [];
83
100
  }
@@ -111,57 +128,60 @@ class InstanceManager {
111
128
  * which self-registers with the cluster service locally on startup.
112
129
  */
113
130
  async registerInstanceFromSDK(systemId, instanceId, info) {
114
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
115
- let instance = this.getInstance(systemId, instanceId);
116
- //Get target address
117
- const address = await serviceManager_1.serviceManager.getProviderAddress(systemId, instanceId, info.portType ?? DEFAULT_HEALTH_PORT_TYPE);
118
- const healthUrl = this.getHealthUrl(info, address);
119
- if (instance) {
120
- if (instance.status === types_1.InstanceStatus.STOPPING && instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
121
- //If instance is stopping do not interfere
122
- return;
123
- }
124
- if (info.owner === types_1.InstanceOwner.EXTERNAL) {
125
- //If instance was started externally - then we want to replace the internal instance with that
126
- if (instance.owner === types_1.InstanceOwner.INTERNAL &&
127
- (instance.status === types_1.InstanceStatus.READY ||
128
- instance.status === types_1.InstanceStatus.STARTING ||
129
- instance.status === types_1.InstanceStatus.UNHEALTHY)) {
130
- throw new Error(`Instance ${instanceId} is already running`);
131
+ return this.exclusive(systemId, instanceId, async () => {
132
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
133
+ let instance = this.getInstance(systemId, instanceId);
134
+ //Get target address
135
+ const address = await serviceManager_1.serviceManager.getProviderAddress(systemId, instanceId, info.portType ?? DEFAULT_HEALTH_PORT_TYPE);
136
+ const healthUrl = this.getHealthUrl(info, address);
137
+ if (instance) {
138
+ if (instance.status === types_1.InstanceStatus.STOPPING &&
139
+ instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
140
+ //If instance is stopping do not interfere
141
+ return;
131
142
  }
132
- instance.desiredStatus = info.desiredStatus;
133
- instance.owner = info.owner;
134
- instance.status = types_1.InstanceStatus.STARTING;
135
- instance.startedAt = Date.now();
136
- }
137
- instance.pid = info.pid;
138
- instance.address = address;
139
- if (info.type) {
140
- instance.type = info.type;
143
+ if (info.owner === types_1.InstanceOwner.EXTERNAL) {
144
+ //If instance was started externally - then we want to replace the internal instance with that
145
+ if (instance.owner === types_1.InstanceOwner.INTERNAL &&
146
+ (instance.status === types_1.InstanceStatus.READY ||
147
+ instance.status === types_1.InstanceStatus.STARTING ||
148
+ instance.status === types_1.InstanceStatus.UNHEALTHY)) {
149
+ throw new Error(`Instance ${instanceId} is already running`);
150
+ }
151
+ instance.desiredStatus = info.desiredStatus;
152
+ instance.owner = info.owner;
153
+ instance.status = types_1.InstanceStatus.STARTING;
154
+ instance.startedAt = Date.now();
155
+ }
156
+ instance.pid = info.pid;
157
+ instance.address = address;
158
+ if (info.type) {
159
+ instance.type = info.type;
160
+ }
161
+ if (healthUrl) {
162
+ instance.health = healthUrl;
163
+ }
164
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
141
165
  }
142
- if (healthUrl) {
143
- instance.health = healthUrl;
166
+ else {
167
+ //If instance was not found - then we're receiving an externally started instance
168
+ instance = {
169
+ ...info,
170
+ systemId,
171
+ instanceId,
172
+ status: types_1.InstanceStatus.STARTING,
173
+ startedAt: Date.now(),
174
+ desiredStatus: types_1.DesiredInstanceStatus.EXTERNAL,
175
+ owner: types_1.InstanceOwner.EXTERNAL,
176
+ health: healthUrl,
177
+ address,
178
+ };
179
+ this._instances.push(instance);
180
+ this.emitSystemEvent(systemId, EVENT_INSTANCE_CREATED, instance);
144
181
  }
145
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
146
- }
147
- else {
148
- //If instance was not found - then we're receiving an externally started instance
149
- instance = {
150
- ...info,
151
- systemId,
152
- instanceId,
153
- status: types_1.InstanceStatus.STARTING,
154
- startedAt: Date.now(),
155
- desiredStatus: types_1.DesiredInstanceStatus.EXTERNAL,
156
- owner: types_1.InstanceOwner.EXTERNAL,
157
- health: healthUrl,
158
- address,
159
- };
160
- this._instances.push(instance);
161
- this.emitSystemEvent(systemId, EVENT_INSTANCE_CREATED, instance);
162
- }
163
- this.save();
164
- return instance;
182
+ this.save();
183
+ return instance;
184
+ });
165
185
  }
166
186
  getHealthUrl(info, address) {
167
187
  let healthUrl = null;
@@ -175,15 +195,17 @@ class InstanceManager {
175
195
  return healthUrl;
176
196
  }
177
197
  markAsStopped(systemId, instanceId) {
178
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
179
- const instance = lodash_1.default.find(this._instances, { systemId, instanceId });
180
- if (instance && instance.owner === types_1.InstanceOwner.EXTERNAL && instance.status !== types_1.InstanceStatus.STOPPED) {
181
- instance.status = types_1.InstanceStatus.STOPPED;
182
- instance.pid = null;
183
- instance.health = null;
184
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
185
- this.save();
186
- }
198
+ return this.exclusive(systemId, instanceId, async () => {
199
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
200
+ const instance = lodash_1.default.find(this._instances, { systemId, instanceId });
201
+ if (instance && instance.owner === types_1.InstanceOwner.EXTERNAL && instance.status !== types_1.InstanceStatus.STOPPED) {
202
+ instance.status = types_1.InstanceStatus.STOPPED;
203
+ instance.pid = null;
204
+ instance.health = null;
205
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
206
+ this.save();
207
+ }
208
+ });
187
209
  }
188
210
  async startAllForPlan(systemId) {
189
211
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
@@ -212,54 +234,59 @@ class InstanceManager {
212
234
  return settled.map((p) => (p.status === 'fulfilled' ? p.value : null)).filter((p) => !!p);
213
235
  }
214
236
  async stop(systemId, instanceId) {
215
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
216
- const instance = this.getInstance(systemId, instanceId);
217
- if (!instance) {
218
- return;
219
- }
220
- if (instance.status === types_1.InstanceStatus.STOPPED) {
221
- return;
222
- }
223
- if (instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
224
- instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
225
- }
226
- instance.status = types_1.InstanceStatus.STOPPING;
227
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
228
- console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
229
- this.save();
230
- try {
231
- if (instance.type === 'docker') {
232
- const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
233
- const container = await containerManager_1.containerManager.getContainerByName(containerName);
234
- if (container) {
235
- try {
236
- await container.stop();
237
- instance.status = types_1.InstanceStatus.STOPPED;
238
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
239
- this.save();
237
+ return this.stopInner(systemId, instanceId, true);
238
+ }
239
+ async stopInner(systemId, instanceId, changeDesired = false) {
240
+ return this.exclusive(systemId, instanceId, async () => {
241
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
242
+ const instance = this.getInstance(systemId, instanceId);
243
+ if (!instance) {
244
+ return;
245
+ }
246
+ if (instance.status === types_1.InstanceStatus.STOPPED) {
247
+ return;
248
+ }
249
+ if (changeDesired && instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
250
+ instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
251
+ }
252
+ instance.status = types_1.InstanceStatus.STOPPING;
253
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
254
+ console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
255
+ this.save();
256
+ try {
257
+ if (instance.type === 'docker') {
258
+ const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
259
+ const container = await containerManager_1.containerManager.getContainerByName(containerName);
260
+ if (container) {
261
+ try {
262
+ await container.stop();
263
+ instance.status = types_1.InstanceStatus.STOPPED;
264
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
265
+ this.save();
266
+ }
267
+ catch (e) {
268
+ console.error('Failed to stop container', e);
269
+ }
240
270
  }
241
- catch (e) {
242
- console.error('Failed to stop container', e);
271
+ else {
272
+ console.warn('Container not found', containerName);
243
273
  }
274
+ return;
244
275
  }
245
- else {
246
- console.warn('Container not found', containerName);
276
+ if (!instance.pid) {
277
+ instance.status = types_1.InstanceStatus.STOPPED;
278
+ this.save();
279
+ return;
247
280
  }
248
- return;
249
- }
250
- if (!instance.pid) {
281
+ process.kill(instance.pid, 'SIGTERM');
251
282
  instance.status = types_1.InstanceStatus.STOPPED;
283
+ this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
252
284
  this.save();
253
- return;
254
285
  }
255
- process.kill(instance.pid, 'SIGTERM');
256
- instance.status = types_1.InstanceStatus.STOPPED;
257
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
258
- this.save();
259
- }
260
- catch (e) {
261
- console.error('Failed to stop process', e);
262
- }
286
+ catch (e) {
287
+ console.error('Failed to stop process', e);
288
+ }
289
+ });
263
290
  }
264
291
  async stopAllForPlan(systemId) {
265
292
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
@@ -267,105 +294,132 @@ class InstanceManager {
267
294
  return this.stopInstances(instancesForPlan);
268
295
  }
269
296
  async start(systemId, instanceId) {
270
- systemId = (0, utils_1.normalizeKapetaUri)(systemId);
271
- const plan = await assetManager_1.assetManager.getPlan(systemId, true);
272
- if (!plan) {
273
- throw new Error('Plan not found: ' + systemId);
274
- }
275
- const blockInstance = plan.spec && plan.spec.blocks ? lodash_1.default.find(plan.spec.blocks, { id: instanceId }) : null;
276
- if (!blockInstance) {
277
- throw new Error('Block instance not found: ' + instanceId);
278
- }
279
- const blockRef = (0, utils_1.normalizeKapetaUri)(blockInstance.block.ref);
280
- const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
281
- if (!blockAsset) {
282
- throw new Error('Block not found: ' + blockRef);
283
- }
284
- const existingInstance = this.getInstance(systemId, instanceId);
285
- if (existingInstance) {
286
- if (existingInstance.status === types_1.InstanceStatus.READY) {
287
- // Instance is already running
288
- return existingInstance;
297
+ return this.exclusive(systemId, instanceId, async () => {
298
+ systemId = (0, utils_1.normalizeKapetaUri)(systemId);
299
+ const plan = await assetManager_1.assetManager.getPlan(systemId, true);
300
+ if (!plan) {
301
+ throw new Error('Plan not found: ' + systemId);
289
302
  }
290
- if (existingInstance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
291
- existingInstance.status === types_1.InstanceStatus.STARTING) {
292
- // Internal instance is already starting - don't start it again
293
- return existingInstance;
303
+ const blockInstance = plan.spec && plan.spec.blocks ? lodash_1.default.find(plan.spec.blocks, { id: instanceId }) : null;
304
+ if (!blockInstance) {
305
+ throw new Error('Block instance not found: ' + instanceId);
294
306
  }
295
- if (existingInstance.owner === types_1.InstanceOwner.EXTERNAL &&
296
- existingInstance.status === types_1.InstanceStatus.STARTING) {
297
- // External instance is already starting - don't start it again
298
- return existingInstance;
307
+ const blockRef = (0, utils_1.normalizeKapetaUri)(blockInstance.block.ref);
308
+ const blockAsset = await assetManager_1.assetManager.getAsset(blockRef, true);
309
+ if (!blockAsset) {
310
+ throw new Error('Block not found: ' + blockRef);
299
311
  }
300
- }
301
- let instance = {
302
- systemId,
303
- instanceId,
304
- ref: blockRef,
305
- name: blockAsset.data.metadata.name,
306
- desiredStatus: types_1.DesiredInstanceStatus.RUN,
307
- owner: types_1.InstanceOwner.INTERNAL,
308
- type: existingInstance?.type ?? types_1.InstanceType.UNKNOWN,
309
- status: types_1.InstanceStatus.STARTING,
310
- startedAt: Date.now(),
311
- };
312
- console.log('Starting instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
313
- // Save the instance before starting it, so that we can track the status
314
- await this.saveInternalInstance(instance);
315
- if (existingInstance) {
316
- // Check if the instance is already running - but after we've commmuicated the desired status
317
- const currentStatus = await this.requestInstanceStatus(existingInstance);
318
- if (currentStatus === types_1.InstanceStatus.READY) {
319
- // Instance is already running
320
- return existingInstance;
312
+ const existingInstance = this.getInstance(systemId, instanceId);
313
+ if (existingInstance) {
314
+ if (existingInstance.status === types_1.InstanceStatus.READY) {
315
+ // Instance is already running
316
+ return existingInstance;
317
+ }
318
+ if (existingInstance.desiredStatus === types_1.DesiredInstanceStatus.RUN &&
319
+ existingInstance.status === types_1.InstanceStatus.STARTING) {
320
+ // Internal instance is already starting - don't start it again
321
+ return existingInstance;
322
+ }
323
+ if (existingInstance.owner === types_1.InstanceOwner.EXTERNAL &&
324
+ existingInstance.status === types_1.InstanceStatus.STARTING) {
325
+ // External instance is already starting - don't start it again
326
+ return existingInstance;
327
+ }
321
328
  }
322
- }
323
- const instanceConfig = await configManager_1.configManager.getConfigForSection(systemId, instanceId);
324
- const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
325
- const startTime = Date.now();
326
- try {
327
- const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
328
- instance.status = types_1.InstanceStatus.READY;
329
- return this.saveInternalInstance({
330
- ...instance,
331
- type: processInfo.type,
332
- pid: processInfo.pid ?? -1,
333
- health: null,
334
- portType: processInfo.portType,
335
- status: types_1.InstanceStatus.READY,
336
- });
337
- }
338
- catch (e) {
339
- console.warn('Failed to start instance', e);
340
- const logs = [
341
- {
342
- source: 'stdout',
343
- level: 'ERROR',
344
- message: e.message,
345
- time: Date.now(),
346
- },
347
- ];
348
- const out = await this.saveInternalInstance({
349
- ...instance,
350
- type: types_1.InstanceType.LOCAL,
351
- pid: null,
352
- health: null,
353
- portType: DEFAULT_HEALTH_PORT_TYPE,
354
- status: types_1.InstanceStatus.FAILED,
355
- });
356
- this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
357
- this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
358
- error: `Failed to start instance: ${e.message}`,
359
- status: EVENT_INSTANCE_EXITED,
360
- instanceId: blockInstance.id,
361
- });
362
- return out;
363
- }
329
+ let instance = {
330
+ systemId,
331
+ instanceId,
332
+ ref: blockRef,
333
+ name: blockAsset.data.metadata.name,
334
+ desiredStatus: types_1.DesiredInstanceStatus.RUN,
335
+ owner: types_1.InstanceOwner.INTERNAL,
336
+ type: existingInstance?.type ?? types_1.InstanceType.UNKNOWN,
337
+ status: types_1.InstanceStatus.STARTING,
338
+ startedAt: Date.now(),
339
+ };
340
+ console.log('Starting instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
341
+ // Save the instance before starting it, so that we can track the status
342
+ await this.saveInternalInstance(instance);
343
+ const blockSpec = blockAsset.data.spec;
344
+ if (blockSpec.consumers) {
345
+ const promises = blockSpec.consumers.map((consumer) => {
346
+ const consumerUri = (0, nodejs_utils_1.parseKapetaUri)(consumer.kind);
347
+ const asset = definitionsManager_1.definitionsManager.getDefinition(consumer.kind);
348
+ if (!asset) {
349
+ // Definition not found
350
+ return Promise.resolve();
351
+ }
352
+ if (operatorManager_1.KIND_OPERATOR.toLowerCase() !== asset.definition.kind.toLowerCase()) {
353
+ // Not an operator
354
+ return Promise.resolve();
355
+ }
356
+ console.log('Ensuring resource: %s in %s', consumerUri.id, systemId);
357
+ return operatorManager_1.operatorManager.ensureResource(systemId, consumerUri.fullName, consumerUri.version);
358
+ });
359
+ await Promise.all(promises);
360
+ }
361
+ if (existingInstance) {
362
+ // Check if the instance is already running - but after we've commmuicated the desired status
363
+ const currentStatus = await this.requestInstanceStatus(existingInstance);
364
+ if (currentStatus === types_1.InstanceStatus.READY) {
365
+ // Instance is already running
366
+ return existingInstance;
367
+ }
368
+ }
369
+ const instanceConfig = await configManager_1.configManager.getConfigForSection(systemId, instanceId);
370
+ const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
371
+ const startTime = Date.now();
372
+ try {
373
+ const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
374
+ instance.status = types_1.InstanceStatus.READY;
375
+ return this.saveInternalInstance({
376
+ ...instance,
377
+ type: processInfo.type,
378
+ pid: processInfo.pid ?? -1,
379
+ health: null,
380
+ portType: processInfo.portType,
381
+ status: types_1.InstanceStatus.READY,
382
+ });
383
+ }
384
+ catch (e) {
385
+ console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e.message);
386
+ const logs = [
387
+ {
388
+ source: 'stdout',
389
+ level: 'ERROR',
390
+ message: e.message,
391
+ time: Date.now(),
392
+ },
393
+ ];
394
+ const out = await this.saveInternalInstance({
395
+ ...instance,
396
+ type: types_1.InstanceType.LOCAL,
397
+ pid: null,
398
+ health: null,
399
+ portType: DEFAULT_HEALTH_PORT_TYPE,
400
+ status: types_1.InstanceStatus.FAILED,
401
+ errorMessage: e.message ?? 'Failed to start - Check logs for details.',
402
+ });
403
+ this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
404
+ this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
405
+ error: `Failed to start instance: ${e.message}`,
406
+ status: EVENT_INSTANCE_EXITED,
407
+ instanceId: blockInstance.id,
408
+ });
409
+ return out;
410
+ }
411
+ });
364
412
  }
365
- async restart(systemId, instanceId) {
413
+ /**
414
+ * Stops an instance but does not remove it from the list of active instances
415
+ *
416
+ * It will be started again next time the system checks the status of the instance
417
+ *
418
+ * We do it this way to not cause the user to wait for the instance to start again
419
+ */
420
+ async prepareForRestart(systemId, instanceId) {
366
421
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
367
- await this.stop(systemId, instanceId);
368
- return this.start(systemId, instanceId);
422
+ await this.stopInner(systemId, instanceId);
369
423
  }
370
424
  async stopAll() {
371
425
  return this.stopInstances(this._instances);
@@ -391,7 +445,7 @@ class InstanceManager {
391
445
  const all = [...this._instances];
392
446
  while (all.length > 0) {
393
447
  // Check a few instances at a time - docker doesn't like too many concurrent requests
394
- const chunk = all.splice(0, 20);
448
+ const chunk = all.splice(0, 30);
395
449
  const promises = chunk.map(async (instance) => {
396
450
  if (!instance.systemId) {
397
451
  return;
@@ -423,8 +477,7 @@ class InstanceManager {
423
477
  const oldStatus = instance.status;
424
478
  const skipUpdate = (newStatus === types_1.InstanceStatus.STOPPED && instance.status === types_1.InstanceStatus.FAILED) ||
425
479
  ([types_1.InstanceStatus.READY, types_1.InstanceStatus.UNHEALTHY].includes(newStatus) &&
426
- instance.status === types_1.InstanceStatus.STOPPING &&
427
- instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) ||
480
+ instance.status === types_1.InstanceStatus.STOPPING) ||
428
481
  (newStatus === types_1.InstanceStatus.STOPPED &&
429
482
  instance.status === types_1.InstanceStatus.STARTING &&
430
483
  instance.desiredStatus === types_1.DesiredInstanceStatus.RUN);
@@ -451,7 +504,7 @@ class InstanceManager {
451
504
  [types_1.InstanceStatus.READY, types_1.InstanceStatus.STARTING, types_1.InstanceStatus.UNHEALTHY].includes(newStatus)) {
452
505
  //If the instance is running but we want it to stop, stop it
453
506
  try {
454
- await this.stop(instance.systemId, instance.instanceId);
507
+ await this.stopInner(instance.systemId, instance.instanceId);
455
508
  }
456
509
  catch (e) {
457
510
  console.warn('Failed to stop instance', instance.systemId, instance.instanceId, e);
@@ -464,7 +517,7 @@ class InstanceManager {
464
517
  //If the instance is unhealthy, try to restart it
465
518
  console.log('Restarting unhealthy instance', instance);
466
519
  try {
467
- await this.restart(instance.systemId, instance.instanceId);
520
+ await this.prepareForRestart(instance.systemId, instance.instanceId);
468
521
  }
469
522
  catch (e) {
470
523
  console.warn('Failed to restart instance', instance.systemId, instance.instanceId, e);
@@ -1,5 +1,6 @@
1
1
  import { ContainerInfo } from './containerManager';
2
2
  import { EnvironmentType, OperatorInfo } from './types';
3
+ export declare const KIND_OPERATOR = "core/resource-type-operator";
3
4
  declare class Operator {
4
5
  private _data;
5
6
  constructor(data: any);
@@ -8,6 +9,7 @@ declare class Operator {
8
9
  }
9
10
  declare class OperatorManager {
10
11
  private _mountDir;
12
+ private operatorLock;
11
13
  constructor();
12
14
  _getMountPoint(operatorType: string, mountName: string): string;
13
15
  /**