kontena-cli 1.4.0.pre6 → 1.4.0.pre7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/VERSION +1 -1
  4. data/bin/kontena +1 -1
  5. data/kontena-cli.gemspec +3 -3
  6. data/lib/kontena/cli/certificate/authorize_command.rb +67 -6
  7. data/lib/kontena/cli/certificate/get_command.rb +7 -0
  8. data/lib/kontena/cli/certificate/list_command.rb +75 -0
  9. data/lib/kontena/cli/certificate/register_command.rb +13 -2
  10. data/lib/kontena/cli/certificate/request_command.rb +20 -0
  11. data/lib/kontena/cli/certificate/show_command.rb +19 -0
  12. data/lib/kontena/cli/certificate_command.rb +4 -1
  13. data/lib/kontena/cli/cloud/master/add_command.rb +1 -1
  14. data/lib/kontena/cli/common.rb +21 -33
  15. data/lib/kontena/cli/etcd/health_command.rb +21 -27
  16. data/lib/kontena/cli/helpers/exec_helper.rb +15 -6
  17. data/lib/kontena/cli/helpers/health_helper.rb +12 -0
  18. data/lib/kontena/cli/helpers/log_helper.rb +2 -2
  19. data/lib/kontena/cli/helpers/time_helper.rb +29 -0
  20. data/lib/kontena/cli/master/init_cloud_command.rb +19 -0
  21. data/lib/kontena/cli/master/list_command.rb +1 -1
  22. data/lib/kontena/cli/master/ssh_command.rb +3 -1
  23. data/lib/kontena/cli/master/use_command.rb +1 -2
  24. data/lib/kontena/cli/node_command.rb +1 -0
  25. data/lib/kontena/cli/nodes/health_command.rb +28 -13
  26. data/lib/kontena/cli/nodes/list_command.rb +19 -3
  27. data/lib/kontena/cli/nodes/show_command.rb +4 -2
  28. data/lib/kontena/cli/nodes/ssh_command.rb +5 -2
  29. data/lib/kontena/cli/nodes/update_command.rb +2 -0
  30. data/lib/kontena/cli/plugins/install_command.rb +11 -8
  31. data/lib/kontena/cli/plugins/list_command.rb +5 -3
  32. data/lib/kontena/cli/plugins/search_command.rb +4 -2
  33. data/lib/kontena/cli/plugins/show_command.rb +17 -0
  34. data/lib/kontena/cli/plugins/uninstall_command.rb +9 -13
  35. data/lib/kontena/cli/registry/create_command.rb +1 -1
  36. data/lib/kontena/cli/services/create_command.rb +6 -0
  37. data/lib/kontena/cli/services/services_helper.rb +33 -6
  38. data/lib/kontena/cli/services/update_command.rb +6 -0
  39. data/lib/kontena/cli/stacks/build_command.rb +3 -3
  40. data/lib/kontena/cli/stacks/common.rb +105 -90
  41. data/lib/kontena/cli/stacks/deploy_command.rb +7 -3
  42. data/lib/kontena/cli/stacks/install_command.rb +39 -6
  43. data/lib/kontena/cli/stacks/list_command.rb +36 -4
  44. data/lib/kontena/cli/stacks/logs_command.rb +9 -2
  45. data/lib/kontena/cli/stacks/registry/pull_command.rb +2 -2
  46. data/lib/kontena/cli/stacks/registry/push_command.rb +20 -9
  47. data/lib/kontena/cli/stacks/registry/remove_command.rb +4 -4
  48. data/lib/kontena/cli/stacks/registry/show_command.rb +4 -4
  49. data/lib/kontena/cli/stacks/remove_command.rb +27 -1
  50. data/lib/kontena/cli/stacks/service_generator.rb +12 -2
  51. data/lib/kontena/cli/stacks/show_command.rb +35 -5
  52. data/lib/kontena/cli/stacks/stack_name.rb +71 -0
  53. data/lib/kontena/cli/stacks/upgrade_command.rb +127 -14
  54. data/lib/kontena/cli/stacks/validate_command.rb +38 -10
  55. data/lib/kontena/cli/stacks/yaml/custom_validators/certificates_validator.rb +22 -0
  56. data/lib/kontena/cli/stacks/yaml/opto/prompt_resolver.rb +1 -2
  57. data/lib/kontena/cli/stacks/yaml/reader.rb +211 -185
  58. data/lib/kontena/cli/stacks/yaml/service_extender.rb +6 -12
  59. data/lib/kontena/cli/stacks/yaml/stack_file_loader.rb +97 -0
  60. data/lib/kontena/cli/stacks/yaml/stack_file_loader/file_loader.rb +41 -0
  61. data/lib/kontena/cli/stacks/yaml/stack_file_loader/registry_loader.rb +24 -0
  62. data/lib/kontena/cli/stacks/yaml/stack_file_loader/uri_loader.rb +23 -0
  63. data/lib/kontena/cli/stacks/yaml/validations.rb +16 -0
  64. data/lib/kontena/cli/stacks/yaml/validator_v3.rb +25 -8
  65. data/lib/kontena/client.rb +2 -2
  66. data/lib/kontena/command.rb +11 -0
  67. data/lib/kontena/main_command.rb +3 -1
  68. data/lib/kontena/plugin_manager.rb +11 -198
  69. data/lib/kontena/plugin_manager/cleaner.rb +33 -0
  70. data/lib/kontena/plugin_manager/common.rb +86 -0
  71. data/lib/kontena/plugin_manager/installer.rb +54 -0
  72. data/lib/kontena/plugin_manager/loader.rb +93 -0
  73. data/lib/kontena/plugin_manager/rubygems_client.rb +42 -23
  74. data/lib/kontena/plugin_manager/uninstaller.rb +34 -0
  75. data/lib/kontena/util.rb +24 -0
  76. data/lib/kontena_cli.rb +1 -0
  77. data/omnibus/config/projects/kontena.rb +7 -1
  78. data/omnibus/config/software/{kontena.rb → kontena-cli.rb} +2 -0
  79. data/spec/fixtures/api/node.json +2 -1
  80. data/spec/fixtures/stack-internal-extend.yml +6 -1
  81. data/spec/fixtures/stack-with-dependencies-dep-1-1.yml +8 -0
  82. data/spec/fixtures/stack-with-dependencies-dep-1.yml +17 -0
  83. data/spec/fixtures/stack-with-dependencies-dep-2.yml +8 -0
  84. data/spec/fixtures/stack-with-dependencies-dep-3.yml +5 -0
  85. data/spec/fixtures/stack-with-dependencies-dep_2-removed.yml +17 -0
  86. data/spec/fixtures/stack-with-dependencies-dep_3-added.yml +25 -0
  87. data/spec/fixtures/stack-with-dependencies.yml +22 -0
  88. data/spec/fixtures/stack-with-variables.yml +3 -0
  89. data/spec/kontena/cli/etcd/health_command_spec.rb +45 -33
  90. data/spec/kontena/cli/helpers/exec_helper_spec.rb +2 -1
  91. data/spec/kontena/cli/master/init_cloud_command_spec.rb +14 -0
  92. data/spec/kontena/cli/nodes/health_command_spec.rb +74 -10
  93. data/spec/kontena/cli/nodes/list_command_spec.rb +381 -232
  94. data/spec/kontena/cli/nodes/show_command_spec.rb +31 -0
  95. data/spec/kontena/cli/nodes/ssh_command_spec.rb +18 -3
  96. data/spec/kontena/cli/plugins/install_command_spec.rb +1 -1
  97. data/spec/kontena/cli/stacks/build_command_spec.rb +6 -12
  98. data/spec/kontena/cli/stacks/common_spec.rb +42 -69
  99. data/spec/kontena/cli/stacks/install_command_spec.rb +57 -31
  100. data/spec/kontena/cli/stacks/list_command_spec.rb +44 -0
  101. data/spec/kontena/cli/stacks/logs_command_spec.rb +12 -1
  102. data/spec/kontena/cli/stacks/remove_command_spec.rb +39 -0
  103. data/spec/kontena/cli/stacks/show_command_spec.rb +16 -0
  104. data/spec/kontena/cli/stacks/stack_name_spec.rb +21 -0
  105. data/spec/kontena/cli/stacks/upgrade_command_spec.rb +73 -56
  106. data/spec/kontena/cli/stacks/validate_command_spec.rb +81 -0
  107. data/spec/kontena/cli/stacks/yaml/custom_validators/affinities_validator_spec.rb +22 -0
  108. data/spec/kontena/cli/stacks/yaml/reader_spec.rb +173 -169
  109. data/spec/kontena/cli/stacks/yaml/service_extender_spec.rb +12 -3
  110. data/spec/kontena/cli/stacks/yaml/stack_file_loader/file_loader_spec.rb +47 -0
  111. data/spec/kontena/cli/stacks/yaml/stack_file_loader/registry_loader_spec.rb +53 -0
  112. data/spec/kontena/cli/stacks/yaml/stack_file_loader/uri_loader_spec.rb +53 -0
  113. data/spec/kontena/cli/stacks/yaml/stack_file_loader_spec.rb +104 -0
  114. data/spec/kontena/cli/stacks/yaml/validator_v3_spec.rb +19 -0
  115. data/spec/kontena/plugin_manager/cleaner_spec.rb +20 -0
  116. data/spec/kontena/plugin_manager/common_spec.rb +39 -0
  117. data/spec/kontena/plugin_manager/installer_spec.rb +50 -0
  118. data/spec/kontena/plugin_manager/loader_spec.rb +5 -0
  119. data/spec/kontena/plugin_manager/rubygems_client_spec.rb +11 -25
  120. data/spec/kontena/plugin_manager/uninstaller_spec.rb +19 -0
  121. data/spec/kontena/plugin_manager_spec.rb +7 -7
  122. metadata +64 -97
  123. data/lib/kontena/cli/app_command.rb +0 -22
  124. data/lib/kontena/cli/apps/build_command.rb +0 -28
  125. data/lib/kontena/cli/apps/common.rb +0 -172
  126. data/lib/kontena/cli/apps/config_command.rb +0 -25
  127. data/lib/kontena/cli/apps/deploy_command.rb +0 -137
  128. data/lib/kontena/cli/apps/docker_compose_generator.rb +0 -61
  129. data/lib/kontena/cli/apps/docker_helper.rb +0 -80
  130. data/lib/kontena/cli/apps/dockerfile_generator.rb +0 -16
  131. data/lib/kontena/cli/apps/init_command.rb +0 -89
  132. data/lib/kontena/cli/apps/kontena_yml_generator.rb +0 -105
  133. data/lib/kontena/cli/apps/list_command.rb +0 -59
  134. data/lib/kontena/cli/apps/logs_command.rb +0 -37
  135. data/lib/kontena/cli/apps/monitor_command.rb +0 -93
  136. data/lib/kontena/cli/apps/remove_command.rb +0 -74
  137. data/lib/kontena/cli/apps/restart_command.rb +0 -39
  138. data/lib/kontena/cli/apps/scale_command.rb +0 -33
  139. data/lib/kontena/cli/apps/service_generator.rb +0 -114
  140. data/lib/kontena/cli/apps/service_generator_v2.rb +0 -27
  141. data/lib/kontena/cli/apps/show_command.rb +0 -23
  142. data/lib/kontena/cli/apps/start_command.rb +0 -40
  143. data/lib/kontena/cli/apps/stop_command.rb +0 -40
  144. data/lib/kontena/cli/apps/yaml/custom_validators/affinities_validator.rb +0 -19
  145. data/lib/kontena/cli/apps/yaml/custom_validators/build_validator.rb +0 -22
  146. data/lib/kontena/cli/apps/yaml/custom_validators/extends_validator.rb +0 -20
  147. data/lib/kontena/cli/apps/yaml/custom_validators/hooks_validator.rb +0 -54
  148. data/lib/kontena/cli/apps/yaml/custom_validators/secrets_validator.rb +0 -22
  149. data/lib/kontena/cli/apps/yaml/reader.rb +0 -213
  150. data/lib/kontena/cli/apps/yaml/service_extender.rb +0 -77
  151. data/lib/kontena/cli/apps/yaml/validations.rb +0 -71
  152. data/lib/kontena/cli/apps/yaml/validator.rb +0 -38
  153. data/lib/kontena/cli/apps/yaml/validator_v2.rb +0 -53
  154. data/spec/fixtures/app.json +0 -42
  155. data/spec/fixtures/health.yml +0 -26
  156. data/spec/fixtures/kontena-build.yml +0 -16
  157. data/spec/fixtures/kontena-internal-extend.yml +0 -8
  158. data/spec/fixtures/kontena-invalid.yml +0 -4
  159. data/spec/fixtures/kontena-with-env-file.yml +0 -18
  160. data/spec/fixtures/kontena-with-variables.yml +0 -19
  161. data/spec/fixtures/kontena.yml +0 -17
  162. data/spec/fixtures/kontena_build_v2.yml +0 -26
  163. data/spec/fixtures/kontena_numeric_version.yml +0 -9
  164. data/spec/fixtures/kontena_v2.yml +0 -35
  165. data/spec/fixtures/mysql.yml +0 -3
  166. data/spec/fixtures/wordpress-scaled.yml +0 -3
  167. data/spec/fixtures/wordpress.yml +0 -2
  168. data/spec/kontena/cli/app/build_command_spec.rb +0 -55
  169. data/spec/kontena/cli/app/common_spec.rb +0 -110
  170. data/spec/kontena/cli/app/config_command_spec.rb +0 -78
  171. data/spec/kontena/cli/app/deploy_command_spec.rb +0 -217
  172. data/spec/kontena/cli/app/docker_helper_spec.rb +0 -155
  173. data/spec/kontena/cli/app/init_command_spec.rb +0 -109
  174. data/spec/kontena/cli/app/logs_command_spec.rb +0 -131
  175. data/spec/kontena/cli/app/scale_spec.rb +0 -51
  176. data/spec/kontena/cli/app/service_generator_spec.rb +0 -384
  177. data/spec/kontena/cli/app/service_generator_v2_spec.rb +0 -73
  178. data/spec/kontena/cli/app/yaml/reader_spec.rb +0 -457
  179. data/spec/kontena/cli/app/yaml/service_extender_spec.rb +0 -127
  180. data/spec/kontena/cli/app/yaml/validator_spec.rb +0 -380
  181. data/spec/kontena/cli/app/yaml/validator_v2_spec.rb +0 -301
