kontena-cli 1.1.6 → 1.2.0.dev1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/VERSION +1 -1
  4. data/bin/kontena +8 -28
  5. data/kontena-cli.gemspec +2 -2
  6. data/lib/kontena/cli/app_command.rb +14 -27
  7. data/lib/kontena/cli/certificate_command.rb +4 -7
  8. data/lib/kontena/cli/cloud/master_command.rb +5 -12
  9. data/lib/kontena/cli/cloud_command.rb +4 -7
  10. data/lib/kontena/cli/container_command.rb +4 -9
  11. data/lib/kontena/cli/etcd/health_command.rb +1 -0
  12. data/lib/kontena/cli/etcd_command.rb +6 -13
  13. data/lib/kontena/cli/external_registry_command.rb +3 -7
  14. data/lib/kontena/cli/grid_command.rb +14 -29
  15. data/lib/kontena/cli/grids/cloud_config_command.rb +1 -0
  16. data/lib/kontena/cli/grids/common.rb +2 -0
  17. data/lib/kontena/cli/grids/create_command.rb +5 -0
  18. data/lib/kontena/cli/grids/trusted_subnet_command.rb +6 -8
  19. data/lib/kontena/cli/grids/user_command.rb +3 -6
  20. data/lib/kontena/cli/master/config_command.rb +6 -12
  21. data/lib/kontena/cli/master/token_command.rb +6 -11
  22. data/lib/kontena/cli/master/users/role_command.rb +2 -4
  23. data/lib/kontena/cli/master/users_command.rb +4 -8
  24. data/lib/kontena/cli/master_command.rb +14 -33
  25. data/lib/kontena/cli/node_command.rb +7 -15
  26. data/lib/kontena/cli/nodes/health_command.rb +1 -1
  27. data/lib/kontena/cli/nodes/label_command.rb +3 -9
  28. data/lib/kontena/cli/nodes/show_command.rb +1 -1
  29. data/lib/kontena/cli/nodes/ssh_command.rb +13 -4
  30. data/lib/kontena/cli/plugin_command.rb +5 -9
  31. data/lib/kontena/cli/registry_command.rb +2 -5
  32. data/lib/kontena/cli/service_command.rb +22 -45
  33. data/lib/kontena/cli/services/container_command.rb +2 -3
  34. data/lib/kontena/cli/services/env_command.rb +3 -6
  35. data/lib/kontena/cli/services/secret_command.rb +2 -4
  36. data/lib/kontena/cli/stack_command.rb +11 -24
  37. data/lib/kontena/cli/stacks/registry_command.rb +5 -12
  38. data/lib/kontena/cli/stacks/upgrade_command.rb +6 -0
  39. data/lib/kontena/cli/stacks/yaml/reader.rb +17 -2
  40. data/lib/kontena/cli/subcommand_loader.rb +82 -0
  41. data/lib/kontena/cli/vault_command.rb +7 -15
  42. data/lib/kontena/cli/version.rb +6 -1
  43. data/lib/kontena/cli/vpn_command.rb +3 -7
  44. data/lib/kontena/command.rb +28 -1
  45. data/lib/kontena/machine/cloud_config/cloudinit.yml +1 -1
  46. data/lib/kontena/main_command.rb +22 -38
  47. data/lib/kontena/scripts/completer +2 -233
  48. data/lib/kontena/scripts/completer.rb +230 -0
  49. data/lib/kontena/scripts/init +5 -8
  50. data/lib/kontena/scripts/kontena.bash +8 -0
  51. data/lib/kontena/scripts/kontena.zsh +11 -0
  52. data/lib/kontena_cli.rb +9 -1
  53. data/omnibus/wrappers/sh/kontena +1 -1
  54. data/spec/fixtures/stack-with-liquid-optional.yml +14 -0
  55. data/spec/fixtures/stack-with-liquid-undefined.yml +12 -0
  56. data/spec/kontena/cli/app/build_command_spec.rb +1 -2
  57. data/spec/kontena/cli/app/common_spec.rb +1 -2
  58. data/spec/kontena/cli/app/config_command_spec.rb +0 -1
  59. data/spec/kontena/cli/app/deploy_command_spec.rb +2 -3
  60. data/spec/kontena/cli/app/docker_helper_spec.rb +0 -1
  61. data/spec/kontena/cli/app/init_command_spec.rb +0 -1
  62. data/spec/kontena/cli/app/logs_command_spec.rb +0 -1
  63. data/spec/kontena/cli/app/scale_spec.rb +2 -3
  64. data/spec/kontena/cli/app/service_generator_spec.rb +1 -2
  65. data/spec/kontena/cli/app/service_generator_v2_spec.rb +0 -1
  66. data/spec/kontena/cli/app/yaml/reader_spec.rb +0 -1
  67. data/spec/kontena/cli/app/yaml/service_extender_spec.rb +0 -1
  68. data/spec/kontena/cli/app/yaml/validator_spec.rb +0 -1
  69. data/spec/kontena/cli/app/yaml/validator_v2_spec.rb +0 -1
  70. data/spec/kontena/cli/cloud/login_command_spec.rb +0 -1
  71. data/spec/kontena/cli/cloud/logout_command_spec.rb +0 -1
  72. data/spec/kontena/cli/cloud/master/add_command_spec.rb +0 -1
  73. data/spec/kontena/cli/common_spec.rb +0 -1
  74. data/spec/kontena/cli/containers/list_command_spec.rb +0 -1
  75. data/spec/kontena/cli/containers/logs_command_spec.rb +0 -1
  76. data/spec/kontena/cli/etcd/health_command_spec.rb +2 -0
  77. data/spec/kontena/cli/grids/trusted_subnets/add_command_spec.rb +0 -11
  78. data/spec/kontena/cli/grids/trusted_subnets/list_command_spec.rb +4 -13
  79. data/spec/kontena/cli/grids/trusted_subnets/remove_command_spec.rb +0 -11
  80. data/spec/kontena/cli/grids/use_command_spec.rb +0 -1
  81. data/spec/kontena/cli/helpers/log_helper_spec.rb +0 -1
  82. data/spec/kontena/cli/main_command_spec.rb +2 -3
  83. data/spec/kontena/cli/master/current_command_spec.rb +5 -15
  84. data/spec/kontena/cli/master/init_cloud_command_spec.rb +0 -1
  85. data/spec/kontena/cli/master/login_command_spec.rb +0 -1
  86. data/spec/kontena/cli/master/logout_command_spec.rb +0 -1
  87. data/spec/kontena/cli/master/use_command_spec.rb +0 -1
  88. data/spec/kontena/cli/master/users/invite_command_spec.rb +1 -5
  89. data/spec/kontena/cli/master/users/remove_command_spec.rb +2 -14
  90. data/spec/kontena/cli/master/users/roles/add_command_spec.rb +0 -1
  91. data/spec/kontena/cli/master/users/roles/remove_command_spec.rb +0 -1
  92. data/spec/kontena/cli/nodes/list_command_spec.rb +2 -0
  93. data/spec/kontena/cli/services/containers_command_spec.rb +0 -18
  94. data/spec/kontena/cli/services/exec_command_spec.rb +6 -4
  95. data/spec/kontena/cli/services/link_command_spec.rb +5 -19
  96. data/spec/kontena/cli/services/restart_command_spec.rb +0 -16
  97. data/spec/kontena/cli/services/secrets/link_command_spec.rb +0 -11
  98. data/spec/kontena/cli/services/secrets/unlink_command_spec.rb +1 -12
  99. data/spec/kontena/cli/services/services_helper_spec.rb +0 -1
  100. data/spec/kontena/cli/services/unlink_command_spec.rb +7 -21
  101. data/spec/kontena/cli/services/update_command_spec.rb +0 -15
  102. data/spec/kontena/cli/stacks/build_command_spec.rb +0 -1
  103. data/spec/kontena/cli/stacks/deploy_command_spec.rb +10 -13
  104. data/spec/kontena/cli/stacks/install_command_spec.rb +0 -15
  105. data/spec/kontena/cli/stacks/list_command_spec.rb +4 -10
  106. data/spec/kontena/cli/stacks/remove_command_spec.rb +1 -16
  107. data/spec/kontena/cli/stacks/service_generator_spec.rb +0 -1
  108. data/spec/kontena/cli/stacks/service_generator_v2_spec.rb +0 -1
  109. data/spec/kontena/cli/stacks/show_command_spec.rb +1 -14
  110. data/spec/kontena/cli/stacks/upgrade_command_spec.rb +19 -1
  111. data/spec/kontena/cli/stacks/yaml/reader_spec.rb +163 -130
  112. data/spec/kontena/cli/stacks/yaml/service_extender_spec.rb +0 -1
  113. data/spec/kontena/cli/stacks/yaml/validator_v3_spec.rb +0 -1
  114. data/spec/kontena/cli/vault/export_spec.rb +0 -1
  115. data/spec/kontena/cli/vault/import_spec.rb +4 -5
  116. data/spec/kontena/cli/version_command_spec.rb +8 -6
  117. data/spec/kontena/cli/vpn/create_command_spec.rb +3 -4
  118. data/spec/kontena/client_spec.rb +0 -1
  119. data/spec/kontena/config_spec.rb +0 -1
  120. data/spec/kontena/kontena_cli_spec.rb +1 -1
  121. data/spec/kontena/main_command_spec.rb +0 -1
  122. data/spec/kontena/plugin_manager_spec.rb +0 -1
  123. data/spec/spec_helper.rb +1 -0
  124. data/spec/support/client_helpers.rb +1 -1
  125. data/spec/support/exit_with_error_helper.rb +36 -0
  126. data/spec/support/fixtures_helpers.rb +5 -2
  127. metadata +18 -7
