@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
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.19.7](https://github.com/kapetacom/local-cluster-service/compare/v0.19.6...v0.19.7) (2023-09-10)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Replaced node-docker-api with dockerode and types ([#72](https://github.com/kapetacom/local-cluster-service/issues/72)) ([3d4894c](https://github.com/kapetacom/local-cluster-service/commit/3d4894c6f78dc6efe5e75587d6463f33a86ded62))
|
7
|
+
|
8
|
+
## [0.19.6](https://github.com/kapetacom/local-cluster-service/compare/v0.19.5...v0.19.6) (2023-09-08)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* Allow more docker opts from language targets ([#71](https://github.com/kapetacom/local-cluster-service/issues/71)) ([dd9e9bb](https://github.com/kapetacom/local-cluster-service/commit/dd9e9bb91fb6aa6d082b1864b268234ece9b8920))
|
14
|
+
|
1
15
|
## [0.19.5](https://github.com/kapetacom/local-cluster-service/compare/v0.19.4...v0.19.5) (2023-09-07)
|
2
16
|
|
3
17
|
|
@@ -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[]>;
|
@@ -3,13 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.containerManager = exports.toLocalBindVolume = exports.getExtraHosts = exports.ContainerInfo = exports.HEALTH_CHECK_TIMEOUT = exports.CONTAINER_LABEL_PORT_PREFIX = void 0;
|
6
|
+
exports.containerManager = exports.toLocalBindVolume = exports.getExtraHosts = exports.ContainerInfo = exports.HEALTH_CHECK_TIMEOUT = exports.COMPOSE_LABEL_SERVICE = exports.COMPOSE_LABEL_PROJECT = exports.CONTAINER_LABEL_PORT_PREFIX = void 0;
|
7
7
|
const path_1 = __importDefault(require("path"));
|
8
8
|
const storageService_1 = require("./storageService");
|
9
9
|
const os_1 = __importDefault(require("os"));
|
10
10
|
const lodash_1 = __importDefault(require("lodash"));
|
11
11
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
12
|
-
const
|
12
|
+
const dockerode_1 = __importDefault(require("dockerode"));
|
13
13
|
const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
14
14
|
const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-config"));
|
15
15
|
const node_uuid_1 = __importDefault(require("node-uuid"));
|
@@ -18,15 +18,45 @@ const utils_1 = require("./utils/utils");
|
|
18
18
|
const nodejs_api_client_1 = require("@kapeta/nodejs-api-client");
|
19
19
|
const taskManager_1 = require("./taskManager");
|
20
20
|
const node_events_1 = require("node:events");
|
21
|
+
const StreamValues_1 = __importDefault(require("stream-json/streamers/StreamValues"));
|
21
22
|
exports.CONTAINER_LABEL_PORT_PREFIX = 'kapeta_port-';
|
22
23
|
const NANO_SECOND = 1000000;
|
23
24
|
const HEALTH_CHECK_INTERVAL = 3000;
|
24
25
|
const HEALTH_CHECK_MAX = 20;
|
26
|
+
exports.COMPOSE_LABEL_PROJECT = 'com.docker.compose.project';
|
27
|
+
exports.COMPOSE_LABEL_SERVICE = 'com.docker.compose.service';
|
25
28
|
exports.HEALTH_CHECK_TIMEOUT = HEALTH_CHECK_INTERVAL * HEALTH_CHECK_MAX * 2;
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
var DockerPullEventTypes;
|
30
|
+
(function (DockerPullEventTypes) {
|
31
|
+
DockerPullEventTypes["PreparingPhase"] = "Preparing";
|
32
|
+
DockerPullEventTypes["WaitingPhase"] = "Waiting";
|
33
|
+
DockerPullEventTypes["PullingFsPhase"] = "Pulling fs layer";
|
34
|
+
DockerPullEventTypes["DownloadingPhase"] = "Downloading";
|
35
|
+
DockerPullEventTypes["DownloadCompletePhase"] = "Download complete";
|
36
|
+
DockerPullEventTypes["ExtractingPhase"] = "Extracting";
|
37
|
+
DockerPullEventTypes["VerifyingChecksumPhase"] = "Verifying Checksum";
|
38
|
+
DockerPullEventTypes["AlreadyExistsPhase"] = "Already exists";
|
39
|
+
DockerPullEventTypes["PullCompletePhase"] = "Pull complete";
|
40
|
+
})(DockerPullEventTypes || (DockerPullEventTypes = {}));
|
41
|
+
const processJsonStream = (purpose, stream, handler) => new Promise((resolve, reject) => {
|
42
|
+
const jsonStream = StreamValues_1.default.withParser();
|
43
|
+
jsonStream.on('data', (data) => {
|
44
|
+
try {
|
45
|
+
handler(data.value);
|
46
|
+
}
|
47
|
+
catch (e) {
|
48
|
+
console.error('Failed while processing data for stream: %s', purpose, e);
|
49
|
+
}
|
50
|
+
});
|
51
|
+
jsonStream.on('end', () => {
|
52
|
+
console.log('Docker stream ended: %s', purpose);
|
53
|
+
resolve();
|
54
|
+
});
|
55
|
+
jsonStream.on('error', (err) => {
|
56
|
+
console.error('Docker stream failed: %s', purpose, err);
|
57
|
+
reject(err);
|
58
|
+
});
|
59
|
+
stream.pipe(jsonStream);
|
30
60
|
});
|
31
61
|
class ContainerManager {
|
32
62
|
_docker;
|
@@ -64,9 +94,9 @@ class ContainerManager {
|
|
64
94
|
];
|
65
95
|
for (const opts of connectOptions) {
|
66
96
|
try {
|
67
|
-
const client = new
|
97
|
+
const client = new dockerode_1.default({
|
68
98
|
...opts,
|
69
|
-
timeout:
|
99
|
+
timeout: 15 * 60 * 1000, //15 minutes should be enough for any operation
|
70
100
|
});
|
71
101
|
await client.ping();
|
72
102
|
this._docker = client;
|
@@ -143,13 +173,12 @@ class ContainerManager {
|
|
143
173
|
return this._docker;
|
144
174
|
}
|
145
175
|
async getContainerByName(containerName) {
|
146
|
-
const containers = await this.docker().
|
176
|
+
const containers = await this.docker().listContainers({ all: true });
|
147
177
|
const out = containers.find((container) => {
|
148
|
-
|
149
|
-
return containerData.Names.indexOf(`/${containerName}`) > -1;
|
178
|
+
return container.Names.indexOf(`/${containerName}`) > -1;
|
150
179
|
});
|
151
180
|
if (out) {
|
152
|
-
return
|
181
|
+
return this.get(out.Id);
|
153
182
|
}
|
154
183
|
return undefined;
|
155
184
|
}
|
@@ -158,8 +187,7 @@ class ContainerManager {
|
|
158
187
|
if (!tag) {
|
159
188
|
tag = 'latest';
|
160
189
|
}
|
161
|
-
const imageTagList = (await this.docker().
|
162
|
-
.map((image) => image.data)
|
190
|
+
const imageTagList = (await this.docker().listImages({}))
|
163
191
|
.filter((imageData) => !!imageData.RepoTags)
|
164
192
|
.map((imageData) => imageData.RepoTags);
|
165
193
|
if (imageTagList.some((imageTags) => imageTags.indexOf(image) > -1)) {
|
@@ -184,66 +212,61 @@ class ContainerManager {
|
|
184
212
|
serveraddress: 'docker.kapeta.com',
|
185
213
|
}
|
186
214
|
: {};
|
187
|
-
const stream =
|
188
|
-
|
189
|
-
|
190
|
-
}));
|
215
|
+
const stream = await this.docker().pull(image, {
|
216
|
+
authconfig: auth,
|
217
|
+
});
|
191
218
|
const chunks = {};
|
192
219
|
let lastEmitted = Date.now();
|
193
|
-
await
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
220
|
+
await processJsonStream(`image:pull:${image}`, stream, (data) => {
|
221
|
+
if (!chunks[data.id]) {
|
222
|
+
chunks[data.id] = {
|
223
|
+
downloading: {
|
224
|
+
total: 0,
|
225
|
+
current: 0,
|
226
|
+
},
|
227
|
+
extracting: {
|
228
|
+
total: 0,
|
229
|
+
current: 0,
|
230
|
+
},
|
231
|
+
done: false,
|
232
|
+
};
|
233
|
+
}
|
234
|
+
const chunk = chunks[data.id];
|
235
|
+
switch (data.status) {
|
236
|
+
case DockerPullEventTypes.PreparingPhase:
|
237
|
+
case DockerPullEventTypes.WaitingPhase:
|
238
|
+
case DockerPullEventTypes.PullingFsPhase:
|
239
|
+
//Do nothing
|
240
|
+
break;
|
241
|
+
case DockerPullEventTypes.DownloadingPhase:
|
242
|
+
case DockerPullEventTypes.VerifyingChecksumPhase:
|
243
|
+
chunk.downloading = {
|
244
|
+
total: data.progressDetail?.total ?? 0,
|
245
|
+
current: data.progressDetail?.current ?? 0,
|
218
246
|
};
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
break;
|
243
|
-
}
|
244
|
-
});
|
245
|
-
if (Date.now() - lastEmitted < 1000) {
|
246
|
-
return;
|
247
|
+
break;
|
248
|
+
case DockerPullEventTypes.ExtractingPhase:
|
249
|
+
chunk.extracting = {
|
250
|
+
total: data.progressDetail?.total ?? 0,
|
251
|
+
current: data.progressDetail?.current ?? 0,
|
252
|
+
};
|
253
|
+
break;
|
254
|
+
case DockerPullEventTypes.DownloadCompletePhase:
|
255
|
+
chunk.downloading.current = chunks[data.id].downloading.total;
|
256
|
+
break;
|
257
|
+
case DockerPullEventTypes.PullCompletePhase:
|
258
|
+
chunk.extracting.current = chunks[data.id].extracting.total;
|
259
|
+
chunk.done = true;
|
260
|
+
break;
|
261
|
+
}
|
262
|
+
if (data.status === DockerPullEventTypes.AlreadyExistsPhase ||
|
263
|
+
data.status.includes('Image is up to date') ||
|
264
|
+
data.status.includes('Downloaded newer image')) {
|
265
|
+
chunk.downloading.current = 1;
|
266
|
+
chunk.downloading.total = 1;
|
267
|
+
chunk.extracting.current = 1;
|
268
|
+
chunk.extracting.total = 1;
|
269
|
+
chunk.done = true;
|
247
270
|
}
|
248
271
|
const chunkList = Object.values(chunks);
|
249
272
|
let totals = {
|
@@ -255,6 +278,7 @@ class ContainerManager {
|
|
255
278
|
total: 0,
|
256
279
|
current: 0,
|
257
280
|
},
|
281
|
+
percent: 0,
|
258
282
|
total: chunkList.length,
|
259
283
|
done: 0,
|
260
284
|
};
|
@@ -275,14 +299,17 @@ class ContainerManager {
|
|
275
299
|
totals.done++;
|
276
300
|
}
|
277
301
|
});
|
278
|
-
|
302
|
+
totals.percent = totals.total > 0 ? (totals.done / totals.total) * 100 : 0;
|
279
303
|
task.metadata = {
|
280
304
|
...task.metadata,
|
281
305
|
image,
|
282
|
-
progress,
|
306
|
+
progress: totals.percent,
|
283
307
|
status: totals,
|
284
308
|
timeTaken: Date.now() - timeStarted,
|
285
309
|
};
|
310
|
+
if (Date.now() - lastEmitted < 1000) {
|
311
|
+
return;
|
312
|
+
}
|
286
313
|
task.emitUpdate();
|
287
314
|
lastEmitted = Date.now();
|
288
315
|
//console.log('Pulling image %s: %s % [done: %s, total: %s]', image, Math.round(percent), totals.done, totals.total);
|
@@ -299,6 +326,7 @@ class ContainerManager {
|
|
299
326
|
name: taskName,
|
300
327
|
image,
|
301
328
|
progress: -1,
|
329
|
+
group: 'docker:pull', //It's faster to pull images one at a time
|
302
330
|
});
|
303
331
|
await task.wait();
|
304
332
|
return true;
|
@@ -344,32 +372,32 @@ class ContainerManager {
|
|
344
372
|
console.log('Starting unnamed container: %s', opts.Image);
|
345
373
|
return this.startContainer(opts);
|
346
374
|
}
|
347
|
-
const
|
375
|
+
const container = await this.getContainerByName(opts.name);
|
348
376
|
if (imagePulled) {
|
377
|
+
// If image was pulled always recreate
|
349
378
|
console.log('New version of image was pulled: %s', opts.Image);
|
350
379
|
}
|
351
380
|
else {
|
352
|
-
|
353
|
-
if (!containerInfo) {
|
381
|
+
if (!container) {
|
354
382
|
console.log('Starting new container: %s', opts.name);
|
355
383
|
return this.startContainer(opts);
|
356
384
|
}
|
357
|
-
const containerData =
|
358
|
-
if (containerData?.Labels?.HASH === opts.Labels.HASH) {
|
359
|
-
if (!(await
|
385
|
+
const containerData = await container.inspect();
|
386
|
+
if (containerData?.Config.Labels?.HASH === opts.Labels.HASH) {
|
387
|
+
if (!(await container.isRunning())) {
|
360
388
|
console.log('Starting previously created container: %s', opts.name);
|
361
|
-
await
|
389
|
+
await container.start();
|
362
390
|
}
|
363
391
|
else {
|
364
392
|
console.log('Previously created container already running: %s', opts.name);
|
365
393
|
}
|
366
|
-
return
|
394
|
+
return container.native;
|
367
395
|
}
|
368
396
|
}
|
369
|
-
if (
|
397
|
+
if (container) {
|
370
398
|
// Remove the container and start a new one
|
371
399
|
console.log('Replacing previously created container: %s', opts.name);
|
372
|
-
await
|
400
|
+
await container.remove({ force: true });
|
373
401
|
}
|
374
402
|
console.log('Starting new container: %s', opts.name);
|
375
403
|
return this.startContainer(opts);
|
@@ -385,7 +413,7 @@ class ContainerManager {
|
|
385
413
|
}
|
386
414
|
opts.HostConfig.ExtraHosts = opts.HostConfig.ExtraHosts.concat(extraHosts);
|
387
415
|
}
|
388
|
-
const dockerContainer = await this.docker().
|
416
|
+
const dockerContainer = await this.docker().createContainer(opts);
|
389
417
|
await dockerContainer.start();
|
390
418
|
return dockerContainer;
|
391
419
|
}
|
@@ -414,30 +442,28 @@ class ContainerManager {
|
|
414
442
|
async _isReady(container) {
|
415
443
|
let info;
|
416
444
|
try {
|
417
|
-
info = await container.
|
445
|
+
info = await container.inspect();
|
418
446
|
}
|
419
447
|
catch (err) {
|
420
448
|
return false;
|
421
449
|
}
|
422
|
-
const
|
423
|
-
|
424
|
-
if (state?.Status === 'exited' || state?.Status === 'removing' || state?.Status === 'dead') {
|
450
|
+
const state = info.State;
|
451
|
+
if (state.Status === 'exited' || state?.Status === 'removing' || state?.Status === 'dead') {
|
425
452
|
throw new Error('Container exited unexpectedly');
|
426
453
|
}
|
427
|
-
if (
|
454
|
+
if (state.Health) {
|
428
455
|
// If container has health info - wait for it to become healthy
|
429
|
-
return
|
456
|
+
return state.Health.Status === 'healthy';
|
430
457
|
}
|
431
458
|
else {
|
432
|
-
return
|
459
|
+
return state.Running ?? false;
|
433
460
|
}
|
434
461
|
}
|
435
462
|
async remove(container, opts) {
|
436
463
|
const newName = 'deleting-' + node_uuid_1.default.v4();
|
437
|
-
const containerData = container.data;
|
438
464
|
// Rename the container first to avoid name conflicts if people start the same container
|
439
465
|
await container.rename({ name: newName });
|
440
|
-
await container.
|
466
|
+
await container.remove({ force: !!opts?.force });
|
441
467
|
}
|
442
468
|
/**
|
443
469
|
*
|
@@ -447,15 +473,15 @@ class ContainerManager {
|
|
447
473
|
async get(name) {
|
448
474
|
let dockerContainer = null;
|
449
475
|
try {
|
450
|
-
dockerContainer = await this.docker().
|
451
|
-
await dockerContainer.
|
476
|
+
dockerContainer = await this.docker().getContainer(name);
|
477
|
+
await dockerContainer.stats();
|
452
478
|
}
|
453
479
|
catch (err) {
|
454
480
|
//Ignore
|
455
481
|
dockerContainer = null;
|
456
482
|
}
|
457
483
|
if (!dockerContainer) {
|
458
|
-
return
|
484
|
+
return undefined;
|
459
485
|
}
|
460
486
|
return new ContainerInfo(dockerContainer);
|
461
487
|
}
|
@@ -647,12 +673,12 @@ class ContainerInfo {
|
|
647
673
|
_container;
|
648
674
|
/**
|
649
675
|
*
|
650
|
-
* @param {Container} dockerContainer
|
676
|
+
* @param {Docker.Container} dockerContainer
|
651
677
|
*/
|
652
678
|
constructor(dockerContainer) {
|
653
679
|
/**
|
654
680
|
*
|
655
|
-
* @type {Container}
|
681
|
+
* @type {Docker.Container}
|
656
682
|
* @private
|
657
683
|
*/
|
658
684
|
this._container = dockerContainer;
|
@@ -668,12 +694,21 @@ class ContainerInfo {
|
|
668
694
|
return inspectResult.State.Running || inspectResult.State.Restarting;
|
669
695
|
}
|
670
696
|
async start() {
|
697
|
+
if (await this.isRunning()) {
|
698
|
+
return;
|
699
|
+
}
|
671
700
|
await this._container.start();
|
672
701
|
}
|
673
702
|
async restart() {
|
703
|
+
if (!(await this.isRunning())) {
|
704
|
+
return this.start();
|
705
|
+
}
|
674
706
|
await this._container.restart();
|
675
707
|
}
|
676
708
|
async stop() {
|
709
|
+
if (!(await this.isRunning())) {
|
710
|
+
return;
|
711
|
+
}
|
677
712
|
await this._container.stop();
|
678
713
|
}
|
679
714
|
async remove(opts) {
|
@@ -688,16 +723,15 @@ class ContainerInfo {
|
|
688
723
|
}
|
689
724
|
async inspect() {
|
690
725
|
try {
|
691
|
-
|
692
|
-
return result ? result.data : null;
|
726
|
+
return await this._container.inspect();
|
693
727
|
}
|
694
728
|
catch (err) {
|
695
|
-
return
|
729
|
+
return undefined;
|
696
730
|
}
|
697
731
|
}
|
698
732
|
async status() {
|
699
733
|
const result = await this.inspect();
|
700
|
-
return result
|
734
|
+
return result?.State;
|
701
735
|
}
|
702
736
|
async getPorts() {
|
703
737
|
const inspectResult = await this.inspect();
|
@@ -742,17 +776,13 @@ class ContainerInfo {
|
|
742
776
|
}
|
743
777
|
}
|
744
778
|
async getLogs() {
|
745
|
-
const
|
779
|
+
const logs = await this.native.logs({
|
746
780
|
stdout: true,
|
747
781
|
stderr: true,
|
748
782
|
follow: false,
|
749
783
|
timestamps: true,
|
750
|
-
}));
|
751
|
-
const chunks = [];
|
752
|
-
await promisifyStream(logStream, (data) => {
|
753
|
-
chunks.push(data);
|
754
784
|
});
|
755
|
-
const out = readLogBuffer(
|
785
|
+
const out = readLogBuffer(logs);
|
756
786
|
if (out.length === 0) {
|
757
787
|
out.push({
|
758
788
|
time: Date.now(),
|