@@ -6,12 +6,14 @@ describe Kontena::Cli::Stacks::RemoveCommand do
6
6
 
7
7
  describe '#execute' do
8
8
  it 'sends remove command to master' do
9
+ allow(subject).to receive(:fetch_stack).and_return({})
9
10
  allow(subject).to receive(:wait_stack_removal)
10
11
  expect(client).to receive(:delete).with('stacks/test-grid/test-stack')
11
12
  subject.run(['--force', 'test-stack'])
12
13
  end
13
14
 
14
15
  it 'waits until service is removed' do
16
+ allow(subject).to receive(:fetch_stack).and_return({})
15
17
  allow(client).to receive(:delete).with('stacks/test-grid/test-stack')
16
18
  expect(client).to receive(:get).with('stacks/test-grid/test-stack')
17
19
  .and_raise(Kontena::Errors::StandardError.new(404, 'Not Found'))
@@ -19,6 +21,7 @@ describe Kontena::Cli::Stacks::RemoveCommand do
19
21
  end
20
22
 
21
23
  it 'raises exception on server error' do
24
+ allow(subject).to receive(:fetch_stack).and_return({})
22
25
  expect(client).to receive(:delete).with('stacks/test-grid/test-stack')
23
26
  expect(client).to receive(:get).with('stacks/test-grid/test-stack')
