containerify 3.1.0 → 3.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/README.md +1 -1
- package/lib/cli.js +1 -1
- package/lib/fileutil.js +2 -3
- package/lib/httpRequest.d.ts +0 -3
- package/lib/httpRequest.js +25 -12
- package/lib/registry.js +27 -19
- package/lib/utils.js +7 -8
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ This will take the `nginx:alpine` image, and copy the files from `./dist/` into
|
|
|
45
45
|
|
|
46
46
|
1. Create the repository in GitLab
|
|
47
47
|
2. Login using your username and password, [CI-credentials](https://docs.gitlab.com/ee/ci/jobs/ci_job_token.html), or [obtain a token from GitLab](https://docs.gitlab.com/ee/api/container_registry.html#obtain-token-from-gitlab)
|
|
48
|
-
3. Example using CI-credentials `containerify --toToken "Basic $(echo -n
|
|
48
|
+
3. Example using CI-credentials `containerify --toToken "Basic $(echo -n "${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD}" | base64)" --to registry.gitlab.com/<Gitlab organisation>/<repository>:<tag>`
|
|
49
49
|
|
|
50
50
|
### Command line options
|
|
51
51
|
|
package/lib/cli.js
CHANGED
|
@@ -226,8 +226,8 @@ Object.keys(options.extraContent).forEach((k) => {
|
|
|
226
226
|
exitWithErrorIf(!fs.existsSync(options.folder + k), "Could not find `" + k + "` in the folder " + options.folder);
|
|
227
227
|
});
|
|
228
228
|
function run(options) {
|
|
229
|
-
var _a;
|
|
230
229
|
return __awaiter(this, void 0, void 0, function* () {
|
|
230
|
+
var _a;
|
|
231
231
|
if (!(yield fse.pathExists(options.folder)))
|
|
232
232
|
throw new Error("No such folder: " + options.folder);
|
|
233
233
|
const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "containerify-"));
|
package/lib/fileutil.js
CHANGED
|
@@ -9,7 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.sizeOf = sizeOf;
|
|
13
|
+
exports.ensureEmptyDir = ensureEmptyDir;
|
|
13
14
|
const fs_1 = require("fs");
|
|
14
15
|
const fse = require("fs-extra");
|
|
15
16
|
const fss = require("fs");
|
|
@@ -18,7 +19,6 @@ function sizeOf(file) {
|
|
|
18
19
|
return (yield fs_1.promises.lstat(file)).size;
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
|
-
exports.sizeOf = sizeOf;
|
|
22
22
|
function ensureEmptyDir(path) {
|
|
23
23
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
24
|
if (fss.existsSync(path))
|
|
@@ -27,7 +27,6 @@ function ensureEmptyDir(path) {
|
|
|
27
27
|
return path;
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
exports.ensureEmptyDir = ensureEmptyDir;
|
|
31
30
|
exports.default = {
|
|
32
31
|
sizeOf,
|
|
33
32
|
ensureEmptyDir,
|
package/lib/httpRequest.d.ts
CHANGED
package/lib/httpRequest.js
CHANGED
|
@@ -9,7 +9,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.redirectCodes = void 0;
|
|
13
|
+
exports.isOk = isOk;
|
|
14
|
+
exports.createHttpOptions = createHttpOptions;
|
|
15
|
+
exports.buildHeaders = buildHeaders;
|
|
16
|
+
exports.request = request;
|
|
17
|
+
exports.toError = toError;
|
|
18
|
+
exports.waitForResponseEnd = waitForResponseEnd;
|
|
19
|
+
exports.dlJson = dlJson;
|
|
20
|
+
exports.followRedirects = followRedirects;
|
|
13
21
|
const https = require("https");
|
|
14
22
|
const http = require("http");
|
|
15
23
|
const URL = require("url");
|
|
@@ -19,21 +27,22 @@ exports.redirectCodes = [308, 307, 303, 302, 301];
|
|
|
19
27
|
function isOk(httpStatus) {
|
|
20
28
|
return httpStatus >= 200 && httpStatus < 300;
|
|
21
29
|
}
|
|
22
|
-
exports.isOk = isOk;
|
|
23
30
|
function createHttpOptions(method, url, headers) {
|
|
24
31
|
const options = Object.assign({}, URL.parse(url));
|
|
25
32
|
options.headers = headers;
|
|
26
33
|
options.method = method;
|
|
34
|
+
if (url.includes("X-Amz-Algorithm") && method == "GET") {
|
|
35
|
+
//We are using a pre-signed URL, so we don't need to send the Authorization header
|
|
36
|
+
options.headers["Authorization"] = "";
|
|
37
|
+
}
|
|
27
38
|
return options;
|
|
28
39
|
}
|
|
29
|
-
exports.createHttpOptions = createHttpOptions;
|
|
30
40
|
function buildHeaders(accept, auth) {
|
|
31
41
|
const headers = { accept: accept };
|
|
32
42
|
if (auth)
|
|
33
43
|
headers.authorization = auth;
|
|
34
44
|
return headers;
|
|
35
45
|
}
|
|
36
|
-
exports.buildHeaders = buildHeaders;
|
|
37
46
|
function request(options, allowInsecure, callback) {
|
|
38
47
|
if (allowInsecure == types_1.InsecureRegistrySupport.YES)
|
|
39
48
|
options.rejectUnauthorized = false;
|
|
@@ -46,17 +55,14 @@ function request(options, allowInsecure, callback) {
|
|
|
46
55
|
});
|
|
47
56
|
return req;
|
|
48
57
|
}
|
|
49
|
-
exports.request = request;
|
|
50
58
|
function toError(res) {
|
|
51
59
|
return `Unexpected HTTP status ${res.statusCode} : ${res.statusMessage}`;
|
|
52
60
|
}
|
|
53
|
-
exports.toError = toError;
|
|
54
61
|
function waitForResponseEnd(res, cb) {
|
|
55
62
|
const data = [];
|
|
56
63
|
res.on("data", (d) => data.push(d));
|
|
57
64
|
res.on("end", () => cb(Buffer.concat(data)));
|
|
58
65
|
}
|
|
59
|
-
exports.waitForResponseEnd = waitForResponseEnd;
|
|
60
66
|
function dl(uri, headers, allowInsecure) {
|
|
61
67
|
logger_1.default.debug("dl", uri);
|
|
62
68
|
return new Promise((resolve, reject) => {
|
|
@@ -66,9 +72,17 @@ function dl(uri, headers, allowInsecure) {
|
|
|
66
72
|
return reject(result.error);
|
|
67
73
|
const { res } = result;
|
|
68
74
|
logger_1.default.debug(res.statusCode, res.statusMessage, res.headers["content-type"], res.headers["content-length"]);
|
|
69
|
-
if (!isOk((_a = res.statusCode) !== null && _a !== void 0 ? _a : 0))
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
if (!isOk((_a = res.statusCode) !== null && _a !== void 0 ? _a : 0)) {
|
|
76
|
+
const d = [];
|
|
77
|
+
res.on("data", (dt) => d.push(dt));
|
|
78
|
+
res.on("end", () => {
|
|
79
|
+
logger_1.default.error("ERROR", Buffer.concat(d).toString());
|
|
80
|
+
reject(toError(res));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
waitForResponseEnd(res, (data) => resolve(data.toString()));
|
|
85
|
+
}
|
|
72
86
|
});
|
|
73
87
|
});
|
|
74
88
|
}
|
|
@@ -78,7 +92,6 @@ function dlJson(uri, headers, allowInsecure) {
|
|
|
78
92
|
return JSON.parse(Buffer.from(data).toString("utf-8"));
|
|
79
93
|
});
|
|
80
94
|
}
|
|
81
|
-
exports.dlJson = dlJson;
|
|
82
95
|
function followRedirects(uri, headers, allowInsecure, cb, count = 0) {
|
|
83
96
|
logger_1.default.debug("rc", uri);
|
|
84
97
|
const options = createHttpOptions("GET", uri, headers);
|
|
@@ -88,6 +101,7 @@ function followRedirects(uri, headers, allowInsecure, cb, count = 0) {
|
|
|
88
101
|
if (count > 10)
|
|
89
102
|
return cb({ error: "Too many redirects for " + uri });
|
|
90
103
|
const location = res.headers.location;
|
|
104
|
+
console.log(res.headers);
|
|
91
105
|
if (!location)
|
|
92
106
|
return cb({ error: "Redirect, but missing location header" });
|
|
93
107
|
return followRedirects(location, headers, allowInsecure, cb, count + 1);
|
|
@@ -95,4 +109,3 @@ function followRedirects(uri, headers, allowInsecure, cb, count = 0) {
|
|
|
95
109
|
cb({ res });
|
|
96
110
|
}).end();
|
|
97
111
|
}
|
|
98
|
-
exports.followRedirects = followRedirects;
|
package/lib/registry.js
CHANGED
|
@@ -9,7 +9,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.DEFAULT_DOCKER_REGISTRY = void 0;
|
|
13
|
+
exports.createRegistry = createRegistry;
|
|
14
|
+
exports.parseFullImageUrl = parseFullImageUrl;
|
|
13
15
|
const URL = require("url");
|
|
14
16
|
const fss = require("fs");
|
|
15
17
|
const fs_1 = require("fs");
|
|
@@ -130,19 +132,12 @@ function processToken(registryBaseUrl, allowInsecure, imagePath, token) {
|
|
|
130
132
|
return __awaiter(this, void 0, void 0, function* () {
|
|
131
133
|
const { hostname } = URL.parse(registryBaseUrl);
|
|
132
134
|
const image = (0, utils_1.parseImage)(imagePath);
|
|
133
|
-
if ((hostname === null || hostname === void 0 ? void 0 : hostname.endsWith(".docker.io")) && !token)
|
|
134
|
-
|
|
135
|
-
return `Bearer ${resp.token}`;
|
|
136
|
-
}
|
|
137
|
-
if ((hostname === null || hostname === void 0 ? void 0 : hostname.endsWith(".gitlab.com")) && (token === null || token === void 0 ? void 0 : token.startsWith("Basic"))) {
|
|
138
|
-
if (token === null || token === void 0 ? void 0 : token.includes(":")) {
|
|
139
|
-
token = "Basic " + Buffer.from(token === null || token === void 0 ? void 0 : token.replace("Basic ", "")).toString("base64");
|
|
140
|
-
}
|
|
141
|
-
const resp = yield (0, httpRequest_1.dlJson)(`https://gitlab.com/jwt/auth?service=container_registry&scope=repository:${image.path}:pull,push`, { Authorization: token }, allowInsecure);
|
|
142
|
-
return `Bearer ${resp.token}`;
|
|
143
|
-
}
|
|
135
|
+
if ((hostname === null || hostname === void 0 ? void 0 : hostname.endsWith(".docker.io")) && !token)
|
|
136
|
+
return getDockerToken(image.path, allowInsecure);
|
|
144
137
|
if (!token)
|
|
145
138
|
return ""; //We allow to pull from tokenless registries
|
|
139
|
+
if ((hostname === null || hostname === void 0 ? void 0 : hostname.endsWith(".gitlab.com")) && token.startsWith("Basic "))
|
|
140
|
+
return getGitLabToken(token, image.path, allowInsecure);
|
|
146
141
|
if (token.startsWith("Basic "))
|
|
147
142
|
return token;
|
|
148
143
|
if (token.startsWith("ghp_"))
|
|
@@ -150,8 +145,23 @@ function processToken(registryBaseUrl, allowInsecure, imagePath, token) {
|
|
|
150
145
|
return "Bearer " + token;
|
|
151
146
|
});
|
|
152
147
|
}
|
|
153
|
-
function
|
|
148
|
+
function getDockerToken(imagePath, allowInsecure) {
|
|
149
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
const resp = yield (0, httpRequest_1.dlJson)(`https://auth.docker.io/token?service=registry.docker.io&scope=repository:${imagePath}:pull`, {}, allowInsecure);
|
|
151
|
+
return `Bearer ${resp.token}`;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function getGitLabToken(token, imagePath, allowInsecure) {
|
|
154
155
|
return __awaiter(this, void 0, void 0, function* () {
|
|
156
|
+
if (token.includes(":")) {
|
|
157
|
+
token = "Basic " + Buffer.from(token.replace("Basic ", "")).toString("base64");
|
|
158
|
+
}
|
|
159
|
+
const resp = yield (0, httpRequest_1.dlJson)(`https://gitlab.com/jwt/auth?service=container_registry&scope=repository:${imagePath}:pull,push`, { Authorization: token }, allowInsecure);
|
|
160
|
+
return `Bearer ${resp.token}`;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function createRegistry(registryBaseUrl_1, imagePath_1, allowInsecure_1, auth_1) {
|
|
164
|
+
return __awaiter(this, arguments, void 0, function* (registryBaseUrl, imagePath, allowInsecure, auth, optimisticToRegistryCheck = false) {
|
|
155
165
|
const token = yield processToken(registryBaseUrl, allowInsecure, imagePath, auth);
|
|
156
166
|
function exists(image, layer) {
|
|
157
167
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -166,15 +176,15 @@ function createRegistry(registryBaseUrl, imagePath, allowInsecure, auth, optimis
|
|
|
166
176
|
yield uploadContent(uploadUrl, file, layer, allowInsecure, token);
|
|
167
177
|
});
|
|
168
178
|
}
|
|
169
|
-
function getUploadUrl(
|
|
170
|
-
return __awaiter(this,
|
|
179
|
+
function getUploadUrl(image_1) {
|
|
180
|
+
return __awaiter(this, arguments, void 0, function* (image, mountParameters = undefined) {
|
|
171
181
|
return new Promise((resolve, reject) => {
|
|
172
182
|
const parameters = new URLSearchParams(mountParameters);
|
|
173
183
|
const url = `${registryBaseUrl}${image.path}/blobs/uploads/${parameters.size > 0 ? "?" + parameters : ""}`;
|
|
174
184
|
const options = URL.parse(url);
|
|
175
185
|
options.method = "POST";
|
|
176
|
-
if (
|
|
177
|
-
options.headers = { authorization:
|
|
186
|
+
if (token)
|
|
187
|
+
options.headers = { authorization: token };
|
|
178
188
|
(0, httpRequest_1.request)(options, allowInsecure, (res) => {
|
|
179
189
|
logger_1.default.debug("POST", `${url}`, res.statusCode);
|
|
180
190
|
if (res.statusCode == 202) {
|
|
@@ -329,7 +339,6 @@ function createRegistry(registryBaseUrl, imagePath, allowInsecure, auth, optimis
|
|
|
329
339
|
};
|
|
330
340
|
});
|
|
331
341
|
}
|
|
332
|
-
exports.createRegistry = createRegistry;
|
|
333
342
|
exports.DEFAULT_DOCKER_REGISTRY = "https://registry-1.docker.io/v2/";
|
|
334
343
|
function parseFullImageUrl(imageStr) {
|
|
335
344
|
const [registry, ...rest] = imageStr.split("/");
|
|
@@ -344,4 +353,3 @@ function parseFullImageUrl(imageStr) {
|
|
|
344
353
|
image: rest.join("/"),
|
|
345
354
|
};
|
|
346
355
|
}
|
|
347
|
-
exports.parseFullImageUrl = parseFullImageUrl;
|
package/lib/utils.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.unique = unique;
|
|
4
|
+
exports.omit = omit;
|
|
5
|
+
exports.getPreferredPlatform = getPreferredPlatform;
|
|
6
|
+
exports.getManifestLayerType = getManifestLayerType;
|
|
7
|
+
exports.getLayerTypeFileEnding = getLayerTypeFileEnding;
|
|
8
|
+
exports.getHash = getHash;
|
|
9
|
+
exports.parseImage = parseImage;
|
|
4
10
|
const MIMETypes_1 = require("./MIMETypes");
|
|
5
11
|
function unique(vals) {
|
|
6
12
|
return [...new Set(vals)];
|
|
7
13
|
}
|
|
8
|
-
exports.unique = unique;
|
|
9
14
|
function omit(obj, keys) {
|
|
10
15
|
return Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k)));
|
|
11
16
|
}
|
|
12
|
-
exports.omit = omit;
|
|
13
17
|
function getPreferredPlatform(platform) {
|
|
14
18
|
var _a, _b;
|
|
15
19
|
// We assume the input is similar to docker which accepts `<os>/<arch>` and `<arch>`
|
|
@@ -68,7 +72,6 @@ function getPreferredPlatform(platform) {
|
|
|
68
72
|
architecture: targetArch,
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
|
-
exports.getPreferredPlatform = getPreferredPlatform;
|
|
72
75
|
function getManifestLayerType(manifest) {
|
|
73
76
|
if (manifest.mediaType === MIMETypes_1.OCI.manifest) {
|
|
74
77
|
return MIMETypes_1.OCI.layer.gzip;
|
|
@@ -78,7 +81,6 @@ function getManifestLayerType(manifest) {
|
|
|
78
81
|
}
|
|
79
82
|
throw new Error(`${manifest.mediaType} not recognized.`);
|
|
80
83
|
}
|
|
81
|
-
exports.getManifestLayerType = getManifestLayerType;
|
|
82
84
|
function getLayerTypeFileEnding(layer) {
|
|
83
85
|
switch (layer.mediaType) {
|
|
84
86
|
case MIMETypes_1.OCI.layer.gzip:
|
|
@@ -91,15 +93,12 @@ function getLayerTypeFileEnding(layer) {
|
|
|
91
93
|
throw new Error(`Layer mediaType ${layer.mediaType} not known.`);
|
|
92
94
|
}
|
|
93
95
|
}
|
|
94
|
-
exports.getLayerTypeFileEnding = getLayerTypeFileEnding;
|
|
95
96
|
function getHash(digest) {
|
|
96
97
|
return digest.split(":")[1];
|
|
97
98
|
}
|
|
98
|
-
exports.getHash = getHash;
|
|
99
99
|
function parseImage(imageStr) {
|
|
100
100
|
const ar = imageStr.split(":");
|
|
101
101
|
const tag = ar[1] || "latest";
|
|
102
102
|
const ipath = ar[0];
|
|
103
103
|
return { path: ipath, tag: tag };
|
|
104
104
|
}
|
|
105
|
-
exports.parseImage = parseImage;
|
package/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "3.1.
|
|
1
|
+
export declare const VERSION = "3.1.2";
|
package/lib/version.js
CHANGED