navy 6.0.0 → 7.0.0-alpha.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.
Files changed (149) hide show
  1. package/lib/__tests__/config-provider.js +75 -0
  2. package/lib/__tests__/config.js +130 -0
  3. package/lib/__tests__/driver-logging.js +148 -0
  4. package/lib/__tests__/driver.js +19 -0
  5. package/lib/__tests__/errors.js +49 -0
  6. package/lib/__tests__/http-proxy.js +214 -0
  7. package/lib/__tests__/index.js +25 -0
  8. package/lib/__tests__/service.js +16 -0
  9. package/lib/cli/__tests__/develop.js +239 -0
  10. package/lib/cli/__tests__/external-ip.js +68 -0
  11. package/lib/cli/__tests__/health.js +257 -0
  12. package/lib/cli/__tests__/https.js +210 -0
  13. package/lib/cli/__tests__/import.js +110 -0
  14. package/lib/cli/__tests__/index.js +118 -0
  15. package/lib/cli/__tests__/lan-ip.js +90 -0
  16. package/lib/cli/__tests__/launch.js +179 -0
  17. package/lib/cli/__tests__/live.js +155 -0
  18. package/lib/cli/__tests__/local-ip.js +72 -0
  19. package/lib/cli/__tests__/logs.js +52 -0
  20. package/lib/cli/__tests__/open.js +65 -0
  21. package/lib/cli/__tests__/program.js +472 -0
  22. package/lib/cli/__tests__/ps.js +345 -0
  23. package/lib/cli/__tests__/refresh-config.js +95 -0
  24. package/lib/cli/__tests__/run.js +54 -0
  25. package/lib/cli/__tests__/status.js +204 -0
  26. package/lib/cli/__tests__/updates.js +243 -0
  27. package/lib/cli/__tests__/wait-for-healthy.js +134 -0
  28. package/lib/cli/config/__tests__/index.js +275 -0
  29. package/lib/cli/config/__tests__/wrapper.js +53 -0
  30. package/lib/cli/config/index.js +19 -37
  31. package/lib/cli/config/wrapper.js +0 -6
  32. package/lib/cli/develop.js +7 -21
  33. package/lib/cli/doctor/__tests__/clean-compose-files.js +78 -0
  34. package/lib/cli/doctor/__tests__/index.js +67 -0
  35. package/lib/cli/doctor/__tests__/invalid-compose-config.js +103 -0
  36. package/lib/cli/doctor/__tests__/invalid-state.js +83 -0
  37. package/lib/cli/doctor/__tests__/util.js +91 -0
  38. package/lib/cli/doctor/clean-compose-files.js +5 -13
  39. package/lib/cli/doctor/index.js +0 -12
  40. package/lib/cli/doctor/invalid-compose-config.js +0 -4
  41. package/lib/cli/doctor/invalid-state.js +0 -6
  42. package/lib/cli/doctor/util.js +0 -10
  43. package/lib/cli/external-ip.js +0 -4
  44. package/lib/cli/health.js +0 -12
  45. package/lib/cli/https.js +9 -25
  46. package/lib/cli/import.js +0 -12
  47. package/lib/cli/index.js +0 -9
  48. package/lib/cli/lan-ip.js +2 -8
  49. package/lib/cli/launch.js +0 -9
  50. package/lib/cli/live.js +6 -16
  51. package/lib/cli/local-ip.js +2 -7
  52. package/lib/cli/logs.js +0 -3
  53. package/lib/cli/open.js +2 -7
  54. package/lib/cli/program.js +73 -101
  55. package/lib/cli/ps.js +1 -21
  56. package/lib/cli/refresh-config.js +0 -7
  57. package/lib/cli/run.js +0 -3
  58. package/lib/cli/status.js +0 -11
  59. package/lib/cli/updates.js +0 -22
  60. package/lib/cli/util/__tests__/get-or-initialise-navy.js +66 -0
  61. package/lib/cli/util/__tests__/import.js +123 -0
  62. package/lib/cli/util/__tests__/index.js +17 -0
  63. package/lib/cli/util/__tests__/merge-action-options.js +47 -0
  64. package/lib/cli/util/__tests__/reconfigure.js +78 -0
  65. package/lib/cli/util/get-or-initialise-navy.js +0 -7
  66. package/lib/cli/util/import.js +0 -9
  67. package/lib/cli/util/index.js +0 -2
  68. package/lib/cli/util/merge-action-options.js +20 -0
  69. package/lib/cli/util/reconfigure.js +0 -4
  70. package/lib/cli/wait-for-healthy.js +0 -21
  71. package/lib/client/registry/__tests__/get-credentials.js +62 -0
  72. package/lib/client/registry/__tests__/get-endpoint.js +124 -0
  73. package/lib/client/registry/__tests__/get-fat-manifest.js +67 -0
  74. package/lib/client/registry/__tests__/get-token.js +66 -0
  75. package/lib/client/registry/__tests__/helpers.js +26 -0
  76. package/lib/client/registry/get-credentials.js +3 -9
  77. package/lib/client/registry/get-endpoint.js +29 -63
  78. package/lib/client/registry/get-fat-manifest.js +2 -9
  79. package/lib/client/registry/get-token.js +2 -13
  80. package/lib/client/registry/helpers.js +0 -4
  81. package/lib/config-provider.js +0 -12
  82. package/lib/config-providers/filesystem/__tests__/index.js +176 -0
  83. package/lib/config-providers/filesystem/index.js +5 -23
  84. package/lib/config-providers/npm/__tests__/index.js +226 -0
  85. package/lib/config-providers/npm/__tests__/util.js +1 -2
  86. package/lib/config-providers/npm/index.js +12 -35
  87. package/lib/config-providers/npm/util.js +0 -3
  88. package/lib/config.js +4 -19
  89. package/lib/domain/__tests__/container-image.js +81 -0
  90. package/lib/domain/__tests__/oci-api-specification.js +23 -0
  91. package/lib/domain/container-image.js +8 -21
  92. package/lib/domain/oci-api-specification.js +3 -5
  93. package/lib/driver-logging.js +0 -19
  94. package/lib/driver.js +0 -4
  95. package/lib/drivers/docker-compose/__tests__/client.js +249 -0
  96. package/lib/drivers/docker-compose/__tests__/index.js +430 -0
  97. package/lib/drivers/docker-compose/client.js +0 -16
  98. package/lib/drivers/docker-compose/index.js +7 -49
  99. package/lib/errors.js +0 -10
  100. package/lib/http-proxy.js +28 -23
  101. package/lib/index.js +1 -9
  102. package/lib/middleware/__tests__/add-service-proxy-config.js +258 -0
  103. package/lib/middleware/__tests__/develop.js +120 -0
  104. package/lib/middleware/__tests__/helpers.js +154 -0
  105. package/lib/middleware/__tests__/port-override.js +125 -0
  106. package/lib/middleware/__tests__/set-env-vars.js +94 -0
  107. package/lib/middleware/__tests__/set-image.js +76 -0
  108. package/lib/middleware/__tests__/set-logging-driver.js +94 -0
  109. package/lib/middleware/__tests__/tag-override.js +92 -0
  110. package/lib/middleware/add-service-proxy-config.js +8 -16
  111. package/lib/middleware/develop.js +2 -5
  112. package/lib/middleware/helpers.js +6 -12
  113. package/lib/middleware/port-override.js +5 -8
  114. package/lib/middleware/set-env-vars.js +6 -6
  115. package/lib/middleware/set-image.js +4 -5
  116. package/lib/middleware/set-logging-driver.js +6 -6
  117. package/lib/middleware/tag-override.js +4 -6
  118. package/lib/navy/__tests__/default-middleware.js +40 -0
  119. package/lib/navy/__tests__/index.js +1612 -0
  120. package/lib/navy/__tests__/middleware.js +71 -0
  121. package/lib/navy/__tests__/plugin-interface.js +121 -0
  122. package/lib/navy/__tests__/state.js +103 -0
  123. package/lib/navy/__tests__/util.js +24 -0
  124. package/lib/navy/default-middleware.js +0 -10
  125. package/lib/navy/index.js +83 -138
  126. package/lib/navy/middleware.js +0 -6
  127. package/lib/navy/plugin-interface.js +2 -10
  128. package/lib/navy/state.js +12 -24
  129. package/lib/navy/util.js +0 -1
  130. package/lib/service.js +2 -3
  131. package/lib/util/__tests__/exec-async.js +83 -0
  132. package/lib/util/__tests__/external-ip.js +97 -2
  133. package/lib/util/__tests__/get-lan-ip.js +46 -0
  134. package/lib/util/__tests__/has-update.js +136 -0
  135. package/lib/util/__tests__/https.js +301 -0
  136. package/lib/util/__tests__/navyrc.js +45 -0
  137. package/lib/util/__tests__/service-host.js +63 -5
  138. package/lib/util/__tests__/table.js +44 -0
  139. package/lib/util/docker-client.js +2 -10
  140. package/lib/util/exec-async.js +0 -4
  141. package/lib/util/external-ip.js +8 -12
  142. package/lib/util/fs.js +1 -6
  143. package/lib/util/get-lan-ip.js +0 -5
  144. package/lib/util/has-update.js +2 -14
  145. package/lib/util/https.js +11 -55
  146. package/lib/util/navyrc.js +0 -5
  147. package/lib/util/service-host.js +0 -17
  148. package/lib/util/table.js +0 -6
  149. package/package.json +14 -13