@@ -1,4 +1,3 @@
1
- require_relative "../../../../spec_helper"
2
1
  require "kontena/cli/grid_options"
3
2
  require "kontena/cli/services/secrets/link_command"
4
3
 
@@ -7,16 +6,6 @@ describe Kontena::Cli::Services::Secrets::LinkCommand do
7
6
  include ClientHelpers
8
7
 
9
8
  describe '#execute' do
10
- it 'requires api url' do
11
- expect(subject).to receive(:require_api_url).once
12
- subject.run(['service', 'secret:name:env'])
13
- end
14
-
15
- it 'requires token' do
16
- expect(subject).to receive(:require_token).and_return(token)
17
- subject.run(['service', 'secret:name:env'])
18
- end
19
-
20
9
  it 'requires service as param' do
21
10
  expect {
22
11
  subject.run([])
@@ -1,4 +1,3 @@
1
- require_relative "../../../../spec_helper"
2
1
  require "kontena/cli/grid_options"
3
2
  require "kontena/cli/services/secrets/unlink_command"
4
3
 
@@ -7,16 +6,6 @@ describe Kontena::Cli::Services::Secrets::UnlinkCommand do
7
6
  include ClientHelpers
8
7
 
9
8
  describe '#execute' do
10
- it 'requires api url' do
11
- expect(subject).to receive(:require_api_url).once
12
- subject.run(['service', 'secret:name:env'])
13
- end
14
-
15
- it 'requires token' do
16
- expect(subject).to receive(:require_token).and_return(token)
17
- subject.run(['service', 'secret:name:env'])
18
- end
19
-
20
9
  it 'requires service as param' do
21
10
  expect {
22
11
  subject.run([])
@@ -41,7 +30,7 @@ describe Kontena::Cli::Services::Secrets::UnlinkCommand do
41
30
  {'secret' => 'BAR', 'name' => 'BAZ', 'type' => 'env'}
42
31
  ]
43
32
  }
