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
@@ -0,0 +1,17 @@
1
+ require 'kontena/plugin_manager'
2
+
3
+ module Kontena::Cli::Plugins
4
+ class ShowCommand < Kontena::Command
5
+ parameter 'NAME', 'Plugin name' do |name|
6
+ "kontena-plugin-#{name}"
7
+ end
8
+
9
+ def execute
10
+ require 'yaml'
11
+ plug = Gem::Specification.find { |g| g.name == name }
12
+ out = {}
13
+ out[:path] = plug.gem_dir
14
+ puts out.to_yaml
15
+ end
16
+ end
17
+ end
@@ -1,26 +1,22 @@
1
- require 'open3'
1
+ require 'kontena/plugin_manager'
2
2
 
3
3
  module Kontena::Cli::Plugins
4
4
  class UninstallCommand < Kontena::Command
5
5
  include Kontena::Util
6
6
  include Kontena::Cli::Common
7
+ include Kontena::PluginManager::Common
7
8
 
8
9
  parameter 'NAME', 'Plugin name'
9
10
 
10
- option "--force", :flag, "Force remove", default: false, attribute_name: :forced
11
+ def uninstaller
12
+ Kontena::PluginManager::Uninstaller.new(name)
13
+ end
11
14
 
12
15
  def execute
13
- exit_with_error "Plugin #{pastel.cyan(name)} is not installed" unless Kontena::PluginManager.instance.installed(name) && !ENV['NO_PLUGINS']
14
- confirm unless forced?
15
- spinner "Uninstalling plugin #{pastel.cyan(name)}" do |spin|
16
- begin
17
- Kontena::PluginManager.instance.uninstall_plugin(name)
18
- rescue => ex
19
- $stderr.puts pastel.red("#{ex.class.name} : #{ex.message}")
20
- logger.error(ex)
21
- spin.fail
22
- end
16
+ exit_with_error "Plugin #{name} has not been installed" unless installed?(name)
17
+ spinner "Uninstalling plugin #{pastel.cyan(name)}" do
18
+ uninstaller.uninstall
23
19
  end
24
20
  end
25
21
  end
26
- end
22
+ end
@@ -14,7 +14,7 @@ module Kontena::Cli::Registry
14
14
  option '--s3-region', 'S3_REGION', 'S3 region', default: 'eu-west-1'
15
15
  option '--s3-encrypt', :flag, 'Encrypt S3 objects', default: false
16
16
  option '--s3-secure', :flag, 'Use secure connection in S3', default: true
17
- option '--s3-v4auth', :flag, 'Use v4auth on S3', default: false
17
+ option '--s3-v4auth', :flag, 'Use v4auth on S3', default: true
18
18
  option '--azure-account-name', 'AZURE_ACCOUNT_NAME', 'Azure account name'
19
19
  option '--azure-container-name', 'AZURE_CONTAINER_NAME', 'Azure container name'
20
20
 
@@ -16,9 +16,13 @@ module Kontena::Cli::Services
16
16
  option ["-v", "--volume"], "VOLUME", "Add a volume or bind mount it from the host", multivalued: true
17
17
  option "--volumes-from", "VOLUMES_FROM", "Mount volumes from another container", multivalued: true
18
18
  option ["-a", "--affinity"], "AFFINITY", "Set service affinity", multivalued: true
19
+ option "--cpus", "CPUS", "Number of CPUs" do |cpus|
20
+ Float(cpus)
21
+ end
19
22
  option ["-c", "--cpu-shares"], "CPU_SHARES", "CPU shares (relative weight)"
20
23
  option ["-m", "--memory"], "MEMORY", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)"
21
24
  option ["--memory-swap"], "MEMORY_SWAP", "Total memory usage (memory + swap), set \'-1\' to disable swap (format: <number><optional unit>, where unit = b, k, m or g)"
25
+ option ["--shm-size"], "SHM_SIZE", "Size of /dev/shm (format: <number><optional unit>, where unit = b, k, m or g)"
22
26
  option "--cmd", "CMD", "Command to execute"
23
27
  option "--instances", "INSTANCES", "How many instances should be deployed"
24
28
  option ["-u", "--user"], "USER", "Username who executes first process inside container"
@@ -69,6 +73,8 @@ module Kontena::Cli::Services
69
73
  data[:volumes_from] = volumes_from_list unless volumes_from_list.empty?
70
74
  data[:memory] = parse_memory(memory) if memory
