navy 5.0.0 → 5.0.1-rc.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 +3 -3
- package/lib/cli/doctor/index.js +1 -1
- package/lib/client/registry/get-credentials.js +36 -0
- package/lib/client/registry/get-endpoint.js +113 -0
- package/lib/client/registry/get-fat-manifest.js +41 -0
- package/lib/client/registry/get-token.js +45 -0
- package/lib/client/registry/helpers.js +19 -0
- package/lib/domain/container-image.js +73 -0
- package/lib/domain/oci-api-specification.js +15 -0
- package/lib/index.d.ts +4 -0
- package/lib/util/has-update.js +32 -21
- package/package.json +9 -8
- package/lib/util/__tests__/registry-client.js +0 -82
- package/lib/util/registry-client.js +0 -81
package/README.md
CHANGED
|
@@ -51,11 +51,11 @@ You can customise the functionality of Navy by writing Javascript plugins which
|
|
|
51
51
|
$ npm install -g navy
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
- [Read more of the documentation on the website](https://
|
|
55
|
-
- [GitHub page](https://github.com/
|
|
54
|
+
- [Read more of the documentation on the website](https://moneyhub.github.io/navy/)
|
|
55
|
+
- [GitHub page](https://github.com/moneyhub/navy)
|
|
56
56
|
|
|
57
57
|
## License
|
|
58
58
|
|
|
59
59
|
Licensed under the MIT License.
|
|
60
60
|
|
|
61
|
-
[View the full license here](https://raw.githubusercontent.com/
|
|
61
|
+
[View the full license here](https://raw.githubusercontent.com/moneyhub/navy/master/LICENSE).
|
package/lib/cli/doctor/index.js
CHANGED
|
@@ -43,7 +43,7 @@ async function _default() {
|
|
|
43
43
|
console.log(_chalk.default.green(' ✔ Finished tests'));
|
|
44
44
|
console.log();
|
|
45
45
|
console.log(' Please try running Navy again if it wasn\'t working before.');
|
|
46
|
-
console.log(' If you still have problems, please open an issue at https://github.com/
|
|
46
|
+
console.log(' If you still have problems, please open an issue at https://github.com/moneyhub/navy/issues/new');
|
|
47
47
|
console.log();
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _ramda = require("ramda");
|
|
9
|
+
|
|
10
|
+
var _execAsync = require("../../util/exec-async");
|
|
11
|
+
|
|
12
|
+
var _containerImage = require("../../domain/container-image");
|
|
13
|
+
|
|
14
|
+
const getAuthenticationForRegistry = async registry => {
|
|
15
|
+
try {
|
|
16
|
+
const credentialStore = registry === _containerImage.DEFAULT_REGISTRY ? _containerImage.DEFAULT_REGISTRY_AUTH : registry;
|
|
17
|
+
const options = [credentialStore, '|', 'docker-credential-desktop', 'get'];
|
|
18
|
+
const stdout = await (0, _execAsync.execAsync)('echo', options, _ramda.always, {
|
|
19
|
+
cwd: process.cwd()
|
|
20
|
+
});
|
|
21
|
+
const credentials = JSON.parse(stdout);
|
|
22
|
+
return {
|
|
23
|
+
username: credentials['Username'],
|
|
24
|
+
password: credentials['Secret']
|
|
25
|
+
};
|
|
26
|
+
} catch (exception) {
|
|
27
|
+
return {
|
|
28
|
+
username: '',
|
|
29
|
+
password: ''
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
var _default = getAuthenticationForRegistry;
|
|
35
|
+
exports.default = _default;
|
|
36
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
|
|
10
|
+
var R = _interopRequireWildcard(require("ramda"));
|
|
11
|
+
|
|
12
|
+
var _https = _interopRequireDefault(require("https"));
|
|
13
|
+
|
|
14
|
+
var _nodeFetch = _interopRequireDefault(require("node-fetch"));
|
|
15
|
+
|
|
16
|
+
var _getToken = _interopRequireDefault(require("./get-token"));
|
|
17
|
+
|
|
18
|
+
var _ociApiSpecification = require("../../domain/oci-api-specification");
|
|
19
|
+
|
|
20
|
+
var _getCredentials = _interopRequireDefault(require("./get-credentials"));
|
|
21
|
+
|
|
22
|
+
var _helpers = require("./helpers");
|
|
23
|
+
|
|
24
|
+
var _parsers = _interopRequireDefault(require("www-authenticate/lib/parsers"));
|
|
25
|
+
|
|
26
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
27
|
+
|
|
28
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
29
|
+
|
|
30
|
+
const handleUnauthorizedResponse = async ({
|
|
31
|
+
response,
|
|
32
|
+
context,
|
|
33
|
+
url
|
|
34
|
+
}) => {
|
|
35
|
+
const challenges = response.headers.get('WWW-Authenticate');
|
|
36
|
+
const {
|
|
37
|
+
scheme,
|
|
38
|
+
parms
|
|
39
|
+
} = new _parsers.default.WWW_Authenticate(challenges);
|
|
40
|
+
|
|
41
|
+
if (scheme === 'Basic') {
|
|
42
|
+
throw new Error('Invalid authentication');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (scheme !== 'Bearer') {
|
|
46
|
+
throw new Error('Invalid authentication');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const token = await (0, _getToken.default)(parms, context);
|
|
51
|
+
const options = R.mergeDeepRight(context, {
|
|
52
|
+
headers: {
|
|
53
|
+
'Authorization': `Bearer ${token}`
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
const response = await (0, _nodeFetch.default)(url, options);
|
|
57
|
+
|
|
58
|
+
if (response.status === 401) {
|
|
59
|
+
throw new Error('Access denied');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return response;
|
|
63
|
+
} catch (exception) {
|
|
64
|
+
throw exception;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const get = async ({
|
|
69
|
+
allowUnauthorizedRequest = false,
|
|
70
|
+
endpoint,
|
|
71
|
+
registry,
|
|
72
|
+
options = {}
|
|
73
|
+
}) => {
|
|
74
|
+
const url = _ociApiSpecification.restSpecification.operation(registry, endpoint);
|
|
75
|
+
|
|
76
|
+
const credentials = await (0, _getCredentials.default)(registry);
|
|
77
|
+
const context = R.mergeDeepLeft(options, {
|
|
78
|
+
headers: R.reject(R.isNil, {
|
|
79
|
+
'Authorization': (0, _helpers.basicAuthentication)(credentials)
|
|
80
|
+
}),
|
|
81
|
+
agent: new _https.default.Agent({
|
|
82
|
+
rejectUnauthorized: !allowUnauthorizedRequest
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const response = await (0, _nodeFetch.default)(url, context);
|
|
88
|
+
|
|
89
|
+
switch (response.status) {
|
|
90
|
+
case 401:
|
|
91
|
+
return handleUnauthorizedResponse({
|
|
92
|
+
response,
|
|
93
|
+
context,
|
|
94
|
+
url
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
case 404:
|
|
98
|
+
throw new Error('Operation not found', {
|
|
99
|
+
status: 404,
|
|
100
|
+
body: await response.json()
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
default:
|
|
104
|
+
return response;
|
|
105
|
+
}
|
|
106
|
+
} catch (exception) {
|
|
107
|
+
throw exception;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
var _default = get;
|
|
112
|
+
exports.default = _default;
|
|
113
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
|
|
10
|
+
var _getEndpoint = _interopRequireDefault(require("./get-endpoint"));
|
|
11
|
+
|
|
12
|
+
var _ociApiSpecification = require("../../domain/oci-api-specification");
|
|
13
|
+
|
|
14
|
+
const getFatManifest = async ({
|
|
15
|
+
allowUnauthorizedRequest = false,
|
|
16
|
+
repository,
|
|
17
|
+
registry,
|
|
18
|
+
tag
|
|
19
|
+
}) => {
|
|
20
|
+
const endpoint = _ociApiSpecification.restSpecification.getManifest(repository, tag);
|
|
21
|
+
|
|
22
|
+
const options = {
|
|
23
|
+
headers: {
|
|
24
|
+
'Accept': _ociApiSpecification.MEDIA_TYPES.FAT_MANIFEST
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const response = await (0, _getEndpoint.default)({
|
|
28
|
+
allowUnauthorizedRequest,
|
|
29
|
+
endpoint,
|
|
30
|
+
registry,
|
|
31
|
+
options
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
tag: response.headers.get('docker-content-digest'),
|
|
35
|
+
data: await response.json()
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
var _default = getFatManifest;
|
|
40
|
+
exports.default = _default;
|
|
41
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
|
|
10
|
+
var _nodeFetch = _interopRequireDefault(require("node-fetch"));
|
|
11
|
+
|
|
12
|
+
var R = _interopRequireWildcard(require("ramda"));
|
|
13
|
+
|
|
14
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
15
|
+
|
|
16
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
17
|
+
|
|
18
|
+
const getToken = async ({
|
|
19
|
+
realm,
|
|
20
|
+
service,
|
|
21
|
+
scope
|
|
22
|
+
}, {
|
|
23
|
+
headers,
|
|
24
|
+
agent
|
|
25
|
+
}) => {
|
|
26
|
+
const url = `${realm}?service=${service}&scope=${scope}`;
|
|
27
|
+
const options = {
|
|
28
|
+
headers: R.pick(['Authorization'], headers),
|
|
29
|
+
agent
|
|
30
|
+
};
|
|
31
|
+
const response = await (0, _nodeFetch.default)(url, options);
|
|
32
|
+
|
|
33
|
+
if (response.status === 401) {
|
|
34
|
+
throw new Error('Invalid authentication');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
token
|
|
39
|
+
} = await response.json();
|
|
40
|
+
return token;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
var _default = getToken;
|
|
44
|
+
exports.default = _default;
|
|
45
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.basicAuthentication = void 0;
|
|
7
|
+
|
|
8
|
+
var _ramda = require("ramda");
|
|
9
|
+
|
|
10
|
+
const basicAuthentication = credentials => {
|
|
11
|
+
if ((0, _ramda.isEmpty)(credentials)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const parameters = Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64');
|
|
16
|
+
return `Basic ${parameters}`;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
exports.basicAuthentication = basicAuthentication;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DEFAULT_REGISTRY_AUTH = exports.DEFAULT_REGISTRY = void 0;
|
|
7
|
+
exports.getImageFromImageWithTag = getImageFromImageWithTag;
|
|
8
|
+
exports.getRegistryFromImage = getRegistryFromImage;
|
|
9
|
+
exports.getRepositoryFromImage = getRepositoryFromImage;
|
|
10
|
+
exports.getTagFromImageWithTag = getTagFromImageWithTag;
|
|
11
|
+
// docker v2 registry URL
|
|
12
|
+
const DEFAULT_REGISTRY = 'registry-1.docker.io'; // the url which docker stores in .docker/config.json for auth
|
|
13
|
+
|
|
14
|
+
exports.DEFAULT_REGISTRY = DEFAULT_REGISTRY;
|
|
15
|
+
const DEFAULT_REGISTRY_AUTH = 'https://index.docker.io/v1/'; // someregistry.com/some/image:latest -> someregistry.com/some/image
|
|
16
|
+
|
|
17
|
+
exports.DEFAULT_REGISTRY_AUTH = DEFAULT_REGISTRY_AUTH;
|
|
18
|
+
|
|
19
|
+
function getImageFromImageWithTag(imageWithTag) {
|
|
20
|
+
if (imageWithTag.lastIndexOf('/') !== -1) {
|
|
21
|
+
const lastSlash = imageWithTag.lastIndexOf('/');
|
|
22
|
+
|
|
23
|
+
if (imageWithTag.indexOf(':', lastSlash) !== -1) {
|
|
24
|
+
return imageWithTag.substring(0, imageWithTag.indexOf(':', lastSlash));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return imageWithTag;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (imageWithTag.indexOf(':') !== -1) {
|
|
31
|
+
return imageWithTag.substring(0, imageWithTag.indexOf(':'));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return imageWithTag;
|
|
35
|
+
} // someregistry.com/some/image:latest -> latest
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
function getTagFromImageWithTag(imageWithTag) {
|
|
39
|
+
if (imageWithTag.lastIndexOf('/') !== -1) {
|
|
40
|
+
const lastSlash = imageWithTag.lastIndexOf('/');
|
|
41
|
+
|
|
42
|
+
if (imageWithTag.indexOf(':', lastSlash) !== -1) {
|
|
43
|
+
return imageWithTag.substring(imageWithTag.indexOf(':', lastSlash) + 1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return 'latest';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (imageWithTag.indexOf(':') !== -1) {
|
|
50
|
+
return imageWithTag.substring(imageWithTag.indexOf(':') + 1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return 'latest';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getRegistryFromImage(image) {
|
|
57
|
+
const parsedRegistry = image.match(/([a-zA-Z0-9-.:]+)\/[a-zA-Z0-9-]+\/[a-zA-Z0-9-]+/);
|
|
58
|
+
const registry = parsedRegistry != null && parsedRegistry.length > 1 ? parsedRegistry[1] : DEFAULT_REGISTRY;
|
|
59
|
+
return registry;
|
|
60
|
+
} // someregistry.com/some/image -> some/image
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
function getRepositoryFromImage(image) {
|
|
64
|
+
if (image.lastIndexOf('/') === -1) {
|
|
65
|
+
return 'library/' + image;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (image.split('/').length === 3) {
|
|
69
|
+
return image.substring(image.indexOf('/') + 1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return image;
|
|
73
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.restSpecification = exports.MEDIA_TYPES = void 0;
|
|
7
|
+
const restSpecification = {
|
|
8
|
+
operation: (registry, endpoint) => `https://${registry}/v2/${endpoint}`,
|
|
9
|
+
getManifest: (repository, tag) => `${repository}/manifests/${tag}`
|
|
10
|
+
};
|
|
11
|
+
exports.restSpecification = restSpecification;
|
|
12
|
+
const MEDIA_TYPES = {
|
|
13
|
+
FAT_MANIFEST: 'application/vnd.docker.distribution.manifest.list.v2+json'
|
|
14
|
+
};
|
|
15
|
+
exports.MEDIA_TYPES = MEDIA_TYPES;
|
package/lib/index.d.ts
CHANGED
package/lib/util/has-update.js
CHANGED
|
@@ -5,33 +5,44 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", {
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
|
-
exports.default =
|
|
8
|
+
exports.default = void 0;
|
|
9
9
|
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
var _registryClient = require("./registry-client");
|
|
10
|
+
var R = _interopRequireWildcard(require("ramda"));
|
|
13
11
|
|
|
14
12
|
var _dockerClient = _interopRequireDefault(require("./docker-client"));
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
const imageConfig = await _dockerClient.default.getImage(currentImageId).inspect();
|
|
18
|
-
const currentImageContainerId = imageConfig.ContainerConfig.Image;
|
|
19
|
-
const image = (0, _simpleDockerRegistryClient.imageFromImageWithTag)(imageWithTag);
|
|
20
|
-
const client = await (0, _registryClient.getRegistryClient)(image, navyFile);
|
|
21
|
-
let manifest;
|
|
14
|
+
var _getFatManifest = _interopRequireDefault(require("../client/registry/get-fat-manifest"));
|
|
22
15
|
|
|
23
|
-
|
|
24
|
-
manifest = await client.request((0, _simpleDockerRegistryClient.localImageFromImage)(image) + '/manifests/' + (0, _simpleDockerRegistryClient.tagFromImageWithTag)(imageWithTag));
|
|
25
|
-
} catch (ex) {
|
|
26
|
-
if (ex.body && ex.body.errors[0].code === 'MANIFEST_UNKNOWN') {
|
|
27
|
-
return 'UNKNOWN_REMOTE';
|
|
28
|
-
}
|
|
16
|
+
var _containerImage = require("../domain/container-image");
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
19
|
+
|
|
20
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
32
21
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
const hasUpdate = async (imageWithTag, currentImageId, navyFile) => {
|
|
23
|
+
const tag = (0, _containerImage.getTagFromImageWithTag)(imageWithTag);
|
|
24
|
+
const image = (0, _containerImage.getImageFromImageWithTag)(imageWithTag);
|
|
25
|
+
const registry = (0, _containerImage.getRegistryFromImage)(image);
|
|
26
|
+
const repository = (0, _containerImage.getRepositoryFromImage)(image);
|
|
27
|
+
const allowUnauthorizedRequest = R.includes(registry, R.path(['ignoreUnauthorizedRequestsForRegistries'], navyFile));
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const {
|
|
31
|
+
RepoDigests
|
|
32
|
+
} = await _dockerClient.default.getImage(currentImageId).inspect();
|
|
33
|
+
const manifest = await (0, _getFatManifest.default)({
|
|
34
|
+
allowUnauthorizedRequest,
|
|
35
|
+
repository,
|
|
36
|
+
registry,
|
|
37
|
+
tag
|
|
38
|
+
});
|
|
39
|
+
const remoteRepoDigest = `${image}@${manifest.tag}`;
|
|
40
|
+
return !R.includes(remoteRepoDigest, RepoDigests);
|
|
41
|
+
} catch {
|
|
42
|
+
return 'INVALID_REMOTE';
|
|
43
|
+
}
|
|
44
|
+
};
|
|
36
45
|
|
|
46
|
+
var _default = hasUpdate;
|
|
47
|
+
exports.default = _default;
|
|
37
48
|
module.exports = exports.default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "navy",
|
|
3
|
-
"version": "5.0.0",
|
|
3
|
+
"version": "5.0.1-rc.0",
|
|
4
4
|
"description": "Quick and powerful development environments using Docker and Docker Compose",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
},
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "git+https://github.com/
|
|
15
|
+
"url": "git+https://github.com/moneyhub/navy.git"
|
|
16
16
|
},
|
|
17
17
|
"keywords": [
|
|
18
18
|
"docker",
|
|
19
19
|
"environment"
|
|
20
20
|
],
|
|
21
|
-
"author": "
|
|
21
|
+
"author": "Moneyhub Financial Technology Ltd",
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"bugs": {
|
|
24
|
-
"url": "https://github.com/
|
|
24
|
+
"url": "https://github.com/moneyhub/navy/issues"
|
|
25
25
|
},
|
|
26
|
-
"homepage": "https://github.com/
|
|
26
|
+
"homepage": "https://github.com/moneyhub/navy#readme",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@babel/runtime": "^7",
|
|
29
29
|
"bluebird": "^3.7.2",
|
|
@@ -38,13 +38,14 @@
|
|
|
38
38
|
"js-yaml": "^3.14.1",
|
|
39
39
|
"lodash": "^4.17.21",
|
|
40
40
|
"mkdirp": "^0.5.5",
|
|
41
|
-
"node-forge": "^
|
|
41
|
+
"node-forge": "^1.3.0",
|
|
42
42
|
"opn": "^5.5.0",
|
|
43
43
|
"pad": "^3.2.0",
|
|
44
44
|
"promise-retry": "^1.1.1",
|
|
45
|
+
"ramda": "^0.28.0",
|
|
45
46
|
"resolve": "^1.20.0",
|
|
46
47
|
"rimraf": "^2.7.1",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
48
|
+
"strip-ansi": "^3.0.1",
|
|
49
|
+
"www-authenticate": "^0.6.3"
|
|
49
50
|
}
|
|
50
51
|
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _chai = require("chai");
|
|
4
|
-
|
|
5
|
-
var _registryClient = require("../registry-client");
|
|
6
|
-
|
|
7
|
-
/* eslint-env mocha */
|
|
8
|
-
|
|
9
|
-
/* eslint-disable no-unused-expressions */
|
|
10
|
-
|
|
11
|
-
/* eslint-enable chai-friendly/no-unused-expressions */
|
|
12
|
-
describe('docker registry client', function () {
|
|
13
|
-
describe('getAuthForRegistry', function () {
|
|
14
|
-
beforeEach(function () {
|
|
15
|
-
this.config = {
|
|
16
|
-
auths: {
|
|
17
|
-
'quay.io': {
|
|
18
|
-
auth: 'pretend-base64-of-username-and-password',
|
|
19
|
-
email: 'test@navy.rocks'
|
|
20
|
-
},
|
|
21
|
-
'https://index.docker.io/v1/': {
|
|
22
|
-
auth: 'pretend-base64-of-username-and-password',
|
|
23
|
-
email: 'test@navy.rocks'
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
});
|
|
28
|
-
it('should return the auth config for the given registry', function () {
|
|
29
|
-
(0, _chai.expect)((0, _registryClient.getAuthForRegistry)('quay.io', this.config)).to.eql({
|
|
30
|
-
auth: 'pretend-base64-of-username-and-password',
|
|
31
|
-
email: 'test@navy.rocks'
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
it('should return the auth config for Docker Hub if given', function () {
|
|
35
|
-
(0, _chai.expect)((0, _registryClient.getAuthForRegistry)('registry-1.docker.io', this.config)).to.eql({
|
|
36
|
-
auth: 'pretend-base64-of-username-and-password',
|
|
37
|
-
email: 'test@navy.rocks'
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
it('should return null if there is no authentication for the given registry', function () {
|
|
41
|
-
(0, _chai.expect)((0, _registryClient.getAuthForRegistry)('foo.bar', this.config)).to.be.null;
|
|
42
|
-
});
|
|
43
|
-
it('should return null if there is no docker config', function () {
|
|
44
|
-
(0, _chai.expect)((0, _registryClient.getAuthForRegistry)('foo.bar', null)).to.be.null;
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
describe('credentialsFromAuth', function () {
|
|
48
|
-
it('should return the username and password from base64 encoded auth', function () {
|
|
49
|
-
(0, _chai.expect)((0, _registryClient.credentialsFromAuth)({
|
|
50
|
-
auth: 'YmFyOnBhc3N3b3Jk'
|
|
51
|
-
})).to.eql({
|
|
52
|
-
username: 'bar',
|
|
53
|
-
password: 'password'
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
it('should throw invariant violation when auth is invalid base64', function () {
|
|
57
|
-
(0, _chai.expect)(() => (0, _registryClient.credentialsFromAuth)({
|
|
58
|
-
auth: 'zjasj'
|
|
59
|
-
})).to.throw('Invalid base64 string in docker/config.json for registry authentication');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe('registryFromImage', function () {
|
|
63
|
-
it('should return the correct registry domain from the given image', function () {
|
|
64
|
-
(0, _chai.expect)((0, _registryClient.registryFromImage)('quay.io/someorg/someimage')).to.equal('quay.io');
|
|
65
|
-
});
|
|
66
|
-
it('should return the correct registry domain from the given image with tag', function () {
|
|
67
|
-
(0, _chai.expect)((0, _registryClient.registryFromImage)('quay.io/someorg/someimage:some-tag')).to.equal('quay.io');
|
|
68
|
-
});
|
|
69
|
-
it('should return the correct registry domain from the given image from docker hub', function () {
|
|
70
|
-
(0, _chai.expect)((0, _registryClient.registryFromImage)('someuser/someimage')).to.equal('registry-1.docker.io');
|
|
71
|
-
});
|
|
72
|
-
it('should return the correct registry domain from the given image with tag from docker hub', function () {
|
|
73
|
-
(0, _chai.expect)((0, _registryClient.registryFromImage)('someuser/someimage:some-tag')).to.equal('registry-1.docker.io');
|
|
74
|
-
});
|
|
75
|
-
it('should return the correct registry domain from the given library image from docker hub', function () {
|
|
76
|
-
(0, _chai.expect)((0, _registryClient.registryFromImage)('someimage')).to.equal('registry-1.docker.io');
|
|
77
|
-
});
|
|
78
|
-
it('should return the correct registry domain from the given library image with tag from docker hub', function () {
|
|
79
|
-
(0, _chai.expect)((0, _registryClient.registryFromImage)('someimage:some-tag')).to.equal('registry-1.docker.io');
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
});
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
5
|
-
Object.defineProperty(exports, "__esModule", {
|
|
6
|
-
value: true
|
|
7
|
-
});
|
|
8
|
-
exports.credentialsFromAuth = credentialsFromAuth;
|
|
9
|
-
exports.getAuthForRegistry = getAuthForRegistry;
|
|
10
|
-
exports.getRegistryClient = getRegistryClient;
|
|
11
|
-
exports.registryFromImage = registryFromImage;
|
|
12
|
-
|
|
13
|
-
var _path = _interopRequireDefault(require("path"));
|
|
14
|
-
|
|
15
|
-
var _invariant = _interopRequireDefault(require("invariant"));
|
|
16
|
-
|
|
17
|
-
var _simpleDockerRegistryClient = require("simple-docker-registry-client");
|
|
18
|
-
|
|
19
|
-
var _fs = _interopRequireDefault(require("./fs"));
|
|
20
|
-
|
|
21
|
-
const DEFAULT_REGISTRY = 'registry-1.docker.io'; // docker v2 registry URL
|
|
22
|
-
|
|
23
|
-
const DEFAULT_REGISTRY_AUTH = 'https://index.docker.io/v1/'; // the url which docker stores in .docker/config.json for auth
|
|
24
|
-
|
|
25
|
-
async function getDockerUserConfig() {
|
|
26
|
-
(0, _invariant.default)(process.env.HOME, "NO_HOME_DIRECTORY: No home directory available");
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const rawConfig = await _fs.default.readFileAsync(_path.default.join(process.env.HOME, '.docker', 'config.json'));
|
|
30
|
-
const config = JSON.parse(rawConfig);
|
|
31
|
-
return config;
|
|
32
|
-
} catch (ex) {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getAuthForRegistry(registry, dockerUserConfig) {
|
|
38
|
-
if (registry === DEFAULT_REGISTRY) {
|
|
39
|
-
registry = DEFAULT_REGISTRY_AUTH;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (dockerUserConfig && dockerUserConfig.auths && dockerUserConfig.auths[registry]) {
|
|
43
|
-
return dockerUserConfig.auths[registry];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function registryFromImage(image) {
|
|
50
|
-
const parsedRegistry = image.match(/([a-zA-Z0-9-.:]+)\/[a-zA-Z0-9-]+\/[a-zA-Z0-9-]+/);
|
|
51
|
-
const registry = parsedRegistry != null && parsedRegistry.length > 1 ? parsedRegistry[1] : DEFAULT_REGISTRY;
|
|
52
|
-
return registry;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function credentialsFromAuth(auth) {
|
|
56
|
-
if (auth && auth.auth) {
|
|
57
|
-
(0, _invariant.default)(auth.auth.match(/^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/) != null, "DOCKER_CONFIG_INVALID_AUTH_BASE64: Invalid base64 string in docker/config.json for registry authentication");
|
|
58
|
-
const decoded = Buffer.from(auth.auth, 'base64').toString();
|
|
59
|
-
const parts = decoded.split(':');
|
|
60
|
-
return {
|
|
61
|
-
username: parts[0],
|
|
62
|
-
password: parts[1]
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async function getRegistryClient(image, navyFile) {
|
|
70
|
-
const registry = registryFromImage(image);
|
|
71
|
-
const opts = {
|
|
72
|
-
registry: `https://${registry}`,
|
|
73
|
-
allowUnauthorized: navyFile && navyFile.ignoreUnauthorizedRequestsForRegistries && navyFile.ignoreUnauthorizedRequestsForRegistries.indexOf(registry) !== -1
|
|
74
|
-
}; // try and work out auth
|
|
75
|
-
|
|
76
|
-
const auth = getAuthForRegistry(registry, await getDockerUserConfig());
|
|
77
|
-
opts.credentials = credentialsFromAuth(auth);
|
|
78
|
-
return {
|
|
79
|
-
request: url => (0, _simpleDockerRegistryClient.registryRequest)(url, opts)
|
|
80
|
-
};
|
|
81
|
-
}
|