44
- allow(client).to receive(:get).with("services/test-grid/null/mymy").and_return(original)
33
+ expect(client).to receive(:get).with("services/test-grid/null/mymy").and_return(original)
45
34
  expect(client).to receive(:put).with("services/test-grid/null/mymy", data)
46
35
  subject.run(['mymy', 'FOO:BAR:env'])
47
36
  end
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/services/services_helper"
3
2
 
4
3
  module Kontena::Cli::Services
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/services/unlink_command"
3
2
 
4
3
  describe Kontena::Cli::Services::UnlinkCommand do
@@ -6,34 +5,21 @@ describe Kontena::Cli::Services::UnlinkCommand do
6
5
  include ClientHelpers
7
6
 
8
7
  describe '#execute' do
9
- before(:each) do
10
- allow(client).to receive(:get).and_return({
11
- 'links' => [
12
- {'alias' => 'service-b', 'id' => "test-grid/null/service-b", 'name' => 'service-b'}
13
- ]
14
- })
15
- end
16
-
17
- it 'requires api url' do
18
- expect(subject).to receive(:require_api_url).once
19
- subject.run(['service-a', 'service-b'])
20
- end
21
-
22
- it 'requires token' do
23
- expect(subject).to receive(:require_token).and_return(token)
24
- subject.run(['service-a', 'service-b'])
25
- end
26
-
27
8
  it 'aborts if service is not linked' do
28
- allow(client).to receive(:get).and_return({
9
+ expect(client).to receive(:get).with('services/test-grid/null/service-a').and_return({
29
10
  'links' => []
30
11
  })
31
12
  expect {
32
13
  subject.run(['service-a', 'service-b'])
33
- }.to raise_error(SystemExit)
14
+ }.to exit_with_error
34
15
  end
35
16
 
36
17
  it 'sends link to master' do
18
+ expect(client).to receive(:get).with('services/test-grid/null/service-a').and_return({
19
+ 'links' => [
20
+ {'alias' => 'service-b', 'id' => "test-grid/null/service-b", 'name' => 'service-b'}
21
+ ]
22
+ })
37
23
  expect(client).to receive(:put).with(
38
24
  'services/test-grid/null/service-a', {links: []}
39
25
  )
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/grid_options"
3
2
  require "kontena/cli/services/update_command"
4
3
 
@@ -8,20 +7,6 @@ describe Kontena::Cli::Services::UpdateCommand do
8
7
 
9
8
  describe '#execute' do
10
9
 
11
- before(:each) do
12
- allow(subject).to receive(:update_service).and_return({})
13
- end
14
-
15
- it 'requires api url' do
16
- expect(subject).to receive(:require_api_url).once
17
- subject.run(['service'])
18
- end
19
-
20
- it 'requires token' do
21
- expect(subject).to receive(:require_token).once
22
- subject.run(['service'])
23
- end
24
-
25
10
  it 'sends update command' do
26
11
  expect(subject).to receive(:update_service).with(duck_type(:access_token), 'service', {privileged: false})
27
12
  subject.run(['service'])
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/stacks/build_command"
3
2
 
4
3
  describe Kontena::Cli::Stacks::BuildCommand do
@@ -1,29 +1,26 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/stacks/deploy_command"
3
2
 
4
3
  describe Kontena::Cli::Stacks::DeployCommand do
5
4
 
6
5
  include ClientHelpers
6
+ include RequirementsHelper
7
7
 
8
- describe '#execute' do
9
- before(:each) do
10
- allow(subject).to receive(:wait_for_deploy_to_finish).and_return(spy)
11
- end
8
+ expect_to_require_current_master
9
+ expect_to_require_current_master_token
12
10
 
13
- it 'requires api url' do
14
- expect(described_class.requires_current_master?).to be_truthy
15
- subject.run(['test-stack'])
16
- end
11
+ mock_current_master
17
12
 
18
- it 'requires token' do
19
- expect(described_class.requires_current_master_token?).to be_truthy
20
- subject.run(['test-stack'])
13
+ describe '#execute' do
14
+ before(:each) do
15
+ allow(subject).to receive(:wait_for_deploy_to_finish).and_return(true)
16
+ allow(subject).to receive(:wait_for_deployment_to_start).and_return(true)
17
+ allow(subject).to receive(:wait_for_service_deploy).and_return(true)
21
18
  end
22
19
 
23
20
  it 'sends deploy command to master' do
24
21
  expect(client).to receive(:post).with(
25
22
  'stacks/test-grid/test-stack/deploy', {}
26
- )
23
+ ).and_return({})
27
24
  subject.run(['test-stack'])
28
25
  end
29
26
  end
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/stacks/install_command"
3
2
 
4
3
  describe Kontena::Cli::Stacks::InstallCommand do