71
75
  data[:memory_swap] = parse_memory(memory_swap) if memory_swap
76
+ data[:shm_size] = parse_memory(shm_size) if shm_size
77
+ data[:cpus] = cpus if cpus
72
78
  data[:cpu_shares] = cpu_shares if cpu_shares
73
79
  data[:affinity] = affinity_list unless affinity_list.empty?
74
80
  data[:env] = env_list unless env_list.empty?
@@ -100,6 +100,15 @@ module Kontena
100
100
  end
101
101
  end
102
102
 
103
+ if service['certificates'].to_a.size > 0
104
+ puts " certificates: "
105
+ service['certificates'].to_a.each do |c|
106
+ puts " - subject: #{c['subject']}"
107
+ puts " name: #{c['name']}"
108
+ puts " type: #{c['type']}"
109
+ end
110
+ end
111
+
103
112
  if service['env'].to_a.size > 0
104
113
  puts " env: "
105
114
  service['env'].to_a.each do |e|
@@ -161,6 +170,14 @@ module Kontena
161
170
  end
162
171
  end
163
172
 
173
+ unless service['cpus'].to_s.empty?
174
+ puts " cpus: #{service['cpus']}"
175
+ end
176
+
177
+ unless service['cpu_shares'].to_s.empty?
178
+ puts " cpu_shares: #{service['cpu_shares']}"
179
+ end
180
+
164
181
  unless service['memory'].to_s.empty?
165
182
  puts " memory: #{int_to_filesize(service['memory'])}"
166
183
  end
@@ -169,6 +186,10 @@ module Kontena
169
186
  puts " memory_swap: #{int_to_filesize(service['memory_swap'])}"
170
187
  end
171
188
 
189
+ unless service['shm_size'].to_s.empty?
190
+ puts " shm_size: #{int_to_filesize(service['shm_size'])}"
191
+ end
192
+
172
193
  unless service['pid'].to_s.empty?
173
194
  puts " pid: #{service['pid']}"
174
195
  end
@@ -361,13 +382,19 @@ module Kontena
361
382
  def parse_ports(port_options)
362
383
  port_regex = Regexp.new(/\A(?<ip>\d+\.\d+\.\d+\.\d+)?:?(?<node_port>\d+)\:(?<container_port>\d+)\/?(?<protocol>\w+)?\z/)
363
384
  port_options.map do |p|
364
- match_data = port_regex.match(p.to_s)
365
- raise ArgumentError, "Invalid port value #{p}" unless match_data
385
+ if p.kind_of?(Hash)
386
+ raise ArgumentError, "Missing or invalid node port" unless p['node_port'].to_i > 0
387
+ raise ArgumentError, "Missing or invalid container port" unless p['container_port'].to_i > 0
388
+ { ip: p['ip'] || '0.0.0.0', protocol: p['protocol'] || 'tcp', node_port: p['node_port'].to_i, container_port: p['container_port'].to_i }
389
+ else
390
+ match_data = port_regex.match(p.to_s)
391
+ raise ArgumentError, "Invalid port value #{p}" unless match_data
366
392
 
367
- {
368
- ip: '0.0.0.0',
369
- protocol: 'tcp'
370
- }.merge(match_data.names.map { |name| [name.to_sym, match_data[name]] }.to_h.reject { |_,v| v.nil? })
393
+ {
394
+ ip: '0.0.0.0',
395
+ protocol: 'tcp'
396
+ }.merge(match_data.names.map { |name| [name.to_sym, match_data[name]] }.to_h.reject { |_,v| v.nil? })
397
+ end
371
398
  end
372
399
  end
373
400
 
@@ -14,9 +14,13 @@ module Kontena::Cli::Services
14
14
  option ["-e", "--env"], "ENV", "Set environment variables", multivalued: true
15
15
  option ["-l", "--link"], "LINK", "Add link to another service in the form of name:alias", multivalued: true
16
16
  option ["-a", "--affinity"], "AFFINITY", "Set service affinity", multivalued: true
17
+ option "--cpus", "CPUS", "Number of CPUs" do |cpus|
18
+ Float(cpus)
19
+ end
17
20
  option ["-c", "--cpu-shares"], "CPU_SHARES", "CPU shares (relative weight)"
18
21
  option ["-m", "--memory"], "MEMORY", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)"
19
22
  option ["--memory-swap"], "MEMORY_SWAP", "Total memory usage (memory + swap), set \'-1\' to disable swap (format: <number><optional unit>, where unit = b, k, m or g)"