24
27
  .and_raise(Kontena::Errors::StandardError.new(500, 'internal error'))
@@ -26,5 +29,41 @@ describe Kontena::Cli::Stacks::RemoveCommand do
26
29
  subject.run(['--force', 'test-stack'])
27
30
  }.to exit_with_error
28
31
  end
32
+
33
+ describe 'with stack dependencies' do
34
+ context 'when stack has a parent' do
35
+ let(:stack_response_with_parent) do
36
+ { 'parent' => { 'name' => 'foofoo' } }
37
+ end
38
+
39
+ it 'warns' do
40
+ expect(client).to receive(:get).with('stacks/test-grid/test-stack').and_return(
41
+ stack_response_with_parent
42
+ )
43
+ expect(subject).to receive(:confirm_command).and_raise "foo"
44
+ expect{subject.run(['test-stack'])}.to exit_with_error.and output(/depends on/).to_stdout
45
+ end
46
+ end
47
+
48
+ context 'when stack has children' do
49
+ let(:stack_response_with_children) do
50
+ { 'children' => [ { 'name' => 'foofoo' }, { 'name' => 'foobar' } ] }
51
+ end
52
+
53
+ it 'removes the children' do
54
+ allow(subject).to receive(:wait_stack_removal)
55
+
56
+ expect(client).to receive(:get).with('stacks/test-grid/test-stack').and_return(
57
+ stack_response_with_children
58
+ )
59
+
60
+ expect(Kontena).to receive(:run!).with(['stack', 'remove', '--force', 'foofoo'])
61
+ expect(Kontena).to receive(:run!).with(['stack', 'remove', '--force', 'foobar'])
62
+
63
+ expect(subject).to receive(:remove_stack).with('test-stack')
64
+ expect{subject.run(['--force', 'test-stack'])}.not_to exit_with_error
65
+ end
66
+ end
67
+ end
29
68
  end
30
69
  end
@@ -9,5 +9,21 @@ describe Kontena::Cli::Stacks::ShowCommand do
9
9
  expect(client).to receive(:get).with('stacks/test-grid/test-stack').and_return(spy())
10
10
  subject.run(['test-stack'])
11
11
  end
12
+
13
+ context '--values option' do
14
+ let(:stack_response) do
15
+ {
16
+ 'name' => 'stack-a',
17
+ 'stack' => 'foo/stack-a',
18
+ 'services' => [],
19
+ 'variables' => { 'foo' => 'bar' }
20
+ }
21
+ end
22
+
23
+ it 'outputs a yaml of the stack variables and values' do
24
+ expect(client).to receive(:get).with('stacks/test-grid/test-stack').and_return(stack_response)
25
+ expect{subject.run(['--values', 'test-stack'])}.to output(/^foo: bar$/).to_stdout
26
+ end
27
+ end
12
28
  end
13
29
  end
@@ -0,0 +1,21 @@
1
+ require 'kontena/cli/stacks/stack_name'
2
+
3
+ describe Kontena::Cli::Stacks::StackName do
4
+ it 'can parse a stack string' do
5
+ expect(described_class.new('user/stack').to_s).to eq 'user/stack'
6
+ expect(described_class.new('user/stack:0.1.0').to_s).to eq 'user/stack:0.1.0'
7
+ expect(described_class.new('user/stack', '0.1.0').to_s).to eq 'user/stack:0.1.0'
8
+ end
9
+
10
+ it 'can take a hash' do
11
+ expect(described_class.new(user: 'foo', stack: 'stack', version: '0.1.0').to_s).to eq 'foo/stack:0.1.0'
12
+ end
13
+
14
+ it 'has accessors' do
15
+ result = described_class.new('user/stack:0.1.0')
16
+ expect(result.stack_name).to eq 'user/stack'
17
+ expect(result.user).to eq 'user'
18
+ expect(result.stack).to eq 'stack'
19
+ expect(result.version).to eq '0.1.0'
20
+ end
21
+ end
@@ -1,108 +1,125 @@
1
1
  require "kontena/cli/stacks/upgrade_command"
2
+ require 'json'
2
3
 
3
4
  describe Kontena::Cli::Stacks::UpgradeCommand do
4
5
 
5
6
  include ClientHelpers
6
7
  include RequirementsHelper
8
+ include FixturesHelpers
7
9
 
8
10
  mock_current_master
9
11
 
10
- describe '#execute' do
12
+ before(:each) do
13
+ ENV['STACK'] = nil
14
+ end
11
15
 
12
- let(:stack) do
13
- {
14
- 'name' => 'stack-a',
15
- 'stack' => 'foo/stack-a',
16
- 'services' => []
17
- }
18
- end
16
+ describe '#execute' do
19
17
 
20
- let(:stack_with_different_stack_name) do
18
+ let(:stack_expectation) do
21
19
  {
22
- 'name' => 'stack-a',
23
- 'stack' => 'foo/stack-z',
24
- 'services' => []
20
+ 'name' => 'stack-name',
21
+ 'stack' => 'user/stackname',
22
+ 'version' => '0.1.1',
23
+ 'registry' => 'file://',
24
+ 'services' => array_including(hash_including('name', 'image')),
25
+ 'variables' => {},
26
+ 'volumes' => [],
27
+ 'dependencies' => nil,
28
+ 'source' => /stack:/,
29
+ 'expose' => nil
25
30
  }
