@kapeta/local-cluster-service 0.19.5 → 0.19.7

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.
@@ -6,7 +6,12 @@ import { storageService } from './storageService';
6
6
  import { EVENT_INSTANCE_CREATED, EVENT_INSTANCE_EXITED, EVENT_STATUS_CHANGED, socketManager } from './socketManager';
7
7
  import { serviceManager } from './serviceManager';
8
8
  import { assetManager } from './assetManager';
9
- import { containerManager, HEALTH_CHECK_TIMEOUT } from './containerManager';
9
+ import {
10
+ containerManager,
11
+ DockerContainerHealth,
12
+ DockerContainerStatus,
13
+ HEALTH_CHECK_TIMEOUT,
14
+ } from './containerManager';
10
15
  import { configManager } from './configManager';
11
16
  import { DesiredInstanceStatus, InstanceInfo, InstanceOwner, InstanceStatus, InstanceType, LogEntry } from './types';
12
17
  import { BlockDefinitionSpec, BlockInstance, Plan } from '@kapeta/schemas';
@@ -336,6 +341,10 @@ export class InstanceManager {
336
341
  return;
337
342
  }
338
343
 
344
+ if (instance.status === InstanceStatus.STOPPING) {
345
+ return;
346
+ }
347
+
339
348
  if (changeDesired && instance.desiredStatus !== DesiredInstanceStatus.EXTERNAL) {
340
349
  instance.desiredStatus = DesiredInstanceStatus.STOP;
341
350
  }
