docker-client-mx 1.35.0
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 +21 -0
- package/README.md +233 -0
- package/bin/docker-client-mx.js +42 -0
- package/build/icons/icon-1024.png +0 -0
- package/build/icons/icon-128.png +0 -0
- package/build/icons/icon-16.png +0 -0
- package/build/icons/icon-256.png +0 -0
- package/build/icons/icon-32.png +0 -0
- package/build/icons/icon-48.png +0 -0
- package/build/icons/icon-512.png +0 -0
- package/build/icons/icon-64.png +0 -0
- package/build/icons/icon.ico +0 -0
- package/build/icons/icon.iconset/icon_128x128.png +0 -0
- package/build/icons/icon.iconset/icon_128x128@2x.png +0 -0
- package/build/icons/icon.iconset/icon_16x16.png +0 -0
- package/build/icons/icon.iconset/icon_16x16@2x.png +0 -0
- package/build/icons/icon.iconset/icon_256x256.png +0 -0
- package/build/icons/icon.iconset/icon_256x256@2x.png +0 -0
- package/build/icons/icon.iconset/icon_32x32.png +0 -0
- package/build/icons/icon.iconset/icon_32x32@2x.png +0 -0
- package/build/icons/icon.iconset/icon_512x512.png +0 -0
- package/build/icons/icon.iconset/icon_512x512@2x.png +0 -0
- package/build/icons/icon.png +0 -0
- package/dist/docker-desktop-mx/browser/chunk-64WWLMUP.js +39 -0
- package/dist/docker-desktop-mx/browser/chunk-N4VBBC4W.js +1 -0
- package/dist/docker-desktop-mx/browser/favicon.ico +0 -0
- package/dist/docker-desktop-mx/browser/index.html +14 -0
- package/dist/docker-desktop-mx/browser/main-CTIRTSOI.js +5 -0
- package/dist/docker-desktop-mx/browser/styles-JS3HDDI4.css +1 -0
- package/dist-electron/api.js +10 -0
- package/dist-electron/api.js.map +1 -0
- package/dist-electron/assistant-key.js +70 -0
- package/dist-electron/assistant-key.js.map +1 -0
- package/dist-electron/assistant-models.js +19 -0
- package/dist-electron/assistant-models.js.map +1 -0
- package/dist-electron/assistant-tools.js +335 -0
- package/dist-electron/assistant-tools.js.map +1 -0
- package/dist-electron/assistant.js +302 -0
- package/dist-electron/assistant.js.map +1 -0
- package/dist-electron/attention.js +75 -0
- package/dist-electron/attention.js.map +1 -0
- package/dist-electron/channels.js +113 -0
- package/dist-electron/channels.js.map +1 -0
- package/dist-electron/compose-files.js +104 -0
- package/dist-electron/compose-files.js.map +1 -0
- package/dist-electron/compose.js +271 -0
- package/dist-electron/compose.js.map +1 -0
- package/dist-electron/container-events.js +188 -0
- package/dist-electron/container-events.js.map +1 -0
- package/dist-electron/containers.js +165 -0
- package/dist-electron/containers.js.map +1 -0
- package/dist-electron/context-menu.js +56 -0
- package/dist-electron/context-menu.js.map +1 -0
- package/dist-electron/contexts.js +346 -0
- package/dist-electron/contexts.js.map +1 -0
- package/dist-electron/crash.js +129 -0
- package/dist-electron/crash.js.map +1 -0
- package/dist-electron/diagnosis.js +338 -0
- package/dist-electron/diagnosis.js.map +1 -0
- package/dist-electron/docker-client.js +284 -0
- package/dist-electron/docker-client.js.map +1 -0
- package/dist-electron/engine.js +145 -0
- package/dist-electron/engine.js.map +1 -0
- package/dist-electron/event-store.js +146 -0
- package/dist-electron/event-store.js.map +1 -0
- package/dist-electron/events.js +132 -0
- package/dist-electron/events.js.map +1 -0
- package/dist-electron/exec.js +138 -0
- package/dist-electron/exec.js.map +1 -0
- package/dist-electron/files.js +407 -0
- package/dist-electron/files.js.map +1 -0
- package/dist-electron/health.js +92 -0
- package/dist-electron/health.js.map +1 -0
- package/dist-electron/host-overview.js +50 -0
- package/dist-electron/host-overview.js.map +1 -0
- package/dist-electron/host-shell.js +196 -0
- package/dist-electron/host-shell.js.map +1 -0
- package/dist-electron/i18n.js +58 -0
- package/dist-electron/i18n.js.map +1 -0
- package/dist-electron/image-history.js +87 -0
- package/dist-electron/image-history.js.map +1 -0
- package/dist-electron/images.js +141 -0
- package/dist-electron/images.js.map +1 -0
- package/dist-electron/ipc.js +783 -0
- package/dist-electron/ipc.js.map +1 -0
- package/dist-electron/links.js +46 -0
- package/dist-electron/links.js.map +1 -0
- package/dist-electron/logs.js +153 -0
- package/dist-electron/logs.js.map +1 -0
- package/dist-electron/main.js +494 -0
- package/dist-electron/main.js.map +1 -0
- package/dist-electron/merged-logs.js +127 -0
- package/dist-electron/merged-logs.js.map +1 -0
- package/dist-electron/navigation.js +37 -0
- package/dist-electron/navigation.js.map +1 -0
- package/dist-electron/ndjson.js +30 -0
- package/dist-electron/ndjson.js.map +1 -0
- package/dist-electron/networks.js +507 -0
- package/dist-electron/networks.js.map +1 -0
- package/dist-electron/port-conflicts.js +106 -0
- package/dist-electron/port-conflicts.js.map +1 -0
- package/dist-electron/preload.js +257 -0
- package/dist-electron/preload.js.map +1 -0
- package/dist-electron/pull.js +220 -0
- package/dist-electron/pull.js.map +1 -0
- package/dist-electron/redact.js +129 -0
- package/dist-electron/redact.js.map +1 -0
- package/dist-electron/registry-auth.js +150 -0
- package/dist-electron/registry-auth.js.map +1 -0
- package/dist-electron/renew.js +162 -0
- package/dist-electron/renew.js.map +1 -0
- package/dist-electron/restart-policy.js +58 -0
- package/dist-electron/restart-policy.js.map +1 -0
- package/dist-electron/run-command.js +146 -0
- package/dist-electron/run-command.js.map +1 -0
- package/dist-electron/run.js +151 -0
- package/dist-electron/run.js.map +1 -0
- package/dist-electron/self-update.js +163 -0
- package/dist-electron/self-update.js.map +1 -0
- package/dist-electron/servers.js +108 -0
- package/dist-electron/servers.js.map +1 -0
- package/dist-electron/settings.js +146 -0
- package/dist-electron/settings.js.map +1 -0
- package/dist-electron/ssh.js +574 -0
- package/dist-electron/ssh.js.map +1 -0
- package/dist-electron/stats.js +209 -0
- package/dist-electron/stats.js.map +1 -0
- package/dist-electron/streams.js +63 -0
- package/dist-electron/streams.js.map +1 -0
- package/dist-electron/system.js +123 -0
- package/dist-electron/system.js.map +1 -0
- package/dist-electron/terminal-transcript.js +55 -0
- package/dist-electron/terminal-transcript.js.map +1 -0
- package/dist-electron/updates.js +136 -0
- package/dist-electron/updates.js.map +1 -0
- package/dist-electron/volume-backup.js +203 -0
- package/dist-electron/volume-backup.js.map +1 -0
- package/dist-electron/volume-files.js +124 -0
- package/dist-electron/volume-files.js.map +1 -0
- package/dist-electron/volumes.js +253 -0
- package/dist-electron/volumes.js.map +1 -0
- package/dist-electron/wiring-watch.js +93 -0
- package/dist-electron/wiring-watch.js.map +1 -0
- package/dist-electron/wiring.js +71 -0
- package/dist-electron/wiring.js.map +1 -0
- package/dist-electron/wsl.js +167 -0
- package/dist-electron/wsl.js.map +1 -0
- package/package.json +125 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OWNER_LABEL = exports.HELPER_LABEL = exports.HELPER_IMAGE = void 0;
|
|
4
|
+
exports.hostShellLabel = hostShellLabel;
|
|
5
|
+
exports.isOrphan = isOrphan;
|
|
6
|
+
exports.hasHostShells = hasHostShells;
|
|
7
|
+
exports.removeHostShells = removeHostShells;
|
|
8
|
+
exports.openHostShell = openHostShell;
|
|
9
|
+
const docker_client_1 = require("./docker-client");
|
|
10
|
+
const exec_1 = require("./exec");
|
|
11
|
+
const i18n_1 = require("./i18n");
|
|
12
|
+
/**
|
|
13
|
+
* Een shell op de machine waar de daemon draait — de WSL-distro, de VM van Docker Desktop of
|
|
14
|
+
* Colima, of gewoon deze Linux-machine.
|
|
15
|
+
*
|
|
16
|
+
* De app praat alleen met de Engine API, dus een shell op die machine openen kan niet direct.
|
|
17
|
+
* Wat wel kan: een kortstondige container die in de namespaces van PID 1 op de host stapt. Dat
|
|
18
|
+
* is dezelfde truc als `docker run --privileged --pid=host alpine nsenter -t 1 …`, en hij werkt
|
|
19
|
+
* voor elke context, ook een daemon op TCP. Het is wel een root-shell op die machine; de pagina
|
|
20
|
+
* zegt dat erbij.
|
|
21
|
+
*/
|
|
22
|
+
/** Klein, en de busybox erin heeft `nsenter`. */
|
|
23
|
+
exports.HELPER_IMAGE = 'alpine:3';
|
|
24
|
+
/**
|
|
25
|
+
* Zodat de container in de lijst herkenbaar is als van ons. De oude naam van de app, en dat blijft
|
|
26
|
+
* zo: de opruiming zoekt hierop, en een hulpcontainer van vóór de nieuwe naam moet ze ook vinden.
|
|
27
|
+
*/
|
|
28
|
+
exports.HELPER_LABEL = 'docker-desktop-mx.role';
|
|
29
|
+
/**
|
|
30
|
+
* Wat er ná `nsenter` draait, dus al in het bestandssysteem van de host. Bash als die er is,
|
|
31
|
+
* anders sh; allebei als login-shell, zodat PATH en het profiel van root geladen worden.
|
|
32
|
+
*/
|
|
33
|
+
const HOST_COMMAND = 'cd ~ 2>/dev/null; if command -v bash >/dev/null 2>&1; then exec bash -l; else exec sh -l; fi';
|
|
34
|
+
/** Wat de terminal in zijn kopregel zet, in de taal van de app. */
|
|
35
|
+
function hostShellLabel() {
|
|
36
|
+
return (0, i18n_1.t)({ en: 'root on the Docker host', nl: 'root op de Docker-host', de: 'root auf dem Docker-Host', fr: 'root sur l’hôte Docker', es: 'root en el host de Docker' });
|
|
37
|
+
}
|
|
38
|
+
/** Welk app-proces de hulpcontainer startte. Is dat proces weg, dan is de container een wees. */
|
|
39
|
+
exports.OWNER_LABEL = 'docker-desktop-mx.pid';
|
|
40
|
+
/** De hulpcontainers van déze app-sessie. */
|
|
41
|
+
const live = new Set();
|
|
42
|
+
/**
|
|
43
|
+
* Of een hulpcontainer weg mag: van een proces dat niet meer bestaat. Een andere app die nog
|
|
44
|
+
* draait (de smoke test, of `npm run dev` naast de geïnstalleerde app) houdt zijn shell.
|
|
45
|
+
*/
|
|
46
|
+
function isOrphan(labels, ownPid, alive) {
|
|
47
|
+
const pid = Number(labels[exports.OWNER_LABEL]);
|
|
48
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return pid !== ownPid && !alive(pid);
|
|
52
|
+
}
|
|
53
|
+
function processAlive(pid) {
|
|
54
|
+
try {
|
|
55
|
+
process.kill(pid, 0);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
// EPERM: hij bestaat wel, maar is van een ander; alleen ESRCH betekent weg.
|
|
60
|
+
return err.code === 'EPERM';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** Zo lang wacht het afsluiten hooguit op het opruimen; een hangende daemon houdt de app niet vast. */
|
|
64
|
+
const QUIT_TIMEOUT_MS = 3000;
|
|
65
|
+
async function removeHelper(id) {
|
|
66
|
+
live.delete(id);
|
|
67
|
+
await (0, docker_client_1.getConnection)().docker.getContainer(id).remove({ force: true }).catch(() => undefined);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Ruimt hulpcontainers van een eerdere sessie op. Nodig omdat de shell níét vanzelf stopt als
|
|
71
|
+
* de app wegvalt: via de WSL-poortdoorgifte merkt de daemon niet dat de verbinding weg is, dus
|
|
72
|
+
* `StdinOnce` geeft geen EOF. Een crash of een geforceerd afsluiten laat hem dan draaien.
|
|
73
|
+
*/
|
|
74
|
+
async function sweepOrphans() {
|
|
75
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
76
|
+
const found = await docker.listContainers({ all: true, filters: { label: [`${exports.HELPER_LABEL}=host-shell`] } }).catch(() => []);
|
|
77
|
+
const orphans = found.filter((item) => !live.has(item.Id) && isOrphan(item.Labels, process.pid, processAlive));
|
|
78
|
+
await Promise.all(orphans.map((item) => removeHelper(item.Id)));
|
|
79
|
+
}
|
|
80
|
+
/** Of er hulpcontainers van deze sessie draaien; main wacht dan bij het afsluiten. */
|
|
81
|
+
function hasHostShells() {
|
|
82
|
+
return live.size > 0;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Verwijdert de hulpcontainers van deze sessie en wacht erop, met een grens. Voor bij het
|
|
86
|
+
* afsluiten: een `remove` die alleen wordt afgevuurd, haalt de daemon niet voordat het proces
|
|
87
|
+
* weg is.
|
|
88
|
+
*/
|
|
89
|
+
async function removeHostShells() {
|
|
90
|
+
const removals = Promise.all([...live].map((id) => removeHelper(id)));
|
|
91
|
+
await Promise.race([removals, new Promise((resolve) => setTimeout(resolve, QUIT_TIMEOUT_MS))]);
|
|
92
|
+
}
|
|
93
|
+
const dockerBackend = {
|
|
94
|
+
ensureImage: async () => {
|
|
95
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
96
|
+
await sweepOrphans();
|
|
97
|
+
try {
|
|
98
|
+
await docker.getImage(exports.HELPER_IMAGE).inspect();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Niet aanwezig; hieronder ophalen.
|
|
103
|
+
}
|
|
104
|
+
const stream = await docker.pull(exports.HELPER_IMAGE);
|
|
105
|
+
await new Promise((resolve, reject) => {
|
|
106
|
+
docker.modem.followProgress(stream, (err) => (err === null ? resolve() : reject(err)));
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
start: async (size) => {
|
|
110
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
111
|
+
const container = await docker.createContainer({
|
|
112
|
+
name: `docker-client-mx-host-shell-${Date.now().toString(36)}`,
|
|
113
|
+
Image: exports.HELPER_IMAGE,
|
|
114
|
+
Cmd: ['nsenter', '-t', '1', '-m', '-u', '-n', '-i', '--', 'sh', '-c', HOST_COMMAND],
|
|
115
|
+
Env: ['TERM=xterm-256color'],
|
|
116
|
+
Labels: { [exports.HELPER_LABEL]: 'host-shell', [exports.OWNER_LABEL]: String(process.pid) },
|
|
117
|
+
Tty: true,
|
|
118
|
+
OpenStdin: true,
|
|
119
|
+
// Sluit de verbinding netjes, dan krijgt de shell EOF op stdin en stopt hij. Via WSL komt
|
|
120
|
+
// dat niet altijd aan; daarvoor zijn removeHostShells en sweepOrphans.
|
|
121
|
+
StdinOnce: true,
|
|
122
|
+
AttachStdin: true,
|
|
123
|
+
AttachStdout: true,
|
|
124
|
+
AttachStderr: true,
|
|
125
|
+
HostConfig: {
|
|
126
|
+
Privileged: true,
|
|
127
|
+
PidMode: 'host',
|
|
128
|
+
NetworkMode: 'host',
|
|
129
|
+
AutoRemove: true,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
live.add(container.id);
|
|
133
|
+
const stop = () => {
|
|
134
|
+
void removeHelper(container.id);
|
|
135
|
+
};
|
|
136
|
+
try {
|
|
137
|
+
// Eerst aanhaken, dan starten: anders mis je de prompt die de shell meteen schrijft.
|
|
138
|
+
const stream = (await container.attach({
|
|
139
|
+
stream: true,
|
|
140
|
+
stdin: true,
|
|
141
|
+
stdout: true,
|
|
142
|
+
stderr: true,
|
|
143
|
+
hijack: true,
|
|
144
|
+
}));
|
|
145
|
+
await container.start();
|
|
146
|
+
await container.resize({ h: size.rows, w: size.cols });
|
|
147
|
+
return {
|
|
148
|
+
stream,
|
|
149
|
+
resize: (cols, rows) => {
|
|
150
|
+
// Na afsluiten bestaat de container niet meer; een 404 is dan geen fout.
|
|
151
|
+
void container.resize({ h: rows, w: cols }).catch(() => undefined);
|
|
152
|
+
},
|
|
153
|
+
stop,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
stop();
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
/** Opent een root-shell op de Docker-host via een hulpcontainer. */
|
|
163
|
+
async function openHostShell(size, onData, onEnd, backend = dockerBackend) {
|
|
164
|
+
try {
|
|
165
|
+
await backend.ensureImage();
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
problem: (0, i18n_1.t)({
|
|
171
|
+
en: `The helper image ${exports.HELPER_IMAGE} could not be pulled: ${err instanceof Error ? err.message : String(err)}`,
|
|
172
|
+
nl: `De hulpimage ${exports.HELPER_IMAGE} kon niet opgehaald worden: ${err instanceof Error ? err.message : String(err)}`,
|
|
173
|
+
de: `Das Hilfs-Image ${exports.HELPER_IMAGE} konnte nicht geholt werden: ${err instanceof Error ? err.message : String(err)}`,
|
|
174
|
+
fr: `L’image d’appoint ${exports.HELPER_IMAGE} n’a pas pu être téléchargée : ${err instanceof Error ? err.message : String(err)}`,
|
|
175
|
+
es: `No se ha podido descargar la image auxiliar ${exports.HELPER_IMAGE}: ${err instanceof Error ? err.message : String(err)}`,
|
|
176
|
+
}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
const { stream, resize, stop } = await backend.start(size);
|
|
181
|
+
return { ok: true, session: (0, exec_1.wrapSession)(hostShellLabel(), stream, resize, onData, onEnd, stop) };
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
return {
|
|
185
|
+
ok: false,
|
|
186
|
+
problem: (0, i18n_1.t)({
|
|
187
|
+
en: `The shell on the host could not be started: ${err instanceof Error ? err.message : String(err)}`,
|
|
188
|
+
nl: `De shell op de host kon niet gestart worden: ${err instanceof Error ? err.message : String(err)}`,
|
|
189
|
+
de: `Die Shell auf dem Host konnte nicht gestartet werden: ${err instanceof Error ? err.message : String(err)}`,
|
|
190
|
+
fr: `Le shell sur l’hôte n’a pas pu être démarré : ${err instanceof Error ? err.message : String(err)}`,
|
|
191
|
+
es: `No se ha podido iniciar el shell en el host: ${err instanceof Error ? err.message : String(err)}`,
|
|
192
|
+
}),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=host-shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-shell.js","sourceRoot":"","sources":["../electron/host-shell.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* De taal van de teksten die main zelf maakt: meldingen van Windows, het menu van het
|
|
4
|
+
* systeemvak, en de foutmeldingen die de renderer als toast of melding laat zien.
|
|
5
|
+
*
|
|
6
|
+
* Main heeft weinig teksten, en ze horen bij de code die ze maakt. Daarom geen woordenboek met
|
|
7
|
+
* sleutels zoals in de renderer, maar beide talen naast elkaar op de plek zelf:
|
|
8
|
+
*
|
|
9
|
+
* problem: t({ en: `Could not start ${distro}.`, nl: `Kon ${distro} niet starten.`, ... })
|
|
10
|
+
*
|
|
11
|
+
* Technische termen (container, image, volume, engine) blijven in alle talen Engels.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.DEFAULT_LANGUAGE = exports.LANGUAGES = void 0;
|
|
15
|
+
exports.language = language;
|
|
16
|
+
exports.isLanguage = isLanguage;
|
|
17
|
+
exports.setLanguage = setLanguage;
|
|
18
|
+
exports.onLanguageChange = onLanguageChange;
|
|
19
|
+
exports.t = t;
|
|
20
|
+
exports.locale = locale;
|
|
21
|
+
exports.LANGUAGES = ['en', 'nl', 'de', 'fr', 'es'];
|
|
22
|
+
/** Engels tot de instellingen iets anders zeggen. */
|
|
23
|
+
exports.DEFAULT_LANGUAGE = 'en';
|
|
24
|
+
let current = exports.DEFAULT_LANGUAGE;
|
|
25
|
+
const listeners = new Set();
|
|
26
|
+
/** De huidige taal. */
|
|
27
|
+
function language() {
|
|
28
|
+
return current;
|
|
29
|
+
}
|
|
30
|
+
/** Of een waarde uit een bestand een taal is die de app kent. */
|
|
31
|
+
function isLanguage(value) {
|
|
32
|
+
return typeof value === 'string' && exports.LANGUAGES.includes(value);
|
|
33
|
+
}
|
|
34
|
+
/** Zet de taal, en laat wie luistert (het menu van het systeemvak) zich opnieuw opbouwen. */
|
|
35
|
+
function setLanguage(next) {
|
|
36
|
+
if (next === current) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
current = next;
|
|
40
|
+
for (const listener of listeners) {
|
|
41
|
+
listener(next);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Geeft een functie terug die het luisteren weer stopt. */
|
|
45
|
+
function onLanguageChange(listener) {
|
|
46
|
+
listeners.add(listener);
|
|
47
|
+
return () => listeners.delete(listener);
|
|
48
|
+
}
|
|
49
|
+
/** De tekst in de huidige taal. Wordt bij elke aanroep gekozen, dus nooit in een constante bewaren. */
|
|
50
|
+
function t(text) {
|
|
51
|
+
return text[current];
|
|
52
|
+
}
|
|
53
|
+
const LOCALES = { en: 'en-GB', nl: 'nl-NL', de: 'de-DE', fr: 'fr-FR', es: 'es-ES' };
|
|
54
|
+
/** Voor datums en getallen: `toLocaleString(locale())`. */
|
|
55
|
+
function locale() {
|
|
56
|
+
return LOCALES[current];
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=i18n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../electron/i18n.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAeH,4BAGC;AAGD,gCAGC;AAGD,kCAWC;AAGD,4CAKC;AAGD,cAGC;AAKD,wBAGC;AAtDY,QAAA,SAAS,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAwC,CAAC;AAE/F,qDAAqD;AACxC,QAAA,gBAAgB,GAAa,IAAI,CAAC;AAE/C,IAAI,OAAO,GAAa,wBAAgB,CAAC;AACzC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgC,CAAC;AAE1D,uBAAuB;AACvB,SAAgB,QAAQ;IAEtB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,iEAAiE;AACjE,SAAgB,UAAU,CAAC,KAAc;IAEvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,iBAA+B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACvF,CAAC;AAED,6FAA6F;AAC7F,SAAgB,WAAW,CAAC,IAAc;IAExC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IAED,OAAO,GAAG,IAAI,CAAC;IAEf,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;AACH,CAAC;AAED,4DAA4D;AAC5D,SAAgB,gBAAgB,CAAC,QAAsC;IAErE,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAExB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,uGAAuG;AACvG,SAAgB,CAAC,CAAC,IAAwC;IAExD,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,OAAO,GAAuC,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;AAExH,2DAA2D;AAC3D,SAAgB,MAAM;IAEpB,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toInstruction = toInstruction;
|
|
4
|
+
exports.toLayers = toLayers;
|
|
5
|
+
exports.readImageHistory = readImageHistory;
|
|
6
|
+
const docker_client_1 = require("./docker-client");
|
|
7
|
+
/**
|
|
8
|
+
* De lagen van een image: wat elke laag kostte, en het commando dat hem maakte.
|
|
9
|
+
*
|
|
10
|
+
* Het antwoord op "waarom is deze image 1,2 GB". De volgorde is die van `docker history`:
|
|
11
|
+
* nieuwste bovenaan, dus de laatste regel van de Dockerfile eerst.
|
|
12
|
+
*
|
|
13
|
+
* Twee dingen die eruitzien als een fout maar het niet zijn. Lagen van een gepullde image
|
|
14
|
+
* hebben `<missing>` als id: de daemon bewaart alleen de bovenste los, de rest kent hij als
|
|
15
|
+
* onderdeel van het geheel. En een laag van nul bytes is geen lege laag maar een instructie
|
|
16
|
+
* die niets aan het bestandssysteem verandert, zoals `ENV` of `CMD`.
|
|
17
|
+
*/
|
|
18
|
+
/** Wat de daemon invult als hij een laag niet los bijhoudt. */
|
|
19
|
+
const MISSING = '<missing>';
|
|
20
|
+
/** Buildkit plakt dit achter elk commando. */
|
|
21
|
+
const BUILDKIT_SUFFIX = ' # buildkit';
|
|
22
|
+
/** De oude builder schreef metadata-instructies zo weg. */
|
|
23
|
+
const NOP_PREFIX = '/bin/sh -c #(nop) ';
|
|
24
|
+
/** En een gewone RUN zo. */
|
|
25
|
+
const SHELL_PREFIX = '/bin/sh -c ';
|
|
26
|
+
/**
|
|
27
|
+
* Het commando zoals het in de Dockerfile stond, voor zover dat terug te halen is.
|
|
28
|
+
*
|
|
29
|
+
* Buildkit schrijft `RUN /bin/sh -c apk add … # buildkit`, de oude builder
|
|
30
|
+
* `/bin/sh -c #(nop) ENV FOO=bar` voor metadata en `/bin/sh -c apk add …` voor een RUN. Die
|
|
31
|
+
* omhulsels zeggen niets; wat eronder zit is wat je zoekt.
|
|
32
|
+
*/
|
|
33
|
+
function toInstruction(createdBy) {
|
|
34
|
+
let text = (createdBy ?? '').trim();
|
|
35
|
+
if (text.endsWith(BUILDKIT_SUFFIX)) {
|
|
36
|
+
text = text.slice(0, -BUILDKIT_SUFFIX.length).trim();
|
|
37
|
+
}
|
|
38
|
+
if (text.startsWith(NOP_PREFIX)) {
|
|
39
|
+
return text.slice(NOP_PREFIX.length).trim();
|
|
40
|
+
}
|
|
41
|
+
// `RUN /bin/sh -c …` van buildkit: de shell ertussenuit, de RUN blijft staan.
|
|
42
|
+
if (text.startsWith(`RUN ${SHELL_PREFIX}`)) {
|
|
43
|
+
return `RUN ${text.slice(`RUN ${SHELL_PREFIX}`.length).trim()}`;
|
|
44
|
+
}
|
|
45
|
+
if (text.startsWith(SHELL_PREFIX)) {
|
|
46
|
+
return `RUN ${text.slice(SHELL_PREFIX.length).trim()}`;
|
|
47
|
+
}
|
|
48
|
+
return text;
|
|
49
|
+
}
|
|
50
|
+
/** De lagen zoals de renderer ze kent, in de volgorde die de daemon geeft. */
|
|
51
|
+
function toLayers(raw) {
|
|
52
|
+
return raw.map((layer) => ({
|
|
53
|
+
id: layer.Id === undefined || layer.Id === MISSING || layer.Id === '' ? null : layer.Id,
|
|
54
|
+
createdAt: layer.Created ?? 0,
|
|
55
|
+
instruction: toInstruction(layer.CreatedBy),
|
|
56
|
+
sizeBytes: layer.Size ?? 0,
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* De lagen van deze image, met twee getallen ernaast.
|
|
61
|
+
*
|
|
62
|
+
* De image erbij inspecteren kost een tweede aanroep, maar zonder dat getal zou de dialoog de
|
|
63
|
+
* opgetelde lagen tonen als de grootte van de image — en dat klopt niet bij de containerd-opslag,
|
|
64
|
+
* waar de lagen ingepakt gerapporteerd worden. Dan staat er 898 MB onder een lijst die in het
|
|
65
|
+
* scherm ernaast 1,3 GB heet.
|
|
66
|
+
*/
|
|
67
|
+
async function readImageHistory(reference) {
|
|
68
|
+
try {
|
|
69
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
70
|
+
const image = docker.getImage(reference);
|
|
71
|
+
const [raw, inspected] = await Promise.all([
|
|
72
|
+
image.history(),
|
|
73
|
+
image.inspect(),
|
|
74
|
+
]);
|
|
75
|
+
const layers = toLayers(raw ?? []);
|
|
76
|
+
return {
|
|
77
|
+
ok: true,
|
|
78
|
+
layers,
|
|
79
|
+
imageBytes: inspected.Size ?? 0,
|
|
80
|
+
layersBytes: layers.reduce((total, layer) => total + layer.sizeBytes, 0),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
return { ok: false, problem: (0, docker_client_1.readableError)(err, (0, docker_client_1.getConnection)().endpoint).message };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=image-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-history.js","sourceRoot":"","sources":["../electron/image-history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.splitReference = splitReference;
|
|
4
|
+
exports.toImageRows = toImageRows;
|
|
5
|
+
exports.countUsage = countUsage;
|
|
6
|
+
exports.listImages = listImages;
|
|
7
|
+
exports.removeImage = removeImage;
|
|
8
|
+
exports.pruneImages = pruneImages;
|
|
9
|
+
const docker_client_1 = require("./docker-client");
|
|
10
|
+
const i18n_1 = require("./i18n");
|
|
11
|
+
const UNTAGGED = '<none>:<none>';
|
|
12
|
+
const NONE = '<none>';
|
|
13
|
+
/**
|
|
14
|
+
* Splitst een referentie in repository en tag.
|
|
15
|
+
*
|
|
16
|
+
* Op de laatste dubbele punt ná de laatste slash, want een registry mag een poort hebben:
|
|
17
|
+
* in `localhost:5000/app:1.0` hoort `:5000` bij de host en `:1.0` is de tag. Splitsen op de
|
|
18
|
+
* eerste of op elke dubbele punt gaat daar mis.
|
|
19
|
+
*/
|
|
20
|
+
function splitReference(reference) {
|
|
21
|
+
const slash = reference.lastIndexOf('/');
|
|
22
|
+
const colon = reference.lastIndexOf(':');
|
|
23
|
+
if (colon > slash) {
|
|
24
|
+
return { repository: reference.slice(0, colon), tag: reference.slice(colon + 1) };
|
|
25
|
+
}
|
|
26
|
+
return { repository: reference, tag: 'latest' };
|
|
27
|
+
}
|
|
28
|
+
function shortIdOf(id) {
|
|
29
|
+
return id.replace(/^sha256:/, '').slice(0, 12);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Eén regel per tag, zoals `docker images` het ook doet: een image met twee tags staat er
|
|
33
|
+
* twee keer in, want je verwijdert een tag, niet de image eronder.
|
|
34
|
+
*
|
|
35
|
+
* `usage` telt hoeveel containers deze image gebruiken. Dat wordt geteld uit de containerlijst
|
|
36
|
+
* en niet uit het `Containers`-veld van de daemon: dat veld is `-1` als de daemon het niet
|
|
37
|
+
* uitgerekend heeft, en dan zou de kolom stilzwijgend nul tonen.
|
|
38
|
+
*/
|
|
39
|
+
function toImageRows(raw, usage) {
|
|
40
|
+
const tags = (raw.RepoTags ?? []).filter((tag) => tag !== UNTAGGED);
|
|
41
|
+
const base = {
|
|
42
|
+
id: raw.Id,
|
|
43
|
+
shortId: shortIdOf(raw.Id),
|
|
44
|
+
sizeBytes: raw.Size,
|
|
45
|
+
createdAt: raw.Created,
|
|
46
|
+
inUseBy: usage.get(raw.Id) ?? 0,
|
|
47
|
+
};
|
|
48
|
+
if (tags.length === 0) {
|
|
49
|
+
// Een image zonder tag verwijder je op zijn id; die vult daarom het referentieveld.
|
|
50
|
+
return [{ ...base, repository: NONE, tag: NONE, reference: raw.Id, dangling: true }];
|
|
51
|
+
}
|
|
52
|
+
return tags.map((tag) => ({ ...base, ...splitReference(tag), reference: tag, dangling: false }));
|
|
53
|
+
}
|
|
54
|
+
/** Telt per image-id hoeveel containers eraan hangen, draaiend of niet. */
|
|
55
|
+
function countUsage(containers) {
|
|
56
|
+
const usage = new Map();
|
|
57
|
+
for (const container of containers) {
|
|
58
|
+
usage.set(container.ImageID, (usage.get(container.ImageID) ?? 0) + 1);
|
|
59
|
+
}
|
|
60
|
+
return usage;
|
|
61
|
+
}
|
|
62
|
+
/** Alle images, met per regel hoeveel containers eraan hangen. */
|
|
63
|
+
async function listImages() {
|
|
64
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
65
|
+
const [images, containers] = await Promise.all([docker.listImages(), docker.listContainers({ all: true })]);
|
|
66
|
+
const usage = countUsage(containers);
|
|
67
|
+
return images
|
|
68
|
+
.flatMap((image) => toImageRows(image, usage))
|
|
69
|
+
.sort((a, b) => a.repository.localeCompare(b.repository) || a.tag.localeCompare(b.tag));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Verwijdert een tag, of de image zelf als dit de laatste tag was — dat is wat `docker rmi`
|
|
73
|
+
* ook doet. `force` is nodig zodra er nog containers aan hangen.
|
|
74
|
+
*/
|
|
75
|
+
async function removeImage(reference, force) {
|
|
76
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
77
|
+
try {
|
|
78
|
+
await docker.getImage(reference).remove({ force });
|
|
79
|
+
return { ok: true };
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
return { ok: false, problem: describeImageProblem(err, reference) };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Ruimt op. `all` is het verschil tussen `docker image prune` (alleen ongetagde images) en
|
|
87
|
+
* `docker image prune -a` (alles waar geen container aan hangt) — dat laatste gooit ook
|
|
88
|
+
* images weg die je bewust hebt gepulld, dus het is een eigen keuze in de UI.
|
|
89
|
+
*/
|
|
90
|
+
async function pruneImages(all) {
|
|
91
|
+
const { docker } = (0, docker_client_1.getConnection)();
|
|
92
|
+
try {
|
|
93
|
+
const raw = (await docker.pruneImages({
|
|
94
|
+
filters: JSON.stringify({ dangling: { [all ? 'false' : 'true']: true } }),
|
|
95
|
+
}));
|
|
96
|
+
return { ok: true, removed: raw.ImagesDeleted?.length ?? 0, reclaimedBytes: raw.SpaceReclaimed ?? 0 };
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
return { ok: false, problem: (0, i18n_1.t)({
|
|
100
|
+
en: `Prune failed: ${describeError(err)}`,
|
|
101
|
+
nl: `Opruimen mislukte: ${describeError(err)}`,
|
|
102
|
+
de: `Aufräumen fehlgeschlagen: ${describeError(err)}`,
|
|
103
|
+
fr: `Le nettoyage a échoué : ${describeError(err)}`,
|
|
104
|
+
es: `La limpieza ha fallado: ${describeError(err)}`,
|
|
105
|
+
}) };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function describeError(err) {
|
|
109
|
+
return err instanceof Error ? err.message : String(err);
|
|
110
|
+
}
|
|
111
|
+
function describeImageProblem(err, reference) {
|
|
112
|
+
const status = err?.statusCode;
|
|
113
|
+
if (status === 404) {
|
|
114
|
+
return (0, i18n_1.t)({
|
|
115
|
+
en: `Image '${reference}' no longer exists.`,
|
|
116
|
+
nl: `Image '${reference}' bestaat niet meer.`,
|
|
117
|
+
de: `Image '${reference}' existiert nicht mehr.`,
|
|
118
|
+
fr: `L’image '${reference}' n’existe plus.`,
|
|
119
|
+
es: `La image '${reference}' ya no existe.`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
// 409 komt als er nog containers aan hangen. De melding van de daemon noemt die containers
|
|
123
|
+
// niet bij naam, dus we zeggen er zelf bij wat je eraan kunt doen.
|
|
124
|
+
if (status === 409) {
|
|
125
|
+
return (0, i18n_1.t)({
|
|
126
|
+
en: `'${reference}' is still in use by one or more containers. Remove them first, or force it.`,
|
|
127
|
+
nl: `'${reference}' is nog in gebruik door een of meer containers. Verwijder die eerst, of forceer het.`,
|
|
128
|
+
de: `'${reference}' wird noch von einem oder mehreren Containern benutzt. Entfernen Sie die zuerst, oder erzwingen Sie es.`,
|
|
129
|
+
fr: `'${reference}' est encore utilisée par un ou plusieurs conteneurs. Supprimez-les d’abord, ou forcez l’opération.`,
|
|
130
|
+
es: `'${reference}' todavía la usan uno o varios contenedores. Elimínalos primero, o fuérzalo.`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return (0, i18n_1.t)({
|
|
134
|
+
en: `Removing '${reference}' failed: ${describeError(err)}`,
|
|
135
|
+
nl: `Verwijderen van '${reference}' mislukte: ${describeError(err)}`,
|
|
136
|
+
de: `Entfernen von '${reference}' fehlgeschlagen: ${describeError(err)}`,
|
|
137
|
+
fr: `La suppression de '${reference}' a échoué : ${describeError(err)}`,
|
|
138
|
+
es: `La eliminación de '${reference}' ha fallado: ${describeError(err)}`,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=images.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"images.js","sourceRoot":"","sources":["../electron/images.ts"],"names":[],"mappings":"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"}
|