kontena-cli 1.4.0.pre6 → 1.4.0.pre7

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 (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