@@ -496,7 +505,7 @@ export class InstanceManager {
496
505
  try {
497
506
  const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
498
507
 
499
- instance.status = InstanceStatus.READY;
508
+ instance.status = InstanceStatus.STARTING;
500
509
 
501
510
  return this.saveInternalInstance({
502
511
  ...instance,
@@ -504,10 +513,10 @@ export class InstanceManager {
504
513
  pid: processInfo.pid ?? -1,
505
514
  health: null,
506
515
  portType: processInfo.portType,
507
- status: InstanceStatus.READY,
516
+ status: InstanceStatus.STARTING,
508
517
  });
509
518
  } catch (e: any) {
510
- console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e.message);
519
+ console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e);
511
520
  const logs: LogEntry[] = [
512
521
  {
513
522
  source: 'stdout',
@@ -686,7 +695,12 @@ export class InstanceManager {
686
695
  try {
687
696
  await this.start(instance.systemId, instance.instanceId);
688
697
  } catch (e: any) {
689
- console.warn('Failed to start instance', instance.systemId, instance.instanceId, e);
698
+ console.warn(
699
+ 'Failed to start previously stopped instance',
700
+ instance.systemId,
701
+ instance.instanceId,
702
+ e
703
+ );
690
704
  }
691
705
  return;
692
706
  }
@@ -738,37 +752,47 @@ export class InstanceManager {
738
752
  return InstanceStatus.STOPPED;
739
753
  }
740
754
  const state = await container.status();
755
+ if (!state) {
756
+ return InstanceStatus.STOPPED;
757
+ }
741
758
 
742
- if (state.Status === 'running') {
743
- if (state.Health?.Status === 'healthy') {
744
- return InstanceStatus.READY;
745
- }
746
- if (state.Health?.Status === 'starting') {
747
- return InstanceStatus.STARTING;
748
- }
749
- if (state.Health?.Status === 'unhealthy') {
750
- return InstanceStatus.UNHEALTHY;
751
- }
759
+ const statusType = state.Status as DockerContainerStatus;
752
760
 
761
+ if (statusType === 'running') {
762
+ if (state.Health?.Status) {
763
+ const healthStatusType = state.Health.Status as DockerContainerHealth;
764
+ if (healthStatusType === 'healthy' || healthStatusType === 'none') {
765
+ return InstanceStatus.READY;
766
+ }
767
+
768
+ if (healthStatusType === 'starting') {
769
+ return InstanceStatus.STARTING;
770
+ }
771
+
772
+ if (healthStatusType === 'unhealthy') {
773
+ return InstanceStatus.UNHEALTHY;
774
+ }
775
+ }
753
776
  return InstanceStatus.READY;
754
777
  }
755
- if (state.Status === 'created') {
778
+
779
+ if (statusType === 'created') {
756
780
  return InstanceStatus.STARTING;
757
781
  }
758
782
 
759
- if (state.Status === 'exited' || state.Status === 'dead') {
783
+ if (statusType === 'exited' || statusType === 'dead') {
760
784
  return InstanceStatus.STOPPED;
761
785
  }
762
786
 
763
- if (state.Status === 'removing') {
787
+ if (statusType === 'removing') {
764
788
  return InstanceStatus.BUSY;
765
789
  }
766
790
 
767
- if (state.Status === 'restarting') {
791
+ if (statusType === 'restarting') {
768
792
  return InstanceStatus.BUSY;
769
793
  }
770
794
 
771
- if (state.Status === 'paused') {
795
+ if (statusType === 'paused') {
772
796
  return InstanceStatus.BUSY;
773
797
  }
774
798
 
@@ -4,7 +4,13 @@ import md5 from 'md5';
4
4
  import { parseKapetaUri } from '@kapeta/nodejs-utils';
5
5
  import { serviceManager } from './serviceManager';
6
6
  import { storageService } from './storageService';
7
- import { CONTAINER_LABEL_PORT_PREFIX, ContainerInfo, containerManager } from './containerManager';
7
+ import {
8
+ COMPOSE_LABEL_PROJECT,
9
+ COMPOSE_LABEL_SERVICE,
10
+ CONTAINER_LABEL_PORT_PREFIX,
11
+ ContainerInfo,
12
+ containerManager,
13
+ } from './containerManager';
8
14
  import FSExtra from 'fs-extra';
9
15
  import { AnyMap, EnvironmentType, OperatorInfo, StringMap } from './types';
10
16
  import { BlockInstance, Resource } from '@kapeta/schemas';
@@ -199,8 +205,12 @@ class OperatorManager {
199
205
  const PortBindings: { [key: string]: any } = {};
200
206
  const Env: string[] = [];
201
207
 
208
+ const systemUri = parseKapetaUri(systemId);
209
+
202
210
  const Labels: StringMap = {
203
211
  kapeta: 'true',
212
+ [COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
213
+ [COMPOSE_LABEL_SERVICE]: [resourceType, version].join('_').replace(/[^a-z0-9]/gi, '_'),
204
214
  };
205
215
 
206
216
  const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
@@ -139,7 +139,9 @@ class TaskManager {
139
139
 
140
140
  socketManager.emitGlobal(EVENT_TASK_ADDED, task.toData());
141
141
 
142
- this.invokeTask(task).catch(() => {});
142
+ this.invokeTask(task).catch((err) => {
143
+ console.warn(`Task ${task.id} failed`, err);
144
+ });
143
145
 
144
146
  return task;
145
147
  }
@@ -205,6 +207,7 @@ class TaskManager {
205
207
  task.future.resolve(result);
206
208
  task.emitUpdate();
207
209
  } catch (e: any) {
210
+ console.warn(`Task ${task.id} failed while waiting for it to resolve`, e);
208
211
  task.errorMessage = e.message;
209
212
  task.status = TaskStatus.FAILED;
210
213
  task.future.reject(e);
@@ -3,12 +3,18 @@ import ClusterConfig, { DefinitionInfo } from '@kapeta/local-cluster-config';
3
3
  import { getBindHost, getBlockInstanceContainerName, normalizeKapetaUri, readYML } from './utils';
4
4
  import { KapetaURI, parseKapetaUri } from '@kapeta/nodejs-utils';
5
5
  import { serviceManager } from '../serviceManager';
6
- import { containerManager, DockerMounts, toLocalBindVolume } from '../containerManager';
6
+ import {
7
+ COMPOSE_LABEL_PROJECT,
8
+ COMPOSE_LABEL_SERVICE,
9
+ containerManager,
10
+ DockerMounts,
11
+ toLocalBindVolume,
12
+ } from '../containerManager';
7
13
  import { LogData } from './LogData';
8
14
  import { clusterService } from '../clusterService';
9
15
  import { AnyMap, BlockProcessParams, InstanceType, ProcessInfo, StringMap } from '../types';
10
- import { Container } from 'node-docker-api/lib/container';
11
16
  import { definitionsManager } from '../definitionsManager';
17
+ import Docker from 'dockerode';
12
18
 
13
19
  const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
14
20
  const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
@@ -171,6 +177,13 @@ export class BlockInstanceRunner {
171
177
  const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
172
178
  const workingDir = localContainer.workingDir ? localContainer.workingDir : '/workspace';
173
179
 
180
+ const customHostConfigs = localContainer.HostConfig ?? {};
181
+ const customLabels = localContainer.Labels ?? {};
182
+ const customEnvs = localContainer.Env ?? [];
183
+ delete localContainer.HostConfig;
184
+ delete localContainer.Labels;
185
+ delete localContainer.Env;
186
+
174
187
  const { PortBindings, ExposedPorts, addonEnv } = await this.getDockerPortBindings(blockInstance, assetVersion);
175
188
 
176
189
  let HealthCheck = undefined;
@@ -178,17 +191,24 @@ export class BlockInstanceRunner {
178
191
  HealthCheck = containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
179
192
  }
180
193
 
194
+ const systemUri = parseKapetaUri(this._systemId);
195
+
181
196
  return this.ensureContainer({
197
+ ...dockerOpts,
182
198
  Image: dockerImage,
183
199
  name: containerName,
184
200
  WorkingDir: workingDir,
185
201
  Labels: {
202
+ ...customLabels,
186
203
  instance: blockInstance.id,
204
+ [COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
205
+ [COMPOSE_LABEL_SERVICE]: blockInfo.id.replace(/[^a-z0-9]/gi, '_'),
187
206
  },
188
207
  HealthCheck,
189
208
  ExposedPorts,
190
209
  Cmd: startCmd ? startCmd.split(/\s+/g) : [],
191
210
  Env: [
211
+ ...customEnvs,
192
212
  ...DOCKER_ENV_VARS,
193
213
  `KAPETA_LOCAL_CLUSTER_PORT=${clusterService.getClusterServicePort()}`,
194
214
  ...Object.entries({
@@ -197,13 +217,13 @@ export class BlockInstanceRunner {
197
217
  }).map(([key, value]) => `${key}=${value}`),
198
218
  ],
199
219
  HostConfig: {
220
+ ...customHostConfigs,
200
221
  Binds: [
201
222
  `${toLocalBindVolume(ClusterConfig.getKapetaBasedir())}:${homeDir}/.kapeta`,
202
223
  `${toLocalBindVolume(baseDir)}:${workingDir}`,
203
224
  ],
204
225
  PortBindings,
205
226
  },
206
- ...dockerOpts,
207
227
  });
208
228
  }
209
229
 
@@ -240,6 +260,7 @@ export class BlockInstanceRunner {
240
260
 
241
261
  // For windows we need to default to root
242
262
  const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
263
+ const systemUri = parseKapetaUri(this._systemId);
243
264
 
244
265
  return this.ensureContainer({
245
266
  Image: dockerImage,
@@ -247,6 +268,8 @@ export class BlockInstanceRunner {
247
268
  ExposedPorts,
248
269
  Labels: {
249
270
  instance: blockInstance.id,
271
+ [COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
272
+ [COMPOSE_LABEL_SERVICE]: blockInfo.id.replace(/[^a-z0-9]/gi, '_'),
250
273
  },
251
274
  Env: [
252
275
  ...DOCKER_ENV_VARS,
@@ -344,6 +367,7 @@ export class BlockInstanceRunner {
344
367
  // For windows we need to default to root
345
368
  const innerHome = process.platform === 'win32' ? '/root/.kapeta' : ClusterConfig.getKapetaBasedir();
346
369
 
370
+ const systemUri = parseKapetaUri(this._systemId);
347
371
  logs.addLog(`Creating new container for block: ${containerName}`);
348
372
  const out = await this.ensureContainer({
349
373
  Image: dockerImage,
@@ -360,6 +384,8 @@ export class BlockInstanceRunner {
360
384
  },
361
385
  Labels: {
362
386
  instance: blockInstance.id,
387
+ [COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
388
+ [COMPOSE_LABEL_SERVICE]: blockUri.id.replace(/[^a-z0-9]/gi, '_'),
363
389
  },
364
390
  Env: [
365
391
  `KAPETA_INSTANCE_NAME=${blockInstance.ref}`,
@@ -414,7 +440,7 @@ export class BlockInstanceRunner {
414
440
  return this._handleContainer(container);
415
441
  }
416
442
 
417
- private async _handleContainer(container: Container): Promise<ProcessInfo> {
443
+ private async _handleContainer(container: Docker.Container): Promise<ProcessInfo> {
418
444
  return {
419
445
  type: InstanceType.DOCKER,
420
446
  pid: container.id,