@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
@@ -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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
if (instance
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
143
|
-
instance
|
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.
|
146
|
-
|
147
|
-
|
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
|
-
|
179
|
-
|
180
|
-
|
181
|
-
instance.status
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
instance.
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
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
|
-
|
242
|
-
console.
|
271
|
+
else {
|
272
|
+
console.warn('Container not found', containerName);
|
243
273
|
}
|
274
|
+
return;
|
244
275
|
}
|
245
|
-
|
246
|
-
|
276
|
+
if (!instance.pid) {
|
277
|
+
instance.status = types_1.InstanceStatus.STOPPED;
|
278
|
+
this.save();
|
279
|
+
return;
|
247
280
|
}
|
248
|
-
|
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
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
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
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
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
|
-
|
291
|
-
|
292
|
-
|
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
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
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
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
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
|
-
|
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
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
}
|
362
|
-
|
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
|
-
|
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.
|
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,
|
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.
|
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.
|
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
|
/**
|