docker-toolbelt 4.1.1 → 4.1.2

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.
@@ -1,3 +1,22 @@
1
+ - commits:
2
+ - subject: Export ImageNameParts interface
3
+ hash: ad473d4decb373fd8a02fec1d36b57d69c82ef4c
4
+ body: ""
5
+ footer:
6
+ Change-type: patch
7
+ change-type: patch
8
+ author: dfunckt
9
+ - subject: Delete docker-toolbelt.ts
10
+ hash: 53bbb45d81fd2e331d8a9e973b5d5d8f55f27a57
11
+ body: |
12
+ For some reason it was added back by #47
13
+ footer:
14
+ Change-type: patch
15
+ change-type: patch
16
+ author: dfunckt
17
+ version: 4.1.2
18
+ title: ""
19
+ date: 2023-06-23T11:56:11.657Z
1
20
  - commits:
2
21
  - subject: Use Flowzone for CI
3
22
  hash: 24d36412f9ca6dbb141f92a185c8ae9ecb198211
@@ -8,7 +27,7 @@
8
27
  author: Akis Kesoglou
9
28
  version: 4.1.1
10
29
  title: ""
11
- date: 2023-06-23T11:46:28.434Z
30
+ date: 2023-06-23T11:54:41.131Z
12
31
  - commits:
13
32
  - subject: Export ImageNameParts interface
14
33
  hash: e48fe88a7f5cd57696532b8f081f182704876e45
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file
4
4
  automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
5
5
  This project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
+ # v4.1.2
8
+ ## (2023-06-23)
9
+
10
+ * Export ImageNameParts interface [dfunckt]
11
+ * Delete docker-toolbelt.ts [dfunckt]
12
+
7
13
  # v4.1.1
8
14
  ## (2023-06-23)
9
15
 
