@kapeta/local-cluster-service 0.11.1 → 0.12.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 (42) 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 +3 -2
  5. package/dist/cjs/src/containerManager.js +127 -34
  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 +8 -1
  9. package/dist/cjs/src/instanceManager.js +56 -21
  10. package/dist/cjs/src/instances/routes.js +2 -0
  11. package/dist/cjs/src/operatorManager.d.ts +2 -0
  12. package/dist/cjs/src/operatorManager.js +70 -67
  13. package/dist/cjs/src/socketManager.d.ts +1 -0
  14. package/dist/cjs/src/socketManager.js +3 -0
  15. package/dist/cjs/src/types.d.ts +1 -0
  16. package/dist/cjs/src/utils/BlockInstanceRunner.js +2 -3
  17. package/dist/esm/src/config/routes.js +1 -1
  18. package/dist/esm/src/containerManager.d.ts +3 -2
  19. package/dist/esm/src/containerManager.js +128 -35
  20. package/dist/esm/src/definitionsManager.d.ts +1 -0
  21. package/dist/esm/src/definitionsManager.js +8 -5
  22. package/dist/esm/src/instanceManager.d.ts +8 -1
  23. package/dist/esm/src/instanceManager.js +56 -21
  24. package/dist/esm/src/instances/routes.js +2 -0
  25. package/dist/esm/src/operatorManager.d.ts +2 -0
  26. package/dist/esm/src/operatorManager.js +68 -65
  27. package/dist/esm/src/socketManager.d.ts +1 -0
  28. package/dist/esm/src/socketManager.js +3 -0
  29. package/dist/esm/src/types.d.ts +1 -0
  30. package/dist/esm/src/utils/BlockInstanceRunner.js +2 -3
  31. package/dist/esm/src/utils/utils.js +1 -1
  32. package/package.json +1 -1
  33. package/src/config/routes.ts +1 -1
  34. package/src/containerManager.ts +181 -60
  35. package/src/definitionsManager.ts +9 -5
  36. package/src/instanceManager.ts +82 -42
  37. package/src/instances/routes.ts +3 -1
  38. package/src/operatorManager.ts +73 -69
  39. package/src/socketManager.ts +4 -0
  40. package/src/types.ts +1 -1
  41. package/src/utils/BlockInstanceRunner.ts +12 -24
  42. package/src/utils/utils.ts +2 -2
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.12.1](https://github.com/kapetacom/local-cluster-service/compare/v0.12.0...v0.12.1) (2023-08-02)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Adjustments to make starting plans locally smoother ([fc353ad](https://github.com/kapetacom/local-cluster-service/commit/fc353adde350b7e9d4c7eb9347c4cfa0c3a6aa58))
7
+
8
+ # [0.12.0](https://github.com/kapetacom/local-cluster-service/compare/v0.11.1...v0.12.0) (2023-07-31)
9
+
10
+
11
+ ### Features
12
+
13
+ * Send status events to client when pulling image ([#54](https://github.com/kapetacom/local-cluster-service/issues/54)) ([6c6f1b0](https://github.com/kapetacom/local-cluster-service/commit/6c6f1b0cf31d4bbd1fccf10f4a66a3ac97ff7171))
14
+
1
15
  ## [0.11.1](https://github.com/kapetacom/local-cluster-service/compare/v0.11.0...v0.11.1) (2023-07-31)
2
16
 
3
17
 
package/definitions.d.ts CHANGED
@@ -18,4 +18,11 @@ declare module '@kapeta/nodejs-registry-utils' {
18
18
 
19
19
  export const Config: any;
20
20
  export const Actions: any;
21
+
22
+ export const handlers: {
23
+ DockerHandler: ArtifactHandlerFactory;
24
+ NPMHandler: ArtifactHandlerFactory;
25
+ MavenHandler: ArtifactHandlerFactory;
26
+ YAMLHandler: ArtifactHandlerFactory;
27
+ };
21
28
  }
@@ -37,7 +37,7 @@ router.put('/instance', async (req, res) => {
37
37
  if (req.kapeta.instanceId) {
38
38
  configManager_1.configManager.setConfigForSection(req.kapeta.systemId, req.kapeta.instanceId, config);
39
39
  //Restart the instance if it is running after config change
40
- await instanceManager_1.instanceManager.restart(req.kapeta.systemId, req.kapeta.instanceId);
40
+ await instanceManager_1.instanceManager.prepareForRestart(req.kapeta.systemId, req.kapeta.instanceId);
41
41
  }
42
42
  else {
43
43
  configManager_1.configManager.setConfigForSystem(req.kapeta.systemId, config);
@@ -1,6 +1,6 @@
1
1
  import { Docker } from 'node-docker-api';
2
2
  import { Container } from 'node-docker-api/lib/container';
3
- import { InstanceInfo, LogEntry } from "./types";
3
+ import { InstanceInfo, LogEntry } from './types';
4
4
  type StringMap = {
5
5
  [key: string]: string;
6
6
  };
@@ -49,6 +49,7 @@ declare class ContainerManager {
49
49
  private _alive;
50
50
  private _mountDir;
51
51
  private _version;
52
+ private _lastDockerAccessCheck;
52
53
  constructor();
53
54
  initialize(): Promise<void>;
54
55
  checkAlive(): Promise<boolean>;
@@ -58,7 +59,7 @@ declare class ContainerManager {
58
59
  ping(): Promise<void>;
59
60
  docker(): Docker;
60
61
  getContainerByName(containerName: string): Promise<ContainerInfo | undefined>;
61
- pull(image: string, cacheForMS?: number): Promise<boolean>;
62
+ pull(image: string): Promise<boolean>;
62
63
  toDockerMounts(mounts: StringMap): DockerMounts[];
63
64
  toDockerHealth(health: Health): {
64
65
  Test: string[];
@@ -15,12 +15,13 @@ const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-co
15
15
  const node_uuid_1 = __importDefault(require("node-uuid"));
16
16
  const md5_1 = __importDefault(require("md5"));
17
17
  const utils_1 = require("./utils/utils");
18
+ const socketManager_1 = require("./socketManager");
19
+ const nodejs_api_client_1 = require("@kapeta/nodejs-api-client");
20
+ const EVENT_IMAGE_PULL = 'docker-image-pull';
18
21
  exports.CONTAINER_LABEL_PORT_PREFIX = 'kapeta_port-';
19
22
  const NANO_SECOND = 1000000;
20
23
  const HEALTH_CHECK_INTERVAL = 3000;
21
24
  const HEALTH_CHECK_MAX = 20;
22
- const IMAGE_PULL_CACHE_TTL = 30 * 60 * 1000;
23
- const IMAGE_PULL_CACHE = {};
24
25
  exports.HEALTH_CHECK_TIMEOUT = HEALTH_CHECK_INTERVAL * HEALTH_CHECK_MAX * 2;
25
26
  const promisifyStream = (stream, handler) => new Promise((resolve, reject) => {
26
27
  stream.on('data', handler);
@@ -32,6 +33,7 @@ class ContainerManager {
32
33
  _alive;
33
34
  _mountDir;
34
35
  _version;
36
+ _lastDockerAccessCheck = 0;
35
37
  constructor() {
36
38
  this._docker = null;
37
39
  this._alive = false;
@@ -147,17 +149,11 @@ class ContainerManager {
147
149
  }
148
150
  return undefined;
149
151
  }
150
- async pull(image, cacheForMS = IMAGE_PULL_CACHE_TTL) {
152
+ async pull(image) {
151
153
  let [imageName, tag] = image.split(/:/);
152
154
  if (!tag) {
153
155
  tag = 'latest';
154
156
  }
155
- if (IMAGE_PULL_CACHE[image]) {
156
- const timeSince = Date.now() - IMAGE_PULL_CACHE[image];
157
- if (timeSince < cacheForMS) {
158
- return false;
159
- }
160
- }
161
157
  const imageTagList = (await this.docker().image.list())
162
158
  .map((image) => image.data)
163
159
  .filter((imageData) => !!imageData.RepoTags)
@@ -166,17 +162,120 @@ class ContainerManager {
166
162
  console.log('Image found: %s', image);
167
163
  return false;
168
164
  }
169
- console.log('Pulling image: %s', image);
170
- const stream = await this.docker()
171
- .image.create({}, {
165
+ const timeStarted = Date.now();
166
+ socketManager_1.socketManager.emitGlobal(EVENT_IMAGE_PULL, { image, percent: -1 });
167
+ const api = new nodejs_api_client_1.KapetaAPI();
168
+ const accessToken = await api.getAccessToken();
169
+ const auth = image.startsWith('docker.kapeta.com/')
170
+ ? {
171
+ username: 'kapeta',
172
+ password: accessToken,
173
+ serveraddress: 'docker.kapeta.com',
174
+ }
175
+ : {};
176
+ const stream = (await this.docker().image.create(auth, {
172
177
  fromImage: imageName,
173
178
  tag: tag,
179
+ }));
180
+ const chunks = {};
181
+ let lastEmitted = Date.now();
182
+ await promisifyStream(stream, (rawData) => {
183
+ const lines = rawData.toString().trim().split('\n');
184
+ lines.forEach((line) => {
185
+ const data = JSON.parse(line);
186
+ if (![
187
+ 'Waiting',
188
+ 'Downloading',
189
+ 'Extracting',
190
+ 'Download complete',
191
+ 'Pull complete',
192
+ 'Already exists',
193
+ ].includes(data.status)) {
194
+ return;
195
+ }
196
+ if (!chunks[data.id]) {
197
+ chunks[data.id] = {
198
+ downloading: {
199
+ total: 0,
200
+ current: 0,
201
+ },
202
+ extracting: {
203
+ total: 0,
204
+ current: 0,
205
+ },
206
+ done: false,
207
+ };
208
+ }
209
+ const chunk = chunks[data.id];
210
+ switch (data.status) {
211
+ case 'Downloading':
212
+ chunk.downloading = data.progressDetail;
213
+ break;
214
+ case 'Extracting':
215
+ chunk.extracting = data.progressDetail;
216
+ break;
217
+ case 'Download complete':
218
+ chunk.downloading.current = chunks[data.id].downloading.total;
219
+ break;
220
+ case 'Pull complete':
221
+ chunk.extracting.current = chunks[data.id].extracting.total;
222
+ chunk.done = true;
223
+ break;
224
+ case 'Already exists':
225
+ // Force layer to be done
226
+ chunk.downloading.current = 1;
227
+ chunk.downloading.total = 1;
228
+ chunk.extracting.current = 1;
229
+ chunk.extracting.total = 1;
230
+ chunk.done = true;
231
+ break;
232
+ }
233
+ });
234
+ if (Date.now() - lastEmitted < 1000) {
235
+ return;
236
+ }
237
+ const chunkList = Object.values(chunks);
238
+ let totals = {
239
+ downloading: {
240
+ total: 0,
241
+ current: 0,
242
+ },
243
+ extracting: {
244
+ total: 0,
245
+ current: 0,
246
+ },
247
+ total: chunkList.length,
248
+ done: 0,
249
+ };
250
+ chunkList.forEach((chunk) => {
251
+ if (chunk.downloading.current > 0) {
252
+ totals.downloading.current += chunk.downloading.current;
253
+ }
254
+ if (chunk.downloading.total > 0) {
255
+ totals.downloading.total += chunk.downloading.total;
256
+ }
257
+ if (chunk.extracting.current > 0) {
258
+ totals.extracting.current += chunk.extracting.current;
259
+ }
260
+ if (chunk.extracting.total > 0) {
261
+ totals.extracting.total += chunk.extracting.total;
262
+ }
263
+ if (chunk.done) {
264
+ totals.done++;
265
+ }
266
+ });
267
+ const percent = totals.total > 0 ? (totals.done / totals.total) * 100 : 0;
268
+ //We emit at most every second to not spam the client
269
+ socketManager_1.socketManager.emitGlobal(EVENT_IMAGE_PULL, {
270
+ image,
271
+ percent,
272
+ status: totals,
273
+ timeTaken: Date.now() - timeStarted,
274
+ });
275
+ lastEmitted = Date.now();
276
+ //console.log('Pulling image %s: %s % [done: %s, total: %s]', image, Math.round(percent), totals.done, totals.total);
174
277
  });
175
- await promisifyStream(stream, (chunk) => {
176
- console.log('Data from docker: "%s"', chunk.toString());
177
- });
178
- IMAGE_PULL_CACHE[image] = Date.now();
179
- console.log('Image pulled: %s', image);
278
+ socketManager_1.socketManager.emitGlobal(EVENT_IMAGE_PULL, { image, percent: 100, timeTaken: Date.now() - timeStarted });
180
279
  return true;
181
280
  }
182
281
  toDockerMounts(mounts) {
@@ -211,18 +310,10 @@ class ContainerManager {
211
310
  dockerOpts.Labels.HASH = hash;
212
311
  }
213
312
  async ensureContainer(opts) {
214
- const container = await this.createOrUpdateContainer(opts);
215
- await this.waitForReady(container);
216
- return container;
313
+ return await this.createOrUpdateContainer(opts);
217
314
  }
218
315
  async createOrUpdateContainer(opts) {
219
- let imagePulled = false;
220
- try {
221
- imagePulled = await this.pull(opts.Image);
222
- }
223
- catch (e) {
224
- console.warn('Failed to pull image. Continuing...', e);
225
- }
316
+ let imagePulled = await this.pull(opts.Image);
226
317
  this.applyHash(opts);
227
318
  if (!opts.name) {
228
319
  console.log('Starting unnamed container: %s', opts.Image);
@@ -347,12 +438,14 @@ class ContainerManager {
347
438
  const containerName = (0, utils_1.getBlockInstanceContainerName)(instance.systemId, instance.instanceId);
348
439
  const containerInfo = await this.getContainerByName(containerName);
349
440
  if (!containerInfo) {
350
- return [{
351
- source: "stdout",
352
- level: "ERROR",
441
+ return [
442
+ {
443
+ source: 'stdout',
444
+ level: 'ERROR',
353
445
  time: Date.now(),
354
- message: "Container not found"
355
- }];
446
+ message: 'Container not found',
447
+ },
448
+ ];
356
449
  }
357
450
  return containerInfo.getLogs();
358
451
  }
@@ -440,13 +533,13 @@ class ContainerInfo {
440
533
  return ports;
441
534
  }
442
535
  async getLogs() {
443
- const logStream = await this.native.logs({
536
+ const logStream = (await this.native.logs({
444
537
  stdout: true,
445
538
  stderr: true,
446
539
  follow: false,
447
540
  tail: 100,
448
541
  timestamps: true,
449
- });
542
+ }));
450
543
  const out = [];
451
544
  await promisifyStream(logStream, (data) => {
452
545
  const buf = data;
@@ -7,6 +7,7 @@ declare class DefinitionsManager {
7
7
  getDefinitions(kindFilter?: string | string[]): DefinitionInfo[];
8
8
  exists(ref: string): boolean;
9
9
  getProviderDefinitions(): DefinitionInfo[];
10
+ getDefinition(ref: string): DefinitionInfo | undefined;
10
11
  }
11
12
  export declare const definitionsManager: DefinitionsManager;
12
13
  export {};
@@ -39,13 +39,16 @@ class DefinitionsManager {
39
39
  return this.doCached(key, () => local_cluster_config_1.default.getDefinitions(kindFilter));
40
40
  }
41
41
  exists(ref) {
42
- const uri = (0, nodejs_utils_1.parseKapetaUri)(ref);
43
- return !!this.getDefinitions().find((d) => {
44
- return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
45
- });
42
+ return !!this.getDefinition(ref);
46
43
  }
47
44
  getProviderDefinitions() {
48
45
  return this.doCached('providers', () => local_cluster_config_1.default.getProviderDefinitions());
49
46
  }
47
+ getDefinition(ref) {
48
+ const uri = (0, nodejs_utils_1.parseKapetaUri)(ref);
49
+ return this.getDefinitions().find((d) => {
50
+ return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).id === uri.id;
51
+ });
52
+ }
50
53
  }
51
54
  exports.definitionsManager = new DefinitionsManager();
@@ -23,7 +23,14 @@ export declare class InstanceManager {
23
23
  private stopInner;
24
24
  stopAllForPlan(systemId: string): Promise<void>;
25
25
  start(systemId: string, instanceId: string): Promise<InstanceInfo>;
26
- restart(systemId: string, instanceId: string): Promise<InstanceInfo>;
26
+ /**
27
+ * Stops an instance but does not remove it from the list of active instances
28
+ *
29
+ * It will be started again next time the system checks the status of the instance
30
+ *
31
+ * We do it this way to not cause the user to wait for the instance to start again
32
+ */
33
+ prepareForRestart(systemId: string, instanceId: string): Promise<void>;
27
34
  stopAll(): Promise<void>;
28
35
  private stopInstances;
29
36
  private save;
@@ -16,6 +16,9 @@ const containerManager_1 = require("./containerManager");
16
16
  const configManager_1 = require("./configManager");
17
17
  const types_1 = require("./types");
18
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");
19
22
  const CHECK_INTERVAL = 5000;
20
23
  const DEFAULT_HEALTH_PORT_TYPE = 'rest';
21
24
  const EVENT_STATUS_CHANGED = 'status-changed';
@@ -52,7 +55,13 @@ class InstanceManager {
52
55
  return [];
53
56
  }
54
57
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
55
- return this._instances.filter((instance) => instance.systemId === systemId);
58
+ const planInfo = definitionsManager_1.definitionsManager.getDefinition(systemId);
59
+ if (!planInfo) {
60
+ return [];
61
+ }
62
+ const plan = planInfo.definition;
63
+ const instanceIds = plan.spec.blocks.map((block) => block.id);
64
+ return this._instances.filter((instance) => instance.systemId === systemId && instanceIds.includes(instance.instanceId));
56
65
  }
57
66
  getInstance(systemId, instanceId) {
58
67
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
@@ -61,7 +70,10 @@ class InstanceManager {
61
70
  async exclusive(systemId, instanceId, fn) {
62
71
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
63
72
  const key = `${systemId}/${instanceId}`;
64
- return this.instanceLocks.acquire(key, fn);
73
+ //console.log(`Acquiring lock for ${key}`, this.instanceLocks.isBusy(key));
74
+ const result = await this.instanceLocks.acquire(key, fn);
75
+ //console.log(`Releasing lock for ${key}`, this.instanceLocks.isBusy(key));
76
+ return result;
65
77
  }
66
78
  async getLogs(systemId, instanceId) {
67
79
  const instance = this.getInstance(systemId, instanceId);
@@ -72,19 +84,23 @@ class InstanceManager {
72
84
  case types_1.InstanceType.DOCKER:
73
85
  return await containerManager_1.containerManager.getLogs(instance);
74
86
  case types_1.InstanceType.UNKNOWN:
75
- return [{
87
+ return [
88
+ {
76
89
  level: 'INFO',
77
90
  message: 'Instance is starting...',
78
91
  time: Date.now(),
79
92
  source: 'stdout',
80
- }];
93
+ },
94
+ ];
81
95
  case types_1.InstanceType.LOCAL:
82
- return [{
96
+ return [
97
+ {
83
98
  level: 'INFO',
84
99
  message: 'Instance started outside Kapeta - logs not available...',
85
100
  time: Date.now(),
86
101
  source: 'stdout',
87
- }];
102
+ },
103
+ ];
88
104
  }
89
105
  return [];
90
106
  }
@@ -125,7 +141,8 @@ class InstanceManager {
125
141
  const address = await serviceManager_1.serviceManager.getProviderAddress(systemId, instanceId, info.portType ?? DEFAULT_HEALTH_PORT_TYPE);
126
142
  const healthUrl = this.getHealthUrl(info, address);
127
143
  if (instance) {
128
- if (instance.status === types_1.InstanceStatus.STOPPING && instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
144
+ if (instance.status === types_1.InstanceStatus.STOPPING &&
145
+ instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) {
129
146
  //If instance is stopping do not interfere
130
147
  return;
131
148
  }
@@ -235,8 +252,7 @@ class InstanceManager {
235
252
  if (instance.status === types_1.InstanceStatus.STOPPED) {
236
253
  return;
237
254
  }
238
- if (changeDesired &&
239
- instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
255
+ if (changeDesired && instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
240
256
  instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
241
257
  }
242
258
  instance.status = types_1.InstanceStatus.STOPPING;
@@ -330,6 +346,24 @@ class InstanceManager {
330
346
  console.log('Starting instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
331
347
  // Save the instance before starting it, so that we can track the status
332
348
  await this.saveInternalInstance(instance);
349
+ const blockSpec = blockAsset.data.spec;
350
+ if (blockSpec.consumers) {
351
+ const promises = blockSpec.consumers.map((consumer) => {
352
+ const consumerUri = (0, nodejs_utils_1.parseKapetaUri)(consumer.kind);
353
+ const asset = definitionsManager_1.definitionsManager.getDefinition(consumer.kind);
354
+ if (!asset) {
355
+ // Definition not found
356
+ return Promise.resolve();
357
+ }
358
+ if (operatorManager_1.KIND_OPERATOR.toLowerCase() !== asset.definition.kind.toLowerCase()) {
359
+ // Not an operator
360
+ return Promise.resolve();
361
+ }
362
+ console.log('Ensuring resource: %s in %s', consumerUri.id, systemId);
363
+ return operatorManager_1.operatorManager.ensureResource(systemId, consumerUri.fullName, consumerUri.version);
364
+ });
365
+ await Promise.all(promises);
366
+ }
333
367
  if (existingInstance) {
334
368
  // Check if the instance is already running - but after we've commmuicated the desired status
335
369
  const currentStatus = await this.requestInstanceStatus(existingInstance);
@@ -354,7 +388,7 @@ class InstanceManager {
354
388
  });
355
389
  }
356
390
  catch (e) {
357
- console.warn('Failed to start instance', e);
391
+ console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e.message);
358
392
  const logs = [
359
393
  {
360
394
  source: 'stdout',
@@ -365,11 +399,12 @@ class InstanceManager {
365
399
  ];
366
400
  const out = await this.saveInternalInstance({
367
401
  ...instance,
368
- type: types_1.InstanceType.LOCAL,
402
+ type: types_1.InstanceType.UNKNOWN,
369
403
  pid: null,
370
404
  health: null,
371
405
  portType: DEFAULT_HEALTH_PORT_TYPE,
372
406
  status: types_1.InstanceStatus.FAILED,
407
+ errorMessage: e.message ?? 'Failed to start - Check logs for details.',
373
408
  });
374
409
  this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
375
410
  this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
@@ -381,15 +416,16 @@ class InstanceManager {
381
416
  }
382
417
  });
383
418
  }
384
- async restart(systemId, instanceId) {
419
+ /**
420
+ * Stops an instance but does not remove it from the list of active instances
421
+ *
422
+ * It will be started again next time the system checks the status of the instance
423
+ *
424
+ * We do it this way to not cause the user to wait for the instance to start again
425
+ */
426
+ async prepareForRestart(systemId, instanceId) {
385
427
  systemId = (0, utils_1.normalizeKapetaUri)(systemId);
386
428
  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
- }
392
- return this.start(systemId, instanceId);
393
429
  }
394
430
  async stopAll() {
395
431
  return this.stopInstances(this._instances);
@@ -447,8 +483,7 @@ class InstanceManager {
447
483
  const oldStatus = instance.status;
448
484
  const skipUpdate = (newStatus === types_1.InstanceStatus.STOPPED && instance.status === types_1.InstanceStatus.FAILED) ||
449
485
  ([types_1.InstanceStatus.READY, types_1.InstanceStatus.UNHEALTHY].includes(newStatus) &&
450
- instance.status === types_1.InstanceStatus.STOPPING &&
451
- instance.desiredStatus === types_1.DesiredInstanceStatus.STOP) ||
486
+ instance.status === types_1.InstanceStatus.STOPPING) ||
452
487
  (newStatus === types_1.InstanceStatus.STOPPED &&
453
488
  instance.status === types_1.InstanceStatus.STARTING &&
454
489
  instance.desiredStatus === types_1.DesiredInstanceStatus.RUN);
@@ -488,7 +523,7 @@ class InstanceManager {
488
523
  //If the instance is unhealthy, try to restart it
489
524
  console.log('Restarting unhealthy instance', instance);
490
525
  try {
491
- await this.restart(instance.systemId, instance.instanceId);
526
+ await this.prepareForRestart(instance.systemId, instance.instanceId);
492
527
  }
493
528
  catch (e) {
494
529
  console.warn('Failed to restart instance', instance.systemId, instance.instanceId, e);
@@ -122,8 +122,10 @@ router.put('/', async (req, res) => {
122
122
  const oldInstance = instanceManager_1.instanceManager.getInstance(req.kapeta.systemId, req.kapeta.instanceId);
123
123
  if (oldInstance) {
124
124
  instance.pid = oldInstance.pid;
125
+ instance.desiredStatus = oldInstance.desiredStatus;
125
126
  }
126
127
  instance.type = types_1.InstanceType.DOCKER;
128
+ instance.owner = types_1.InstanceOwner.INTERNAL;
127
129
  }
128
130
  else {
129
131
  // Coming from user starting the instance outside of kapeta
@@ -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
  /**