kontena-cli 1.4.0.pre6 → 1.4.0.pre7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/VERSION +1 -1
  4. data/bin/kontena +1 -1
  5. data/kontena-cli.gemspec +3 -3
  6. data/lib/kontena/cli/certificate/authorize_command.rb +67 -6
  7. data/lib/kontena/cli/certificate/get_command.rb +7 -0
  8. data/lib/kontena/cli/certificate/list_command.rb +75 -0
  9. data/lib/kontena/cli/certificate/register_command.rb +13 -2
  10. data/lib/kontena/cli/certificate/request_command.rb +20 -0
  11. data/lib/kontena/cli/certificate/show_command.rb +19 -0
  12. data/lib/kontena/cli/certificate_command.rb +4 -1
  13. data/lib/kontena/cli/cloud/master/add_command.rb +1 -1
  14. data/lib/kontena/cli/common.rb +21 -33
  15. data/lib/kontena/cli/etcd/health_command.rb +21 -27
  16. data/lib/kontena/cli/helpers/exec_helper.rb +15 -6
  17. data/lib/kontena/cli/helpers/health_helper.rb +12 -0
  18. data/lib/kontena/cli/helpers/log_helper.rb +2 -2
  19. data/lib/kontena/cli/helpers/time_helper.rb +29 -0
  20. data/lib/kontena/cli/master/init_cloud_command.rb +19 -0
  21. data/lib/kontena/cli/master/list_command.rb +1 -1
  22. data/lib/kontena/cli/master/ssh_command.rb +3 -1
  23. data/lib/kontena/cli/master/use_command.rb +1 -2
  24. data/lib/kontena/cli/node_command.rb +1 -0
  25. data/lib/kontena/cli/nodes/health_command.rb +28 -13
  26. data/lib/kontena/cli/nodes/list_command.rb +19 -3
  27. data/lib/kontena/cli/nodes/show_command.rb +4 -2
  28. data/lib/kontena/cli/nodes/ssh_command.rb +5 -2
  29. data/lib/kontena/cli/nodes/update_command.rb +2 -0
  30. data/lib/kontena/cli/plugins/install_command.rb +11 -8
  31. data/lib/kontena/cli/plugins/list_command.rb +5 -3
  32. data/lib/kontena/cli/plugins/search_command.rb +4 -2
  33. data/lib/kontena/cli/plugins/show_command.rb +17 -0
  34. data/lib/kontena/cli/plugins/uninstall_command.rb +9 -13
  35. data/lib/kontena/cli/registry/create_command.rb +1 -1
  36. data/lib/kontena/cli/services/create_command.rb +6 -0
  37. data/lib/kontena/cli/services/services_helper.rb +33 -6
  38. data/lib/kontena/cli/services/update_command.rb +6 -0
  39. data/lib/kontena/cli/stacks/build_command.rb +3 -3
  40. data/lib/kontena/cli/stacks/common.rb +105 -90
  41. data/lib/kontena/cli/stacks/deploy_command.rb +7 -3
  42. data/lib/kontena/cli/stacks/install_command.rb +39 -6
  43. data/lib/kontena/cli/stacks/list_command.rb +36 -4
  44. data/lib/kontena/cli/stacks/logs_command.rb +9 -2
  45. data/lib/kontena/cli/stacks/registry/pull_command.rb +2 -2
  46. data/lib/kontena/cli/stacks/registry/push_command.rb +20 -9
  47. data/lib/kontena/cli/stacks/registry/remove_command.rb +4 -4
  48. data/lib/kontena/cli/stacks/registry/show_command.rb +4 -4
  49. data/lib/kontena/cli/stacks/remove_command.rb +27 -1
  50. data/lib/kontena/cli/stacks/service_generator.rb +12 -2
  51. data/lib/kontena/cli/stacks/show_command.rb +35 -5
  52. data/lib/kontena/cli/stacks/stack_name.rb +71 -0
  53. data/lib/kontena/cli/stacks/upgrade_command.rb +127 -14
  54. data/lib/kontena/cli/stacks/validate_command.rb +38 -10
  55. data/lib/kontena/cli/stacks/yaml/custom_validators/certificates_validator.rb +22 -0
  56. data/lib/kontena/cli/stacks/yaml/opto/prompt_resolver.rb +1 -2
  57. data/lib/kontena/cli/stacks/yaml/reader.rb +211 -185
  58. data/lib/kontena/cli/stacks/yaml/service_extender.rb +6 -12
  59. data/lib/kontena/cli/stacks/yaml/stack_file_loader.rb +97 -0
  60. data/lib/kontena/cli/stacks/yaml/stack_file_loader/file_loader.rb +41 -0
  61. data/lib/kontena/cli/stacks/yaml/stack_file_loader/registry_loader.rb +24 -0
  62. data/lib/kontena/cli/stacks/yaml/stack_file_loader/uri_loader.rb +23 -0
  63. data/lib/kontena/cli/stacks/yaml/validations.rb +16 -0
  64. data/lib/kontena/cli/stacks/yaml/validator_v3.rb +25 -8
  65. data/lib/kontena/client.rb +2 -2
  66. data/lib/kontena/command.rb +11 -0
  67. data/lib/kontena/main_command.rb +3 -1
  68. data/lib/kontena/plugin_manager.rb +11 -198
  69. data/lib/kontena/plugin_manager/cleaner.rb +33 -0
  70. data/lib/kontena/plugin_manager/common.rb +86 -0
  71. data/lib/kontena/plugin_manager/installer.rb +54 -0
  72. data/lib/kontena/plugin_manager/loader.rb +93 -0
  73. data/lib/kontena/plugin_manager/rubygems_client.rb +42 -23
  74. data/lib/kontena/plugin_manager/uninstaller.rb +34 -0
  75. data/lib/kontena/util.rb +24 -0
  76. data/lib/kontena_cli.rb +1 -0
  77. data/omnibus/config/projects/kontena.rb +7 -1
  78. data/omnibus/config/software/{kontena.rb → kontena-cli.rb} +2 -0
  79. data/spec/fixtures/api/node.json +2 -1
  80. data/spec/fixtures/stack-internal-extend.yml +6 -1
  81. data/spec/fixtures/stack-with-dependencies-dep-1-1.yml +8 -0
  82. data/spec/fixtures/stack-with-dependencies-dep-1.yml +17 -0
  83. data/spec/fixtures/stack-with-dependencies-dep-2.yml +8 -0
  84. data/spec/fixtures/stack-with-dependencies-dep-3.yml +5 -0
  85. data/spec/fixtures/stack-with-dependencies-dep_2-removed.yml +17 -0
  86. data/spec/fixtures/stack-with-dependencies-dep_3-added.yml +25 -0
  87. data/spec/fixtures/stack-with-dependencies.yml +22 -0
  88. data/spec/fixtures/stack-with-variables.yml +3 -0
  89. data/spec/kontena/cli/etcd/health_command_spec.rb +45 -33
  90. data/spec/kontena/cli/helpers/exec_helper_spec.rb +2 -1
  91. data/spec/kontena/cli/master/init_cloud_command_spec.rb +14 -0
  92. data/spec/kontena/cli/nodes/health_command_spec.rb +74 -10
  93. data/spec/kontena/cli/nodes/list_command_spec.rb +381 -232
  94. data/spec/kontena/cli/nodes/show_command_spec.rb +31 -0
  95. data/spec/kontena/cli/nodes/ssh_command_spec.rb +18 -3
  96. data/spec/kontena/cli/plugins/install_command_spec.rb +1 -1
  97. data/spec/kontena/cli/stacks/build_command_spec.rb +6 -12
  98. data/spec/kontena/cli/stacks/common_spec.rb +42 -69
  99. data/spec/kontena/cli/stacks/install_command_spec.rb +57 -31
  100. data/spec/kontena/cli/stacks/list_command_spec.rb +44 -0
  101. data/spec/kontena/cli/stacks/logs_command_spec.rb +12 -1
  102. data/spec/kontena/cli/stacks/remove_command_spec.rb +39 -0
  103. data/spec/kontena/cli/stacks/show_command_spec.rb +16 -0
  104. data/spec/kontena/cli/stacks/stack_name_spec.rb +21 -0
  105. data/spec/kontena/cli/stacks/upgrade_command_spec.rb +73 -56
  106. data/spec/kontena/cli/stacks/validate_command_spec.rb +81 -0
  107. data/spec/kontena/cli/stacks/yaml/custom_validators/affinities_validator_spec.rb +22 -0
  108. data/spec/kontena/cli/stacks/yaml/reader_spec.rb +173 -169
  109. data/spec/kontena/cli/stacks/yaml/service_extender_spec.rb +12 -3
  110. data/spec/kontena/cli/stacks/yaml/stack_file_loader/file_loader_spec.rb +47 -0
  111. data/spec/kontena/cli/stacks/yaml/stack_file_loader/registry_loader_spec.rb +53 -0
  112. data/spec/kontena/cli/stacks/yaml/stack_file_loader/uri_loader_spec.rb +53 -0
  113. data/spec/kontena/cli/stacks/yaml/stack_file_loader_spec.rb +104 -0
  114. data/spec/kontena/cli/stacks/yaml/validator_v3_spec.rb +19 -0
  115. data/spec/kontena/plugin_manager/cleaner_spec.rb +20 -0
  116. data/spec/kontena/plugin_manager/common_spec.rb +39 -0
  117. data/spec/kontena/plugin_manager/installer_spec.rb +50 -0
  118. data/spec/kontena/plugin_manager/loader_spec.rb +5 -0
  119. data/spec/kontena/plugin_manager/rubygems_client_spec.rb +11 -25
  120. data/spec/kontena/plugin_manager/uninstaller_spec.rb +19 -0
  121. data/spec/kontena/plugin_manager_spec.rb +7 -7
  122. metadata +64 -97
  123. data/lib/kontena/cli/app_command.rb +0 -22
  124. data/lib/kontena/cli/apps/build_command.rb +0 -28
  125. data/lib/kontena/cli/apps/common.rb +0 -172
  126. data/lib/kontena/cli/apps/config_command.rb +0 -25
  127. data/lib/kontena/cli/apps/deploy_command.rb +0 -137
  128. data/lib/kontena/cli/apps/docker_compose_generator.rb +0 -61
  129. data/lib/kontena/cli/apps/docker_helper.rb +0 -80
  130. data/lib/kontena/cli/apps/dockerfile_generator.rb +0 -16
  131. data/lib/kontena/cli/apps/init_command.rb +0 -89
  132. data/lib/kontena/cli/apps/kontena_yml_generator.rb +0 -105
  133. data/lib/kontena/cli/apps/list_command.rb +0 -59
  134. data/lib/kontena/cli/apps/logs_command.rb +0 -37
  135. data/lib/kontena/cli/apps/monitor_command.rb +0 -93
  136. data/lib/kontena/cli/apps/remove_command.rb +0 -74
  137. data/lib/kontena/cli/apps/restart_command.rb +0 -39
  138. data/lib/kontena/cli/apps/scale_command.rb +0 -33
  139. data/lib/kontena/cli/apps/service_generator.rb +0 -114
  140. data/lib/kontena/cli/apps/service_generator_v2.rb +0 -27
  141. data/lib/kontena/cli/apps/show_command.rb +0 -23
  142. data/lib/kontena/cli/apps/start_command.rb +0 -40
  143. data/lib/kontena/cli/apps/stop_command.rb +0 -40
  144. data/lib/kontena/cli/apps/yaml/custom_validators/affinities_validator.rb +0 -19
  145. data/lib/kontena/cli/apps/yaml/custom_validators/build_validator.rb +0 -22
  146. data/lib/kontena/cli/apps/yaml/custom_validators/extends_validator.rb +0 -20
  147. data/lib/kontena/cli/apps/yaml/custom_validators/hooks_validator.rb +0 -54
  148. data/lib/kontena/cli/apps/yaml/custom_validators/secrets_validator.rb +0 -22
  149. data/lib/kontena/cli/apps/yaml/reader.rb +0 -213
  150. data/lib/kontena/cli/apps/yaml/service_extender.rb +0 -77
  151. data/lib/kontena/cli/apps/yaml/validations.rb +0 -71
  152. data/lib/kontena/cli/apps/yaml/validator.rb +0 -38
  153. data/lib/kontena/cli/apps/yaml/validator_v2.rb +0 -53
  154. data/spec/fixtures/app.json +0 -42
  155. data/spec/fixtures/health.yml +0 -26
  156. data/spec/fixtures/kontena-build.yml +0 -16
  157. data/spec/fixtures/kontena-internal-extend.yml +0 -8
  158. data/spec/fixtures/kontena-invalid.yml +0 -4
  159. data/spec/fixtures/kontena-with-env-file.yml +0 -18
  160. data/spec/fixtures/kontena-with-variables.yml +0 -19
  161. data/spec/fixtures/kontena.yml +0 -17
  162. data/spec/fixtures/kontena_build_v2.yml +0 -26
  163. data/spec/fixtures/kontena_numeric_version.yml +0 -9
  164. data/spec/fixtures/kontena_v2.yml +0 -35
  165. data/spec/fixtures/mysql.yml +0 -3
  166. data/spec/fixtures/wordpress-scaled.yml +0 -3
  167. data/spec/fixtures/wordpress.yml +0 -2
  168. data/spec/kontena/cli/app/build_command_spec.rb +0 -55
  169. data/spec/kontena/cli/app/common_spec.rb +0 -110
  170. data/spec/kontena/cli/app/config_command_spec.rb +0 -78
  171. data/spec/kontena/cli/app/deploy_command_spec.rb +0 -217
  172. data/spec/kontena/cli/app/docker_helper_spec.rb +0 -155
  173. data/spec/kontena/cli/app/init_command_spec.rb +0 -109
  174. data/spec/kontena/cli/app/logs_command_spec.rb +0 -131
  175. data/spec/kontena/cli/app/scale_spec.rb +0 -51
  176. data/spec/kontena/cli/app/service_generator_spec.rb +0 -384
  177. data/spec/kontena/cli/app/service_generator_v2_spec.rb +0 -73
  178. data/spec/kontena/cli/app/yaml/reader_spec.rb +0 -457
  179. data/spec/kontena/cli/app/yaml/service_extender_spec.rb +0 -127
  180. data/spec/kontena/cli/app/yaml/validator_spec.rb +0 -380
  181. data/spec/kontena/cli/app/yaml/validator_v2_spec.rb +0 -301
@@ -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