26
31
  end
27
32
 
28
- let(:defaults) do
29
- { 'foo' => 'bar' }
30
- end
31
-
32
33
  let(:stack_response) do
33
- {
34
- 'name' => 'stack-a',
35
- 'stack' => 'foo/stack-a',
36
- 'services' => [],
37
- 'variables' => defaults
38
- }
34
+ stack_expectation.merge('services' => [{'name' => 'foo', 'image' => 'bar'}], 'source' => 'foo')
39
35
  end
40
36
 
41
37
  before(:each) do
42
- allow(File).to receive(:exist?).with('./path/to/kontena.yml').and_return(true)
38
+ allow(File).to receive(:read).with('kontena.yml').and_return(fixture('kontena_v3.yml'))
39
+ allow(File).to receive(:exist?).with('kontena.yml').and_return(true)
40
+ allow(subject.loader_class).to receive(:for).and_call_original
43
41
  end
44
42
 
45
43
  expect_to_require_current_master
46
44
  expect_to_require_current_master_token
47
45
 
48
46
  it 'uses kontena.yml as default stack file' do
49
- expect(client).to receive(:get).with('stacks/test-grid/stack-name').and_return(stack_response)
50
- expect(subject).to receive(:stack_read_and_dump).with('kontena.yml', name: 'stack-name', values: nil, defaults: defaults).and_return(stack)
51
- expect(client).to receive(:put).with('stacks/test-grid/stack-name', stack)
52
- subject.run(['stack-name'])
47
+ expect(subject.instance(['stack-name']).source).to eq 'kontena.yml'
48
+ expect(subject.instance(['stack-name']).stack_name).to eq 'stack-name'
53
49
  end
54
50
 
55
51
  it 'sends stack to master' do
56
52
  expect(client).to receive(:get).with('stacks/test-grid/stack-a').and_return(stack_response)
57
- allow(subject).to receive(:require_config_file).with('./path/to/kontena.yml').and_return(true)
58
- allow(subject).to receive(:stack_read_and_dump).with('./path/to/kontena.yml', name: 'stack-a', values: nil, defaults: defaults).and_return(stack)
59
- expect(client).to receive(:put).with(
60
- 'stacks/test-grid/stack-a', anything
61
- )
62
- subject.run(['stack-a', './path/to/kontena.yml'])
63
- end
64
-
65
- it 'allows to override stack name' do
66
- expect(client).to receive(:get).with('stacks/test-grid/stack-b').and_return(stack_response)
67
- allow(subject).to receive(:require_config_file).with('./path/to/kontena.yml').and_return(true)
68
- allow(subject).to receive(:stack_read_and_dump).with('./path/to/kontena.yml', name: 'stack-b', values: nil, defaults: defaults).and_return(stack)
69
- stack_b = stack
70
- stack_b[:name] = 'stack-b'
71
- expect(client).to receive(:put).with(
72
- 'stacks/test-grid/stack-b', anything
73
- )
74
- subject.run(['stack-b', './path/to/kontena.yml'])
53
+ expect(client).to receive(:put).with('stacks/test-grid/stack-a', hash_including(stack_expectation.merge('name' => 'stack-a'))).and_return(true)
54
+ subject.run(['--no-deploy', 'stack-a', fixture_path('kontena_v3.yml')])
75
55
  end
76
56
 
77
57
  it 'requires confirmation when master stack is different than input stack' do
78
- expect(client).to receive(:get).with('stacks/test-grid/stack-b').and_return(stack_response)
79
- allow(subject).to receive(:require_config_file).with('./path/to/kontena.yml').and_return(true)
80
- allow(subject).to receive(:stack_read_and_dump).with('./path/to/kontena.yml', name: 'stack-b', values: nil, defaults: defaults).and_return(stack_with_different_stack_name)
81
- expect(subject).to receive(:confirm).and_call_original
82
- expect{subject.run(['stack-b', './path/to/kontena.yml'])}.to exit_with_error
58
+ expect(client).to receive(:get).with('stacks/test-grid/stack-b').and_return(stack_response.merge('stack' => 'foo/otherstack'))
59
+ expect(subject).to receive(:confirm).with(/Replacing stack foo\/otherstack on master with user\/stackname/).and_call_original
60
+ expect{subject.run(['stack-b', fixture_path('kontena_v3.yml')])}.to exit_with_error
83
61
  end
84
62
 
85
63
  it 'triggers deploy by default' do
86
64
  expect(client).to receive(:get).with('stacks/test-grid/stack-a').and_return(stack_response)
87
- allow(subject).to receive(:require_config_file).with('./path/to/kontena.yml').and_return(true)
88
- allow(subject).to receive(:stack_read_and_dump).with('./path/to/kontena.yml', name: 'stack-a', values: nil, defaults: defaults).and_return(stack)
89
65
  allow(client).to receive(:put).with(
90
66
  'stacks/test-grid/stack-a', anything
91
67
  ).and_return({})
92
68
  expect(Kontena).to receive(:run!).with(['stack', 'deploy', 'stack-a']).once
93
- subject.run(['stack-a', './path/to/kontena.yml'])
69
+ subject.run(['stack-a', fixture_path('kontena_v3.yml')])
94
70
  end
95
71
 
96
72
  context '--no-deploy option' do
97
73
  it 'does not trigger deploy' do
98
74
  expect(client).to receive(:get).with('stacks/test-grid/stack-a').and_return(stack_response)
99
- allow(subject).to receive(:require_config_file).with('./path/to/kontena.yml').and_return(true)
100
- allow(subject).to receive(:stack_read_and_dump).with('./path/to/kontena.yml', name: 'stack-a', values: nil, defaults: defaults).and_return(stack)
101
75
  allow(client).to receive(:put).with(
102
76
  'stacks/test-grid/stack-a', anything
103
77
  ).and_return({})
104
78
  expect(Kontena).not_to receive(:run!).with(['stack', 'deploy', 'stack-a'])