package/lib/navy/state.js CHANGED
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
4
  Object.defineProperty(exports, "__esModule", {
6
5
  value: true
7
6
  });
@@ -12,64 +11,53 @@ exports.pathToNavyRoot = pathToNavyRoot;
12
11
  exports.pathToNavys = pathToNavys;
13
12
  exports.pathToState = pathToState;
14
13
  exports.saveState = saveState;
15
-
16
14
  var _path = _interopRequireDefault(require("path"));
17
-
18
15
  var _invariant = _interopRequireDefault(require("invariant"));
19
-
20
- var _bluebird = _interopRequireDefault(require("bluebird"));
21
-
22
- var _fs = _interopRequireDefault(require("../util/fs"));
23
-
16
+ var _fs = require("fs");
17
+ var _fs2 = _interopRequireDefault(require("../util/fs"));
24
18
  const debug = require('debug')('navy:state');
25
-
26
- const mkdirp = _bluebird.default.promisify(require('mkdirp'));
27
-
28
- const rimraf = _bluebird.default.promisify(require('rimraf'));
29
-
30
19
  function pathToNavyRoot() {
31
20
  const home = process.env.HOME;
32
21
  (0, _invariant.default)(home, "NO_HOME_DIRECTORY: No home directory available");
33
22
  return _path.default.join(home, '.navy');
34
23
  }