23
+ option ["--shm-size"], "SHM_SIZE", "Size of /dev/shm (format: <number><optional unit>, where unit = b, k, m or g)"
20
24
  option "--cmd", "CMD", "Command to execute"
21
25
  option "--instances", "INSTANCES", "How many instances should be deployed"
22
26
  option ["-u", "--user"], "USER", "Username who executes first process inside container"
@@ -60,6 +64,8 @@ module Kontena::Cli::Services
60
64
  data[:links] = parse_links(link_list) unless link_list.empty?
61
65
  data[:memory] = parse_memory(memory) if memory
62
66
  data[:memory_swap] = parse_memory(memory_swap) if memory_swap
67
+ data[:shm_size] = parse_memory(shm_size) if shm_size
68
+ data[:cpus] = cpus if cpus
63
69
  data[:cpu_shares] = cpu_shares if cpu_shares
64
70
  data[:affinity] = affinity_list unless affinity_list.empty?
65
71
  data[:env] = env_list unless env_list.empty?
@@ -8,7 +8,7 @@ module Kontena::Cli::Stacks
8
8
 
9
9
  banner "Build images listed in a stack file and push them to your image registry"
10
10
 
11
- option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
11
+ option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :source, default: 'kontena.yml'
12
12
 
13
13
  option ['--no-cache'], :flag, 'Do not use cache when building the image', default: false
14
14
  option ['--no-push'], :flag, 'Do not push images to registry', default: false
@@ -26,8 +26,8 @@ module Kontena::Cli::Stacks
26
26
  requires_current_master_token
27
27
 
28
28
  def execute
29
- require_config_file(filename)
30
- stack = stack_read_and_dump(filename, name: name, values: values)
29
+ set_env_variables(stack_name, current_grid)
30
+
31
31
  services = stack['services']
32
32
 
33
33
  unless service_list.empty?
@@ -2,32 +2,72 @@ require_relative 'yaml/reader'
2
2
  require_relative '../services/services_helper'
3
3
  require_relative 'service_generator_v2'
4
4
  require_relative '../../stacks_client'
5
+ require_relative 'yaml/stack_file_loader'
5
6
  require 'yaml'
6
7
 
7
8
  module Kontena::Cli::Stacks
8
9
  module Common
9
10
  include Kontena::Cli::Services::ServicesHelper
10
11
 
11
- module StackNameParam
12
- attr_accessor :stack_version
12
+ # @return [StackFileLoader] a loader for the stack origin defined through command-line options
13
+ def loader
14
+ @loader ||= loader_class.for(source)
15
+ end
16
+
17
+ # @return [YAML::Reader] a YAML reader for the target file
18
+ def reader
19
+ @reader ||= loader.reader
20
+ end
21
+
22
+ # Stack name read from -n parameter or the stack file
23
+ # @return [String]
24
+ def stack_name
25
+ @stack_name ||= (self.respond_to?(:name) && self.name) ? self.name : loader.stack_name.stack
26
+ end
27
+
28
+ # An accessor to the YAML Reader outcome. Passes parent name, values from command line and
29
+ # the stackname to the reader.
30
+ #
31
+ # @return [Hash]
32
+ def stack
33
+ @stack ||= reader.execute(
34
+ name: stack_name,
35
+ parent_name: self.respond_to?(:parent_name) ? self.parent_name : nil,
36
+ values: (self.respond_to?(:values_from_options) ? self.values_from_options : {})
37
+ )
38
+ end
39
+
40
+ # @return [Class] an accessor to StackFileLoader constant, for testing purposes
41
+ def loader_class
42
+ ::Kontena::Cli::Stacks::YAML::StackFileLoader
43
+ end
44
+
45
+ module RegistryNameParam
46
+ def stack_name
47
+ @stack_name ||= Kontena::Cli::Stacks::StackName.new(source)
48
+ end
13
49
 
14
50
  def self.included(where)
15
- where.parameter "STACK_NAME", "Stack name, for example user/stackname or user/stackname:version" do |name|
16
- if name.include?(':')
17
- name, @stack_version = name.split(':',2 )
18
- end
19
- name
20
- end
51
+ where.parameter "STACK_NAME", "Stack name, for example user/stackname or user/stackname:version", attribute_name: :source
52
+ end
53
+ end
54
+
55
+ module StackNameParam
56
+ # Include to add a STACK_NAME parameter
57
+ def self.included(where)
58
+ where.parameter "STACK_NAME", "Stack name, for example user/stackname or user/stackname:version", attribute_name: :source
21
59
  end