105
- subject.run(['--no-deploy', 'stack-a', './path/to/kontena.yml'])
79
+ subject.run(['--no-deploy', 'stack-a', fixture_path('kontena_v3.yml')])
80
+ end
81
+ end
82
+
83
+ context 'with a stack including dependencies' do
84
+
85
+ let(:expectation) {{ 'name' => 'deptest', 'stack' => 'user/depstack1' }}
86
+ let(:expectation_1) {{ 'name' => 'deptest-dep_1', 'stack' => 'user/depstack1child1' }}
87
+ let(:expectation_1_1) {{ 'name' => 'deptest-dep_1-dep_1', 'stack' => 'user/depstack1child1child1', 'services' => array_including(hash_including('image' => 'image:2')) }}
88
+ let(:expectation_2) {{ 'name' => 'deptest-dep_2', 'stack' => 'user/depstack1child2', 'services' => array_including(hash_including('image' => 'image:1')), 'variables' => hash_including('dep_var' => 1) }}
89
+
90
+ let(:response) { expectation.merge('parent' => nil, 'children' => [{'name' => 'deptest-dep_1'}, {'name' => 'deptest-dep_2'}]) }
91
+ let(:response_1) { expectation_1.merge('parent' => { 'name' => 'deptest' }, 'children' => [{'name' => 'deptest-dep_1-dep_1'}]) }
92
+ let(:response_1_1) { expectation_1_1.merge('parent' => { 'name' => 'deptest-dep_1' }, 'children' => []) }
93
+ let(:response_2) { expectation_2.merge('parent' => { 'name' => 'deptest' }, 'children' => [], 'variables' => {}, 'services' => []) }
94
+
95
+ before do
96
+ allow(subject).to receive(:fetch_master_data).with('deptest').and_return(response)
97
+ allow(subject).to receive(:fetch_master_data).with('deptest-dep_1').and_return(response_1)
98
+ allow(subject).to receive(:fetch_master_data).with('deptest-dep_1-dep_1').and_return(response_1_1)
99
+ allow(subject).to receive(:fetch_master_data).with('deptest-dep_2').and_return(response_2)
100
+ end
101
+
102
+ it 'upgrades all dependencies' do
103
+ expect(client).to receive(:put).with('stacks/test-grid/deptest-dep_2', hash_including(expectation_2)).and_return(true)
104
+ expect(client).to receive(:put).with('stacks/test-grid/deptest-dep_1-dep_1', hash_including(expectation_1_1)).and_return(true)
105
+ expect(client).to receive(:put).with('stacks/test-grid/deptest-dep_1', hash_including(expectation_1)).and_return(true)
106
+ expect(client).to receive(:put).with('stacks/test-grid/deptest', hash_including(expectation)).and_return(true)
107
+ subject.run(['--no-deploy', '-v', 'dep_2.dep_var=1', 'deptest', fixture_path('stack-with-dependencies.yml')])
108
+ end
109
+
110
+ context 'when a dependency has been removed' do
111
+ it 'warns if a stack no longer in the dependency chain would be removed' do
112
+ expect(subject).to receive(:confirm).and_call_original
113
+ expect{subject.run(['--no-deploy', 'deptest', fixture_path('stack-with-dependencies-dep_2-removed.yml')])}.to exit_with_error.and output(/- deptest-dep_2.*data will be lost/m).to_stdout
114
+ end
115
+ end
116
+
117
+ context 'when a dependency has been added' do
118
+ it 'installs any new stacks in the dependency chain' do
119
+ allow(client).to receive(:put).and_return(true)
120
+ expect(Kontena).to receive(:run!).with(["stack", "install", "--name", "deptest-dep_3", "--parent-name", "deptest", "--no-deploy", fixture_path('stack-with-dependencies-dep-3.yml')]).and_return(true)
121
+ subject.run(['--no-deploy', '-v', 'dep_2.dep_var=1', 'deptest', fixture_path('stack-with-dependencies-dep_3-added.yml')])
122
+ end
106
123
  end
107
124
  end
108
125
  end
@@ -0,0 +1,81 @@
1
+ require "kontena/cli/stacks/upgrade_command"
2
+
3
+ describe Kontena::Cli::Stacks::UpgradeCommand do
4
+
5
+ include ClientHelpers
6
+ include RequirementsHelper
7
+ include FixturesHelpers
8
+
9
+ context 'without dependencies' do
10
+ before do
11
+ [:read, :exist?].each do |meth|
12
+ allow(File).to receive(meth).with(fixture_path('kontena_v3.yml')).and_call_original
13
+ allow(File).to receive(meth).with(fixture_path('docker-compose_v2.yml')).and_call_original
14
+ end
15
+ end
16
+
17
+ it 'validates a yaml file' do
18
+ expect{Kontena.run!('stack', 'validate', fixture_path('kontena_v3.yml'))}.not_to exit_with_error
19
+ expect{Kontena.run!('stack', 'validate', fixture_path('kontena_v3.yml'))}.to output(/stack:.*version:.*services:.*variables:/m).to_stdout
20
+ end
21
+ end
22
+
23
+ context 'with dependencies' do
24
+ before do
25
+ Dir.glob(File.join(File.dirname(fixture_path('stack-with-dependencies.yml')), 'stack-with-dependencies*yml')).each do |file|
26
+ [:read, :exist?].each do |meth|
27
+ allow(File).to receive(meth).with(file).and_call_original
28
+ end
29
+ end
30
+ end
31
+
32
+ it 'validates all depended yaml files' do
33
+ output = OutputHelpers::CaptureStdoutLines.capture(proc {Kontena.run!('stack', 'validate', fixture_path('stack-with-dependencies.yml'))})
34
+ expect{Kontena.run!('stack', 'validate', fixture_path('stack-with-dependencies.yml'))}.not_to exit_with_error
35
+ expect(output.select { |line| line.start_with?('---') }.size).to eq 4
36
+ expect(output).to include('stack: user/depstack1')
37
+ expect(output).to include('stack: user/depstack1child1')
38
+ expect(output).to include('stack: user/depstack1child1child1')
39
+ expect(output).to include('stack: user/depstack1child2')
40
+ expect(output.select { |line| line.start_with?('stack') }.size).to eq 4
41
+ end
42
+
43
+ describe '--dependency-tree' do
44
+ let(:expectation) do
45
+ {
46
+ "name" => "depstack1",
47
+ "stack" => /stack-with-dependencies.yml$/,
48
+ "depends" => [
49
+ {
50
+ "name" => "dep_1",
51
+ "stack" => /stack-with-dependencies-dep-1.yml$/,
52
+ "variables" => {},
53
+ "depends" => [
54
+ {
55
+ "name" => "dep_1",
56
+ "stack" => /stack-with-dependencies-dep-1-1.yml$/,
57
+ "variables" => {
58
+ "dep_var" => 2
59
+ }
60
+ }
61
+ ]
62
+ },
63
+ {
64
+ "name" => "dep_2",
65
+ "stack" => /stack-with-dependencies-dep-2.yml$/,
66
+ "variables" => {
67
+ "dep_var" => 1
68
+ }
69
+ }
70
+ ]
71
+ }
72
+ end
73
+
74
+ it 'outputs the dependency tree' do
75
+ output = OutputHelpers::CaptureStdoutLines.capture(proc {Kontena.run!('stack', 'validate', '--dependency-tree', fixture_path('stack-with-dependencies.yml'))})
76
+ yaml = ::YAML.safe_load(output.join("\n"))
77
+ expect(yaml).to match hash_including(expectation)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,22 @@
1
+ require 'kontena/cli/stacks/yaml/validator_v3'
2
+ require 'kontena/cli/stacks/yaml/custom_validators/affinities_validator'
3
+
4
+ describe Kontena::Cli::Stacks::YAML::Validations::CustomValidators::AffinitiesValidator do
5
+
6
+ let(:errors) { Hash.new }
7
+
8
+ it 'accepts valid affinity' do
9
+ subject.validate('affinity', ['foo==bar'], [], errors)
10
+ expect(errors.size).to eq(0)
11
+ end
12
+
13
+ it 'accepts valid soft affinity' do
14
+ subject.validate('affinity', ['foo==~bar'], [], errors)
15
+ expect(errors.size).to eq(0)
16
+ end
17
+
18
+ it 'does not accept invalid affinity' do
19
+ subject.validate('affinity', ['foo=bar'], [], errors)
20
+ expect(errors.size).to eq(1)
21
+ end
22
+ end
@@ -1,12 +1,9 @@
1
1
  require 'kontena/cli/stacks/yaml/reader'
