iobroker.docker-manager 0.1.3 → 1.0.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.
- package/LICENSE +1 -1
- package/README.md +14 -2
- package/admin/assets/index-CTR4gZWS.js +869 -0
- package/admin/assets/index-CUfNUNDg.css +1 -0
- package/admin/index_m.html +2 -2
- package/admin/tab_m.html +2 -2
- package/build/lib/DockerMonitor.js +221 -0
- package/build/lib/DockerMonitor.js.map +1 -1
- package/build/main.js +23 -0
- package/build/main.js.map +1 -1
- package/io-package.json +40 -30
- package/package.json +29 -25
- package/admin/assets/index-BTW-UszC.css +0 -1
- package/admin/assets/index-DiHGumzD.js +0 -796
- package/build/lib/DockerCommands.js +0 -1042
- package/build/lib/DockerCommands.js.map +0 -1
- package/build/lib/DockerManager.js +0 -2277
- package/build/lib/DockerManager.js.map +0 -1
- package/build/lib/dockerManager.types.js +0 -3
- package/build/lib/dockerManager.types.js.map +0 -1
|
@@ -1,1042 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// This class implements docker commands using CLI and
|
|
3
|
-
// it monitors periodically the docker daemon status.
|
|
4
|
-
// It manages containers defined in adapter.config.containers and monitors other containers
|
|
5
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
-
};
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
const node_util_1 = require("node:util");
|
|
10
|
-
const node_child_process_1 = require("node:child_process");
|
|
11
|
-
const node_os_1 = require("node:os");
|
|
12
|
-
const promises_1 = require("node:dns/promises");
|
|
13
|
-
const node_http_1 = __importDefault(require("node:http"));
|
|
14
|
-
const range_check_1 = require("range_check");
|
|
15
|
-
const execPromise = (0, node_util_1.promisify)(node_child_process_1.exec);
|
|
16
|
-
function isHttpResponse(url, timeout = 1000) {
|
|
17
|
-
return new Promise(resolve => {
|
|
18
|
-
try {
|
|
19
|
-
const req = node_http_1.default.request(url, { method: 'HEAD', timeout }, res => {
|
|
20
|
-
resolve(typeof res.statusCode === 'number');
|
|
21
|
-
req.destroy();
|
|
22
|
-
});
|
|
23
|
-
req.on('error', error => {
|
|
24
|
-
if (error.toString().toUpperCase().includes('TIMEDOUT') ||
|
|
25
|
-
error.toString().toUpperCase().includes('TIMEOUT')) {
|
|
26
|
-
resolve('timeout');
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
resolve(false);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
req.end();
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
resolve(false);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
function findOwnIpFor(ipToAccess) {
|
|
40
|
-
if (!(0, range_check_1.isIP)(ipToAccess)) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
const interfaces = (0, node_os_1.networkInterfaces)();
|
|
44
|
-
for (const iface of Object.values(interfaces)) {
|
|
45
|
-
if (iface) {
|
|
46
|
-
for (const alias of iface) {
|
|
47
|
-
if (alias.family === 'IPv4' && !alias.internal && alias.cidr) {
|
|
48
|
-
if ((0, range_check_1.inRange)(ipToAccess, alias.cidr)) {
|
|
49
|
-
return alias.address;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
async function getIpForDomain(domain) {
|
|
58
|
-
try {
|
|
59
|
-
const result = await (0, promises_1.lookup)(domain);
|
|
60
|
-
return result.address;
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
class DockerCommands {
|
|
67
|
-
#installed = false;
|
|
68
|
-
#dockerVersion = '';
|
|
69
|
-
#needSudo = false;
|
|
70
|
-
#waitReady;
|
|
71
|
-
#adapter;
|
|
72
|
-
#ownContainers = [];
|
|
73
|
-
#timers = {
|
|
74
|
-
container: {},
|
|
75
|
-
};
|
|
76
|
-
#runningCommands = {};
|
|
77
|
-
checkedURLs = {};
|
|
78
|
-
#ownIps = [];
|
|
79
|
-
#domain2ip = {};
|
|
80
|
-
constructor(adapter) {
|
|
81
|
-
this.#adapter = adapter;
|
|
82
|
-
this.#ownContainers = adapter.config.containers || [];
|
|
83
|
-
this.#waitReady = new Promise(resolve => this.init().then(() => resolve()));
|
|
84
|
-
}
|
|
85
|
-
isReady() {
|
|
86
|
-
return this.#waitReady;
|
|
87
|
-
}
|
|
88
|
-
async init() {
|
|
89
|
-
const version = await this.#isDockerInstalled();
|
|
90
|
-
this.#installed = !!version;
|
|
91
|
-
if (version) {
|
|
92
|
-
this.#dockerVersion = version;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
const daemonRunning = await this.#isDockerDaemonRunning();
|
|
96
|
-
if (daemonRunning) {
|
|
97
|
-
// Docker daemon is running, but docker command not found
|
|
98
|
-
this.#adapter.log.warn('Docker daemon is running, but docker command not found. May be "iobroker" user has no access to Docker. Run "iob fix" command to fix it.');
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
this.#adapter.log.warn('Docker is not installed. Please install Docker.');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (this.#installed) {
|
|
105
|
-
this.#needSudo = await this.#isNeedSudo();
|
|
106
|
-
await this.#checkOwnContainers();
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
async #isDockerDaemonRunning() {
|
|
110
|
-
try {
|
|
111
|
-
const { stdout, stderr } = await execPromise('systemctl status docker');
|
|
112
|
-
// ● docker.service - Docker Application Container Engine
|
|
113
|
-
// Loaded: loaded (/lib/systemd/system/docker.service; enabled; preset: enabled)
|
|
114
|
-
// Active: active (running) since Fri 2025-08-15 08:37:22 CEST; 3 weeks 2 days ago
|
|
115
|
-
// TriggeredBy: ● docker.socket
|
|
116
|
-
// Docs: https://docs.docker.com
|
|
117
|
-
// Main PID: 785 (dockerd)
|
|
118
|
-
// Tasks: 30
|
|
119
|
-
// CPU: 4min 17.003s
|
|
120
|
-
// CGroup: /system.slice/docker.service
|
|
121
|
-
// ├─ 785 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
|
|
122
|
-
// ├─97032 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 5000 -container-ip 172.17.0.2 -container-port 5000 -use-listen-fd
|
|
123
|
-
// └─97039 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 5000 -container-ip 172.17.0.2 -container-port 5000 -use-listen-fd
|
|
124
|
-
if (stderr?.includes('could not be found') || stderr.includes('not-found')) {
|
|
125
|
-
this.#adapter.log.error(`Docker is not installed: ${stderr}`);
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
this.#adapter.log.error(`Docker daemon is not running`);
|
|
129
|
-
return stdout.includes('(running)');
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
async #checkOwnContainers() {
|
|
136
|
-
if (!this.#ownContainers.length) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
const status = await this.containerList(true);
|
|
140
|
-
let images = await this.imageList();
|
|
141
|
-
for (let c = 0; c < this.#ownContainers.length; c++) {
|
|
142
|
-
const container = this.#ownContainers[c];
|
|
143
|
-
if (container.enabled !== false) {
|
|
144
|
-
// Check if container is running
|
|
145
|
-
const containerInfo = status.find(it => it.names === container.name);
|
|
146
|
-
if (containerInfo) {
|
|
147
|
-
if (containerInfo.status !== 'running' && containerInfo.status !== 'restarting') {
|
|
148
|
-
// Start the container
|
|
149
|
-
this.#adapter.log.info(`Starting own container ${container.name}`);
|
|
150
|
-
try {
|
|
151
|
-
const result = await this.containerStart(containerInfo.id);
|
|
152
|
-
if (result.stderr) {
|
|
153
|
-
this.#adapter.log.warn(`Cannot start own container ${container.name}: ${result.stderr}`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
catch (e) {
|
|
157
|
-
this.#adapter.log.warn(`Cannot start own container ${container.name}: ${e.message}`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
this.#adapter.log.debug(`Own container ${container.name} is already running`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
// Create and start the container
|
|
166
|
-
this.#adapter.log.info(`Creating and starting own container ${container.name}`);
|
|
167
|
-
if (!images.find(it => `${it.repository}:${it.tag}` === container.image)) {
|
|
168
|
-
this.#adapter.log.info(`Pulling image ${container.image} for own container ${container.name}`);
|
|
169
|
-
try {
|
|
170
|
-
const result = await this.imagePull(container.image);
|
|
171
|
-
if (result.stderr) {
|
|
172
|
-
this.#adapter.log.warn(`Cannot pull image ${container.image}: ${result.stderr}`);
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
catch (e) {
|
|
177
|
-
this.#adapter.log.warn(`Cannot pull image ${container.image}: ${e.message}`);
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
// Check that image is available now
|
|
181
|
-
images = await this.imageList();
|
|
182
|
-
if (!images.find(it => `${it.repository}:${it.tag}` === container.image)) {
|
|
183
|
-
this.#adapter.log.warn(`Image ${container.image} for own container ${container.name} not found after pull`);
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
try {
|
|
188
|
-
const result = await this.containerRun(container.image, container);
|
|
189
|
-
if (result.stderr) {
|
|
190
|
-
this.#adapter.log.warn(`Cannot start own container ${container.name}: ${result.stderr}`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
catch (e) {
|
|
194
|
-
this.#adapter.log.warn(`Cannot start own container ${container.name}: ${e.message}`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
#exec(command) {
|
|
201
|
-
if (!this.#installed) {
|
|
202
|
-
return Promise.reject(new Error('Docker is not installed'));
|
|
203
|
-
}
|
|
204
|
-
const finalCommand = this.#needSudo ? `sudo docker ${command}` : `docker ${command}`;
|
|
205
|
-
return execPromise(finalCommand);
|
|
206
|
-
}
|
|
207
|
-
async #isDockerInstalled() {
|
|
208
|
-
try {
|
|
209
|
-
const result = await execPromise('docker --version');
|
|
210
|
-
if (!result.stderr && result.stdout) {
|
|
211
|
-
// "Docker version 28.3.2, build 578ccf6\n"
|
|
212
|
-
return result.stdout.split('\n')[0].trim();
|
|
213
|
-
}
|
|
214
|
-
this.#adapter.log.debug(`Docker not installed: ${result.stderr}`);
|
|
215
|
-
}
|
|
216
|
-
catch (e) {
|
|
217
|
-
this.#adapter.log.debug(`Docker not installed: ${e.message}`);
|
|
218
|
-
}
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
async #isNeedSudo() {
|
|
222
|
-
try {
|
|
223
|
-
await execPromise('docker ps');
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
catch {
|
|
227
|
-
return true;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
async discUsage() {
|
|
231
|
-
const { stdout } = await this.#exec(`system df`);
|
|
232
|
-
const result = { total: { size: 0, reclaimable: 0 } };
|
|
233
|
-
// parse the output
|
|
234
|
-
// TYPE TOTAL ACTIVE SIZE RECLAIMABLE
|
|
235
|
-
// Images 2 1 2.715GB 2.715GB (99%)
|
|
236
|
-
// Containers 1 1 26.22MB 0B (0%)
|
|
237
|
-
// Local Volumes 0 0 0B 0B
|
|
238
|
-
// Build Cache 0 0 0B 0B
|
|
239
|
-
const lines = stdout.split('\n');
|
|
240
|
-
for (const line of lines) {
|
|
241
|
-
const parts = line.trim().split(/\s+/);
|
|
242
|
-
if (parts.length >= 5 && parts[0] !== 'TYPE') {
|
|
243
|
-
let size;
|
|
244
|
-
let reclaimable;
|
|
245
|
-
if (parts[0] === 'Images') {
|
|
246
|
-
const sizeStr = parts[3];
|
|
247
|
-
const reclaimableStr = parts[4].split(' ')[0];
|
|
248
|
-
size = this.#parseSize(sizeStr);
|
|
249
|
-
reclaimable = this.#parseSize(reclaimableStr);
|
|
250
|
-
result.images = {
|
|
251
|
-
total: parseInt(parts[1], 10),
|
|
252
|
-
active: parseInt(parts[2], 10),
|
|
253
|
-
size,
|
|
254
|
-
reclaimable: reclaimable,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
else if (parts[0] === 'Containers') {
|
|
258
|
-
const sizeStr = parts[3];
|
|
259
|
-
const reclaimableStr = parts[4].split(' ')[0];
|
|
260
|
-
size = this.#parseSize(sizeStr);
|
|
261
|
-
reclaimable = this.#parseSize(reclaimableStr);
|
|
262
|
-
result.containers = {
|
|
263
|
-
total: parseInt(parts[1], 10),
|
|
264
|
-
active: parseInt(parts[2], 10),
|
|
265
|
-
size,
|
|
266
|
-
reclaimable: reclaimable,
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
else if (parts[0] === 'Local' && parts[1] === 'Volumes') {
|
|
270
|
-
const sizeStr = parts[4];
|
|
271
|
-
const reclaimableStr = parts[5].split(' ')[0];
|
|
272
|
-
size = this.#parseSize(sizeStr);
|
|
273
|
-
reclaimable = this.#parseSize(reclaimableStr);
|
|
274
|
-
result.volumes = {
|
|
275
|
-
total: parseInt(parts[2], 10),
|
|
276
|
-
active: parseInt(parts[3], 10),
|
|
277
|
-
size,
|
|
278
|
-
reclaimable: reclaimable,
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
else if (parts[0] === 'Build' && parts[1] === 'Cache') {
|
|
282
|
-
const sizeStr = parts[4];
|
|
283
|
-
const reclaimableStr = parts[5].split(' ')[0];
|
|
284
|
-
size = this.#parseSize(sizeStr);
|
|
285
|
-
reclaimable = this.#parseSize(reclaimableStr);
|
|
286
|
-
result.buildCache = {
|
|
287
|
-
total: parseInt(parts[2], 10),
|
|
288
|
-
active: parseInt(parts[3], 10),
|
|
289
|
-
size,
|
|
290
|
-
reclaimable: reclaimable,
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
result.total.size += size;
|
|
294
|
-
result.total.reclaimable += reclaimable;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
return result;
|
|
298
|
-
}
|
|
299
|
-
async imagePull(image) {
|
|
300
|
-
try {
|
|
301
|
-
const result = await this.#exec(`pull ${image}`);
|
|
302
|
-
const images = await this.imageList();
|
|
303
|
-
if (!images.find(it => `${it.repository}:${it.tag}` === image)) {
|
|
304
|
-
throw new Error(`Image ${image} not found after pull`);
|
|
305
|
-
}
|
|
306
|
-
await this.#adapter.sendToGui({
|
|
307
|
-
command: 'images',
|
|
308
|
-
data: images,
|
|
309
|
-
});
|
|
310
|
-
return result;
|
|
311
|
-
}
|
|
312
|
-
catch (e) {
|
|
313
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
async imageNameAutocomplete(partialName) {
|
|
317
|
-
try {
|
|
318
|
-
// Read stars and descriptions
|
|
319
|
-
const { stdout } = await this.#exec(`search ${partialName} --format "{{.Name}};{{.Description}};{{.IsOfficial}};{{.StarCount}}" --limit 50`);
|
|
320
|
-
return stdout
|
|
321
|
-
.split('\n')
|
|
322
|
-
.filter(line => line.trim() !== '')
|
|
323
|
-
.map(line => {
|
|
324
|
-
const [name, description, isOfficial, starCount] = line.split(';');
|
|
325
|
-
return {
|
|
326
|
-
name,
|
|
327
|
-
description,
|
|
328
|
-
isOfficial: isOfficial === 'true',
|
|
329
|
-
starCount: parseInt(starCount, 10) || 0,
|
|
330
|
-
};
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
catch (e) {
|
|
334
|
-
this.#adapter.log.debug(`Cannot search images: ${e.message}`);
|
|
335
|
-
return [];
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
async containerRun(image, config) {
|
|
339
|
-
try {
|
|
340
|
-
const result = await this.#exec(`run ${this.#toDockerRun({ ...config, image })}`);
|
|
341
|
-
const containers = await this.containerList();
|
|
342
|
-
await this.#adapter.sendToGui({
|
|
343
|
-
command: 'containers',
|
|
344
|
-
data: containers,
|
|
345
|
-
});
|
|
346
|
-
return result;
|
|
347
|
-
}
|
|
348
|
-
catch (e) {
|
|
349
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
async containerCreate(image, config) {
|
|
353
|
-
try {
|
|
354
|
-
const result = await this.#exec(`create ${this.#toDockerRun({ ...config, image })}`);
|
|
355
|
-
const containers = await this.containerList();
|
|
356
|
-
await this.#adapter.sendToGui({
|
|
357
|
-
command: 'containers',
|
|
358
|
-
data: containers,
|
|
359
|
-
});
|
|
360
|
-
return result;
|
|
361
|
-
}
|
|
362
|
-
catch (e) {
|
|
363
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
async imageList() {
|
|
367
|
-
try {
|
|
368
|
-
const { stdout } = await this.#exec('images --format "{{.Repository}}:{{.Tag}};{{.ID}};{{.CreatedAt}};{{.Size}}"');
|
|
369
|
-
return stdout
|
|
370
|
-
.split('\n')
|
|
371
|
-
.filter(line => line.trim() !== '')
|
|
372
|
-
.map(line => {
|
|
373
|
-
const [repositoryTag, id, createdSince, size] = line.split(';');
|
|
374
|
-
const [repository, tag] = repositoryTag.split(':');
|
|
375
|
-
return {
|
|
376
|
-
repository,
|
|
377
|
-
tag,
|
|
378
|
-
id,
|
|
379
|
-
createdSince,
|
|
380
|
-
size: this.#parseSize(size),
|
|
381
|
-
};
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
catch (e) {
|
|
385
|
-
this.#adapter.log.debug(`Cannot list images: ${e.message}`);
|
|
386
|
-
return [];
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
async imageBuild(dockerfilePath, tag) {
|
|
390
|
-
try {
|
|
391
|
-
const result = await this.#exec(`build -t ${tag} -f ${dockerfilePath} .`);
|
|
392
|
-
const images = await this.imageList();
|
|
393
|
-
await this.#adapter.sendToGui({
|
|
394
|
-
command: 'images',
|
|
395
|
-
data: images,
|
|
396
|
-
});
|
|
397
|
-
return result;
|
|
398
|
-
}
|
|
399
|
-
catch (e) {
|
|
400
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
async imageTag(imageId, newTag) {
|
|
404
|
-
try {
|
|
405
|
-
const result = await this.#exec(`tag ${imageId} ${newTag}`);
|
|
406
|
-
const images = await this.imageList();
|
|
407
|
-
await this.#adapter.sendToGui({
|
|
408
|
-
command: 'images',
|
|
409
|
-
data: images,
|
|
410
|
-
});
|
|
411
|
-
return result;
|
|
412
|
-
}
|
|
413
|
-
catch (e) {
|
|
414
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
async imageRemove(imageId) {
|
|
418
|
-
try {
|
|
419
|
-
const result = await this.#exec(`rmi ${imageId}`);
|
|
420
|
-
const images = await this.imageList();
|
|
421
|
-
if (images.find(it => `${it.repository}:${it.tag}` === imageId)) {
|
|
422
|
-
return { stdout: '', stderr: `Image ${imageId} still found after deletion` };
|
|
423
|
-
}
|
|
424
|
-
await this.#adapter.sendToGui({
|
|
425
|
-
command: 'images',
|
|
426
|
-
data: images,
|
|
427
|
-
});
|
|
428
|
-
return result;
|
|
429
|
-
}
|
|
430
|
-
catch (e) {
|
|
431
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
async imageInspect(imageId) {
|
|
435
|
-
try {
|
|
436
|
-
const { stdout } = await this.#exec(`inspect ${imageId}`);
|
|
437
|
-
return JSON.parse(stdout)[0];
|
|
438
|
-
}
|
|
439
|
-
catch (e) {
|
|
440
|
-
this.#adapter.log.debug(`Cannot inspect image: ${e.message.toString()}`);
|
|
441
|
-
return null;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
#parseSize(sizeStr) {
|
|
445
|
-
const units = {
|
|
446
|
-
B: 1,
|
|
447
|
-
KB: 1024,
|
|
448
|
-
MB: 1024 * 1024,
|
|
449
|
-
GB: 1024 * 1024 * 1024,
|
|
450
|
-
TB: 1024 * 1024 * 1024 * 1024,
|
|
451
|
-
};
|
|
452
|
-
const match = sizeStr.match(/^([\d.]+)([KMGTP]?B)$/);
|
|
453
|
-
if (match) {
|
|
454
|
-
const value = parseFloat(match[1]);
|
|
455
|
-
const unit = match[2];
|
|
456
|
-
return value * (units[unit] || 1);
|
|
457
|
-
}
|
|
458
|
-
return 0;
|
|
459
|
-
}
|
|
460
|
-
async containerStop(container) {
|
|
461
|
-
try {
|
|
462
|
-
let containers = await this.containerList();
|
|
463
|
-
// find ID of container
|
|
464
|
-
const containerInfo = containers.find(it => it.names === container || it.id === container);
|
|
465
|
-
if (!containerInfo) {
|
|
466
|
-
throw new Error(`Container ${container} not found`);
|
|
467
|
-
}
|
|
468
|
-
const result = await this.#exec(`stop ${containerInfo.id}`);
|
|
469
|
-
containers = await this.containerList();
|
|
470
|
-
if (containers.find(it => it.id === containerInfo.id && it.status === 'running')) {
|
|
471
|
-
throw new Error(`Container ${container} still running after stop`);
|
|
472
|
-
}
|
|
473
|
-
await this.#adapter.sendToGui({
|
|
474
|
-
command: 'containers',
|
|
475
|
-
data: containers,
|
|
476
|
-
});
|
|
477
|
-
return result;
|
|
478
|
-
}
|
|
479
|
-
catch (e) {
|
|
480
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
async containerStart(container) {
|
|
484
|
-
try {
|
|
485
|
-
let containers = await this.containerList();
|
|
486
|
-
// find ID of container
|
|
487
|
-
const containerInfo = containers.find(it => it.names === container || it.id === container);
|
|
488
|
-
if (!containerInfo) {
|
|
489
|
-
throw new Error(`Container ${container} not found`);
|
|
490
|
-
}
|
|
491
|
-
const result = await this.#exec(`start ${containerInfo.id}`);
|
|
492
|
-
containers = await this.containerList();
|
|
493
|
-
if (containers.find(it => it.id === containerInfo.id && it.status !== 'running' && it.status !== 'restarting')) {
|
|
494
|
-
throw new Error(`Container ${container} still running after stop`);
|
|
495
|
-
}
|
|
496
|
-
await this.#adapter.sendToGui({
|
|
497
|
-
command: 'containers',
|
|
498
|
-
data: containers,
|
|
499
|
-
});
|
|
500
|
-
return result;
|
|
501
|
-
}
|
|
502
|
-
catch (e) {
|
|
503
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
async containerRestart(container, timeoutSeconds) {
|
|
507
|
-
try {
|
|
508
|
-
let containers = await this.containerList();
|
|
509
|
-
// find ID of container
|
|
510
|
-
const containerInfo = containers.find(it => it.names === container || it.id === container);
|
|
511
|
-
if (!containerInfo) {
|
|
512
|
-
throw new Error(`Container ${container} not found`);
|
|
513
|
-
}
|
|
514
|
-
const result = await this.#exec(`restart -t ${timeoutSeconds || 5} ${containerInfo.id}`);
|
|
515
|
-
containers = await this.containerList();
|
|
516
|
-
await this.#adapter.sendToGui({
|
|
517
|
-
command: 'containers',
|
|
518
|
-
data: containers,
|
|
519
|
-
});
|
|
520
|
-
return result;
|
|
521
|
-
}
|
|
522
|
-
catch (e) {
|
|
523
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
async containerRemove(container) {
|
|
527
|
-
try {
|
|
528
|
-
let containers = await this.containerList();
|
|
529
|
-
// find ID of container
|
|
530
|
-
const containerInfo = containers.find(it => it.names === container || it.id === container);
|
|
531
|
-
if (!containerInfo) {
|
|
532
|
-
throw new Error(`Container ${container} not found`);
|
|
533
|
-
}
|
|
534
|
-
const result = await this.#exec(`rm ${container}`);
|
|
535
|
-
containers = await this.containerList();
|
|
536
|
-
if (containers.find(it => it.id === containerInfo.id)) {
|
|
537
|
-
throw new Error(`Container ${container} still found after stop`);
|
|
538
|
-
}
|
|
539
|
-
await this.#adapter.sendToGui({
|
|
540
|
-
command: 'containers',
|
|
541
|
-
data: containers,
|
|
542
|
-
});
|
|
543
|
-
return result;
|
|
544
|
-
}
|
|
545
|
-
catch (e) {
|
|
546
|
-
return { stdout: '', stderr: e.message.toString() };
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
async containerList(all = true, ownIps) {
|
|
550
|
-
try {
|
|
551
|
-
const { stdout } = await this.#exec(`ps ${all ? '-a' : ''} --format "{{.Names}};{{.Status}};{{.ID}};{{.Image}};{{.Command}};{{.CreatedAt}};{{.Ports}}"`);
|
|
552
|
-
const containers = stdout
|
|
553
|
-
.split('\n')
|
|
554
|
-
.filter(line => line.trim() !== '')
|
|
555
|
-
.map(line => {
|
|
556
|
-
const [names, statusInfo, id, image, command, createdAt, ports] = line.split(';');
|
|
557
|
-
const [status, ...uptime] = statusInfo.split(' ');
|
|
558
|
-
let statusKey = status.toLowerCase();
|
|
559
|
-
if (statusKey === 'up') {
|
|
560
|
-
statusKey = 'running';
|
|
561
|
-
}
|
|
562
|
-
return { id, image, command, createdAt, status: statusKey, uptime: uptime.join(' '), ports, names };
|
|
563
|
-
});
|
|
564
|
-
if (ownIps) {
|
|
565
|
-
// Try to define if the provided ports are HTTP server
|
|
566
|
-
for (const container of containers) {
|
|
567
|
-
if (!container.ports || container.status !== 'running') {
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
const ports = container.ports?.split(',').map(p => p.trim()) || [];
|
|
571
|
-
// We have something like '127.0.0.1:8086->8086/tcp'
|
|
572
|
-
for (const port of ports) {
|
|
573
|
-
const parts = port.split('->');
|
|
574
|
-
if (parts.length === 2) {
|
|
575
|
-
const [hostIp, hostPort] = parts[0].split(':');
|
|
576
|
-
const [, protocol] = parts[1].split('/');
|
|
577
|
-
if (protocol === 'tcp') {
|
|
578
|
-
// request to the port and check if we get HTTP response
|
|
579
|
-
// create url
|
|
580
|
-
const url = `http://${hostIp === '127.0.0.1' ? 'localhost' : hostIp}:${hostPort}`;
|
|
581
|
-
if (this.checkedURLs[url] === undefined || this.checkedURLs[url] === 'timeout') {
|
|
582
|
-
this.checkedURLs[url] = await isHttpResponse(url);
|
|
583
|
-
}
|
|
584
|
-
if (this.checkedURLs[url] === true) {
|
|
585
|
-
if (hostIp === '0.0.0.0') {
|
|
586
|
-
// find the local network interface IP that suits to ownIp
|
|
587
|
-
for (const ownIp of ownIps) {
|
|
588
|
-
const realIp = this.#domain2ip[ownIp] || ownIp;
|
|
589
|
-
if ((0, range_check_1.isIP)(realIp)) {
|
|
590
|
-
const hostIp = findOwnIpFor(this.#domain2ip[ownIp] || ownIp);
|
|
591
|
-
container.httpLinks ||= {};
|
|
592
|
-
container.httpLinks[ownIp] ||= [];
|
|
593
|
-
if (hostIp) {
|
|
594
|
-
container.httpLinks[ownIp].push(url.replace('0.0.0.0', ownIp));
|
|
595
|
-
}
|
|
596
|
-
else {
|
|
597
|
-
container.httpLinks[ownIp].push(url);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
else {
|
|
603
|
-
for (const ownIp of ownIps) {
|
|
604
|
-
container.httpLinks ||= {};
|
|
605
|
-
container.httpLinks[ownIp] ||= [];
|
|
606
|
-
container.httpLinks[ownIp].push(url);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return containers;
|
|
616
|
-
}
|
|
617
|
-
catch (e) {
|
|
618
|
-
this.#adapter.log.debug(`Cannot list containers: ${e.message}`);
|
|
619
|
-
return [];
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
async containerLogs(containerNameOrId, options = {}) {
|
|
623
|
-
try {
|
|
624
|
-
const args = [];
|
|
625
|
-
if (options.tail !== undefined) {
|
|
626
|
-
args.push(`--tail ${options.tail}`);
|
|
627
|
-
}
|
|
628
|
-
if (options.follow) {
|
|
629
|
-
args.push(`--follow`);
|
|
630
|
-
}
|
|
631
|
-
const result = await this.#exec(`logs${args.length ? ` ${args.join(' ')}` : ''} ${containerNameOrId}`);
|
|
632
|
-
return (result.stdout || result.stderr).split('\n').filter(line => line.trim() !== '');
|
|
633
|
-
}
|
|
634
|
-
catch (e) {
|
|
635
|
-
return e
|
|
636
|
-
.toString()
|
|
637
|
-
.split('\n')
|
|
638
|
-
.map((line) => line.trim());
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
containerExecTerminate(sid, force, onUnsubscribe) {
|
|
642
|
-
if (this.#runningCommands[sid]) {
|
|
643
|
-
this.#runningCommands[sid].onUnsubscribe = onUnsubscribe;
|
|
644
|
-
if (force) {
|
|
645
|
-
this.#runningCommands[sid].p.kill('SIGKILL');
|
|
646
|
-
}
|
|
647
|
-
else {
|
|
648
|
-
this.#runningCommands[sid].p.kill('SIGTERM');
|
|
649
|
-
this.#runningCommands[sid].killTimeout ||= setTimeout(() => {
|
|
650
|
-
if (this.#runningCommands[sid]) {
|
|
651
|
-
this.#runningCommands[sid].killTimeout = null;
|
|
652
|
-
this.#runningCommands[sid].p.kill('SIGKILL');
|
|
653
|
-
}
|
|
654
|
-
}, 2000);
|
|
655
|
-
}
|
|
656
|
-
return true;
|
|
657
|
-
}
|
|
658
|
-
return false;
|
|
659
|
-
}
|
|
660
|
-
containerExec(container, command, sid) {
|
|
661
|
-
const hasTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
662
|
-
const args = ['exec'];
|
|
663
|
-
if (!command) {
|
|
664
|
-
this.#adapter.log.error('No command specified for exec');
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
if (hasTTY) {
|
|
668
|
-
args.push('-t');
|
|
669
|
-
} // add TTY only when available
|
|
670
|
-
args.push(container);
|
|
671
|
-
command.split(' ').forEach(part => args.push(part));
|
|
672
|
-
let p;
|
|
673
|
-
try {
|
|
674
|
-
if (this.#needSudo) {
|
|
675
|
-
this.#adapter.log.debug(`Executing: sudo docker ${args.join(' ')}`);
|
|
676
|
-
p = (0, node_child_process_1.spawn)('sudo', ['docker', ...args], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
677
|
-
}
|
|
678
|
-
else {
|
|
679
|
-
this.#adapter.log.debug(`Executing: docker ${args.join(' ')}`);
|
|
680
|
-
p = (0, node_child_process_1.spawn)('docker', args, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
catch (e) {
|
|
684
|
-
void this.#adapter.sendToGui({
|
|
685
|
-
command: 'exec',
|
|
686
|
-
data: { stdout: '', stderr: e.message.toString(), containerId: container, code: 1 },
|
|
687
|
-
}, sid);
|
|
688
|
-
return;
|
|
689
|
-
}
|
|
690
|
-
this.#runningCommands[sid] = { p, killTimeout: null };
|
|
691
|
-
let stdout = '';
|
|
692
|
-
let stderr = '';
|
|
693
|
-
p.stdout.on('data', d => {
|
|
694
|
-
if (this.#runningCommands[sid] && !this.#runningCommands[sid].onUnsubscribe) {
|
|
695
|
-
stdout += d;
|
|
696
|
-
void this.#adapter.sendToGui({ command: 'exec', data: { stdout, stderr, containerId: container } }, sid);
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
p.stderr.on('data', d => {
|
|
700
|
-
if (this.#runningCommands[sid] && !this.#runningCommands[sid].onUnsubscribe) {
|
|
701
|
-
stderr += d;
|
|
702
|
-
void this.#adapter.sendToGui({ command: 'exec', data: { stdout, stderr, containerId: container } }, sid);
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
p.on('close', code => {
|
|
706
|
-
this.#adapter.log.debug(`Command finished with code ${code}`);
|
|
707
|
-
const killTimeout = this.#runningCommands[sid]?.killTimeout;
|
|
708
|
-
if (killTimeout) {
|
|
709
|
-
clearTimeout(killTimeout);
|
|
710
|
-
}
|
|
711
|
-
if (this.#runningCommands[sid] && !this.#runningCommands[sid].onUnsubscribe) {
|
|
712
|
-
void this.#adapter.sendToGui({ command: 'exec', data: { stdout, stderr, containerId: container, code } }, sid);
|
|
713
|
-
}
|
|
714
|
-
delete this.#runningCommands[sid];
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
|
-
async containerInspect(containerNameOrId) {
|
|
718
|
-
try {
|
|
719
|
-
const { stdout } = await this.#exec(`inspect ${containerNameOrId}`);
|
|
720
|
-
return JSON.parse(stdout)[0];
|
|
721
|
-
}
|
|
722
|
-
catch (e) {
|
|
723
|
-
this.#adapter.log.debug(`Cannot inspect container: ${e.message.toString()}`);
|
|
724
|
-
return null;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
/**
|
|
728
|
-
* Build a docker run command string from ContainerConfig
|
|
729
|
-
*/
|
|
730
|
-
#toDockerRun(config) {
|
|
731
|
-
const args = [];
|
|
732
|
-
// detach / interactive
|
|
733
|
-
if (config.detach !== false) {
|
|
734
|
-
// default is true
|
|
735
|
-
args.push('-d');
|
|
736
|
-
}
|
|
737
|
-
if (config.tty) {
|
|
738
|
-
args.push('-t');
|
|
739
|
-
}
|
|
740
|
-
if (config.stdinOpen) {
|
|
741
|
-
args.push('-i');
|
|
742
|
-
}
|
|
743
|
-
if (config.removeOnExit) {
|
|
744
|
-
args.push('--rm');
|
|
745
|
-
}
|
|
746
|
-
// name
|
|
747
|
-
if (config.name) {
|
|
748
|
-
args.push('--name', config.name);
|
|
749
|
-
}
|
|
750
|
-
// hostname / domain
|
|
751
|
-
if (config.hostname) {
|
|
752
|
-
args.push('--hostname', config.hostname);
|
|
753
|
-
}
|
|
754
|
-
if (config.domainname) {
|
|
755
|
-
args.push('--domainname', config.domainname);
|
|
756
|
-
}
|
|
757
|
-
// environment
|
|
758
|
-
if (config.environment) {
|
|
759
|
-
for (const [key, value] of Object.entries(config.environment)) {
|
|
760
|
-
args.push('-e', `${key}=${value}`);
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
if (config.envFile) {
|
|
764
|
-
for (const file of config.envFile) {
|
|
765
|
-
args.push('--env-file', file);
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
// labels
|
|
769
|
-
if (config.labels) {
|
|
770
|
-
for (const [key, value] of Object.entries(config.labels)) {
|
|
771
|
-
args.push('--label', `${key}=${value}`);
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
// ports
|
|
775
|
-
if (config.publishAllPorts) {
|
|
776
|
-
args.push('-P');
|
|
777
|
-
}
|
|
778
|
-
if (config.ports) {
|
|
779
|
-
for (const p of config.ports) {
|
|
780
|
-
if (!p.containerPort) {
|
|
781
|
-
continue;
|
|
782
|
-
}
|
|
783
|
-
const mapping = (p.hostIP ? `${p.hostIP}:` : '') +
|
|
784
|
-
(p.hostPort ? `${p.hostPort}:` : '') +
|
|
785
|
-
p.containerPort +
|
|
786
|
-
(p.protocol ? `/${p.protocol}` : '');
|
|
787
|
-
args.push('-p', mapping);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
// volumes / mounts
|
|
791
|
-
if (config.volumes) {
|
|
792
|
-
for (const v of config.volumes) {
|
|
793
|
-
args.push('-v', v);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
if (config.mounts) {
|
|
797
|
-
for (const m of config.mounts) {
|
|
798
|
-
let mount = `type=${m.type},target=${m.target}`;
|
|
799
|
-
if (m.source) {
|
|
800
|
-
mount += `,source=${m.source}`;
|
|
801
|
-
}
|
|
802
|
-
if (m.readOnly) {
|
|
803
|
-
mount += `,readonly`;
|
|
804
|
-
}
|
|
805
|
-
args.push('--mount', mount);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
// restart policy
|
|
809
|
-
if (config.restart?.policy) {
|
|
810
|
-
const val = config.restart.policy === 'on-failure' && config.restart.maxRetries
|
|
811
|
-
? `on-failure:${config.restart.maxRetries}`
|
|
812
|
-
: config.restart.policy;
|
|
813
|
-
args.push('--restart', val);
|
|
814
|
-
}
|
|
815
|
-
// user & workdir
|
|
816
|
-
if (config.user) {
|
|
817
|
-
args.push('--user', String(config.user));
|
|
818
|
-
}
|
|
819
|
-
if (config.workdir) {
|
|
820
|
-
args.push('--workdir', config.workdir);
|
|
821
|
-
}
|
|
822
|
-
// logging
|
|
823
|
-
if (config.logging?.driver) {
|
|
824
|
-
args.push('--log-driver', config.logging.driver);
|
|
825
|
-
if (config.logging.options) {
|
|
826
|
-
for (const [k, v] of Object.entries(config.logging.options)) {
|
|
827
|
-
args.push('--log-opt', `${k}=${v}`);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
// security
|
|
832
|
-
if (config.security?.privileged) {
|
|
833
|
-
args.push('--privileged');
|
|
834
|
-
}
|
|
835
|
-
if (config.security?.capAdd) {
|
|
836
|
-
for (const cap of config.security.capAdd) {
|
|
837
|
-
args.push('--cap-add', cap);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
if (config.security?.capDrop) {
|
|
841
|
-
for (const cap of config.security.capDrop) {
|
|
842
|
-
args.push('--cap-drop', cap);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
if (config.security?.noNewPrivileges) {
|
|
846
|
-
args.push('--security-opt', 'no-new-privileges');
|
|
847
|
-
}
|
|
848
|
-
if (config.security?.apparmor) {
|
|
849
|
-
args.push('--security-opt', `apparmor=${config.security.apparmor}`);
|
|
850
|
-
}
|
|
851
|
-
// network
|
|
852
|
-
if (config.networkMode) {
|
|
853
|
-
args.push('--network', config.networkMode);
|
|
854
|
-
}
|
|
855
|
-
// extra hosts
|
|
856
|
-
if (config.extraHosts) {
|
|
857
|
-
for (const host of config.extraHosts) {
|
|
858
|
-
if (typeof host === 'string') {
|
|
859
|
-
args.push('--add-host', host);
|
|
860
|
-
}
|
|
861
|
-
else {
|
|
862
|
-
args.push('--add-host', `${host.host}:${host.ip}`);
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
// sysctls
|
|
867
|
-
if (config.sysctls) {
|
|
868
|
-
for (const [k, v] of Object.entries(config.sysctls)) {
|
|
869
|
-
args.push('--sysctl', `${k}=${v}`);
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
// stop signal / timeout
|
|
873
|
-
if (config.stop?.signal) {
|
|
874
|
-
args.push('--stop-signal', config.stop.signal);
|
|
875
|
-
}
|
|
876
|
-
if (config.stop?.gracePeriodSec !== undefined) {
|
|
877
|
-
args.push('--stop-timeout', String(config.stop.gracePeriodSec));
|
|
878
|
-
}
|
|
879
|
-
// resources
|
|
880
|
-
if (config.resources?.cpus) {
|
|
881
|
-
args.push('--cpus', String(config.resources.cpus));
|
|
882
|
-
}
|
|
883
|
-
if (config.resources?.memory) {
|
|
884
|
-
args.push('--memory', String(config.resources.memory));
|
|
885
|
-
}
|
|
886
|
-
// image
|
|
887
|
-
if (!config.image) {
|
|
888
|
-
throw new Error('ContainerConfig.image is required for docker run');
|
|
889
|
-
}
|
|
890
|
-
args.push(config.image);
|
|
891
|
-
// command override
|
|
892
|
-
if (config.command) {
|
|
893
|
-
if (Array.isArray(config.command)) {
|
|
894
|
-
args.push(...config.command);
|
|
895
|
-
}
|
|
896
|
-
else {
|
|
897
|
-
args.push(config.command);
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
return args.join(' ');
|
|
901
|
-
}
|
|
902
|
-
async #pollingInfo() {
|
|
903
|
-
await this.isReady();
|
|
904
|
-
if (!this.#installed) {
|
|
905
|
-
await this.#adapter.sendToGui({
|
|
906
|
-
command: 'info',
|
|
907
|
-
error: 'not installed',
|
|
908
|
-
});
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
const data = await this.discUsage();
|
|
912
|
-
await this.#adapter.sendToGui({
|
|
913
|
-
command: 'info',
|
|
914
|
-
data,
|
|
915
|
-
version: this.#dockerVersion,
|
|
916
|
-
});
|
|
917
|
-
}
|
|
918
|
-
async #pollingImages() {
|
|
919
|
-
await this.isReady();
|
|
920
|
-
if (!this.#installed) {
|
|
921
|
-
await this.#adapter.sendToGui({
|
|
922
|
-
command: 'info',
|
|
923
|
-
error: 'not installed',
|
|
924
|
-
});
|
|
925
|
-
return;
|
|
926
|
-
}
|
|
927
|
-
const data = await this.imageList();
|
|
928
|
-
await this.#adapter.sendToGui({
|
|
929
|
-
command: 'images',
|
|
930
|
-
data,
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
async #pollingContainers() {
|
|
934
|
-
await this.isReady();
|
|
935
|
-
if (!this.#installed) {
|
|
936
|
-
await this.#adapter.sendToGui({
|
|
937
|
-
command: 'info',
|
|
938
|
-
error: 'not installed',
|
|
939
|
-
});
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
const data = await this.containerList(true, this.#ownIps?.length ? this.#ownIps : undefined);
|
|
943
|
-
await this.#adapter.sendToGui({
|
|
944
|
-
command: 'containers',
|
|
945
|
-
data,
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
async #pollingContainer(container) {
|
|
949
|
-
await this.isReady();
|
|
950
|
-
if (!this.#installed) {
|
|
951
|
-
await this.#adapter.sendToGui({
|
|
952
|
-
command: 'info',
|
|
953
|
-
error: 'not installed',
|
|
954
|
-
});
|
|
955
|
-
return;
|
|
956
|
-
}
|
|
957
|
-
const data = await this.containerInspect(container);
|
|
958
|
-
await this.#adapter.sendToGui({
|
|
959
|
-
command: 'container',
|
|
960
|
-
container,
|
|
961
|
-
data,
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
|
-
async pollingUpdate(scan) {
|
|
965
|
-
if (scan.info) {
|
|
966
|
-
this.#timers.info ||= setInterval(async () => this.#pollingInfo(), 10_000);
|
|
967
|
-
setTimeout(() => void this.#pollingInfo(), 50); // do it immediately, too
|
|
968
|
-
}
|
|
969
|
-
else if (this.#timers.info) {
|
|
970
|
-
clearInterval(this.#timers.info);
|
|
971
|
-
this.#timers.info = undefined;
|
|
972
|
-
}
|
|
973
|
-
if (scan.images) {
|
|
974
|
-
this.#timers.images ||= setInterval(async () => {
|
|
975
|
-
await this.#pollingImages();
|
|
976
|
-
}, 10_000);
|
|
977
|
-
setTimeout(() => void this.#pollingImages(), 50); // do it immediately, too
|
|
978
|
-
}
|
|
979
|
-
else if (this.#timers.images) {
|
|
980
|
-
clearInterval(this.#timers.images);
|
|
981
|
-
this.#timers.images = undefined;
|
|
982
|
-
}
|
|
983
|
-
if (scan.containers) {
|
|
984
|
-
this.#ownIps = scan.containers;
|
|
985
|
-
// try to find for all domain names the own IP
|
|
986
|
-
for (const ipOrDomain of scan.containers) {
|
|
987
|
-
if (this.#domain2ip[ipOrDomain] === undefined && !(0, range_check_1.isIP)(ipOrDomain)) {
|
|
988
|
-
const ip = await getIpForDomain(ipOrDomain);
|
|
989
|
-
if (ip) {
|
|
990
|
-
this.#domain2ip[ipOrDomain] = ip;
|
|
991
|
-
}
|
|
992
|
-
else {
|
|
993
|
-
this.#domain2ip[ipOrDomain] = false;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
this.#timers.containers ||= setInterval(async () => this.#pollingContainers(), 10_000);
|
|
998
|
-
setTimeout(() => void this.#pollingContainers(), 50); // do it immediately, too
|
|
999
|
-
}
|
|
1000
|
-
else if (this.#timers.containers) {
|
|
1001
|
-
clearInterval(this.#timers.containers);
|
|
1002
|
-
this.#timers.containers = undefined;
|
|
1003
|
-
}
|
|
1004
|
-
scan.container.forEach(container => {
|
|
1005
|
-
if (container) {
|
|
1006
|
-
this.#timers.container[container] ||= setInterval(async (_container) => this.#pollingContainer(_container), 10_000, container);
|
|
1007
|
-
setTimeout(_container => void this.#pollingContainer(_container), 50, container); // do it immediately, too
|
|
1008
|
-
}
|
|
1009
|
-
});
|
|
1010
|
-
// Check deleted containers
|
|
1011
|
-
Object.keys(this.#timers.container).forEach(container => {
|
|
1012
|
-
if (container && !scan.container.includes(container)) {
|
|
1013
|
-
clearInterval(this.#timers.container[container]);
|
|
1014
|
-
delete this.#timers.container[container];
|
|
1015
|
-
}
|
|
1016
|
-
});
|
|
1017
|
-
}
|
|
1018
|
-
destroy() {
|
|
1019
|
-
// Destroy all running commands
|
|
1020
|
-
Object.keys(this.#runningCommands).forEach(sid => {
|
|
1021
|
-
this.containerExecTerminate(sid, true);
|
|
1022
|
-
});
|
|
1023
|
-
// destroy all timers
|
|
1024
|
-
Object.keys(this.#timers.container).forEach((id) => {
|
|
1025
|
-
clearTimeout(this.#timers.container[id]);
|
|
1026
|
-
});
|
|
1027
|
-
if (this.#timers.images) {
|
|
1028
|
-
clearInterval(this.#timers.images);
|
|
1029
|
-
delete this.#timers.images;
|
|
1030
|
-
}
|
|
1031
|
-
if (this.#timers.containers) {
|
|
1032
|
-
clearInterval(this.#timers.containers);
|
|
1033
|
-
delete this.#timers.containers;
|
|
1034
|
-
}
|
|
1035
|
-
if (this.#timers.info) {
|
|
1036
|
-
clearInterval(this.#timers.info);
|
|
1037
|
-
delete this.#timers.info;
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
exports.default = DockerCommands;
|
|
1042
|
-
//# sourceMappingURL=DockerCommands.js.map
|