package/build/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as Docker from 'dockerode';
2
- interface ImageNameParts {
2
+ export interface ImageNameParts {
3
3
  registry: string;
4
4
  imageName: string;
5
5
  tagName: string;
@@ -15,4 +15,3 @@ export declare class DockerToolbelt extends Docker {
15
15
  compileRegistryAndName({ registry, imageName, tagName, digest, }: ImageNameParts): Promise<string>;
16
16
  normaliseImageName(image: string): Promise<string>;
17
17
  }
18
- export {};
package/lib/index.ts CHANGED
@@ -12,7 +12,7 @@ const execAsync = promisify(exec);
12
12
 
13
13
  const MIN_PAGE_SIZE = 4096;
14
14
 
15
- interface ImageNameParts {
15
+ export interface ImageNameParts {
16
16
  registry: string;
17
17
  imageName: string;
18
18
  tagName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docker-toolbelt",
3
- "version": "4.1.1",
3
+ "version": "4.1.2",
4
4
  "description": "Some tools for docker",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -52,6 +52,6 @@
52
52
  "typescript": "^4.4.4"
53
53
  },
54
54
  "versionist": {
55
- "publishedAt": "2023-06-23T11:46:28.562Z"
55
+ "publishedAt": "2023-06-23T11:56:11.804Z"
56
56
  }
57
57
  }
@@ -1,17 +0,0 @@
1
- import * as Docker from 'dockerode';
2
- export interface ImageNameParts {
3
- registry: string;
4
- imageName: string;
5
- tagName: string;
6
- digest: string;
7
- }
8
- export declare class DockerToolbelt extends Docker {
9
- imageRootDir(image: string): Promise<string>;
10
- withImageRootDirMounted<T>(image: string, fn: (target: string) => T): Promise<T>;
11
- diffPaths(image: string): Promise<string[]>;
12
- createEmptyImage(imageConfig: any): Promise<string>;
13
- createDeltaAsync(src: any, dest: any, onProgress: (arg0: any) => void): Promise<string>;
14
- getRegistryAndName(image: string): ImageNameParts;
15
- compileRegistryAndName({ registry, imageName, tagName, digest, }: ImageNameParts): Promise<string>;
16
- normaliseImageName(image: string): Promise<string>;
17
- }
@@ -1,371 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.DockerToolbelt = void 0;
13
- const crypto = require("crypto");
14
- const util_1 = require("util");
15
- const Docker = require("dockerode");
16
- const semver = require("balena-semver");
17
- const tar = require("tar-stream");
18
- const es = require("event-stream");
19
- const fs_1 = require("fs");
20
- const path = require("path");
21
- const randomstring = require("randomstring");
22
- const child_process_1 = require("child_process");
23
- const execAsync = (0, util_1.promisify)(child_process_1.exec);
24
- const MIN_PAGE_SIZE = 4096;
25
- const promiseFromCallback = (fn) => {
26
- return new Promise((resolve, reject) => {
27
- fn((err, data) => {
28
- if (err) {
29
- return reject(err);
30
- }
31
- resolve(data);
32
- });
33
- });
34
- };
35
- const sha256sum = (data) => {
36
- const hash = crypto.createHash('sha256');
37
- hash.update(data);
38
- return hash.digest('hex');
39
- };
40
- const getDigest = (data) => 'sha256:' + sha256sum(data);
41
- const createChainId = (diffIds) => createChainIdFromParent('', diffIds);
42
- const getAllChainIds = function (diffIds) {
43
- const chainIds = [diffIds[0]];
44
- for (let i = 0, end = diffIds.length - 1, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) {
45
- chainIds.push(createChainIdFromParent(chainIds[i], [diffIds[i + 1]]));
46
- }
47
- return chainIds;
48
- };
49
- const createChainIdFromParent = (parent, dgsts) => {
50
- if (dgsts.length === 0) {
51
- return parent;
52
- }
53
- if (parent === '') {
54
- return createChainIdFromParent(dgsts[0], dgsts.slice(1));
55
- }
56
- const dgst = getDigest(parent + ' ' + dgsts[0]);
57
- return createChainIdFromParent(dgst, dgsts.slice(1));
58
- };
59
- const getDiffIds = (dkroot, driver, imageId) => __awaiter(void 0, void 0, void 0, function* () {
60
- const [hashType, hash] = Array.from(imageId.split(':'));
61
- const content = yield fs_1.promises.readFile(path.join(dkroot, `image/${driver}/imagedb/content`, hashType, hash));
62
- return JSON.parse(content.toString()).rootfs.diff_ids;
63
- });
64
- const getCacheId = function (dkroot, driver, layerId) {
65
- return __awaiter(this, void 0, void 0, function* () {
66
- const [hashType, hash] = Array.from(layerId.split(':'));
67
- const cacheIdPath = path.join(dkroot, `image/${driver}/layerdb`, hashType, hash, 'cache-id');
68
- const content = yield fs_1.promises.readFile(cacheIdPath, { encoding: 'utf8' });
69
- return content.toString();
70
- });
71
- };
72
- const getRandomFileName = (imageId) => `tmp-${imageId.split(':')[1]}-${randomstring.generate(8)}`;
73
- const usesContentAddressableFormat = (version) => !(semver.valid(version) && semver.lt(version, '1.10.0'));
74
- const pathPrefixRemover = (prefix) => (value) => {
75
- const slice = value.substr(prefix.length);
76
- if (`${prefix}${slice}` === value) {
77
- return slice;
78
- }
79
- else {
80
- return value;
81
- }
82
- };
83
- const withOverlay2Mount = (fsRoot, target, lowers, diffDir, workDir, fn) => __awaiter(void 0, void 0, void 0, function* () {
84
- if (!lowers) {
85
- return fn(diffDir);
86
- }
87
- try {
88
- yield fs_1.promises.mkdir(target);
89
- }
90
- catch (err) {
91
- if (err.code !== 'EEXIST') {
92
- throw err;
93
- }
94
- }
95
- const options = `lowerdir=${lowers},upperdir=${diffDir},workdir=${workDir}`;
96
- let parts = [];
97
- if (options.length < MIN_PAGE_SIZE) {
98
- parts = [undefined, lowers, diffDir, workDir];
99
- }
100
- else {
101
- const makeRelative = pathPrefixRemover(path.join(fsRoot, path.sep));
102
- const results = yield Promise.all(lowers.split(':').map(function (lower) {
103
- return __awaiter(this, void 0, void 0, function* () {
104
- const layerId = makeRelative(lower).replace(/\/diff$/, '');
105
- const linkPath = path.join(fsRoot, layerId, 'link');
106
- const link = yield fs_1.promises.readFile(linkPath);
107
- return path.join('l', link.toString());
108
- });
109
- }));
110
- parts = [
111
- fsRoot,
112
- results.join(':'),
113
- makeRelative(diffDir),
114
- makeRelative(workDir),
115
- ];
116
- }
117
- const [mountFsRoot, mountLowers, mountDiffDir, mountWorkDir] = parts;
118
- const mountOptions = `lowerdir=${mountLowers},upperdir=${mountDiffDir},workdir=${mountWorkDir}`;
119
- yield execAsync(`mount -t overlay overlay -o '${mountOptions}' ${target}`, {
120
- cwd: mountFsRoot,
121
- });
122
- try {
123
- return yield fn(target);
124
- }
125
- finally {
126
- try {
127
- yield execAsync(`umount ${target}`);
128
- yield fs_1.promises.rmdir(target);
129
- }
130
- catch (err) {
131
- console.error('Failed to clean up after mounting overlay2', err, err.stack);
132
- }
133
- }
134
- });
135
- const withAufsMount = (target, layerDiffPaths, fn) => __awaiter(void 0, void 0, void 0, function* () {
136
- try {
137
- yield fs_1.promises.mkdir(target);
138
- }
139
- catch (err) {
140
- if (err.code !== 'EEXIST') {
141
- throw err;
142
- }
143
- }
144
- let options = 'noxino,ro,br=';
145
- let remainingBytes = MIN_PAGE_SIZE - options.length;
146
- layerDiffPaths = layerDiffPaths.map((result) => `${result}=ro+wh`);
147
- let appendFromIndex = layerDiffPaths.findIndex(function (result) {
148
- remainingBytes -= result.length + 1;
149
- return remainingBytes < -1;
150
- });
151
- if (appendFromIndex === -1) {
152
- appendFromIndex = layerDiffPaths.length;
153
- }
154
- const appendLayerPaths = layerDiffPaths.slice(appendFromIndex);
155
- options += layerDiffPaths.slice(0, appendFromIndex).join(':');
156
- yield execAsync(`mount -t aufs -o '${options}' none ${target}`);
157
- for (const layerPath of appendLayerPaths) {
158
- yield execAsync(`mount -t aufs -o 'remount,append:${layerPath}' none ${layerPath}`);
159
- }
160
- try {
161
- return fn(target);
162
- }
163
- finally {
164
- try {
165
- yield execAsync(`umount ${target}`);
166
- yield fs_1.promises.rmdir(target);
167
- }
168
- catch (err) {
169
- console.error('Failed to clean up after mounting aufs', err, err.stack);
170
- }
171
- }
172
- });
173
- class DockerToolbelt extends Docker {
174
- imageRootDir(image) {
175
- return __awaiter(this, void 0, void 0, function* () {
176
- const [dockerInfo, { Version: dockerVersion }, imageInfo] = yield Promise.all([
177
- this.info(),
178
- this.version(),
179
- this.getImage(image).inspect(),
180
- ]);
181
- const dkroot = dockerInfo.DockerRootDir;
182
- const imageId = imageInfo.Id;
183
- if (!usesContentAddressableFormat(dockerVersion)) {
184
- return imageId;
185
- }
186
- const diffIds = yield getDiffIds(dkroot, dockerInfo.Driver, imageId);
187
- const layerId = createChainId(diffIds);
188
- const destId = yield getCacheId(dkroot, dockerInfo.Driver, layerId);
189
- switch (dockerInfo.Driver) {
190
- case 'btrfs':
191
- return path.join(dkroot, 'btrfs/subvolumes', destId);
192
- case 'overlay':
193
- return imageInfo.GraphDriver.Data.RootDir;
194
- case 'overlay2':
195
- return imageInfo.GraphDriver.Data.UpperDir;
196
- case 'vfs':
197
- return path.join(dkroot, 'vfs/dir', destId);
198
- case 'aufs':
199
- return path.join(dkroot, 'aufs/diff', destId);
200
- default:
201
- throw new Error(`Unsupported driver: ${dockerInfo.Driver}/`);
202
- }
203
- });
204
- }
205
- withImageRootDirMounted(image, fn) {
206
- return __awaiter(this, void 0, void 0, function* () {
207
- const [dockerInfo, imageInfo] = yield Promise.all([
208
- this.info(),
209
- this.getImage(image).inspect(),
210
- ]);
211
- const driver = dockerInfo.Driver;
212
- const dkroot = dockerInfo.DockerRootDir;
213
- const imageId = imageInfo.Id;
214
- if (driver === 'aufs') {
215
- const layerDiffPaths = yield this.diffPaths(image);
216
- const mountDir = path.join(dkroot, 'aufs/mnt', getRandomFileName(imageId));
217
- return withAufsMount(mountDir, layerDiffPaths, fn);
218
- }
219
- else if (driver === 'overlay2') {
220
- const rootDir = path.join(dkroot, 'overlay2');
221
- const mountDir = path.join(rootDir, getRandomFileName(imageId));
222
- const { LowerDir, UpperDir, WorkDir } = imageInfo.GraphDriver.Data;
223
- return withOverlay2Mount(rootDir, mountDir, LowerDir, UpperDir, WorkDir, fn);
224
- }
225
- else {
226
- const rootDir = yield this.imageRootDir(image);
227
- return fn(rootDir);
228
- }
229
- });
230
- }
231
- diffPaths(image) {
232
- return __awaiter(this, void 0, void 0, function* () {
233
- const [dockerInfo, { Version: dockerVersion }, imageInfo] = yield Promise.all([
234
- this.info(),
235
- this.version(),
236
- this.getImage(image).inspect(),
237
- ]);
238
- const driver = dockerInfo.Driver;
239
- if (!(driver === 'aufs' || driver === 'overlay2')) {
240
- throw new Error('diffPaths can only be used on aufs and overlay2');
241
- }
242
- const dkroot = dockerInfo.DockerRootDir;
243
- const imageId = imageInfo.Id;
244
- const ids = yield getDiffIds(dkroot, driver, imageId).then(function (diffIds) {
245
- if (!usesContentAddressableFormat(dockerVersion)) {
246
- return diffIds;
247
- }
248
- return Promise.all(getAllChainIds(diffIds).map((layerId) => __awaiter(this, void 0, void 0, function* () { return getCacheId(dkroot, driver, layerId); })));
249
- });
250
- return ids.reverse().map(function (layerId) {
251
- return driver === 'aufs'
252
- ? path.join(dkroot, 'aufs/diff', layerId)
253
- : path.join(dkroot, 'overlay2', layerId, 'diff');
254
- });
255
- });
256
- }
257
- createEmptyImage(imageConfig) {
258
- return __awaiter(this, void 0, void 0, function* () {
259
- const manifest = [
260
- {
261
- Config: 'config.json',
262
- RepoTags: null,
263
- Layers: ['0000/layer.tar'],
264
- },
265
- ];
266
- const layer = tar.pack();
267
- layer.entry({ name: 'seed' }, String(Date.now() + Math.random()));
268
- layer.finalize();
269
- const buf = yield promiseFromCallback((callback) => layer.pipe(es.wait(callback)));
270
- const now = new Date().toISOString();
271
- const config = {
272
- config: imageConfig,
273
- created: now,
274
- rootfs: {
275
- type: 'layers',
276
- diff_ids: [getDigest(buf)],
277
- },
278
- };
279
- const imageId = sha256sum(JSON.stringify(config));
280
- const layerConfig = {
281
- id: imageId,
282
- created: now,
283
- config: imageConfig,
284
- };
285
- const image = tar.pack();
286
- image.entry({ name: 'manifest.json' }, JSON.stringify(manifest));
287
- image.entry({ name: 'config.json' }, JSON.stringify(config));
288
- image.entry({ name: '0000/VERSION' }, '1.0');
289
- image.entry({ name: '0000/json' }, JSON.stringify(layerConfig));
290
- image.entry({ name: '0000/layer.tar' }, buf);
291
- image.finalize();
292
- const stream = yield this.loadImage(image);
293
- yield promiseFromCallback((callback) => stream.pipe(es.wait(callback)));
294
- return imageId;
295
- });
296
- }
297
- createDeltaAsync(src, dest, onProgress) {
298
- return __awaiter(this, void 0, void 0, function* () {
299
- const optsf = {
300
- path: '/images/delta?',
301
- method: 'POST',
302
- options: { src, dest },
303
- isStream: true,
304
- statusCodes: {
305
- 200: true,
306
- 404: 'no such image',
307
- 500: 'server error',
308
- },
309
- };
310
- const stream = yield promiseFromCallback((cb) => {
311
- return this.modem.dial(optsf, cb);
312
- });
313
- const id = yield promiseFromCallback((cb) => {
314
- return this.modem.followProgress(stream, cb, function (e) {
315
- if (typeof onProgress === 'function') {
316
- onProgress(e);
317
- }
318
- const match = /^Created delta: (sha256:\w+)$/.exec(e.status);
319
- if (match) {
320
- return cb(null, match[1]);
321
- }
322
- });
323
- });
324
- return id;
325
- });
326
- }
327
- getRegistryAndName(image) {
328
- const match = image.match(/^(?:(localhost|.*?[.:].*?)\/)?(.+?)(?::(.*?))?(?:@(.*?))?$/);
329
- if (match == null) {
330
- throw new Error(`Could not parse the image: ${image}`);
331
- }
332
- const registry = match[match.length - 4];
333
- const imageName = match[match.length - 3];
334
- let tagName = match[match.length - 2];
335
- const digest = match[match.length - 1];
336
- if (digest == null && tagName == null) {
337
- tagName = 'latest';
338
- }
339
- const digestMatch = digest != null
340
- ? digest.match(/^[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*:[0-9a-f-A-F]{32,}$/)
341
- : undefined;
342
- if (!imageName || (digest && !digestMatch)) {
343
- throw new Error('Invalid image name, expected [domain.tld/]repo/image[:tag][@digest] format');
344
- }
345
- return { registry, imageName, tagName, digest };
346
- }
347
- compileRegistryAndName({ registry = '', imageName, tagName = '', digest, }) {
348
- return __awaiter(this, void 0, void 0, function* () {
349
- if (registry !== '') {
350
- registry += '/';
351
- }
352
- if (digest == null) {
353
- if (tagName === '') {
354
- tagName = 'latest';
355
- }
356
- return `${registry}${imageName}:${tagName}`;
357
- }
358
- else {
359
- return `${registry}${imageName}@${digest}`;
360
- }
361
- });
362
- }
363
- normaliseImageName(image) {
364
- return __awaiter(this, void 0, void 0, function* () {
365
- const result = yield this.getRegistryAndName(image);
366
- return this.compileRegistryAndName(result);
367
- });
368
- }
369
- }
370
- exports.DockerToolbelt = DockerToolbelt;
371
- //# sourceMappingURL=docker-toolbelt.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"docker-toolbelt.js","sourceRoot":"","sources":["../lib/docker-toolbelt.ts"],"names":[],"mappings":"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"}
@@ -1,562 +0,0 @@
1
- import * as crypto from 'crypto';
2
- import { promisify } from 'util';
3
- import * as Docker from 'dockerode';
4
- import * as semver from 'balena-semver';
5
- import * as tar from 'tar-stream';
6
- import * as es from 'event-stream';
7
- import { promises as fs } from 'fs';
8
- import * as path from 'path';
9
- import * as randomstring from 'randomstring';
10
- import { exec } from 'child_process';
11
- const execAsync = promisify(exec);
12
-
13
- const MIN_PAGE_SIZE = 4096;
14
-
15
- export interface ImageNameParts {
16
- registry: string;
17
- imageName: string;
18
- tagName: string;
19
- digest: string;
20
- }
21
-
22
- const promiseFromCallback = <T>(
23
- fn: (callback: (err: any, data: T) => any) => any,
24
- ): Promise<T> => {
25
- return new Promise((resolve, reject) => {
26
- fn((err, data) => {
27
- if (err) {
28
- return reject(err);
29
- }
30
- resolve(data);
31
- });
32
- });
33
- };
34
-
35
- const sha256sum = (data: string): string => {
36
- const hash = crypto.createHash('sha256');
37
- hash.update(data);
38
- return hash.digest('hex');
39
- };
40
-
41
- const getDigest = (data: string): string => 'sha256:' + sha256sum(data);
42
-
43
- // Function adapted to JavaScript from
44
- // https://github.com/docker/docker/blob/v1.10.3/layer/layer.go#L223-L226
45
- const createChainId = (diffIds: string[]): string =>
46
- createChainIdFromParent('', diffIds);
47
-
48
- const getAllChainIds = function (diffIds: string[]): string[] {
49
- const chainIds = [diffIds[0]];
50
- for (
51
- let i = 0, end = diffIds.length - 1, asc = 0 <= end;
52
- asc ? i < end : i > end;
53
- asc ? i++ : i--
54
- ) {
55
- chainIds.push(createChainIdFromParent(chainIds[i], [diffIds[i + 1]]));
56
- }
57
- return chainIds;
58
- };
59
-
60
- // Function adapted to JavaScript from
61
- // https://github.com/docker/docker/blob/v1.10.3/layer/layer.go#L223-L226
62
- const createChainIdFromParent = (parent: string, dgsts: string[]): string => {
63
- if (dgsts.length === 0) {
64
- return parent;
65
- }
66
-
67
- if (parent === '') {
68
- return createChainIdFromParent(dgsts[0], dgsts.slice(1));
69
- }
70
-
71
- // H = "H(n-1) SHA256(n)"
72
- const dgst = getDigest(parent + ' ' + dgsts[0]);
73
-
74
- return createChainIdFromParent(dgst, dgsts.slice(1));
75
- };
76
-
77
- const getDiffIds = async (
78
- dkroot: string,
79
- driver: string,
80
- imageId: string,
81
- ): Promise<string[]> => {
82
- const [hashType, hash] = Array.from(imageId.split(':'));
83
- const content = await fs.readFile(
84
- path.join(dkroot, `image/${driver}/imagedb/content`, hashType, hash),
85
- );
86
- return JSON.parse(content.toString()).rootfs.diff_ids;
87
- };
88
-
89
- const getCacheId = async function (
90
- dkroot: string,
91
- driver: string,
92
- layerId: string,
93
- ): Promise<string> {
94
- const [hashType, hash] = Array.from(layerId.split(':'));
95
- const cacheIdPath = path.join(
96
- dkroot,
97
- `image/${driver}/layerdb`,
98
- hashType,
99
- hash,
100
- 'cache-id',
101
- );
102
- // Resolves with 'rootId'
103
- const content = await fs.readFile(cacheIdPath, { encoding: 'utf8' });
104
- return content.toString();
105
- };
106
-
107
- const getRandomFileName = (imageId: string): string =>
108
- `tmp-${imageId.split(':')[1]}-${randomstring.generate(8)}`;
109
-
110
- // Check if the docker version is a release after 1.10.0, or if its one of the fun
111
- // new non-semver versions, which we incidentally know all appeared after 1.10.0
112
- // Docker version 1.10.0 changes the way images are stored on disk and referenced
113
- // If the docker version supports the new "content-addressable" layer format, this function returns true
114
- const usesContentAddressableFormat = (version: string): boolean =>
115
- !(semver.valid(version) && semver.lt(version, '1.10.0'));
116
-
117
- const pathPrefixRemover =
118
- (prefix: string): ((value: string) => string) =>
119
- (value: string): string => {
120
- const slice = value.substr(prefix.length);
121
- // return original if path doesn't start with given prefix
122
- if (`${prefix}${slice}` === value) {
123
- return slice;
124
- } else {
125
- return value;
126
- }
127
- };
128
-
129
- // This function creates an overlay2 mount using the disposer pattern,
130
- // calling the provided function before finally cleaning up the mount
131
- // example:
132
- // await withOverlay2Mount('/abc', '/def', '/ghi', '/jkl', (mountDir) => {
133
- // // ...do something with the mount
134
- // })
135
- const withOverlay2Mount = async <T>(
136
- fsRoot: string,
137
- target: string,
138
- lowers: string,
139
- diffDir: string,
140
- workDir: string,
141
- fn: (mountDir: string) => T,
142
- ): Promise<T> => {
143
- // If no lower, just return
144
- if (!lowers) {
145
- return fn(diffDir);
146
- }
147
-
148
- try {
149
- await fs.mkdir(target);
150
- } catch (err: any) {
151
- if (err.code !== 'EEXIST') {
152
- throw err;
153
- }
154
- }
155
-
156
- const options = `lowerdir=${lowers},upperdir=${diffDir},workdir=${workDir}`;
157
-
158
- let parts: Array<string | undefined> = [];
159
- if (options.length < MIN_PAGE_SIZE) {
160
- parts = [undefined, lowers, diffDir, workDir];
161
- } else {
162
- // Use relative paths when the mount data has exceeded the page size.
163
- // The mount syscall fails if the mount data cannot fit within a page and
164
- // relative links make the mount data much smaller.
165
- const makeRelative = pathPrefixRemover(path.join(fsRoot, path.sep));
166
- const results = await Promise.all(
167
- lowers.split(':').map(async function (lower) {
168
- // Read the layer's "link" file which contains its shortened layer identifier.
169
- // Then replace the layer's lowerdir entry with its shortened alias.
170
- // See: https://docs.docker.com/engine/userguide/storagedriver/overlayfs-driver/#image-and-container-layers-on-disk
171
- const layerId = makeRelative(lower).replace(/\/diff$/, '');
172
- const linkPath = path.join(fsRoot, layerId, 'link');
173
- const link = await fs.readFile(linkPath);
174
- return path.join('l', link.toString());
175
- }),
176
- );
177
- parts = [
178
- fsRoot,
179
- results.join(':'),
180
- makeRelative(diffDir),
181
- makeRelative(workDir),
182
- ];
183
- }
184
-
185
- const [mountFsRoot, mountLowers, mountDiffDir, mountWorkDir] = parts;
186
- const mountOptions = `lowerdir=${mountLowers},upperdir=${mountDiffDir},workdir=${mountWorkDir}`;
187
- await execAsync(`mount -t overlay overlay -o '${mountOptions}' ${target}`, {
188
- cwd: mountFsRoot,
189
- });
190
-
191
- // Execute the provided function with the target as the first argument,
192
- // and then finally clean up the mount
193
- try {
194
- return await fn(target);
195
- } finally {
196
- try {
197
- await execAsync(`umount ${target}`);
198
- await fs.rmdir(target);
199
- } catch (err: any) {
200
- // We don't want to crash the node process if something failed here...
201
- console.error(
202
- 'Failed to clean up after mounting overlay2',
203
- err,
204
- err.stack,
205
- );
206
- }
207
- }
208
- };
209
-
210
- // This function creates an aufs mount using the disposer pattern,
211
- // calling the provided function before finally cleaning up the mount
212
- // example:
213
- // await withAufsMount('/abc/def', [ '/tmp/1',/tmp/2' ], (mountDir) => {
214
- // // ...do something with the mount
215
- // })
216
- const withAufsMount = async <T>(
217
- target: string,
218
- layerDiffPaths: string[], // We try to create the target directory.
219
- fn: (target: string) => T,
220
- ): Promise<T> => {
221
- // If it exists, it's *probably* from a previous run of this same function,
222
- // and the mount will fail if the directory is not empty or something's already mounted there.
223
- try {
224
- await fs.mkdir(target);
225
- } catch (err: any) {
226
- if (err.code !== 'EEXIST') {
227
- throw err;
228
- }
229
- }
230
-
231
- let options = 'noxino,ro,br=';
232
- let remainingBytes = MIN_PAGE_SIZE - options.length;
233
- layerDiffPaths = layerDiffPaths.map((result: string) => `${result}=ro+wh`);
234
- let appendFromIndex = layerDiffPaths.findIndex(function (
235
- result: string | any[],
236
- ) {
237
- remainingBytes -= result.length + 1;
238
- // < -1 because if this is the last entry we won't actually add the comma
239
- return remainingBytes < -1;
240
- });
241
- if (appendFromIndex === -1) {
242
- appendFromIndex = layerDiffPaths.length;
243
- }
244
- const appendLayerPaths = layerDiffPaths.slice(appendFromIndex);
245
- options += layerDiffPaths.slice(0, appendFromIndex).join(':');
246
-
247
- await execAsync(`mount -t aufs -o '${options}' none ${target}`);
248
- for (const layerPath of appendLayerPaths) {
249
- await execAsync(
250
- `mount -t aufs -o 'remount,append:${layerPath}' none ${layerPath}`,
251
- );
252
- }
253
-
254
- // Execute the provided function with the target as the first argument,
255
- // and then finally clean up the mount
256
- try {
257
- return fn(target);
258
- } finally {
259
- try {
260
- await execAsync(`umount ${target}`);
261
- await fs.rmdir(target);
262
- } catch (err: any) {
263
- // We don't want to crash the node process if something failed here...
264
- console.error('Failed to clean up after mounting aufs', err, err.stack);
265
- }
266
- }
267
- };
268
-
269
- export class DockerToolbelt extends Docker {
270
- // Gets an string `image` as input and returns a promise that
271
- // resolves to the absolute path of the root directory for that image
272
- //
273
- // Note: in aufs, the path corresponds to the directory for only
274
- // the specific layer's fs.
275
- async imageRootDir(image: string): Promise<string> {
276
- const [dockerInfo, { Version: dockerVersion }, imageInfo] =
277
- await Promise.all([
278
- this.info(),
279
- this.version(),
280
- this.getImage(image).inspect(),
281
- ]);
282
-
283
- const dkroot = dockerInfo.DockerRootDir;
284
-
285
- const imageId = imageInfo.Id;
286
-
287
- if (!usesContentAddressableFormat(dockerVersion)) {
288
- return imageId;
289
- }
290
-
291
- const diffIds = await getDiffIds(dkroot, dockerInfo.Driver, imageId);
292
- const layerId = createChainId(diffIds);
293
- const destId = await getCacheId(dkroot, dockerInfo.Driver, layerId);
294
-
295
- switch (dockerInfo.Driver) {
296
- case 'btrfs':
297
- return path.join(dkroot, 'btrfs/subvolumes', destId);
298
- case 'overlay':
299
- // TODO: fix any typing
300
- return (imageInfo.GraphDriver.Data as any).RootDir;
301
- case 'overlay2':
302
- // TODO: fix any typing
303
- return (imageInfo.GraphDriver.Data as any).UpperDir;
304
- case 'vfs':
305
- return path.join(dkroot, 'vfs/dir', destId);
306
- case 'aufs':
307
- return path.join(dkroot, 'aufs/diff', destId);
308
- default:
309
- throw new Error(`Unsupported driver: ${dockerInfo.Driver}/`);
310
- }
311
- }
312
-
313
- // Same as imageRootDir, but provides the full mounted rootfs for AUFS and overlay2,
314
- // and has a disposer to unmount.
315
- async withImageRootDirMounted<T>(
316
- image: string,
317
- fn: (target: string) => T,
318
- ): Promise<T> {
319
- const [dockerInfo, imageInfo] = await Promise.all([
320
- this.info(),
321
- this.getImage(image).inspect(),
322
- ]);
323
-
324
- const driver = dockerInfo.Driver;
325
- const dkroot = dockerInfo.DockerRootDir;
326
- const imageId = imageInfo.Id;
327
- // We add a random string to the path to avoid conflicts between several calls to this function
328
- if (driver === 'aufs') {
329
- const layerDiffPaths = await this.diffPaths(image);
330
- const mountDir = path.join(
331
- dkroot,
332
- 'aufs/mnt',
333
- getRandomFileName(imageId),
334
- );
335
- return withAufsMount<T>(mountDir, layerDiffPaths, fn);
336
- } else if (driver === 'overlay2') {
337
- const rootDir = path.join(dkroot, 'overlay2');
338
- const mountDir = path.join(rootDir, getRandomFileName(imageId));
339
- // TODO: fix this any typing
340
- const { LowerDir, UpperDir, WorkDir } = imageInfo.GraphDriver.Data as any;
341
- return withOverlay2Mount<T>(
342
- rootDir,
343
- mountDir,
344
- LowerDir,
345
- UpperDir,
346
- WorkDir,
347
- fn,
348
- );
349
- } else {
350
- const rootDir = await this.imageRootDir(image);
351
- return fn(rootDir);
352
- }
353
- }
354
-
355
- // Only for aufs and overlay2: get the diff paths for each layer in the image.
356
- // Ordered from latest to parent.
357
- async diffPaths(image: string): Promise<string[]> {
358
- const [dockerInfo, { Version: dockerVersion }, imageInfo] =
359
- await Promise.all([
360
- this.info(),
361
- this.version(),
362
- this.getImage(image).inspect(),
363
- ]);
364
-
365
- const driver = dockerInfo.Driver;
366
- if (!(driver === 'aufs' || driver === 'overlay2')) {
367
- throw new Error('diffPaths can only be used on aufs and overlay2');
368
- }
369
- const dkroot = dockerInfo.DockerRootDir;
370
- const imageId = imageInfo.Id;
371
- const ids = await getDiffIds(dkroot, driver, imageId).then(function (
372
- diffIds,
373
- ) {
374
- if (!usesContentAddressableFormat(dockerVersion)) {
375
- return diffIds;
376
- }
377
- return Promise.all(
378
- getAllChainIds(diffIds).map(async (layerId) =>
379
- getCacheId(dkroot, driver, layerId),
380
- ),
381
- );
382
- });
383
- return ids.reverse().map<string>(function (layerId: string) {
384
- return driver === 'aufs'
385
- ? path.join(dkroot, 'aufs/diff', layerId)
386
- : path.join(dkroot, 'overlay2', layerId, 'diff');
387
- });
388
- }
389
-
390
- // Given an image configuration it constructs a valid tar archive in the same
391
- // way a `docker save` would have done that contains an empty filesystem image
392
- // with the given configuration.
393
- //
394
- // We have to go through the `docker load` mechanism in order for docker to
395
- // compute the correct digests and properly load it in the content store
396
- //
397
- // It returns a promise that resolves to the new image id
398
- async createEmptyImage(imageConfig: any): Promise<string> {
399
- const manifest = [
400
- {
401
- Config: 'config.json',
402
- RepoTags: null,
403
- Layers: ['0000/layer.tar'],
404
- },
405
- ];
406
-
407
- // Since docker versions after 1.10 use a content addressable store we have
408
- // to make sure we always load a uniqe image so that we end up with
409
- // different image IDs on which we can later apply a delta stream
410
- const layer = tar.pack();
411
- layer.entry({ name: 'seed' }, String(Date.now() + Math.random()));
412
- layer.finalize();
413
-
414
- const buf = await promiseFromCallback<string>((callback) =>
415
- layer.pipe(es.wait(callback)),
416
- );
417
- const now = new Date().toISOString();
418
-
419
- const config = {
420
- config: imageConfig,
421
- created: now,
422
- rootfs: {
423
- type: 'layers',
424
- diff_ids: [getDigest(buf)],
425
- },
426
- };
427
-
428
- const imageId = sha256sum(JSON.stringify(config));
429
-
430
- const layerConfig = {
431
- id: imageId,
432
- created: now,
433
- config: imageConfig,
434
- };
435
-
436
- const image = tar.pack();
437
- image.entry({ name: 'manifest.json' }, JSON.stringify(manifest));
438
- image.entry({ name: 'config.json' }, JSON.stringify(config));
439
- image.entry({ name: '0000/VERSION' }, '1.0');
440
- image.entry({ name: '0000/json' }, JSON.stringify(layerConfig));
441
- image.entry({ name: '0000/layer.tar' }, buf);
442
-
443
- image.finalize();
444
-
445
- const stream = await this.loadImage(image);
446
- await promiseFromCallback((callback) => stream.pipe(es.wait(callback)));
447
- return imageId;
448
- }
449
-
450
- // Given a source and destination image, generates a delta and returns a promise
451
- // that resolves with the ID of the generated image. `onProgress` is an optional
452
- // callback that receives a single argument for the progress event that can used
453
- // to follow progress.
454
- //
455
- // Deltas are currently only available with Balena, but this method makes no
456
- // effort to determine whether that's the case.
457
- //
458
- // The name of this method is intentionally unconventional for docker-toolbelt,
459
- // anticipating the appearance of a similar method *in* dockerode in the future.
460
- async createDeltaAsync(
461
- src: any,
462
- dest: any,
463
- onProgress: (arg0: any) => void,
464
- ): Promise<string> {
465
- const optsf = {
466
- path: '/images/delta?',
467
- method: 'POST',
468
- options: { src, dest },
469
- isStream: true,
470
- statusCodes: {
471
- 200: true,
472
- 404: 'no such image',
473
- 500: 'server error',
474
- },
475
- };
476
- // TODO: fix this any typing
477
- const stream: any = await promiseFromCallback((cb: any) => {
478
- return this.modem.dial(optsf, cb);
479
- });
480
-
481
- const id: string = await promiseFromCallback((cb: any) => {
482
- return this.modem.followProgress(stream, cb, function (e) {
483
- if (typeof onProgress === 'function') {
484
- onProgress(e);
485
- }
486
- const match = /^Created delta: (sha256:\w+)$/.exec(e.status);
487
- if (match) {
488
- return cb(null, match[1]);
489
- }
490
- });
491
- });
492
-
493
- return id;
494
- }
495
-
496
- // Separate string containing registry and image name into its parts.
497
- // Example: registry.resinstaging.io/resin/rpi
498
- // { registry: "registry.resinstaging.io", imageName: "resin/rpi" }
499
- getRegistryAndName(image: string): ImageNameParts {
500
- // Matches (registry)/(repo)(optional :tag or @digest)
501
- // regex adapted from Docker's source code:
502
- // https://github.com/docker/distribution/blob/release/2.7/reference/normalize.go#L62
503
- // https://github.com/docker/distribution/blob/release/2.7/reference/regexp.go#L44
504
- const match = image.match(
505
- /^(?:(localhost|.*?[.:].*?)\/)?(.+?)(?::(.*?))?(?:@(.*?))?$/,
506
- );
507
- if (match == null) {
508
- throw new Error(`Could not parse the image: ${image}`);
509
- }
510
- const registry = match[match.length - 4];
511
- const imageName = match[match.length - 3];
512
- let tagName = match[match.length - 2];
513
- const digest = match[match.length - 1];
514
- if (digest == null && tagName == null) {
515
- tagName = 'latest';
516
- }
517
- const digestMatch =
518
- digest != null
519
- ? digest.match(
520
- /^[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*:[0-9a-f-A-F]{32,}$/,
521
- )
522
- : undefined;
523
- if (!imageName || (digest && !digestMatch)) {
524
- throw new Error(
525
- 'Invalid image name, expected [domain.tld/]repo/image[:tag][@digest] format',
526
- );
527
- }
528
- return { registry, imageName, tagName, digest };
529
- }
530
-
531
- // Given an object representing a docker image, in the same format as given
532
- // by getRegistryAndName, compile it back into a docker image string, which
533
- // can be used in Docker command etc
534
- // Example: { registry: "registry.resinstaging.io", imageName: "resin/rpi", tagName: "1234"}
535
- // => registry.resinstaging.io/resin/rpi:1234
536
- async compileRegistryAndName({
537
- registry = '',
538
- imageName,
539
- tagName = '',
540
- digest,
541
- }: ImageNameParts): Promise<string> {
542
- if (registry !== '') {
543
- registry += '/';
544
- }
545
-
546
- if (digest == null) {
547
- if (tagName === '') {
548
- tagName = 'latest';
549
- }
550
- return `${registry}${imageName}:${tagName}`;
551
- } else {
552
- // Intentionally discard the tag when a digest exists
553
- return `${registry}${imageName}@${digest}`;
554
- }
555
- }
556
-
557
- // Normalise an image name to always have a tag, with :latest being the default
558
- async normaliseImageName(image: string): Promise<string> {
559
- const result = await this.getRegistryAndName(image);
560
- return this.compileRegistryAndName(result);
561
- }
562
- }