@@ -26,20 +25,6 @@ describe Kontena::Cli::Stacks::InstallCommand do
26
25
  allow(subject).to receive(:yaml_content).and_return("YAML content")
27
26
  end
28
27
 
29
- it 'requires api url' do
30
- allow(File).to receive(:exist?).with('kontena.yml').and_return(true)
31
- allow(subject).to receive(:stack_from_yaml).with('kontena.yml', hash_including(name: nil, values: nil)).and_return(stack)
32
- expect(described_class.requires_current_master?).to be_truthy
33
- subject.run([])
34
- end
35
-
36
- it 'requires token' do
37
- allow(File).to receive(:exist?).with('kontena.yml').and_return(true)
38
- allow(subject).to receive(:stack_from_yaml).with('kontena.yml', hash_including(name: nil, values: nil)).and_return(stack)
39
- expect(described_class.requires_current_master_token?).to be_truthy
40
- subject.run([])
41
- end
42
-
43
28
  it 'sends stack to master' do
44
29
  allow(File).to receive(:exist?).with('kontena.yml').and_return(true)
45
30
  allow(subject).to receive(:require_config_file).with('kontena.yml').and_return(true)
@@ -1,19 +1,13 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/stacks/list_command"
3
2
 
4
3
  describe Kontena::Cli::Stacks::ListCommand do
5
-
6
4
  include ClientHelpers
5
+ include RequirementsHelper
7
6
 
8
- describe '#execute' do
9
- it 'requires api url' do
10
- expect(subject.class.requires_current_master).to be_truthy
11
- end
12
-
13
- it 'requires token' do
14
- expect(subject.class.requires_current_master_token).to be_truthy
15
- end
7
+ expect_to_require_current_master
8
+ expect_to_require_current_master_token
16
9
 
10
+ describe '#execute' do
17
11
  it 'fetches stacks from master' do
18
12
  stacks = {
19
13
  'stacks' => []
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/stacks/remove_command"
3
2
 
4
3
  describe Kontena::Cli::Stacks::RemoveCommand do
@@ -6,20 +5,6 @@ describe Kontena::Cli::Stacks::RemoveCommand do
6
5
  include ClientHelpers
7
6
 
8
7
  describe '#execute' do
9
- it 'requires api url' do
10
- allow(subject).to receive(:forced?).and_return(true)
11
- allow(subject).to receive(:wait_stack_removal)
12
- expect(described_class.requires_current_master?).to be_truthy
13
- subject.run(['test-stack'])
14
- end
15
-
16
- it 'requires token' do
17
- allow(subject).to receive(:forced?).and_return(true)
18
- allow(subject).to receive(:wait_stack_removal)
19
- expect(described_class.requires_current_master_token?).to be_truthy
20
- subject.run(['test-stack'])
21
- end
22
-
23
8
  it 'sends remove command to master' do
24
9
  allow(subject).to receive(:wait_stack_removal)
25
10
  expect(client).to receive(:delete).with('stacks/test-grid/test-stack')
@@ -39,7 +24,7 @@ describe Kontena::Cli::Stacks::RemoveCommand do
39
24
  .and_raise(Kontena::Errors::StandardError.new(500, 'internal error'))
40
25
  expect{
41
26
  subject.run(['--force', 'test-stack'])
42
- }.to raise_error(Kontena::Errors::StandardError)
27
+ }.to exit_with_error
43
28
  end
44
29
  end
45
30
  end
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/stacks/service_generator"
3
2
 
4
3
  describe Kontena::Cli::Stacks::ServiceGenerator do
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/stacks/service_generator_v2"
3
2
  require 'ruby_dig'
4
3
 
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/stacks/show_command"
3
2
 
4
3
  describe Kontena::Cli::Stacks::ShowCommand do
@@ -6,20 +5,8 @@ describe Kontena::Cli::Stacks::ShowCommand do
6
5
  include ClientHelpers
7
6
 
8
7
  describe '#execute' do
9
- it 'requires api url' do
10
- allow(subject).to receive(:forced?).and_return(true)
11
- expect(described_class.requires_current_master?).to be_truthy
12
- subject.run(['test-stack'])
13
- end
14
-
15
- it 'requires token' do
16
- allow(subject).to receive(:forced?).and_return(true)
17
- expect(described_class.requires_current_master_token?).to be_truthy
18
- subject.run(['test-stack'])
19
- end
20
-
21
8
  it 'fetches stack info from master' do
22
- expect(client).to receive(:get).with('stacks/test-grid/test-stack')
9
+ expect(client).to receive(:get).with('stacks/test-grid/test-stack').and_return(spy())
23
10
  subject.run(['test-stack'])
24
11
  end
25
12
  end
@@ -1,4 +1,3 @@
1
- require_relative "../../../spec_helper"
2
1
  require "kontena/cli/stacks/upgrade_command"
3
2
 
4
3
  describe Kontena::Cli::Stacks::UpgradeCommand do
@@ -13,6 +12,15 @@ describe Kontena::Cli::Stacks::UpgradeCommand do
13
12
  let(:stack) do
14
13
  {
15
14
  'name' => 'stack-a',
15
+ 'stack' => 'foo/stack-a',
16
+ 'services' => []
17
+ }
18
+ end
19
+
20
+ let(:stack_with_different_stack_name) do
21
+ {
22
+ 'name' => 'stack-a',
23
+ 'stack' => 'foo/stack-z',
16
24
  'services' => []
17
25
  }
18
26
  end
