containerify 3.0.0 → 3.1.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/README.md +1 -0
- package/lib/cli.js +4 -3
- package/lib/registry.d.ts +1 -2
- package/lib/registry.js +171 -166
- package/lib/types.d.ts +1 -0
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -80,6 +80,7 @@ Options:
|
|
|
80
80
|
--setTimeStamp <timestamp> Optional: Set a specific ISO 8601 timestamp on all entries (e.g. git commit hash). Default: 1970 in tar files, and current time on manifest/config
|
|
81
81
|
--verbose Verbose logging
|
|
82
82
|
--allowInsecureRegistries Allow insecure registries (with self-signed/untrusted cert)
|
|
83
|
+
--allowNoPushAuth Allow pushing images without authentication/token if the registry allows it
|
|
83
84
|
--customContent <dirs/files> Optional: Skip normal node_modules and applayer and include specified root folder files/directories instead. You can specify as
|
|
84
85
|
local-path:absolute-container-path if you want to place it in a specific location
|
|
85
86
|
--extraContent <dirs/files> Optional: Add specific content. Specify as local-path:absolute-container-path,local-path2:absolute-container-path2 etc
|
package/lib/cli.js
CHANGED
|
@@ -53,6 +53,7 @@ const possibleArgs = {
|
|
|
53
53
|
"--setTimeStamp <timestamp>": "Optional: Set a specific ISO 8601 timestamp on all entries (e.g. git commit hash). Default: 1970 in tar files, and current time on manifest/config",
|
|
54
54
|
"--verbose": "Verbose logging",
|
|
55
55
|
"--allowInsecureRegistries": "Allow insecure registries (with self-signed/untrusted cert)",
|
|
56
|
+
"--allowNoPushAuth": "Allow pushing images without a authentication/token to registries that allow it",
|
|
56
57
|
"--customContent <dirs/files>": "Optional: Skip normal node_modules and applayer and include specified root folder files/directories instead. You can specify as local-path:absolute-container-path if you want to place it in a specific location",
|
|
57
58
|
"--extraContent <dirs/files>": "Optional: Add specific content. Specify as local-path:absolute-container-path,local-path2:absolute-container-path2 etc",
|
|
58
59
|
"--layerOwner <gid:uid>": "Optional: Set specific gid and uid on files in the added layers",
|
|
@@ -196,7 +197,7 @@ exitWithErrorIf(!options.folder, "--folder must be specified");
|
|
|
196
197
|
exitWithErrorIf(!options.fromImage, "--fromImage must be specified");
|
|
197
198
|
exitWithErrorIf(!options.toImage, "--toImage must be specified");
|
|
198
199
|
exitWithErrorIf(!options.toRegistry && !options.toTar && !options.toDocker, "Must specify either --toTar, --toRegistry or --toDocker");
|
|
199
|
-
exitWithErrorIf(!!options.toRegistry && !options.toToken, "A token must be
|
|
200
|
+
exitWithErrorIf(!!options.toRegistry && !options.toToken && !options.allowNoPushAuth, "A token must be provided when uploading images");
|
|
200
201
|
if (options.toRegistry && !options.toRegistry.endsWith("/"))
|
|
201
202
|
options.toRegistry += "/";
|
|
202
203
|
if (options.fromRegistry && !options.fromRegistry.endsWith("/"))
|
|
@@ -235,7 +236,7 @@ function run(options) {
|
|
|
235
236
|
const todir = yield (0, fileutil_1.ensureEmptyDir)(path.join(tmpdir, "to"));
|
|
236
237
|
const allowInsecure = options.allowInsecureRegistries ? types_1.InsecureRegistrySupport.YES : types_1.InsecureRegistrySupport.NO;
|
|
237
238
|
const fromRegistryUrl = (_a = options.fromRegistry) !== null && _a !== void 0 ? _a : registry_1.DEFAULT_DOCKER_REGISTRY;
|
|
238
|
-
const fromRegistry = (0, registry_1.createRegistry)(fromRegistryUrl,
|
|
239
|
+
const fromRegistry = yield (0, registry_1.createRegistry)(fromRegistryUrl, options.fromImage, allowInsecure, options.fromToken);
|
|
239
240
|
const originalManifest = yield fromRegistry.download(options.fromImage, fromdir, (0, utils_1.getPreferredPlatform)(options.platform), options.layerCacheFolder);
|
|
240
241
|
yield appLayerCreator_1.default.addLayers(tmpdir, fromdir, todir, options);
|
|
241
242
|
if (options.toDocker) {
|
|
@@ -250,7 +251,7 @@ function run(options) {
|
|
|
250
251
|
yield tarExporter_1.default.saveToTar(todir, tmpdir, options.toTar, [options.toImage], options);
|
|
251
252
|
}
|
|
252
253
|
if (options.toRegistry) {
|
|
253
|
-
const toRegistry = (0, registry_1.createRegistry)(options.toRegistry,
|
|
254
|
+
const toRegistry = yield (0, registry_1.createRegistry)(options.toRegistry, options.toImage, allowInsecure, options.toToken, options.optimisticToRegistryCheck);
|
|
254
255
|
yield toRegistry.upload(options.toImage, todir, options.doCrossMount, originalManifest, options.fromImage);
|
|
255
256
|
}
|
|
256
257
|
logger_1.default.debug("Deleting " + tmpdir + " ...");
|
package/lib/registry.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { InsecureRegistrySupport, Registry } from "./types";
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function createRegistry(registryBaseUrl: string, auth: string, allowInsecure: InsecureRegistrySupport, optimisticToRegistryCheck?: boolean): Registry;
|
|
2
|
+
export declare function createRegistry(registryBaseUrl: string, imagePath: string, allowInsecure: InsecureRegistrySupport, auth?: string, optimisticToRegistryCheck?: boolean): Promise<Registry>;
|
|
4
3
|
export declare const DEFAULT_DOCKER_REGISTRY = "https://registry-1.docker.io/v2/";
|
|
5
4
|
export declare function parseFullImageUrl(imageStr: string): {
|
|
6
5
|
registry: string;
|
package/lib/registry.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.parseFullImageUrl = exports.DEFAULT_DOCKER_REGISTRY = exports.createRegistry =
|
|
12
|
+
exports.parseFullImageUrl = exports.DEFAULT_DOCKER_REGISTRY = exports.createRegistry = void 0;
|
|
13
13
|
const URL = require("url");
|
|
14
14
|
const fss = require("fs");
|
|
15
15
|
const fs_1 = require("fs");
|
|
@@ -98,11 +98,13 @@ function uploadContent(uploadUrl, file, fileConfig, allowInsecure, auth, content
|
|
|
98
98
|
let url = uploadUrl;
|
|
99
99
|
if (fileConfig.digest)
|
|
100
100
|
url += (url.indexOf("?") == -1 ? "?" : "&") + "digest=" + fileConfig.digest;
|
|
101
|
-
const
|
|
102
|
-
authorization: auth,
|
|
101
|
+
const headers = {
|
|
103
102
|
"content-length": fileConfig.size,
|
|
104
103
|
"content-type": contentType,
|
|
105
|
-
}
|
|
104
|
+
};
|
|
105
|
+
if (auth)
|
|
106
|
+
headers.authorization = auth;
|
|
107
|
+
const options = (0, httpRequest_1.createHttpOptions)("PUT", url, headers);
|
|
106
108
|
logger_1.default.debug(options.method, url);
|
|
107
109
|
const req = (0, httpRequest_1.request)(options, allowInsecure, (res) => {
|
|
108
110
|
var _a;
|
|
@@ -140,7 +142,7 @@ function processToken(registryBaseUrl, allowInsecure, imagePath, token) {
|
|
|
140
142
|
return `Bearer ${resp.token}`;
|
|
141
143
|
}
|
|
142
144
|
if (!token)
|
|
143
|
-
|
|
145
|
+
return ""; //We allow to pull from tokenless registries
|
|
144
146
|
if (token.startsWith("Basic "))
|
|
145
147
|
return token;
|
|
146
148
|
if (token.startsWith("ghp_"))
|
|
@@ -148,181 +150,184 @@ function processToken(registryBaseUrl, allowInsecure, imagePath, token) {
|
|
|
148
150
|
return "Bearer " + token;
|
|
149
151
|
});
|
|
150
152
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
|
|
153
|
+
function createRegistry(registryBaseUrl, imagePath, allowInsecure, auth, optimisticToRegistryCheck = false) {
|
|
154
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
155
|
+
const token = yield processToken(registryBaseUrl, allowInsecure, imagePath, auth);
|
|
156
|
+
function exists(image, layer) {
|
|
157
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
const url = `${registryBaseUrl}${image.path}/blobs/${layer.digest}`;
|
|
159
|
+
return yield checkIfLayerExists(url, (0, httpRequest_1.buildHeaders)(layer.mediaType, token), allowInsecure, optimisticToRegistryCheck, 0);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function uploadLayerContent(uploadUrl, layer, dir) {
|
|
163
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
+
logger_1.default.info(layer.digest);
|
|
165
|
+
const file = path.join(dir, (0, utils_1.getHash)(layer.digest) + (0, utils_1.getLayerTypeFileEnding)(layer));
|
|
166
|
+
yield uploadContent(uploadUrl, file, layer, allowInsecure, token);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function getUploadUrl(image, mountParameters = undefined) {
|
|
170
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
const parameters = new URLSearchParams(mountParameters);
|
|
173
|
+
const url = `${registryBaseUrl}${image.path}/blobs/uploads/${parameters.size > 0 ? "?" + parameters : ""}`;
|
|
174
|
+
const options = URL.parse(url);
|
|
175
|
+
options.method = "POST";
|
|
176
|
+
if (auth)
|
|
177
|
+
options.headers = { authorization: auth };
|
|
178
|
+
(0, httpRequest_1.request)(options, allowInsecure, (res) => {
|
|
179
|
+
logger_1.default.debug("POST", `${url}`, res.statusCode);
|
|
180
|
+
if (res.statusCode == 202) {
|
|
181
|
+
const { location } = res.headers;
|
|
182
|
+
if (location) {
|
|
183
|
+
if (location.startsWith("http")) {
|
|
184
|
+
resolve({ uploadUrl: location });
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
const regURL = URL.parse(registryBaseUrl);
|
|
188
|
+
resolve({
|
|
189
|
+
uploadUrl: `${regURL.protocol}//${regURL.hostname}${regURL.port ? ":" + regURL.port : ""}${location}`,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
181
192
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
193
|
+
reject("Missing location for 202");
|
|
194
|
+
}
|
|
195
|
+
else if (mountParameters && res.statusCode == 201) {
|
|
196
|
+
const returnedDigest = res.headers["docker-content-digest"];
|
|
197
|
+
if (returnedDigest && returnedDigest != mountParameters.mount) {
|
|
198
|
+
reject(`ERROR: Layer mounted with wrong digest: Expected ${mountParameters.mount} but got ${returnedDigest}`);
|
|
187
199
|
}
|
|
200
|
+
resolve({ mountSuccess: true });
|
|
188
201
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (returnedDigest && returnedDigest != mountParameters.mount) {
|
|
194
|
-
reject(`ERROR: Layer mounted with wrong digest: Expected ${mountParameters.mount} but got ${returnedDigest}`);
|
|
202
|
+
else {
|
|
203
|
+
(0, httpRequest_1.waitForResponseEnd)(res, (data) => {
|
|
204
|
+
reject(`Error getting upload URL from ${url}. Got ${res.statusCode} ${res.statusMessage}:\n${data.toString()}`);
|
|
205
|
+
});
|
|
195
206
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
else {
|
|
199
|
-
(0, httpRequest_1.waitForResponseEnd)(res, (data) => {
|
|
200
|
-
reject(`Error getting upload URL from ${url}. Got ${res.statusCode} ${res.statusMessage}:\n${data.toString()}`);
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}).end();
|
|
207
|
+
}).end();
|
|
208
|
+
});
|
|
204
209
|
});
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
210
|
+
}
|
|
211
|
+
function dlManifest(image, preferredPlatform, allowInsecure) {
|
|
212
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
213
|
+
// Accept both manifests and index/manifest lists
|
|
214
|
+
const res = yield (0, httpRequest_1.dlJson)(`${registryBaseUrl}${image.path}/manifests/${image.tag}`, (0, httpRequest_1.buildHeaders)(`${MIMETypes_1.OCI.index}, ${MIMETypes_1.OCI.manifest}, ${MIMETypes_1.DockerV2.index}, ${MIMETypes_1.DockerV2.manifest}`, token), allowInsecure);
|
|
215
|
+
// We've received an OCI Index or Docker Manifest List and need to find which manifest we want
|
|
216
|
+
if (res.mediaType === MIMETypes_1.OCI.index || res.mediaType === MIMETypes_1.DockerV2.index) {
|
|
217
|
+
const availableManifests = res.manifests;
|
|
218
|
+
const adequateManifest = pickManifest(availableManifests, preferredPlatform);
|
|
219
|
+
return dlManifest(Object.assign(Object.assign({}, image), { tag: adequateManifest.digest }), preferredPlatform, allowInsecure);
|
|
220
|
+
}
|
|
221
|
+
return res;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function pickManifest(manifests, preferredPlatform) {
|
|
225
|
+
const matchingArchitectures = new Set();
|
|
226
|
+
const matchingOSes = new Set();
|
|
227
|
+
// Find sets of matching architecture and os
|
|
228
|
+
for (const manifest of manifests) {
|
|
229
|
+
if (manifest.platform.architecture === preferredPlatform.architecture) {
|
|
230
|
+
matchingArchitectures.add(manifest);
|
|
231
|
+
}
|
|
232
|
+
if (manifest.platform.os === preferredPlatform.os) {
|
|
233
|
+
matchingOSes.add(manifest);
|
|
234
|
+
}
|
|
216
235
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const matchingArchitectures = new Set();
|
|
222
|
-
const matchingOSes = new Set();
|
|
223
|
-
// Find sets of matching architecture and os
|
|
224
|
-
for (const manifest of manifests) {
|
|
225
|
-
if (manifest.platform.architecture === preferredPlatform.architecture) {
|
|
226
|
-
matchingArchitectures.add(manifest);
|
|
236
|
+
// If the intersection of matching architectures and OS is one we've found our optimal match
|
|
237
|
+
const intersection = new Set([...matchingArchitectures].filter((x) => matchingOSes.has(x)));
|
|
238
|
+
if (intersection.size == 1) {
|
|
239
|
+
return intersection.values().next().value;
|
|
227
240
|
}
|
|
228
|
-
|
|
229
|
-
|
|
241
|
+
// If we don't have a perfect match we give a warning and try the first matching architecture
|
|
242
|
+
if (matchingArchitectures.size >= 1) {
|
|
243
|
+
const matchingArch = matchingArchitectures.values().next().value;
|
|
244
|
+
logger_1.default.info(`[WARN] Preferred OS '${preferredPlatform.os}' not available.`);
|
|
245
|
+
logger_1.default.info("[WARN] Using closest available manifest:", JSON.stringify(matchingArch.platform));
|
|
246
|
+
return matchingArch;
|
|
230
247
|
}
|
|
248
|
+
// If there's no image matching the wanted architecture we bail
|
|
249
|
+
logger_1.default.error(`No image matching requested architecture: '${preferredPlatform.architecture}'`);
|
|
250
|
+
logger_1.default.error("Available platforms:", JSON.stringify(manifests.map((m) => m.platform)));
|
|
251
|
+
throw new Error("No image matching requested architecture");
|
|
231
252
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
253
|
+
function dlConfig(image, config, allowInsecure) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
255
|
+
return yield (0, httpRequest_1.dlJson)(`${registryBaseUrl}${image.path}/blobs/${config.digest}`, (0, httpRequest_1.buildHeaders)("*/*", token), allowInsecure);
|
|
256
|
+
});
|
|
236
257
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
258
|
+
function dlLayer(image, layer, folder, allowInsecure, cacheFolder) {
|
|
259
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
260
|
+
const file = (0, utils_1.getHash)(layer.digest) + (0, utils_1.getLayerTypeFileEnding)(layer);
|
|
261
|
+
yield dlToFile(`${registryBaseUrl}${image.path}/blobs/${layer.digest}`, path.join(folder, file), (0, httpRequest_1.buildHeaders)(layer.mediaType, token), allowInsecure, cacheFolder);
|
|
262
|
+
return file;
|
|
263
|
+
});
|
|
243
264
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const layersForUpload = layerStatus.filter((l) => !l.exists);
|
|
271
|
-
logger_1.default.debug("Needs upload:", layersForUpload.map((l) => l.layer.digest));
|
|
272
|
-
logger_1.default.info("Uploading layers...");
|
|
273
|
-
yield Promise.all(layersForUpload.map((l) => __awaiter(this, void 0, void 0, function* () {
|
|
274
|
-
if (doCrossMount && originalManifest.layers.find((x) => x.digest == l.layer.digest)) {
|
|
275
|
-
const mount = yield getUploadUrl(image, { mount: l.layer.digest, from: originalRepository });
|
|
276
|
-
if ("mountSuccess" in mount) {
|
|
277
|
-
logger_1.default.info(`Cross mounted layer ${l.layer.digest} from '${originalRepository}'`);
|
|
278
|
-
return;
|
|
265
|
+
function upload(imageStr, folder, doCrossMount, originalManifest, originalRepository) {
|
|
266
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
267
|
+
const image = (0, utils_1.parseImage)(imageStr);
|
|
268
|
+
const manifestFile = path.join(folder, "manifest.json");
|
|
269
|
+
const manifest = (yield fse.readJson(manifestFile));
|
|
270
|
+
logger_1.default.info("Checking layer status...");
|
|
271
|
+
const layerStatus = yield Promise.all(manifest.layers.map((l) => __awaiter(this, void 0, void 0, function* () {
|
|
272
|
+
return { layer: l, exists: yield exists(image, l) };
|
|
273
|
+
})));
|
|
274
|
+
const layersForUpload = layerStatus.filter((l) => !l.exists);
|
|
275
|
+
logger_1.default.debug("Needs upload:", layersForUpload.map((l) => l.layer.digest));
|
|
276
|
+
logger_1.default.info("Uploading layers...");
|
|
277
|
+
yield Promise.all(layersForUpload.map((l) => __awaiter(this, void 0, void 0, function* () {
|
|
278
|
+
if (doCrossMount && originalManifest.layers.find((x) => x.digest == l.layer.digest)) {
|
|
279
|
+
const mount = yield getUploadUrl(image, { mount: l.layer.digest, from: originalRepository });
|
|
280
|
+
if ("mountSuccess" in mount) {
|
|
281
|
+
logger_1.default.info(`Cross mounted layer ${l.layer.digest} from '${originalRepository}'`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
yield uploadLayerContent(mount.uploadUrl, l.layer, folder);
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
const url = yield getUploadUrl(image);
|
|
288
|
+
if ("mountSuccess" in url)
|
|
289
|
+
throw new Error("Mounting not supported for this upload");
|
|
290
|
+
yield uploadLayerContent(url.uploadUrl, l.layer, folder);
|
|
279
291
|
}
|
|
280
|
-
|
|
292
|
+
})));
|
|
293
|
+
logger_1.default.info("Uploading config...");
|
|
294
|
+
const configUploadUrl = yield getUploadUrl(image);
|
|
295
|
+
if ("mountSuccess" in configUploadUrl)
|
|
296
|
+
throw new Error("Mounting not supported for config upload");
|
|
297
|
+
const configFile = path.join(folder, (0, utils_1.getHash)(manifest.config.digest) + ".json");
|
|
298
|
+
yield uploadContent(configUploadUrl.uploadUrl, configFile, manifest.config, allowInsecure, token);
|
|
299
|
+
logger_1.default.info("Uploading manifest...");
|
|
300
|
+
const manifestSize = yield fileutil.sizeOf(manifestFile);
|
|
301
|
+
yield uploadContent(`${registryBaseUrl}${image.path}/manifests/${image.tag}`, manifestFile, { mediaType: manifest.mediaType, size: manifestSize }, allowInsecure, token, manifest.mediaType);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function download(imageStr, folder, preferredPlatform, cacheFolder) {
|
|
305
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
306
|
+
const image = (0, utils_1.parseImage)(imageStr);
|
|
307
|
+
logger_1.default.info("Downloading manifest...");
|
|
308
|
+
const manifest = yield dlManifest(image, preferredPlatform, allowInsecure);
|
|
309
|
+
yield fs_1.promises.writeFile(path.join(folder, "manifest.json"), JSON.stringify(manifest));
|
|
310
|
+
logger_1.default.info("Downloading config...");
|
|
311
|
+
const config = yield dlConfig(image, manifest.config, allowInsecure);
|
|
312
|
+
if (config.architecture != preferredPlatform.architecture) {
|
|
313
|
+
logger_1.default.info(`[WARN] Image architecture (${config.architecture}) does not match preferred architecture (${preferredPlatform.architecture}).`);
|
|
281
314
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if ("mountSuccess" in url)
|
|
285
|
-
throw new Error("Mounting not supported for this upload");
|
|
286
|
-
yield uploadLayerContent(url.uploadUrl, l.layer, folder);
|
|
315
|
+
if (config.os != preferredPlatform.os) {
|
|
316
|
+
logger_1.default.info(`[WARN] Image OS (${config.os}) does not match preferred OS (${preferredPlatform.os}).`);
|
|
287
317
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
302
|
-
const image = (0, utils_1.parseImage)(imageStr);
|
|
303
|
-
logger_1.default.info("Downloading manifest...");
|
|
304
|
-
const manifest = yield dlManifest(image, preferredPlatform, allowInsecure);
|
|
305
|
-
yield fs_1.promises.writeFile(path.join(folder, "manifest.json"), JSON.stringify(manifest));
|
|
306
|
-
logger_1.default.info("Downloading config...");
|
|
307
|
-
const config = yield dlConfig(image, manifest.config, allowInsecure);
|
|
308
|
-
if (config.architecture != preferredPlatform.architecture) {
|
|
309
|
-
logger_1.default.info(`[WARN] Image architecture (${config.architecture}) does not match preferred architecture (${preferredPlatform.architecture}).`);
|
|
310
|
-
}
|
|
311
|
-
if (config.os != preferredPlatform.os) {
|
|
312
|
-
logger_1.default.info(`[WARN] Image OS (${config.os}) does not match preferred OS (${preferredPlatform.os}).`);
|
|
313
|
-
}
|
|
314
|
-
yield fs_1.promises.writeFile(path.join(folder, "config.json"), JSON.stringify(config));
|
|
315
|
-
logger_1.default.info("Downloading layers...");
|
|
316
|
-
yield Promise.all(manifest.layers.map((layer) => dlLayer(image, layer, folder, allowInsecure, cacheFolder)));
|
|
317
|
-
logger_1.default.info("Image downloaded.");
|
|
318
|
-
return manifest;
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
return {
|
|
322
|
-
download: download,
|
|
323
|
-
upload: upload,
|
|
324
|
-
registryBaseUrl,
|
|
325
|
-
};
|
|
318
|
+
yield fs_1.promises.writeFile(path.join(folder, "config.json"), JSON.stringify(config));
|
|
319
|
+
logger_1.default.info("Downloading layers...");
|
|
320
|
+
yield Promise.all(manifest.layers.map((layer) => dlLayer(image, layer, folder, allowInsecure, cacheFolder)));
|
|
321
|
+
logger_1.default.info("Image downloaded.");
|
|
322
|
+
return manifest;
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
download: download,
|
|
327
|
+
upload: upload,
|
|
328
|
+
registryBaseUrl,
|
|
329
|
+
};
|
|
330
|
+
});
|
|
326
331
|
}
|
|
327
332
|
exports.createRegistry = createRegistry;
|
|
328
333
|
exports.DEFAULT_DOCKER_REGISTRY = "https://registry-1.docker.io/v2/";
|
package/lib/types.d.ts
CHANGED
package/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "3.
|
|
1
|
+
export declare const VERSION = "3.1.0";
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "containerify",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Build node.js docker images without docker",
|
|
5
5
|
"main": "./lib/cli.js",
|
|
6
6
|
"scripts": {
|
|
@@ -12,12 +12,16 @@
|
|
|
12
12
|
"check": "npm run lint && npm run typecheck",
|
|
13
13
|
"dev": "tsc --watch",
|
|
14
14
|
"integrationTest": "cd tests/integration/ && ./test.sh",
|
|
15
|
-
"registryTest": "cd tests/localtest/ && ./test.sh"
|
|
15
|
+
"registryTest": "cd tests/localtest/ && ./test.sh && ./test-insecure.sh",
|
|
16
|
+
"allTests": "npm run integrationTest && npm run registryTest"
|
|
16
17
|
},
|
|
17
18
|
"bin": {
|
|
18
19
|
"containerify": "./lib/cli.js"
|
|
19
20
|
},
|
|
20
21
|
"author": "Erlend Oftedal <erlend@oftedal.no>",
|
|
22
|
+
"contributors": [
|
|
23
|
+
"Vegard S. Hagen <vegard@stonegarden.dev>"
|
|
24
|
+
],
|
|
21
25
|
"license": "Apache-2.0",
|
|
22
26
|
"repository": {
|
|
23
27
|
"type": "git",
|