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.
- package/.versionbot/CHANGELOG.yml +20 -1
- package/CHANGELOG.md +6 -0
- package/build/index.d.ts +1 -2
- package/lib/index.ts +1 -1
- package/package.json +2 -2
- package/build/docker-toolbelt.d.ts +0 -17
- package/build/docker-toolbelt.js +0 -371
- package/build/docker-toolbelt.js.map +0 -1
- package/lib/docker-toolbelt.ts +0 -562
|
@@ -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:
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docker-toolbelt",
|
|
3
|
-
"version": "4.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:
|
|
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
|
-
}
|
package/build/docker-toolbelt.js
DELETED
|
@@ -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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"}
|
package/lib/docker-toolbelt.ts
DELETED
|
@@ -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
|
-
}
|