22
60
  end
23
61
 
24
62
  module StackFileOrNameParam
63
+ # Include to add a stack file parameter
25
64
  def self.included(where)
26
- where.parameter "[FILE]", "Kontena stack file, registry stack name (user/stack or user/stack:version) or URL", default: "kontena.yml", attribute_name: :filename
65
+ where.parameter "[FILE]", "Kontena stack file, registry stack name (user/stack or user/stack:version) or URL", default: "kontena.yml", attribute_name: :source
27
66
  end
28
67
  end
29
68
 
30
69
  module StackNameOption
70
+ # Include to add a stack name parameter
31
71
  def self.included(where)
32
72
  where.option ['-n', '--name'], 'NAME', 'Define stack name (by default comes from stack file)'
33
73
  end
@@ -35,102 +75,73 @@ module Kontena::Cli::Stacks
35
75
 
36
76
  module StackValuesToOption
37
77
  attr_accessor :values
78
+ # Include to add --values-to variable value dumping feature
38
79
  def self.included(where)
39
80
  where.option '--values-to', '[FILE]', 'Output variable values as YAML to file'
40
81
  end
41
82
 
42
- def dump_variables(reader)
43
- vals = reader.variables.to_h(values_only: true).reject {|k,_| k == 'STACK' || k == 'GRID' }
44
- File.write(values_to, ::YAML.dump(vals))
83
+ # Writes a YAML file from the values received from YAML::Reader to a file defined through
84
+ # the --values-to option
85
+ def dump_variables
86
+ File.write(values_to, ::YAML.dump(reader.variable_values, without_defaults: true, without_vault: true))
45
87
  end
46
88
  end
47
89
 
48
90
  module StackValuesFromOption
49
- attr_accessor :values
91
+ # Include to add --values-from option to read variable values from a YAML file
92
+ # and the -v variable=value option that can be used to pass variable values
93
+ # directly from command line
50
94
  def self.included(where)
95
+ where.prepend InstanceMethods
96
+
51
97
  where.option '--values-from', '[FILE]', 'Read variable values from YAML' do |filename|
52
- if filename
53
- require_config_file(filename)
54
- @values = ::YAML.safe_load(File.read(filename))
55
- end
56
- filename
98
+ values_from_file.merge!(::YAML.safe_load(File.read(filename)))
99
+ true
100
+ end
101
+
102
+ where.option '-v', "VARIABLE=VALUE", "Set stack variable values, example: -v domain=example.com. Can be used multiple times.", multivalued: true, attribute_name: :var_option do |var_pair|
103
+ var_name, var_value = var_pair.split('=', 2)
104
+ values_from_value_options.merge!(::YAML.safe_load(::YAML.dump(var_name => var_value)))
57
105
  end
58
106
  end
59
- end
60
107
 
61
- def stack_name
62
- @stack_name ||= self.name || stack_name_from_yaml(filename)
63
- end
108
+ module InstanceMethods
109
+ def values_from_file
110
+ @values_from_file ||= {}
111
+ end
64
112
 
65
- def reader_from_yaml(filename, name: nil, values: nil, defaults: nil)
66
- reader = Kontena::Cli::Stacks::YAML::Reader.new(filename, values: values, defaults: defaults)
67
- if reader.stack_name.nil?
68
- exit_with_error "Stack MUST have stack name in YAML top level field 'stack'! Aborting."
69
- end
70
- set_env_variables(name || reader.stack_name, current_grid)
71
- reader
72
- end
73
-
74
- def stack_from_reader(reader)
75
- outcome = reader.execute
76
-
77
- hint_on_validation_notifications(outcome[:notifications]) unless outcome[:notifications].empty?
78
- abort_on_validation_errors(outcome[:errors]) unless outcome[:errors].empty?
79
- kontena_services = generate_services(outcome[:services])
80
- kontena_volumes = generate_volumes(outcome[:volumes])
81
- stack = {
82
- 'name' => outcome[:name],
83
- 'stack' => outcome[:stack],
84
- 'expose' => outcome[:expose],
85
- 'version' => outcome[:version],
86
- 'source' => reader.raw_content,
87
- 'registry' => outcome[:registry],
88
- 'services' => kontena_services,
89
- 'volumes' => kontena_volumes,
90
- 'variables' => outcome[:variables]
91
- }
92
- stack
93
- end
94
-
95
- def stack_from_yaml(filename, name: nil, values: nil, defaults: nil)
96
- reader = reader_from_yaml(filename, name: name, values: values, defaults: defaults)
97
- stack_from_reader(reader)
98
- end
99
-
100
- def stack_read_and_dump(filename, name: nil, values: nil, defaults: nil)
101
- reader = reader_from_yaml(filename, name: name, values: values, defaults: defaults)
102
- stack = stack_from_reader(reader)
103
- dump_variables(reader) if values_to
104
- stack
105
- end
106
-
107
- def require_config_file(filename)
108
- exit_with_error("File #{filename} does not exist") unless File.exists?(filename)
109
- end
110
-
111
- def generate_volumes(yaml_volumes)
112
- return [] unless yaml_volumes
113
- yaml_volumes.map do |name, config|
114
- if config['external'].is_a?(TrueClass)
115
- config['external'] = name
116
- elsif config['external']['name']
117
- config['external'] = config['external']['name']
113
+ def values_from_value_options
114
+ @values_from_value_options ||= {}
118
115
  end