@@ -24,6 +32,7 @@ describe Kontena::Cli::Stacks::UpgradeCommand do
24
32
  let(:stack_response) do
25
33
  {
26
34
  'name' => 'stack-a',
35
+ 'stack' => 'foo/stack-a',
27
36
  'services' => [],
28
37
  'variables' => defaults
29
38
  }
@@ -39,6 +48,7 @@ describe Kontena::Cli::Stacks::UpgradeCommand do
39
48
  it 'uses kontena.yml as default stack file' do
40
49
  expect(client).to receive(:get).with('stacks/test-grid/stack-name').and_return(stack_response)
41
50
  expect(subject).to receive(:stack_from_yaml).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)
42
52
  subject.run(['stack-name'])
43
53
  end
44
54
 
@@ -64,6 +74,14 @@ describe Kontena::Cli::Stacks::UpgradeCommand do
64
74
  subject.run(['stack-b', './path/to/kontena.yml'])
65
75
  end
66
76
 
77
+ 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_from_yaml).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
83
+ end
84
+
67
85
  it 'triggers deploy by default' do
68
86
  expect(client).to receive(:get).with('stacks/test-grid/stack-a').and_return(stack_response)
69
87
  allow(subject).to receive(:require_config_file).with('./path/to/kontena.yml').and_return(true)
@@ -1,18 +1,9 @@
1
- require_relative '../../../../spec_helper'
2
1
  require 'kontena/cli/stacks/yaml/reader'
3
2
  require 'liquid'
4
3
 
5
4
  describe Kontena::Cli::Stacks::YAML::Reader do
6
5
  include FixturesHelpers
7
6
 
8
- def absolute_yaml_path(file = 'kontena_v3.yml')
9
- "#{Dir.pwd}/#{file}"
10
- end
11
-
12
- let(:subject) do
13
- described_class.new('kontena_v3.yml')
14
- end
15
-
16
7
  let(:service_extender) do
17
8
  spy
18
9
  end
@@ -49,30 +40,30 @@ describe Kontena::Cli::Stacks::YAML::Reader do
49
40
  end
50
41
 
51
42
  before(:each) do
52
- allow(File).to receive(:read)
53
- .with(absolute_yaml_path('docker-compose_v2.yml'))
54
- .and_return(fixture('docker-compose_v2.yml'))
55
- allow(File).to receive(:read)
56
- .with(absolute_yaml_path('kontena_v3.yml'))
57
- .and_return(fixture('kontena_v3.yml'))
58
43
  allow_any_instance_of(described_class).to receive(:env)
59
44
  .and_return( { 'STACK' => 'test', 'GRID' => 'test-grid' } )
60
45
  end
61
46
 
62
47
  describe '#initialize' do
48
+ subject do
49
+ described_class.new(fixture_path('kontena_v3.yml'))
50
+ end
51
+
63
52
  it 'reads given file' do
64
53
  expect(File).to receive(:read)
65
- .with(absolute_yaml_path)
54
+ .with(fixture_path('kontena_v3.yml'))
66
55
  .and_return(fixture('kontena_v3.yml'))
56
+
67
57
  subject
68
58
  end
69
59
  end
70
60
 
71
61
  context 'when yaml file is malformed' do
62
+ subject do
63
+ described_class.new(fixture_path('kontena-malformed-yaml.yml'))
64
+ end
65
+
72
66
  it 'exits the execution' do
73
- allow(File).to receive(:read)
74
- .with(absolute_yaml_path)
75
- .and_return(fixture('kontena-malformed-yaml.yml'))
76
67
  expect {
77
68
  subject.execute
78
69
  }.to raise_error(StandardError)
@@ -80,17 +71,21 @@ describe Kontena::Cli::Stacks::YAML::Reader do
80
71
  end
81
72
 
82
73
  context 'when service config is not hash' do
83
- it 'returns error' do
84
- allow(File).to receive(:read)
85
- .with(absolute_yaml_path)
86
- .and_return(fixture('kontena-not-hash-service-config.yml'))
74
+ subject do
75
+ described_class.new(fixture_path('kontena-not-hash-service-config.yml'))
76
+ end
87
77
 
78
+ it 'returns error' do
88
79
  outcome = subject.execute
89
80
  expect(outcome[:errors].size).to eq(1)
90
81
  end
91
82
  end
92
83
 
93
84
  describe '#execute' do
85
+ subject do
86
+ described_class.new(fixture_path('kontena_v3.yml'))
87
+ end
88
+
94
89
  context 'when extending services' do
95
90
  it 'extends services from external file' do
96
91
  docker_compose_yml = YAML.load(fixture('docker-compose_v2.yml'))
@@ -127,9 +122,8 @@ describe Kontena::Cli::Stacks::YAML::Reader do
127
122
  end
128
123
 
129
124
  it 'extends services from the same file' do
130
- expect(File).to receive(:read)
131
- .with(absolute_yaml_path('kontena_v3.yml'))
132
- .and_return(fixture('stack-internal-extend.yml'))
125
+ subject = described_class.new(fixture_path('stack-internal-extend.yml'))
126
+
133
127
  kontena_yml = YAML.load(fixture('stack-internal-extend.yml'))
134
128
 
135
129
  expect(Kontena::Cli::Stacks::YAML::ServiceExtender).to receive(:new)
@@ -142,7 +136,10 @@ describe Kontena::Cli::Stacks::YAML::Reader do
142
136
 
143
137
  it 'merges validation errors' do
144
138
  allow(File).to receive(:read)