35
-
36
24
  function pathToNavys() {
37
25
  return _path.default.join(pathToNavyRoot(), 'navies');
38
26
  }
39
-
40
27
  function pathToNavy(normalisedEnvName) {
41
28
  return _path.default.join(pathToNavys(), normalisedEnvName);
42
29
  }
43
-
44
30
  function pathToState(normalisedEnvName) {
45
31
  return _path.default.join(pathToNavy(normalisedEnvName), 'state.json');
46
32
  }
47
33
  /* eslint-disable no-use-before-define */
48
-
49
-
50
34
  async function getState(normalisedEnvName) {
51
35
  /* eslint-enable no-use-before-define */
52
36
  try {
53
37
  const statePath = pathToState(normalisedEnvName);
54
- const file = (await _fs.default.readFileAsync(statePath)).toString();
38
+ const file = (await _fs2.default.readFileAsync(statePath)).toString();
55
39
  debug('Got raw state for env ' + normalisedEnvName, statePath, file);
56
40
  return JSON.parse(file);
57
41
  } catch (ex) {
58
42
  return null;
59
43
  }
60
44
  }
61
-
62
45
  async function saveState(normalisedEnvName, state) {
63
46
  const statePath = pathToState(normalisedEnvName);
64
- await mkdirp(_path.default.dirname(statePath));
47
+ await _fs.promises.mkdir(_path.default.dirname(statePath), {
48
+ recursive: true
49
+ });
65
50
  debug('Writing state for env ' + normalisedEnvName, statePath, state);
66
- await _fs.default.writeFileAsync(statePath, JSON.stringify(state, null, 2));
51
+ await _fs2.default.writeFileAsync(statePath, JSON.stringify(state, null, 2));
67
52
  }
