kontena-cli 1.1.6 → 1.2.0.dev1

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