145
- .with(absolute_yaml_path('docker-compose_v2.yml'))
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'))
146
143
  .and_return(fixture('docker-compose-invalid.yml'))
147
144
  outcome = subject.execute
148
145
  expect(outcome[:errors]).to eq([{
@@ -159,7 +156,18 @@ describe Kontena::Cli::Stacks::YAML::Reader do
159
156
  end
160
157
 
161
158
  context 'variable interpolation' do
159
+ subject do
160
+ described_class.new(fixture_path('stack-with-variables.yml'))
161
+ end
162
+
162
163
  before(:each) do
164
+ allow(File).to receive(:read)
165
+ .with(fixture_path('docker-compose_v2.yml'))
166
+ .and_return(fixture('docker-compose_v2.yml'))
167
+ allow(File).to receive(:read)
168
+ .with(fixture_path('stack-with-variables.yml'))
169
+ .and_return(fixture('stack-with-variables.yml'))
170
+
163
171
  allow_any_instance_of(described_class).to receive(:env).and_return(
164
172
  {
165
173
  'STACK' => 'test',
@@ -167,32 +175,24 @@ describe Kontena::Cli::Stacks::YAML::Reader do
167
175
  'TAG' => '4.1'
168
176
  }
169
177
  )
178
+ allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
170
179
  allow(ENV).to receive(:[]).with('TEST_ENV_VAR').and_return('foo')
171
180
  allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('mariadb:latest')
172
181
  end
173
182
 
174
183
  it 'interpolates $VAR variables' do
175
- allow(File).to receive(:read)
176
- .with(absolute_yaml_path)
177
- .and_return(fixture('stack-with-variables.yml'))
178
184
  result = subject.execute
179
185
  services = result[:services]
180
186
  expect(services['wordpress']['image']).to eq('wordpress:4.1')
181
187
  end
182
188
 
183
189
  it 'interpolates ${VAR} variables' do
184
- allow(File).to receive(:read)
185
- .with(absolute_yaml_path)
186
- .and_return(fixture('stack-with-variables.yml'))
187
190
  result = subject.execute
188
191
  services = result[:services]
189
192
  expect(services['mysql']['image']).to eq('mariadb:latest')
190
193
  end
191
194
 
192
195
  it 'warns about empty variables' do
193
- allow(File).to receive(:read)
194
- .with(absolute_yaml_path)
195
- .and_return(fixture('stack-with-variables.yml'))
196
196
  allow(ENV).to receive(:[])
197
197
  .with('MYSQL_IMAGE')
198
198
  .and_return('')
@@ -204,52 +204,48 @@ describe Kontena::Cli::Stacks::YAML::Reader do
204
204
  subject.execute
205
205
  }.to output("Value for MYSQL_IMAGE is not set. Substituting with an empty string.\n").to_stdout
206
206
  end
207
- end
208
207
 
209
- it 'replaces $$VAR variables to $VAR format' do
210
- allow_any_instance_of(described_class).to receive(:env).and_return(
211
- {
212
- 'STACK' => 'test',
213
- 'GRID' => 'test-grid'
214
- }
215
- )
216
- allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
217
- allow(ENV).to receive(:[]).with('TEST_ENV_VAR').and_return('foo')
218
- allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('foo')
219
- allow(File).to receive(:read)
220
- .with(absolute_yaml_path)
221
- .and_return(fixture('stack-with-variables.yml'))
222
- allow(File).to receive(:read)
223
- .with(absolute_yaml_path('docker-compose_v2.yml'))
224
- .and_return(fixture('docker-compose_v2.yml'))
225
- services = subject.execute[:services]
226
- expect(services['mysql']['environment'].first).to eq('INTERNAL_VAR=$INTERNAL_VAR')
208
+ it 'replaces $$VAR variables to $VAR format' do
209
+ allow_any_instance_of(described_class).to receive(:env).and_return(
210
+ {
211
+ 'STACK' => 'test',
212
+ 'GRID' => 'test-grid'
213
+ }
214
+ )
215
+ allow(ENV).to receive(:[]).with('TAG').and_return('4.1')
216
+ allow(ENV).to receive(:[]).with('TEST_ENV_VAR').and_return('foo')
217
+ allow(ENV).to receive(:[]).with('MYSQL_IMAGE').and_return('foo')
218
+ services = subject.execute[:services]
219
+ expect(services['mysql']['environment'].first).to eq('INTERNAL_VAR=$INTERNAL_VAR')
220
+ end
227
221
  end
228
222
 
229
223
  context 'environment variables' do
230
- it 'converts env hash to array' do
231
- result = subject.execute[:services]
232
- expect(result['wordpress']['environment']).to eq(['WORDPRESS_DB_PASSWORD=test_secret'])
224
+ subject do
225
+ described_class.new(fixture_path('stack-with-env-file.yml'))
233
226
  end
234
227
 
235
- it 'does nothing to env array' do
236
- result = subject.execute[:services]
237
- expect(result['mysql']['environment']).to eq(['MYSQL_ROOT_PASSWORD=test_secret'])
228
+ context "with an empty env file" do
229
+ before(:each) do
230
+ allow(File).to receive(:readlines).with('.env').and_return([])
231
+ end
232
+
233
+ it 'converts env hash to array' do
234
+ result = subject.execute[:services]
235
+ expect(result['wordpress']['environment']).to eq(['WORDPRESS_DB_PASSWORD=test_secret'])
236
+ end
237
+
238
+ it 'does nothing to env array' do
239
+ result = subject.execute[:services]
240
+ expect(result['mysql']['environment']).to eq(['MYSQL_ROOT_PASSWORD=test_secret'])
241
+ end
238
242
  end
239
243
 
240
244
  context 'when introduced env_file' do
241
245
  before(:each) do
242
- allow(File).to receive(:read)
243
- .with(absolute_yaml_path('kontena_v3.yml'))
244
- .and_return(fixture('stack-with-env-file.yml'))
245
246
  allow(File).to receive(:readlines).with('.env').and_return(env_file)
246
247
  end
247
248
 
248
- it 'reads given file' do
249
- expect(File).to receive(:readlines).with('.env').and_return(env_file)
250
- subject.send(:read_env_file, '.env')
251
- end
252
-
253
249
  it 'discards comment lines' do
254
250
  result = env_file
255
251
  result << "#COMMENTLINE"
@@ -294,42 +290,47 @@ describe Kontena::Cli::Stacks::YAML::Reader do
294
290
  expect(outcome[:services]).to eq(valid_v3_result['services'])
295
291
  end
296
292
 
297
- it 'returns validation errors' do
298
- allow(File).to receive(:read)
299
- .with(absolute_yaml_path('kontena_v3.yml'))
300
- .and_return(fixture('stack-invalid.yml'))
301
- outcome = subject.execute
302
- expect(outcome[:errors].size).to eq(1)
293
+ context "For an invalid stack file" do
294
+ subject do
295
+ described_class.new(fixture_path('stack-invalid.yml'))
296
+ end
297
+
298
+ it 'returns validation errors' do
299
+ outcome = subject.execute
300
+ expect(outcome[:errors].size).to eq(1)
301
+ end
303
302
  end
304
303
  end
305
304
 
306
305
  context 'when build option is string' do
306
+ subject do
307
+ described_class.new(fixture_path('kontena_build_v3.yml'))
308
+ end
309
+
307
310
  it 'expands build option to absolute path' do
308
- allow(File).to receive(:read)
309
- .with(absolute_yaml_path)
310
- .and_return(fixture('kontena_build_v3.yml'))
311
311
  outcome = subject.execute
312
- expect(outcome[:services]['webapp']['build']['context']).to eq(File.expand_path('.'))
312
+ expect(outcome[:services]['webapp']['build']['context']).to eq(fixture_path(''))
313
313
  end
314
314
  end
315
315
 
316
316
  context 'when build option is Hash' do
317
+ subject do
318
+ described_class.new(fixture_path('kontena_build_v3.yml'))
319
+ end
320
+
317
321
  it 'expands build context to absolute path' do
318
- allow(File).to receive(:read)
319
- .with(absolute_yaml_path)
320
- .and_return(fixture('kontena_build_v3.yml'))
321
322
  outcome = subject.execute
322
- expect(outcome[:services]['webapp']['build']['context']).to eq(File.expand_path('.'))
323
+ expect(outcome[:services]['webapp']['build']['context']).to eq(fixture_path(''))
323
324
  end
324
325
  end
325
326
 
326
327
  context 'normalize_build_args' do
328
+ subject do
329
+ described_class.new(fixture_path('kontena_build_v3.yml'))
330
+ end
331
+
327
332
  context 'when build option is string' do
328
333
  it 'skips normalizing' do
329
- allow(File).to receive(:read)
330
- .with(absolute_yaml_path)
331
- .and_return(fixture('kontena_build_v3.yml'))
332
-
333
334
  options = {
334
335
  'build' => '.'
335
336
  }
@@ -341,10 +342,6 @@ describe Kontena::Cli::Stacks::YAML::Reader do
341
342
 
342
343
  context 'when build arguments option is Hash' do
343
344
  it 'does not do anything' do
344
- allow(File).to receive(:read)
345
- .with(absolute_yaml_path)
346
- .and_return(fixture('kontena_build_v3.yml'))
347
-
348
345
  options = {
349
346
  'build' => {
350
347
  'context' => '.',
@@ -363,10 +360,6 @@ describe Kontena::Cli::Stacks::YAML::Reader do
363
360
 
364
361
  context 'when build arguments option is Array' do
365
362
  it 'converts it to array' do
366
- allow(File).to receive(:read)
367
- .with(absolute_yaml_path)
368
- .and_return(fixture('kontena_build_v3.yml'))
369
-
370
363
  options = {
371
364
  'build' => {
372
365
  'context' => '.',
@@ -383,25 +376,39 @@ describe Kontena::Cli::Stacks::YAML::Reader do
383
376
  end
384
377
 
385
378
  describe '#stack_name' do
379
+ subject do
380
+ described_class.new(fixture_path('kontena_v3.yml'))
381
+ end
382
+
386
383
  it 'returns name for v3' do
387
- allow(File).to receive(:read)
388
- .with(absolute_yaml_path)
389
- .and_return(fixture('kontena_v3.yml'))
390
384
  name = subject.stack_name
391
385
  expect(name).to eq('stackname')
392
386
  end
393
387
  end
394
388
 
395
389
  context 'origins' do
390
+ before do
391
+ allow(File).to receive(:read)
392
+ .with(fixture_path('kontena_v3.yml'))
393
+ .and_return(fixture('kontena_v3.yml'))
394
+ end
395
+
396
396
  it 'can read from a file' do
397
- expect(File).to receive(:read)
398
- .with(absolute_yaml_path('kontena_v3.yml'))
399
- .and_return(fixture('stack-with-liquid.yml'))
397
+ allow(File).to receive(:read)
398
+ .with(fixture_path('docker-compose_v2.yml'))
399
+ .and_return(fixture('docker-compose-invalid.yml'))
400
+
401
+ subject = described_class.new(fixture_path('kontena_v3.yml'))
402
+
400
403
  expect(subject.from_file?).to be_truthy
401
404
  expect(subject.execute[:registry]).to eq 'file://'
402
405
  end
403
406
 
404
407
  it 'can read from the registry' do
408
+ allow(File).to receive(:read)
409
+ .with(File.expand_path('docker-compose_v2.yml'))
410
+ .and_return(fixture('docker-compose-invalid.yml'))
411
+
405
412
  stack_double = double
406
413
  allow_any_instance_of(Kontena::StacksCache::RegistryClientFactory).to receive(:cloud_auth?).and_return(true)
407
414
  expect(Kontena::StacksCache).to receive(:cache).with('foo/foo', nil).and_return(stack_double)
@@ -412,6 +419,10 @@ describe Kontena::Cli::Stacks::YAML::Reader do
412
419
  end
413
420
 
414
421
  it 'can read from an url' do
422
+ allow(File).to receive(:read)
423
+ .with(File.expand_path('docker-compose_v2.yml'))
424
+ .and_return(fixture('docker-compose-invalid.yml'))
425
+
415
426
  stub_request(:get, "http://foo.example.com/foo").to_return(:status => 200, :body => fixture('stack-with-liquid.yml'), :headers => {})
416
427
  allow_any_instance_of(described_class).to receive(:load_from_url).and_return(fixture('stack-with-liquid.yml'))
417
428
  instance = described_class.new('http://foo.example.com/foo')
@@ -420,21 +431,10 @@ describe Kontena::Cli::Stacks::YAML::Reader do
420
431
  end
421
432
  end
422
433
 
423
- context 'liquid' do
424
- context 'valid' do
425
- before(:each) do
426
- allow(File).to receive(:read)
427
- .with(absolute_yaml_path('kontena_v3.yml'))
428
- .and_return(fixture('stack-with-liquid.yml'))
429
- allow_any_instance_of(described_class).to receive(:env).and_return(
430
- {
431
- 'STACK' => 'test',
432
- 'GRID' => 'test-grid',
433
- 'TAG' => '4.1',
434
- 'MYSQL_IMAGE' => 'mariadb:latest'
435
- }
436
- )
437
- allow(ENV).to receive(:[]).with('TEST_ENV_VAR').and_return('foo')
434
+ context "using Liquid templates" do
435
+ context "for a valid stack file" do
436
+ subject do
437
+ described_class.new(fixture_path('stack-with-liquid.yml'))
438
438
  end
439
439
 
440
440
  it 'does not interpolate liquid into variables' do
@@ -446,20 +446,9 @@ describe Kontena::Cli::Stacks::YAML::Reader do
446
446
  end
447
447
  end
448
448
 
449
- context 'invalid' do
450
- before(:each) do
451
- allow(File).to receive(:read)
452
- .with(absolute_yaml_path('kontena_v3.yml'))
453
- .and_return(fixture('stack-with-invalid-liquid.yml'))
454
- allow_any_instance_of(described_class).to receive(:env).and_return(
455
- {
456
- 'STACK' => 'test',
457
- 'GRID' => 'test-grid',
458
- 'TAG' => '4.1',
459
- 'MYSQL_IMAGE' => 'mariadb:latest'
460
- }
461
- )
462
- allow(ENV).to receive(:[]).with('TEST_ENV_VAR').and_return('foo')
449
+ context "for invalid Liquid syntax" do
450
+ subject do
451
+ described_class.new(fixture_path('stack-with-invalid-liquid.yml'))
463
452
  end
464
453
 
465
454
  it 'raises' do
@@ -467,4 +456,48 @@ describe Kontena::Cli::Stacks::YAML::Reader do
467
456
  end
468
457
  end
469
458
  end
459
+
460
+ context "for an undefined variable" do
461
+ subject do
462
+ described_class.new(fixture_path('stack-with-liquid-undefined.yml'))
463
+ end
464
+
465
+ it "raises an undefined variable error" do
466
+ expect{subject.execute}.to raise_error(Liquid::UndefinedVariable, /undefined variable asdflol/)
467
+ end
468
+ end
469
+
470
+ context "for an optional variable that is not defined" do
471
+ subject do
472
+ described_class.new(fixture_path('stack-with-liquid-optional.yml'))
473
+ end
474
+
475
+ before do
476
+ allow(ENV).to receive(:[]).with('asdf').and_return(nil)
477
+ end
478
+
479
+ it "omits the env" do
480
+ outcome = subject.execute
481
+
482
+ expect(outcome[:variables]).to eq('asdf' => nil), subject.variables.inspect
483
+ expect(outcome[:services]['test']['environment']).to eq nil
484
+ end
485
+ end
486
+
487
+ context "for an optional variable that is defined" do
488
+ subject do
489
+ described_class.new(fixture_path('stack-with-liquid-optional.yml'))
490
+ end
491
+
492
+ before do
493
+ allow(ENV).to receive(:[]).with('asdf').and_return('test')
494
+ end
495
+
496
+ it "defines the env" do
497
+ outcome = subject.execute
498
+
499
+ expect(outcome[:variables]).to eq 'asdf' => 'test'
500
+ expect(outcome[:services]['test']['environment']).to eq ['ASDF=test']
501
+ end
502
+ end
470
503
  end