68
-
69
53
  async function deleteState(normalisedEnvName) {
70
54
  debug('Deleting state for env ' + normalisedEnvName);
71
- await rimraf(_path.default.dirname(pathToState(normalisedEnvName)));
55
+ await _fs.promises.rm(_path.default.dirname(pathToState(normalisedEnvName)), {
56
+ recursive: true,
57
+ force: true
58
+ });
72
59
  }
60
+
73
61
  /**
74
62
  * A "state" object of the current services, and other internal Navy state.
75
63
  * This can be used to hang "state" off services which can then be used by middleware at runtime to modify the
package/lib/navy/util.js CHANGED
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.normaliseNavyName = normaliseNavyName;
7
-
8
7
  function normaliseNavyName(envName) {
9
8
  return envName.replace(/\W/g, '');
10
9
  }
package/lib/service.js CHANGED
@@ -4,8 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Status = void 0;
7
- const Status = {
7
+ const Status = exports.Status = {
8
8
  RUNNING: 'running',
9
9
  EXITED: 'exited'
10
- };
11
- exports.Status = Status;
10
+ };
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _chai = require("chai");
5
+ var _sinon = _interopRequireDefault(require("sinon"));
6
+ var _events = require("events");
7
+ var _child_process = _interopRequireDefault(require("child_process"));
8
+ var _execAsync = require("../exec-async");
9
+ /* eslint-env mocha */
10
+
11
+ function createFakeChild() {
12
+ const child = new _events.EventEmitter();
13
+ child.stdout = new _events.EventEmitter();
14
+ child.stderr = new _events.EventEmitter();
15
+ return child;
16
+ }
17
+ describe('exec-async', function () {
18
+ describe('execAsync', function () {
19
+ let sandbox;
20
+ let execStub;
21
+ let fakeChild;
22
+ beforeEach(function () {
23
+ sandbox = _sinon.default.createSandbox();
24
+ fakeChild = createFakeChild();
25
+ execStub = sandbox.stub(_child_process.default, 'exec').callsFake((cmd, opts, cb) => {
26
+ process.nextTick(() => {
27
+ fakeChild.stdout.emit('data', Buffer.from('hello'));
28
+ fakeChild.stderr.emit('data', Buffer.from('warn'));
29
+ cb(null, 'hello world', '');
30
+ });
31
+ return fakeChild;
32
+ });
33
+ });
34
+ afterEach(function () {
35
+ sandbox.restore();
36
+ });
37
+ it('should resolve with stdout when the command succeeds', async function () {
38
+ const result = await (0, _execAsync.execAsync)('echo', ['hello', 'world']);
39
+ (0, _chai.expect)(result).to.equal('hello world');
40
+ (0, _chai.expect)(execStub.calledOnce).to.equal(true);
41
+ (0, _chai.expect)(execStub.firstCall.args[0]).to.equal('echo hello world');
42
+ });
43
+ it('should default args to an empty array when not provided', async function () {
44
+ const result = await (0, _execAsync.execAsync)('echo');
45
+ (0, _chai.expect)(result).to.equal('hello world');
46
+ (0, _chai.expect)(execStub.firstCall.args[0]).to.equal('echo ');
47
+ });
48
+ it('should pass opts through to child_process.exec', async function () {
49
+ const opts = {
50
+ cwd: '/tmp',
51
+ env: {
52
+ FOO: 'bar'
53
+ }
54
+ };
55
+ await (0, _execAsync.execAsync)('ls', ['-la'], null, opts);
56
+ (0, _chai.expect)(execStub.firstCall.args[1]).to.equal(opts);
57
+ });
58
+ it('should invoke the callback with the spawned child process', async function () {
59
+ const callback = _sinon.default.spy();
60
+ await (0, _execAsync.execAsync)('echo', ['hi'], callback);
61
+ (0, _chai.expect)(callback.calledOnce).to.equal(true);
62
+ (0, _chai.expect)(callback.firstCall.args[0]).to.equal(fakeChild);
63
+ });
64
+ it('should not invoke the callback when none is supplied', async function () {
65
+ await (0, _execAsync.execAsync)('echo', ['hi']);
66
+ });
67
+ it('should reject when the command fails', async function () {
68
+ execStub.restore();
69
+ const failure = new Error('boom');
70
+ execStub = sandbox.stub(_child_process.default, 'exec').callsFake((cmd, opts, cb) => {
71
+ process.nextTick(() => cb(failure));
72
+ return fakeChild;
73
+ });
74
+ let caught;
75
+ try {
76
+ await (0, _execAsync.execAsync)('false');
77
+ } catch (err) {
78
+ caught = err;
79
+ }
80
+ (0, _chai.expect)(caught).to.equal(failure);
81
+ });
82
+ });
83
+ });
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  var _chai = require("chai");
4
-
5
+ var _sinon = _interopRequireDefault(require("sinon"));
6
+ var _dns = _interopRequireDefault(require("dns"));
5
7
  var _externalIp = require("../external-ip");
