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