@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.
@@ -262,6 +262,9 @@ class InstanceManager {
262
262
  if (instance.status === types_1.InstanceStatus.STOPPED) {
263
263
  return;
264
264
  }
265
+ if (instance.status === types_1.InstanceStatus.STOPPING) {
266
+ return;
267
+ }
265
268
  if (changeDesired && instance.desiredStatus !== types_1.DesiredInstanceStatus.EXTERNAL) {
266
269
  instance.desiredStatus = types_1.DesiredInstanceStatus.STOP;
267
270
  }
@@ -392,18 +395,18 @@ class InstanceManager {
392
395
  const startTime = Date.now();
393
396
  try {
394
397
  const processInfo = await runner.start(blockRef, instanceId, instanceConfig);
395
- instance.status = types_1.InstanceStatus.READY;
398
+ instance.status = types_1.InstanceStatus.STARTING;
396
399
  return this.saveInternalInstance({
397
400
  ...instance,
398
401
  type: processInfo.type,
399
402
  pid: processInfo.pid ?? -1,
400
403
  health: null,
401
404
  portType: processInfo.portType,
402
- status: types_1.InstanceStatus.READY,
405
+ status: types_1.InstanceStatus.STARTING,
403
406
  });
404
407
  }
405
408
  catch (e) {
406
- console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e.message);
409
+ console.warn('Failed to start instance: ', systemId, instanceId, blockRef, e);
407
410
  const logs = [
408
411
  {
409
412
  source: 'stdout',
@@ -550,7 +553,7 @@ class InstanceManager {
550
553
  await this.start(instance.systemId, instance.instanceId);
551
554
  }
552
555
  catch (e) {
553
- console.warn('Failed to start instance', instance.systemId, instance.instanceId, e);
556
+ console.warn('Failed to start previously stopped instance', instance.systemId, instance.instanceId, e);
554
557
  }
555
558
  return;
556
559
  }
@@ -594,31 +597,38 @@ class InstanceManager {
594
597
  return types_1.InstanceStatus.STOPPED;
595
598
  }
596
599
  const state = await container.status();
597
- if (state.Status === 'running') {
598
- if (state.Health?.Status === 'healthy') {
599
- return types_1.InstanceStatus.READY;
600
- }
601
- if (state.Health?.Status === 'starting') {
602
- return types_1.InstanceStatus.STARTING;
603
- }
604
- if (state.Health?.Status === 'unhealthy') {
605
- return types_1.InstanceStatus.UNHEALTHY;
600
+ if (!state) {
601
+ return types_1.InstanceStatus.STOPPED;
602
+ }
603
+ const statusType = state.Status;
604
+ if (statusType === 'running') {
605
+ if (state.Health?.Status) {
606
+ const healthStatusType = state.Health.Status;
607
+ if (healthStatusType === 'healthy' || healthStatusType === 'none') {
608
+ return types_1.InstanceStatus.READY;
609
+ }
610
+ if (healthStatusType === 'starting') {
611
+ return types_1.InstanceStatus.STARTING;
612
+ }
613
+ if (healthStatusType === 'unhealthy') {
614
+ return types_1.InstanceStatus.UNHEALTHY;
615
+ }
606
616
  }
607
617
  return types_1.InstanceStatus.READY;
608
618
  }
609
- if (state.Status === 'created') {
619
+ if (statusType === 'created') {
610
620
  return types_1.InstanceStatus.STARTING;
611
621
  }
612
- if (state.Status === 'exited' || state.Status === 'dead') {
622
+ if (statusType === 'exited' || statusType === 'dead') {
613
623
  return types_1.InstanceStatus.STOPPED;
614
624
  }
615
- if (state.Status === 'removing') {
625
+ if (statusType === 'removing') {
616
626
  return types_1.InstanceStatus.BUSY;
617
627
  }
618
- if (state.Status === 'restarting') {
628
+ if (statusType === 'restarting') {
619
629
  return types_1.InstanceStatus.BUSY;
620
630
  }
621
- if (state.Status === 'paused') {
631
+ if (statusType === 'paused') {
622
632
  return types_1.InstanceStatus.BUSY;
623
633
  }
624
634
  return types_1.InstanceStatus.STOPPED;
@@ -146,8 +146,11 @@ class OperatorManager {
146
146
  const containerName = `kapeta-resource-${(0, md5_1.default)(nameParts.join('_'))}`;
147
147
  const PortBindings = {};
148
148
  const Env = [];
149
+ const systemUri = (0, nodejs_utils_1.parseKapetaUri)(systemId);
149
150
  const Labels = {
150
151
  kapeta: 'true',
152
+ [containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
153
+ [containerManager_1.COMPOSE_LABEL_SERVICE]: [resourceType, version].join('_').replace(/[^a-z0-9]/gi, '_'),
151
154
  };
152
155
  const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
153
156
  const bindHost = (0, utils_1.getBindHost)();
@@ -90,7 +90,9 @@ class TaskManager {
90
90
  });
91
91
  this._tasks.push(task);
92
92
  socketManager_1.socketManager.emitGlobal(EVENT_TASK_ADDED, task.toData());
93
- this.invokeTask(task).catch(() => { });
93
+ this.invokeTask(task).catch((err) => {
94
+ console.warn(`Task ${task.id} failed`, err);
95
+ });
94
96
  return task;
95
97
  }
96
98
  async waitFor(filter) {
@@ -145,6 +147,7 @@ class TaskManager {
145
147
  task.emitUpdate();
146
148
  }
147
149
  catch (e) {
150
+ console.warn(`Task ${task.id} failed while waiting for it to resolve`, e);
148
151
  task.errorMessage = e.message;
149
152
  task.status = TaskStatus.FAILED;
150
153
  task.future.reject(e);
@@ -135,22 +135,34 @@ class BlockInstanceRunner {
135
135
  const dockerOpts = localContainer.options ?? {};
136
136
  const homeDir = localContainer.userHome ? localContainer.userHome : '/root';
137
137
  const workingDir = localContainer.workingDir ? localContainer.workingDir : '/workspace';
138
+ const customHostConfigs = localContainer.HostConfig ?? {};
139
+ const customLabels = localContainer.Labels ?? {};
140
+ const customEnvs = localContainer.Env ?? [];
141
+ delete localContainer.HostConfig;
142
+ delete localContainer.Labels;
143
+ delete localContainer.Env;
138
144
  const { PortBindings, ExposedPorts, addonEnv } = await this.getDockerPortBindings(blockInstance, assetVersion);
139
145
  let HealthCheck = undefined;
140
146
  if (localContainer.healthcheck) {
141
147
  HealthCheck = containerManager_1.containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
142
148
  }
149
+ const systemUri = (0, nodejs_utils_1.parseKapetaUri)(this._systemId);
143
150
  return this.ensureContainer({
151
+ ...dockerOpts,
144
152
  Image: dockerImage,
145
153
  name: containerName,
146
154
  WorkingDir: workingDir,
147
155
  Labels: {
156
+ ...customLabels,
148
157
  instance: blockInstance.id,
158
+ [containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
159
+ [containerManager_1.COMPOSE_LABEL_SERVICE]: blockInfo.id.replace(/[^a-z0-9]/gi, '_'),
149
160
  },
150
161
  HealthCheck,
151
162
  ExposedPorts,
152
163
  Cmd: startCmd ? startCmd.split(/\s+/g) : [],
153
164
  Env: [
165
+ ...customEnvs,
154
166
  ...DOCKER_ENV_VARS,
155
167
  `KAPETA_LOCAL_CLUSTER_PORT=${clusterService_1.clusterService.getClusterServicePort()}`,
156
168
  ...Object.entries({
@@ -159,13 +171,13 @@ class BlockInstanceRunner {
159
171
  }).map(([key, value]) => `${key}=${value}`),
160
172
  ],
161
173
  HostConfig: {
174
+ ...customHostConfigs,
162
175
  Binds: [
163
176
  `${(0, containerManager_1.toLocalBindVolume)(local_cluster_config_1.default.getKapetaBasedir())}:${homeDir}/.kapeta`,
164
177
  `${(0, containerManager_1.toLocalBindVolume)(baseDir)}:${workingDir}`,
165
178
  ],
166
179
  PortBindings,
167
180
  },
168
- ...dockerOpts,
169
181
  });
170
182
  }
171
183
  async _startDockerProcess(blockInstance, blockInfo, env, assetVersion) {
@@ -186,12 +198,15 @@ class BlockInstanceRunner {
186
198
  const containerName = (0, utils_1.getBlockInstanceContainerName)(this._systemId, blockInstance.id);
187
199
  // For windows we need to default to root
188
200
  const innerHome = process.platform === 'win32' ? '/root/.kapeta' : local_cluster_config_1.default.getKapetaBasedir();
201
+ const systemUri = (0, nodejs_utils_1.parseKapetaUri)(this._systemId);
189
202
  return this.ensureContainer({
190
203
  Image: dockerImage,
191
204
  name: containerName,
192
205
  ExposedPorts,
193
206
  Labels: {
194
207
  instance: blockInstance.id,
208
+ [containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
209
+ [containerManager_1.COMPOSE_LABEL_SERVICE]: blockInfo.id.replace(/[^a-z0-9]/gi, '_'),
195
210
  },
196
211
  Env: [
197
212
  ...DOCKER_ENV_VARS,
@@ -264,6 +279,7 @@ class BlockInstanceRunner {
264
279
  }
265
280
  // For windows we need to default to root
266
281
  const innerHome = process.platform === 'win32' ? '/root/.kapeta' : local_cluster_config_1.default.getKapetaBasedir();
282
+ const systemUri = (0, nodejs_utils_1.parseKapetaUri)(this._systemId);
267
283
  logs.addLog(`Creating new container for block: ${containerName}`);
268
284
  const out = await this.ensureContainer({
269
285
  Image: dockerImage,
@@ -280,6 +296,8 @@ class BlockInstanceRunner {
280
296
  },
281
297
  Labels: {
282
298
  instance: blockInstance.id,
299
+ [containerManager_1.COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
300
+ [containerManager_1.COMPOSE_LABEL_SERVICE]: blockUri.id.replace(/[^a-z0-9]/gi, '_'),
283
301
  },
284
302
  Env: [
285
303
  `KAPETA_INSTANCE_NAME=${blockInstance.ref}`,
@@ -1,7 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import FSExtra from 'fs-extra';
3
- import { Docker } from 'node-docker-api';
4
- import { Container } from 'node-docker-api/lib/container';
3
+ import Docker from 'dockerode';
5
4
  import { InstanceInfo, LogEntry } from './types';
6
5
  type StringMap = {
7
6
  [key: string]: string;
@@ -20,24 +19,8 @@ export interface DockerMounts {
20
19
  ReadOnly: boolean;
21
20
  Consistency: string;
22
21
  }
23
- interface DockerState {
24
- Status: 'created' | 'running' | 'paused' | 'restarting' | 'removing' | 'exited' | 'dead';
25
- Running: boolean;
26
- Paused: boolean;
27
- Restarting: boolean;
28
- OOMKilled: boolean;
29
- Dead: boolean;
30
- Pid: number;
31
- ExitCode: number;
32
- Error: string;
33
- StartedAt: string;
34
- FinishedAt: string;
35
- Health?: {
36
- Status: 'starting' | 'healthy' | 'unhealthy' | 'none';
37
- FailingStreak: number;
38
- Log: any[] | null;
39
- };
40
- }
22
+ export type DockerContainerStatus = 'created' | 'running' | 'paused' | 'restarting' | 'removing' | 'exited' | 'dead';
23
+ export type DockerContainerHealth = 'starting' | 'healthy' | 'unhealthy' | 'none';
41
24
  interface Health {
42
25
  cmd: string;
43
26
  interval?: number;
@@ -45,6 +28,8 @@ interface Health {
45
28
  retries?: number;
46
29
  }
47
30
  export declare const CONTAINER_LABEL_PORT_PREFIX = "kapeta_port-";
31
+ export declare const COMPOSE_LABEL_PROJECT = "com.docker.compose.project";
32
+ export declare const COMPOSE_LABEL_SERVICE = "com.docker.compose.service";
48
33
  export declare const HEALTH_CHECK_TIMEOUT: number;
49
34
  declare class ContainerManager {
50
35
  private _docker;
@@ -71,12 +56,12 @@ declare class ContainerManager {
71
56
  Retries: number;
72
57
  };
73
58
  private applyHash;
74
- ensureContainer(opts: any): Promise<Container>;
59
+ ensureContainer(opts: any): Promise<Docker.Container>;
75
60
  private createOrUpdateContainer;
76
- startContainer(opts: any): Promise<Container>;
77
- waitForReady(container: Container, attempt?: number): Promise<void>;
78
- _isReady(container: Container): Promise<any>;
79
- remove(container: Container, opts?: {
61
+ private startContainer;
62
+ waitForReady(container: Docker.Container, attempt?: number): Promise<void>;
63
+ _isReady(container: Docker.Container): Promise<boolean>;
64
+ remove(container: Docker.Container, opts?: {
80
65
  force?: boolean;
81
66
  }): Promise<void>;
82
67
  /**
@@ -84,7 +69,7 @@ declare class ContainerManager {
84
69
  * @param name
85
70
  * @return {Promise<ContainerInfo>}
86
71
  */
87
- get(name: string): Promise<ContainerInfo | null>;
72
+ get(name: string): Promise<ContainerInfo | undefined>;
88
73
  getLogs(instance: InstanceInfo): Promise<LogEntry[]>;
89
74
  stopLogListening(systemId: string, instanceId: string): Promise<void>;
90
75
  ensureLogListening(systemId: string, instanceId: string, handler: (log: LogEntry) => void): Promise<void>;
@@ -102,11 +87,11 @@ export declare class ContainerInfo {
102
87
  private readonly _container;
103
88
  /**
104
89
  *
105
- * @param {Container} dockerContainer
90
+ * @param {Docker.Container} dockerContainer
106
91
  */
107
- constructor(dockerContainer: Container);
108
- get native(): Container;
109
- isRunning(): Promise<any>;
92
+ constructor(dockerContainer: Docker.Container);
93
+ get native(): Docker.Container;
94
+ isRunning(): Promise<boolean>;
110
95
  start(): Promise<void>;
111
96
  restart(): Promise<void>;
112
97
  stop(): Promise<void>;
@@ -118,8 +103,30 @@ export declare class ContainerInfo {
118
103
  protocol: string;
119
104
  hostPort: string;
120
105
  } | null>;
121
- inspect(): Promise<any>;
122
- status(): Promise<DockerState>;
106
+ inspect(): Promise<Docker.ContainerInspectInfo | undefined>;
107
+ status(): Promise<{
108
+ Status: string;
109
+ Running: boolean;
110
+ Paused: boolean;
111
+ Restarting: boolean;
112
+ OOMKilled: boolean;
113
+ Dead: boolean;
114
+ Pid: number;
115
+ ExitCode: number;
116
+ Error: string;
117
+ StartedAt: string;
118
+ FinishedAt: string;
119
+ Health?: {
120
+ Status: string;
121
+ FailingStreak: number;
122
+ Log: {
123
+ Start: string;
124
+ End: string;
125
+ ExitCode: number;
126
+ Output: string;
127
+ }[];
128
+ } | undefined;
129
+ } | undefined>;
123
130
  getPorts(): Promise<PortMap | false>;
124
131
  getLogStream(): Promise<ClosableLogStream>;
125
132
  getLogs(): Promise<LogEntry[]>;