6
-
7
8
  /* eslint-env mocha */
9
+
8
10
  describe('getExternalIP', function () {
9
11
  it('should return NAVY_EXTERNAL_IP if set', async function () {
10
12
  process.env.NAVY_EXTERNAL_IP = '192.168.1.10';
@@ -23,4 +25,97 @@ describe('getExternalIP', function () {
23
25
  (0, _chai.expect)(await (0, _externalIp.getExternalIP)()).to.equal('192.168.1.13');
24
26
  delete process.env.DOCKER_HOST;
25
27
  });
28
+ });
29
+ describe('external-ip (mocked)', function () {
30
+ let sandbox;
31
+ let originalEnv;
32
+ beforeEach(function () {
33
+ sandbox = _sinon.default.createSandbox();
34
+ originalEnv = {
35
+ DOCKER_HOST: process.env.DOCKER_HOST,
36
+ NAVY_HOST: process.env.NAVY_HOST,
37
+ NAVY_EXTERNAL_IP: process.env.NAVY_EXTERNAL_IP
38
+ };
39
+ delete process.env.DOCKER_HOST;
40
+ delete process.env.NAVY_HOST;
41
+ delete process.env.NAVY_EXTERNAL_IP;
42
+ });
43
+ afterEach(function () {
44
+ sandbox.restore();
45
+ for (const [name, value] of Object.entries(originalEnv)) {
46
+ if (value === undefined) {
47
+ delete process.env[name];
48
+ } else {
49
+ process.env[name] = value;
50
+ }
51
+ }
52
+ });
53
+ describe('dnsLookup', function () {
54
+ it('should resolve with the IPv4 address when dns returns family 4', async function () {
55
+ sandbox.stub(_dns.default, 'lookup').callsFake((hostname, opts, cb) => {
56
+ cb(null, '10.20.30.40', 4);
57
+ });
58
+ const ip = await (0, _externalIp.dnsLookup)('example.com');
59
+ (0, _chai.expect)(ip).to.equal('10.20.30.40');
60
+ });
61
+ it('should reject when dns.lookup returns an error', async function () {
62
+ sandbox.stub(_dns.default, 'lookup').callsFake((hostname, opts, cb) => {
63
+ cb(new Error('ENOTFOUND'));
64
+ });
65
+ let caught;
66
+ try {
67
+ await (0, _externalIp.dnsLookup)('does-not-exist');
68
+ } catch (err) {
69
+ caught = err;
70
+ }
71
+ (0, _chai.expect)(caught).to.be.an('error');
72
+ (0, _chai.expect)(caught.message).to.equal('Failed to lookup hostname "does-not-exist"');
73
+ });
74
+ it('should reject when dns.lookup returns a non-ipv4 family', async function () {
75
+ sandbox.stub(_dns.default, 'lookup').callsFake((hostname, opts, cb) => {
76
+ cb(null, '::1', 6);
77
+ });
78
+ let caught;
79
+ try {
80
+ await (0, _externalIp.dnsLookup)('ipv6-only.example');
81
+ } catch (err) {
82
+ caught = err;
83
+ }
84
+ (0, _chai.expect)(caught).to.be.an('error');
85
+ (0, _chai.expect)(caught.message).to.equal('Failed to lookup hostname "ipv6-only.example"');
86
+ });
87
+ });
88
+ describe('getExternalIP (additional branches)', function () {
89
+ it('should return NAVY_HOST (deprecated) when set', async function () {
90
+ process.env.NAVY_HOST = 'legacy.host';
91
+ (0, _chai.expect)(await (0, _externalIp.getExternalIP)()).to.equal('legacy.host');
92
+ });
93
+ it('should give NAVY_HOST precedence over NAVY_EXTERNAL_IP', async function () {
94
+ process.env.NAVY_HOST = 'legacy.host';
95
+ process.env.NAVY_EXTERNAL_IP = '1.2.3.4';
96
+ (0, _chai.expect)(await (0, _externalIp.getExternalIP)()).to.equal('legacy.host');
97
+ });
98
+ it('should DNS-resolve ipInConfig when provided', async function () {
99
+ sandbox.stub(_dns.default, 'lookup').callsFake((hostname, opts, cb) => {
100
+ (0, _chai.expect)(hostname).to.equal('docker.internal');
101
+ cb(null, '172.16.0.5', 4);
102
+ });
103
+ (0, _chai.expect)(await (0, _externalIp.getExternalIP)('docker.internal')).to.equal('172.16.0.5');
104
+ });
105
+ it('should resolve a hostname embedded in DOCKER_HOST tcp:// using DNS', async function () {
106
+ process.env.DOCKER_HOST = 'tcp://my-docker-host.example:2375';
107
+ sandbox.stub(_dns.default, 'lookup').callsFake((hostname, opts, cb) => {
108
+ (0, _chai.expect)(hostname).to.equal('my-docker-host.example');
109
+ cb(null, '203.0.113.7', 4);
110
+ });
111
+ (0, _chai.expect)(await (0, _externalIp.getExternalIP)()).to.equal('203.0.113.7');
112
+ });
113
+ it('should fall back to 127.0.0.1 when no env vars or config are set', async function () {
114
+ (0, _chai.expect)(await (0, _externalIp.getExternalIP)()).to.equal('127.0.0.1');
115
+ });
116
+ it('should fall back to 127.0.0.1 when DOCKER_HOST is set without tcp://', async function () {
117
+ process.env.DOCKER_HOST = 'unix:///var/run/docker.sock';
118
+ (0, _chai.expect)(await (0, _externalIp.getExternalIP)()).to.equal('127.0.0.1');
119
+ });
120
+ });
26
121
  });
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _chai = require("chai");
5
+ var _sinon = _interopRequireDefault(require("sinon"));
6
+ var _dns = _interopRequireDefault(require("dns"));
7
+ var _os = _interopRequireDefault(require("os"));
8
+ var _getLanIp = require("../get-lan-ip");
9
+ /* eslint-env mocha */
10
+
11
+ describe('get-lan-ip', function () {
12
+ describe('getLANIP', function () {
13
+ let sandbox;
14
+ beforeEach(function () {
15
+ sandbox = _sinon.default.createSandbox();
16
+ });
17
+ afterEach(function () {
18
+ sandbox.restore();
19
+ });
20
+ it('should resolve with the looked-up address for the host name', async function () {
21
+ sandbox.stub(_os.default, 'hostname').returns('my-laptop.local');
22
+ const dnsStub = sandbox.stub(_dns.default, 'lookup').callsFake((host, opts, cb) => {
23
+ cb(null, '10.0.0.42');
24
+ });
25
+ const result = await (0, _getLanIp.getLANIP)();
26
+ (0, _chai.expect)(result).to.equal('10.0.0.42');
27
+ (0, _chai.expect)(dnsStub.calledOnce).to.equal(true);
28
+ (0, _chai.expect)(dnsStub.firstCall.args[0]).to.equal('my-laptop.local');
29
+ (0, _chai.expect)(dnsStub.firstCall.args[1]).to.equal(null);
30
+ });
31
+ it('should reject if dns.lookup fails', async function () {
32
+ sandbox.stub(_os.default, 'hostname').returns('unknown.local');
33
+ const lookupError = new Error('lookup failed');
34
+ sandbox.stub(_dns.default, 'lookup').callsFake((host, opts, cb) => {
35
+ cb(lookupError);
36
+ });
37
+ let caught;
38
+ try {
39
+ await (0, _getLanIp.getLANIP)();
40
+ } catch (err) {
41
+ caught = err;
42
+ }
43
+ (0, _chai.expect)(caught).to.equal(lookupError);
44
+ });
45
+ });
46
+ });
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _chai = require("chai");
5
+ var _sinon = _interopRequireDefault(require("sinon"));
6
+ var _proxyquire = _interopRequireDefault(require("proxyquire"));
7
+ /* eslint-env mocha */
8
+
9
+ // NOTE: hasUpdate has a defect at line 17 in `../has-update`: when the
10
+ // supplied navyFile is null/undefined or has no
11
+ // `ignoreUnauthorizedRequestsForRegistries` array, `R.includes(registry,
12
+ // undefined)` throws (in current ramda). The tests below provide a valid
13
+ // navyFile shape to exercise the supported call patterns.
14
+ function withMocks({
15
+ inspect,
16
+ getFatManifest = _sinon.default.stub()
17
+ }) {
18
+ const docker = {
19
+ getImage: _sinon.default.stub().returns({
20
+ inspect
21
+ })
22
+ };
23
+ const stubs = {
24
+ './docker-client': docker,
25
+ '../client/registry/get-fat-manifest': getFatManifest
26
+ };
27
+ return {
28
+ docker,
29
+ getFatManifest,
30
+ hasUpdate: _proxyquire.default.noCallThru()('../has-update', stubs)
31
+ };
32
+ }
33
+ describe('has-update', function () {
34
+ it('should return false when the remote digest matches one of the local RepoDigests', async function () {
35
+ const inspect = _sinon.default.stub().resolves({
36
+ RepoDigests: ['library/node@sha256:abc', 'library/node@sha256:other']
37
+ });
38
+ const {
39
+ hasUpdate
40
+ } = withMocks({
41
+ inspect,
42
+ getFatManifest: _sinon.default.stub().resolves({
43
+ tag: 'sha256:abc'
44
+ })
45
+ });
46
+ const result = await hasUpdate('library/node:lts', 'image-id', {
47
+ ignoreUnauthorizedRequestsForRegistries: []
48
+ });
49
+ (0, _chai.expect)(result).to.equal(false);
50
+ });
51
+ it('should return true when the remote digest is not in the local RepoDigests', async function () {
52
+ const inspect = _sinon.default.stub().resolves({
53
+ RepoDigests: ['library/node@sha256:old']
54
+ });
55
+ const {
56
+ hasUpdate
57
+ } = withMocks({
58
+ inspect,
59
+ getFatManifest: _sinon.default.stub().resolves({
60
+ tag: 'sha256:new'
61
+ })
62
+ });
63
+ const result = await hasUpdate('library/node:lts', 'image-id', {
64
+ ignoreUnauthorizedRequestsForRegistries: []
65
+ });
66
+ (0, _chai.expect)(result).to.equal(true);
67
+ });
68
+ it('should return "INVALID_REMOTE" when getFatManifest fails', async function () {
69
+ const inspect = _sinon.default.stub().resolves({
70
+ RepoDigests: []
71
+ });
72
+ const {
73
+ hasUpdate
74
+ } = withMocks({
75
+ inspect,
76
+ getFatManifest: _sinon.default.stub().rejects(new Error('boom'))
77
+ });
78
+ const result = await hasUpdate('library/node:lts', 'image-id', {
79
+ ignoreUnauthorizedRequestsForRegistries: []
80
+ });
81
+ (0, _chai.expect)(result).to.equal('INVALID_REMOTE');
82
+ });
83
+ it('should return "INVALID_REMOTE" when docker.getImage().inspect() fails', async function () {
84
+ const inspect = _sinon.default.stub().rejects(new Error('no such image'));
85
+ const {
86
+ hasUpdate
87
+ } = withMocks({
88
+ inspect
89
+ });
90
+ const result = await hasUpdate('library/node:lts', 'image-id', {
91
+ ignoreUnauthorizedRequestsForRegistries: []
92
+ });
93
+ (0, _chai.expect)(result).to.equal('INVALID_REMOTE');
94
+ });
95
+ it('should pass allowUnauthorizedRequest=true when navyFile lists the registry as ignored', async function () {
96
+ const inspect = _sinon.default.stub().resolves({
97
+ RepoDigests: ['someregistry.com/some/image@sha256:abc']
98
+ });
99
+ const getFatManifest = _sinon.default.stub().resolves({
100
+ tag: 'sha256:abc'
101
+ });
102
+ const {
103
+ hasUpdate
104
+ } = withMocks({
105
+ inspect,
106
+ getFatManifest
107
+ });
108
+ await hasUpdate('someregistry.com/some/image:1', 'image-id', {
109
+ ignoreUnauthorizedRequestsForRegistries: ['someregistry.com']
110
+ });
111
+ (0, _chai.expect)(getFatManifest.firstCall.args[0]).to.eql({
112
+ allowUnauthorizedRequest: true,
113
+ registry: 'someregistry.com',
114
+ repository: 'some/image',
115
+ tag: '1'
116
+ });
117
+ });
118
+ it('should pass allowUnauthorizedRequest=false when registry is not in the ignore list', async function () {
119
+ const inspect = _sinon.default.stub().resolves({
120
+ RepoDigests: []
121
+ });
122
+ const getFatManifest = _sinon.default.stub().resolves({
123
+ tag: 'sha256:abc'
124
+ });
125
+ const {
126
+ hasUpdate
127
+ } = withMocks({
128
+ inspect,
129
+ getFatManifest
130
+ });
131
+ await hasUpdate('someregistry.com/some/image:1', 'image-id', {
132
+ ignoreUnauthorizedRequestsForRegistries: ['otherregistry.com']
133
+ });
134
+ (0, _chai.expect)(getFatManifest.firstCall.args[0].allowUnauthorizedRequest).to.equal(false);
135
+ });
136
+ });