@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.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/src/containerManager.d.ts +39 -32
- package/dist/cjs/src/containerManager.js +138 -108
- package/dist/cjs/src/instanceManager.js +28 -18
- package/dist/cjs/src/operatorManager.js +3 -0
- package/dist/cjs/src/taskManager.js +4 -1
- package/dist/cjs/src/utils/BlockInstanceRunner.js +19 -1
- package/dist/esm/src/containerManager.d.ts +39 -32
- package/dist/esm/src/containerManager.js +138 -108
- package/dist/esm/src/instanceManager.js +28 -18
- package/dist/esm/src/operatorManager.js +3 -0
- package/dist/esm/src/taskManager.js +4 -1
- package/dist/esm/src/utils/BlockInstanceRunner.js +19 -1
- package/package.json +5 -2
- package/src/containerManager.ts +188 -140
- package/src/instanceManager.ts +44 -20
- package/src/operatorManager.ts +11 -1
- package/src/taskManager.ts +4 -1
- package/src/utils/BlockInstanceRunner.ts +30 -4
@@ -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.
|
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.
|
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
|
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
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
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 (
|
619
|
+
if (statusType === 'created') {
|
610
620
|
return types_1.InstanceStatus.STARTING;
|
611
621
|
}
|
612
|
-
if (
|
622
|
+
if (statusType === 'exited' || statusType === 'dead') {
|
613
623
|
return types_1.InstanceStatus.STOPPED;
|
614
624
|
}
|
615
|
-
if (
|
625
|
+
if (statusType === 'removing') {
|
616
626
|
return types_1.InstanceStatus.BUSY;
|
617
627
|
}
|
618
|
-
if (
|
628
|
+
if (statusType === 'restarting') {
|
619
629
|
return types_1.InstanceStatus.BUSY;
|
620
630
|
}
|
621
|
-
if (
|
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
|
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
|
-
|
24
|
-
|
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
|
77
|
-
waitForReady(container: Container, attempt?: number): Promise<void>;
|
78
|
-
_isReady(container: Container): Promise<
|
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 |
|
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<
|
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<
|
122
|
-
status(): Promise<
|
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[]>;
|