119
- config.merge('name' => name)
120
- end
121
- end
122
116
 
123
- def generate_services(yaml_services)
124
- return [] unless yaml_services
125
- yaml_services.map do |name, config|
126
- exit_with_error("Image is missing for #{name}. Aborting.") unless config['image'] # why isn't this a validation?
127
- ServiceGeneratorV2.new(config).generate.merge('name' => name)
117
+ def values_from_options
118
+ @values_from_options ||= values_from_file.merge(values_from_value_options)
119
+ end
120
+
121
+ # Transforms a hash
122
+ # dependency_values_from_options('foo.bar' => 1, 'foo')
123
+ # => { 'bar' => 1 }
124
+ # Used for dependency variable injection
125
+ def dependency_values_from_options(name)
126
+ name_with_dot = name.to_s + '.'
127
+ values_from_options.each_with_object({}) do |kv_pair, obj|
128
+ key = kv_pair.first.to_s
129
+ value = kv_pair.last
130
+ next unless key.start_with?(name_with_dot)
131
+ obj[key.sub(name_with_dot, '')] = value
132
+ end
133
+ end
128
134
  end
129
135
  end
130
136
 
131
- def set_env_variables(stack, grid)
137
+ # Sets environment variables from parameters
138
+ # @param stack [String] current stack name
139
+ # @param grid [String] current grid name
140
+ # @param platform [String] current platform name, defaults to param grid value
141
+ def set_env_variables(stack, grid, platform = grid)
132
142
  ENV['STACK'] = stack
133
143
  ENV['GRID'] = grid
144
+ ENV['PLATFORM'] = platform
134
145
  end
135
146
 
136
147
  # @return [String]
@@ -139,20 +150,24 @@ module Kontena::Cli::Stacks
139
150
  end
140
151
 
141
152
  def display_notifications(messages, color = :yellow)
142
- $stderr.puts(Kontena.pastel.send(color, messages.to_yaml.gsub(/^---$/, '')))
153
+ $stderr.puts(pastel.send(color, messages.to_yaml.gsub(/^---$/, '')))
143
154
  end
144
155
 
145
- def hint_on_validation_notifications(errors)
146
- $stderr.puts "YAML contains the following unsupported options and they were rejected:".colorize(:yellow)
147
- display_notifications(errors)
156
+ def hint_on_validation_notifications(notifications, filename = nil)
157
+ return if notifications.nil? || notifications.empty?
158
+ $stderr.puts pastel.yellow("#{"(#{filename}) " if filename}YAML contains the following unsupported options and they were rejected:")
159
+ display_notifications(notifications)
148
160
  end
149
161
 
150
- def abort_on_validation_errors(errors)
151
- $stderr.puts "YAML validation failed! Aborting.".colorize(:red)
162
+ def abort_on_validation_errors(errors, filename = nil)
163
+ return if errors.nil? || errors.empty?
164
+ $stderr.puts pastel.red("#{"(#{filename}) " if filename} YAML validation failed! Aborting.")
152
165
  display_notifications(errors, :red)
153
166
  abort
154
167
  end
155
168
 
169
+ # An accessor to stack registry client
170
+ # @return [Kontena::StacksClient]
156
171
  def stacks_client
157
172
  @stacks_client ||= Kontena::StacksClient.new(current_account.stacks_url, current_account.token, read_requires_token: current_account.stacks_read_authentication)
158
173
  end