2
- require 'liquid'
3
2
 
4
3
  describe Kontena::Cli::Stacks::YAML::Reader do
5
4
  include FixturesHelpers
6
5
 
7
- let(:service_extender) do
8
- spy
9
- end
6
+ let(:service_extender) { spy("Kontena::Cli::Stacks::YAML::ServiceExtender") }
10
7
 
11
8
  let(:env_file) do
12
9
  ['APIKEY=12345
@@ -14,34 +11,9 @@ describe Kontena::Cli::Stacks::YAML::Reader do
14
11
  ', 'WP_ADMIN_PASSWORD=verysecret']
15
12
  end
16
13
 
17
- let(:valid_v3_result) do
18
- {
19
- 'stack' => 'user/stackname',
20
- 'version' => '2',
21
- 'services' => {
22
- 'wordpress' => {
23
- 'image' => 'wordpress:4.1',
24
- 'ports' => ['80:80'],
25
- 'depends_on' => ['mysql'],
26
- 'stateful' => true,
27
- 'environment' => ['WORDPRESS_DB_PASSWORD=test_secret'],
28
- 'instances' => 2,
29
- 'deploy' => { 'strategy' => 'ha' },
30
- 'secrets' => []
31
- },
32
- 'mysql' => {
33
- 'image' => 'mysql:5.6',
34
- 'stateful' => true,
35
- 'environment' => ['MYSQL_ROOT_PASSWORD=test_secret'],
36
- 'secrets' => []
37
- }
38
- }
39
- }
40
- end
41
-
42
14
  before(:each) do
43
15
  allow_any_instance_of(described_class).to receive(:env)
44
- .and_return( { 'STACK' => 'test', 'GRID' => 'test-grid' } )
16
+ .and_return( { 'STACK' => 'test', 'GRID' => 'test-grid', 'PLATFORM' => 'test-grid' } )
45
17
  end
46
18
 
47
19
  describe '#initialize' do
@@ -76,85 +48,118 @@ describe Kontena::Cli::Stacks::YAML::Reader do
76
48
  end
77
49
 
78
50
  it 'returns error' do
79
- outcome = subject.execute
80
- expect(outcome[:errors].size).to eq(1)
51
+ subject.execute
52
+ expect(subject.errors.size).to eq(1)
81
53
  end
82
54
  end
83
55
 
84
56
  describe '#execute' do
85
- subject do
57
+
58
+ let(:subject) do
86
59
  described_class.new(fixture_path('kontena_v3.yml'))
87
60
  end
88
61
 
89
- context 'when extending services' do
90
- it 'extends services from external file' do
91
- docker_compose_yml = YAML.load(fixture('docker-compose_v2.yml'))
92
- wordpress_options = {
93
- 'extends' => {
94
- 'file' => 'docker-compose_v2.yml',
95
- 'service' => 'wordpress'
96
- },
97
- 'stateful' => true,
98
- 'environment' => ['WORDPRESS_DB_PASSWORD=test_secret'],
99
- 'instances' => 2,
100
- 'deploy' => { 'strategy' => 'ha' }
101
- }
102
- mysql_options = {
103
- 'extends' => {
104
- 'file' => 'docker-compose_v2.yml',
105
- 'service' => 'mysql'
106
- },
107
- 'stateful' => true,
108
- 'environment' => ['MYSQL_ROOT_PASSWORD=test_secret']
109
- }
110
- expect(Kontena::Cli::Stacks::YAML::ServiceExtender).to receive(:new)
111
- .with(wordpress_options)
112
- .once
113
- .and_return(service_extender)
114
- expect(Kontena::Cli::Stacks::YAML::ServiceExtender).to receive(:new)
115
- .with(mysql_options)
116
- .once
117
- .and_return(service_extender)
118
- expect(service_extender).to receive(:extend_from).with(docker_compose_yml['services']['wordpress'])
119
- expect(service_extender).to receive(:extend_from).with(docker_compose_yml['services']['mysql'])
120
-
121
- subject.execute
62
+ it 'returns result hash' do
63
+ result = subject.execute
64
+ expect(result).to be_kind_of(Hash)
65
+ %w(
66
+ stack
67
+ version
68
+ name
69
+ registry
70
+ expose
71
+ services
72
+ volumes
73
+ dependencies
74
+ source
75
+ variables
76
+ parent_name
77
+ ).each do |k|
78
+ expect(result.key?(k)).to be_truthy
122
79
  end
80
+ end
123
81
 
124
- it 'extends services from the same file' do
125
- subject = described_class.new(fixture_path('stack-internal-extend.yml'))
82
+ context 'when extending services' do
83
+ context 'from external file' do
84
+ let(:subject) do
85
+ described_class.new(fixture_path('kontena_v3.yml'))
86
+ end
126
87
 
127
- kontena_yml = YAML.load(fixture('stack-internal-extend.yml'))
88
+ before do
89
+ [:exist?, :read].each do |meth|
90
+ allow(File).to receive(meth).with(fixture_path('docker-compose_v2.yml')).and_call_original
91
+ allow(File).to receive(meth).with(fixture_path('kontena_v3.yml')).and_call_original
92
+ end
93
+ end
128
94
 
129
- expect(Kontena::Cli::Stacks::YAML::ServiceExtender).to receive(:new)
130
- .with(kontena_yml['services']['app'])
131
- .once
132
- .and_return(service_extender)
133
- expect(service_extender).to receive(:extend_from).with(kontena_yml['services']['base'])
134
- subject.execute
135
- end
95
+ it 'extends services from an external file' do
96
+ expect(File).to receive(:read).with(fixture_path('docker-compose_v2.yml')).and_call_original
97
+ expect(subject.execute['services']).to match array_including(
98
+ hash_including(
99
+ "instances"=>2,
100
+ "image"=>"wordpress:4.1",
101
+ "env"=>["WORDPRESS_DB_PASSWORD=test_secret"],
102
+ "links"=>[{"name"=>"mysql", "alias"=>"mysql"}],
103
+ "ports"=>[{"ip"=>"0.0.0.0", "container_port"=>80, "node_port"=>80, "protocol"=>"tcp"}],
104
+ "stateful"=>true,
105
+ "strategy"=>"ha",
106
+ "name"=>"wordpress"
107
+ ),
108
+ hash_including(
109
+ "instances"=>nil,
110
+ "image"=>"mysql:5.6",
111
+ "env"=>["MYSQL_ROOT_PASSWORD=test_secret"],
112
+ "links"=>[],
113
+ "ports"=>[],
114
+ "stateful"=>true,
115
+ "name"=>"mysql"
116
+ )
117
+ )
118
+ end
136
119
 
137
- it 'merges validation errors' do
138
- allow(File).to receive(:read)
139
- .with(fixture_path('kontena_v3.yml'))
140
- .and_return(fixture('kontena_v3.yml'))
141
- allow(File).to receive(:read)
142
- .with(fixture_path('docker-compose_v2.yml'))
143
- .and_return(fixture('docker-compose-invalid.yml'))
144
- outcome = subject.execute
145
- expect(outcome[:errors]).to eq([{
146
- 'docker-compose_v2.yml' =>[
147
- {
148
- 'services' => {
149
- 'wordpress' => {
150
- 'networks' => 'key not expected'
120
+ it 'merges validation errors' do
121
+ expect(File).to receive(:read).with(fixture_path('docker-compose_v2.yml')).and_return(fixture('docker-compose-invalid.yml'))
122
+ outcome = subject.execute
123
+ expect(outcome['errors']).to eq([{
124
+ 'docker-compose_v2.yml' =>[
125
+ {
126
+ 'services' => {
127
+ 'wordpress' => {
128
+ 'networks' => 'key not expected'
129
+ }
151
130
  }
152
131
  }
153
- }
154
- ]
155
- }])
132
+ ]
133
+ }])
134
+ end
156
135
  end
157
136
 
137
+ context 'from the same file' do
138
+ subject do
139
+ described_class.new(fixture_path('stack-internal-extend.yml'))
140
+ end
141
+
142
+ before do
143
+ [:exist?, :read].each do |meth|
144
+ allow(File).to receive(meth).with(fixture_path('stack-internal-extend.yml')).and_call_original
145
+ end
146
+ end
147
+
148
+ it 'extends services from the same file' do
149
+ app_svc = subject.execute['services'].find { |s| s['name'] == 'app' }
150
+ expect(app_svc).not_to be_nil
151
+ puts app_svc.inspect
152
+ expect(app_svc).to match hash_including(
153
+ "image" => "base:latest",
154
+ "instances" => 2,
155
+ "env" => [
156
+ "TEST1=test1",
157
+ "TEST2=changed"
158
+ ],
159
+ "stateful" => true
160
+ )
161
+ end
162
+ end
158
163
  end
159
164
 
160
165
  context 'variable interpolation' do
@@ -174,6 +179,7 @@ describe Kontena::Cli::Stacks::YAML::Reader do
174
179
  {
175
180
  'STACK' => 'test',
176
181
  'GRID' => 'test-grid',
182
+ 'PLATFORM' => 'test-grid',
177
183
  'TAG' => '4.1'
178
184
  }
179
185
  )
@@ -183,15 +189,21 @@ describe Kontena::Cli::Stacks::YAML::Reader do
183
189
  end
184
190
 
185
191
  it 'interpolates $VAR variables' do
186
- result = subject.execute
187
- services = result[:services]
188
- expect(services['wordpress']['image']).to eq('wordpress:4.1')
192
+ expect(subject.execute['services']).to match array_including(hash_including('image' => 'wordpress:4.1'))
193
+ end
194
+
195
+ it 'interpolates default variables' do
196
+ expect(subject.execute['services']).to match array_including(
197
+ hash_including(
198
+ 'name' => 'wordpress', 'env' => array_including(
199
+ 'STACK=test', 'GRID=test-grid', 'PLATFORM=test-grid'
200
+ )
201
+ )
202
+ )
189
203
  end
190
204
 
191
205
  it 'interpolates ${VAR} variables' do
192
- result = subject.execute
193
- services = result[:services]
194
- expect(services['mysql']['image']).to eq('mariadb:latest')
206
+ expect(subject.execute['services']).to match array_including(hash_including('name' => 'mysql', 'image' => 'mariadb:latest'))
195
207
  end
196
208
 
197
209
  it 'warns about empty variables' do
@@ -217,8 +229,22 @@ describe Kontena::Cli::Stacks::YAML::Reader do
217
229
  allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
218
230
  allow(ENV).to receive(:[]).with('TEST_ENV_VAR').and_return('foo')
219
231
  allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('foo')
220
- services = subject.execute[:services]
221
- expect(services['mysql']['environment'].first).to eq('INTERNAL_VAR=$INTERNAL_VAR')
232
+
233
+ expect(subject.execute['services']).to match array_including(
234
+ hash_including('name' => 'mysql', 'env' => array_including('INTERNAL_VAR=$INTERNAL_VAR'))
235
+ )
236
+ end
237
+
238
+ it 'raises runtime error for undeclared variables' do
239
+ subject.variables.delete(subject.variables.option('test_var'))
240
+ expect{subject.execute}.to raise_error(RuntimeError, /Undeclared variable 'test_var'/)
241
+ end
242
+
243
+ it 'considers variables declared when they are listed as to: env targets' do
244
+ subject.variables.option('tag').to[:env] = "BAG"
245
+ expect{subject.execute}.to raise_error(RuntimeError, /Undeclared variable 'TAG'/)
246
+ subject.variables.option('tag').to[:env] = "TAG"
247
+ expect{subject.execute}.not_to raise_error
222
248
  end
223
249
  end
224
250
 
@@ -233,13 +259,13 @@ describe Kontena::Cli::Stacks::YAML::Reader do
233
259
  end
234
260
 
235
261
  it 'converts env hash to array' do
236
- result = subject.execute[:services]
237
- expect(result['wordpress']['environment']).to eq(['WORDPRESS_DB_PASSWORD=test_secret'])
262
+ expect(subject.execute['services']).to match array_including(hash_including('name' => 'wordpress', 'env' => ['WORDPRESS_DB_PASSWORD=test_secret']))
238
263
  end
239
264
 
240
265
  it 'does nothing to env array' do
241
- result = subject.execute[:services]
242
- expect(result['mysql']['environment']).to eq(['MYSQL_ROOT_PASSWORD=test_secret'])
266
+ expect(subject.execute['services']).to match array_including(
267
+ hash_including('name' => 'mysql', 'env' => ['MYSQL_ROOT_PASSWORD=test_secret'])
268
+ )
243
269
  end
244
270
  end
245
271
 
@@ -275,31 +301,18 @@ describe Kontena::Cli::Stacks::YAML::Reader do
275
301
  end
276
302
 
277
303
  it 'merges variables' do
278
- result = subject.execute[:services]
279
- expect(result['wordpress']['environment']).to eq([
280
- 'WORDPRESS_DB_PASSWORD=test_secret',
281
- 'APIKEY=12345',
282
- 'MYSQL_ROOT_PASSWORD=secret',
283
- 'WP_ADMIN_PASSWORD=verysecret'
284
- ])
304
+ expect(subject.execute['services']).to match array_including(
305
+ hash_including(
306
+ 'name' => 'wordpress',
307
+ 'env' => [
308
+ 'WORDPRESS_DB_PASSWORD=test_secret',
309
+ 'APIKEY=12345',
310
+ 'MYSQL_ROOT_PASSWORD=secret',
311
+ 'WP_ADMIN_PASSWORD=verysecret'
312
+ ]
313
+ )
314
+ )
285
315
  end
286
-
287
- end
288
- end
289
-
290
- it 'returns result hash' do
291
- outcome = subject.execute
292
- expect(outcome[:services]).to eq(valid_v3_result['services'])
293
- end
294
-
295
- context "For an invalid stack file" do
296
- subject do
297
- described_class.new(fixture_path('stack-invalid.yml'))
298
- end
299
-
300
- it 'returns validation errors' do
301
- outcome = subject.execute
302
- expect(outcome[:errors].size).to eq(1)
303
316
  end
304
317
  end
305
318
  end
@@ -311,7 +324,7 @@ describe Kontena::Cli::Stacks::YAML::Reader do
311
324
 
312
325
  it 'expands build option to absolute path' do
313
326
  outcome = subject.execute
314
- expect(outcome[:services]['webapp']['build']['context']).to eq(fixture_path(''))
327
+ expect(outcome['services']['webapp']['build']['context']).to eq(fixture_path(''))
315
328
  end
316
329
  end
317
330
 
@@ -322,7 +335,7 @@ describe Kontena::Cli::Stacks::YAML::Reader do
322
335
 
323
336
  it 'expands build context to absolute path' do
324
337
  outcome = subject.execute
325
- expect(outcome[:services]['webapp']['build']['context']).to eq(fixture_path(''))
338
+ expect(outcome['services']['webapp']['build']['context']).to eq(fixture_path(''))
326
339
  end
327
340
  end
328
341
 
@@ -370,7 +383,7 @@ describe Kontena::Cli::Stacks::YAML::Reader do
370
383
  }
371
384
 
372
385
  subject.send(:normalize_build_args, options)
373
- expect(options.dig('build', 'args')).to eq({
386
+ expect(options['build']['args']).to eq({
374
387
  'foo' => 'bar'
375
388
  })
376
389
  end
@@ -383,53 +396,42 @@ describe Kontena::Cli::Stacks::YAML::Reader do
383
396
  end
384
397
 
385
398
  it 'returns name for v3' do
386
- name = subject.stack_name
399
+ name = subject.loader.stack_name.stack
387
400
  expect(name).to eq('stackname')
388
401
  end
389
402
  end
390
403
 
391
404
  context 'origins' do
392
405
  before do
393
- allow(File).to receive(:read)
394
- .with(fixture_path('kontena_v3.yml'))
395
- .and_return(fixture('kontena_v3.yml'))
406
+ ['docker-compose_v2.yml', 'kontena_v3.yml'].each do |file|
407
+ [:exist?, :read].each do |meth|
408
+ allow(File).to receive(meth)
409
+ .with(fixture_path(file))
410
+ .and_call_original
411
+ end
412
+ end
396
413
  end
397
414
 
398
415
  it 'can read from a file' do
399
- allow(File).to receive(:read)
400
- .with(fixture_path('docker-compose_v2.yml'))
401
- .and_return(fixture('docker-compose-invalid.yml'))
402
-
403
416
  subject = described_class.new(fixture_path('kontena_v3.yml'))
404
-
405
- expect(subject.from_file?).to be_truthy
406
- expect(subject.execute[:registry]).to eq 'file://'
417
+ expect(subject.loader.origin).to eq 'file'
418
+ expect(subject.execute['registry']).to eq 'file://'
407
419
  end
408
420
 
409
421
  it 'can read from the registry' do
410
- allow(File).to receive(:read)
411
- .with(File.expand_path('docker-compose_v2.yml'))
412
- .and_return(fixture('docker-compose-invalid.yml'))
413
-
414
- stack_double = double
415
- allow_any_instance_of(Kontena::StacksCache::RegistryClientFactory).to receive(:cloud_auth?).and_return(true)
416
- expect(Kontena::StacksCache).to receive(:cache).with('foo/foo', nil).and_return(stack_double)
417
- expect(stack_double).to receive(:read).and_return(fixture('kontena_v3.yml'))
422
+ allow(File).to receive(:exist?)
423
+ .with(/foo\/foo$/)
424
+ .and_return(false)
418
425
  instance = described_class.new('foo/foo')
419
- expect(instance.from_registry?).to be_truthy
420
- expect(instance.execute[:registry]).to eq instance.current_account.stacks_url
426
+ expect(instance.loader.origin).to eq 'registry'
421
427
  end
422
428
 
423
429
  it 'can read from an url' do
424
- allow(File).to receive(:read)
425
- .with(File.expand_path('docker-compose_v2.yml'))
426
- .and_return(fixture('docker-compose-invalid.yml'))
427
-
428
- stub_request(:get, "http://foo.example.com/foo").to_return(:status => 200, :body => fixture('stack-with-liquid.yml'), :headers => {})
429
- allow_any_instance_of(described_class).to receive(:load_from_url).and_return(fixture('stack-with-liquid.yml'))
430
+ allow(File).to receive(:exist?)
431
+ .with(/\/foo$/)
432
+ .and_return(false)
430
433
  instance = described_class.new('http://foo.example.com/foo')
431
- expect(instance.from_url?).to be_truthy
432
- expect(instance.execute[:registry]).to eq 'file://'
434
+ expect(instance.loader.origin).to eq 'uri'
433
435
  end
434
436
  end
435
437
 
@@ -444,7 +446,7 @@ describe Kontena::Cli::Stacks::YAML::Reader do
444
446
  end
445
447
 
446
448
  it 'interpolates variables into services' do
447
- expect(subject.execute[:services].size).to eq 5
449
+ expect(subject.execute['services'].size).to eq 5
448
450
  end
449
451
  end
450
452
 
@@ -479,10 +481,11 @@ describe Kontena::Cli::Stacks::YAML::Reader do
479
481
  end
480
482
 
481
483
  it "omits the env" do
482
- outcome = subject.execute
483
-
484
- expect(outcome[:variables]).to eq('asdf' => nil), subject.variables.inspect
485
- expect(outcome[:services]['test']['environment']).to eq nil
484
+ result = subject.execute
485
+ expect(result['variables']).to match hash_including('asdf' => nil)
486
+ expect(result['services']).to match array_including(
487
+ hash_including('name' => 'test', 'env' => nil)
488
+ )
486
489
  end
487
490
  end
488
491
 
@@ -497,9 +500,10 @@ describe Kontena::Cli::Stacks::YAML::Reader do
497
500
 
498
501
  it "defines the env" do
499
502
  outcome = subject.execute
500
-
501
- expect(outcome[:variables]).to eq 'asdf' => 'test'
502
- expect(outcome[:services]['test']['environment']).to eq ['ASDF=test']
503
+ expect(outcome['variables']).to match hash_including('asdf' => 'test')
504
+ expect(outcome['services']).to match array_including(
505
+ hash_including('name' => 'test', 'env' => ['ASDF=test'])
506
+ )
503
507
  end
504
508
  end
505
509
  end