kontena-cli 0.16.0.pre7 → 0.16.0.pre8

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/kontena-cli.gemspec +2 -5
  4. data/lib/kontena/callbacks/master/deploy/05_before_deploy_configuration_wizard.rb +15 -21
  5. data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +5 -3
  6. data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +3 -1
  7. data/lib/kontena/callbacks/master/deploy/60_configure_auth_provider_after_deploy.rb +7 -18
  8. data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +93 -0
  9. data/lib/kontena/callbacks/master/deploy/90_proptip_after_deploy.rb +33 -0
  10. data/lib/kontena/cli/apps/build_command.rb +2 -1
  11. data/lib/kontena/cli/apps/common.rb +3 -2
  12. data/lib/kontena/cli/apps/config_command.rb +3 -3
  13. data/lib/kontena/cli/apps/deploy_command.rb +1 -0
  14. data/lib/kontena/cli/apps/docker_helper.rb +7 -7
  15. data/lib/kontena/cli/apps/list_command.rb +1 -1
  16. data/lib/kontena/cli/apps/logs_command.rb +1 -1
  17. data/lib/kontena/cli/apps/monitor_command.rb +2 -2
  18. data/lib/kontena/cli/apps/remove_command.rb +1 -1
  19. data/lib/kontena/cli/apps/restart_command.rb +1 -1
  20. data/lib/kontena/cli/apps/scale_command.rb +1 -1
  21. data/lib/kontena/cli/apps/start_command.rb +1 -1
  22. data/lib/kontena/cli/apps/stop_command.rb +1 -1
  23. data/lib/kontena/cli/apps/yaml/custom_validators/affinities_validator.rb +19 -0
  24. data/lib/kontena/cli/apps/yaml/custom_validators/build_validator.rb +22 -0
  25. data/lib/kontena/cli/apps/yaml/custom_validators/extends_validator.rb +21 -0
  26. data/lib/kontena/cli/apps/yaml/custom_validators/hooks_validator.rb +54 -0
  27. data/lib/kontena/cli/apps/yaml/custom_validators/secrets_validator.rb +22 -0
  28. data/lib/kontena/cli/apps/yaml/validations.rb +60 -90
  29. data/lib/kontena/cli/apps/yaml/validator.rb +9 -31
  30. data/lib/kontena/cli/apps/yaml/validator_v2.rb +13 -40
  31. data/lib/kontena/cli/cloud/login_command.rb +2 -2
  32. data/lib/kontena/cli/cloud/master/add_command.rb +114 -34
  33. data/lib/kontena/cli/cloud/master/list_command.rb +5 -2
  34. data/lib/kontena/cli/cloud/master/remove_command.rb +69 -0
  35. data/lib/kontena/cli/cloud/master_command.rb +2 -2
  36. data/lib/kontena/cli/common.rb +10 -17
  37. data/lib/kontena/cli/config.rb +4 -3
  38. data/lib/kontena/cli/grids/env_command.rb +5 -5
  39. data/lib/kontena/cli/localhost_web_server.rb +1 -1
  40. data/lib/kontena/cli/master/init_cloud_command.rb +21 -0
  41. data/lib/kontena/cli/master/login_command.rb +28 -29
  42. data/lib/kontena/cli/master/remove_command.rb +58 -0
  43. data/lib/kontena/cli/master/token/create_command.rb +6 -0
  44. data/lib/kontena/cli/master/users/invite_command.rb +4 -1
  45. data/lib/kontena/cli/master/users/roles/add_command.rb +4 -3
  46. data/lib/kontena/cli/master_command.rb +8 -9
  47. data/lib/kontena/cli/services/list_command.rb +3 -2
  48. data/lib/kontena/cli/services/services_helper.rb +1 -1
  49. data/lib/kontena/client.rb +31 -47
  50. data/lib/kontena/presets/kontena_auth_provider.yml +2 -2
  51. data/lib/kontena_cli.rb +12 -0
  52. data/spec/kontena/cli/app/docker_helper_spec.rb +9 -4
  53. data/spec/kontena/cli/app/yaml/validator_spec.rb +66 -71
  54. data/spec/kontena/cli/app/yaml/validator_v2_spec.rb +51 -58
  55. metadata +25 -59
  56. data/lib/kontena/callbacks/master/deploy/90_suggest_inviting_yourself_after_deploy.rb +0 -24
  57. data/lib/kontena/cli/cloud/master/delete_command.rb +0 -20
@@ -0,0 +1,58 @@
1
+ module Kontena::Cli::Master
2
+ class RemoveCommand < Kontena::Command
3
+ include Kontena::Cli::Common
4
+
5
+ parameter '[NAME]', "Master name"
6
+
7
+ banner "Note: This command only removes the master from your local configuration file"
8
+
9
+ option '--force', :flag, "Don't ask questions"
10
+
11
+ def run_interactive
12
+ selections = prompt.multi_select("Select masters to remove from configuration file:") do |menu|
13
+ config.servers.each do |server|
14
+ menu.choice " #{pastel.green("* ") if config.current_server == server.name}#{server.name} (#{server.username || 'unknown'} @ #{server.url})", server
15
+ end
16
+ end
17
+ if selections.empty?
18
+ puts "No masters selected"
19
+ exit 0
20
+ end
21
+ delete_servers(selections)
22
+ end
23
+
24
+ def delete_servers(servers)
25
+ case servers.size
26
+ when 0
27
+ abort "Master not found in configuration"
28
+ when 1
29
+ config.servers.delete(config.servers.delete(servers.first))
30
+ puts "Removed Master '#{servers.first.name}'"
31
+ else
32
+ unless self.force?
33
+ abort("Aborted") unless prompt.yes?("Remove #{servers.size} masters from configuration?")
34
+ end
35
+ config.servers.delete_if {|s| servers.include?(s) }
36
+ puts "Removed #{servers.size} masters from configuration"
37
+ end
38
+ unless config.find_server(config.current_server)
39
+ puts
40
+ puts "Current master was removed, to select a new current master use:"
41
+ puts " " + pastel.green.on_black(" kontena master use <master_name> ")
42
+ puts "Or log into another master by using:"
43
+ puts " " + pastel.green.on_black(" kontena master login <master_url> ")
44
+ config.current_server = nil
45
+ end
46
+ config.write
47
+ end
48
+
49
+ def execute
50
+ if self.name.nil?
51
+ run_interactive
52
+ else
53
+ delete_servers(config.servers.select {|s| s.name == self.name})
54
+ end
55
+ end
56
+ end
57
+ end
58
+
@@ -12,17 +12,23 @@ module Kontena::Cli::Master::Token
12
12
  option ['-s', '--scopes'], '[SCOPES]', "Comma separated list of access scopes for the generated token", default: 'user'
13
13
  option ['-e', '--expires-in'], '[SECONDS]', "Access token expiration time. Use 0 for never.", default: '7200'
14
14
  option ['-c', '--code'], :flag, "Generate an authorization code"
15
+ option ['-u', '--user'], '[EMAIL]', 'Generate a token for another user'
15
16
  option ['--id'], :flag, "Only output the token ID"
16
17
  option ['--token'], :flag, "Only output the access_token (or authorization code)"
17
18
 
19
+ option ['--return'], :flag, "Return the response hash", hidden: true
20
+
18
21
  def execute
19
22
  params = {
20
23
  response_type: self.code? ? 'code' : 'token',
21
24
  scope: self.scopes,
22
25
  expires_in: self.expires_in
23
26
  }
27
+ params[:user] = self.user if self.user
24
28
  data = token_data_to_hash(client.post("/oauth2/authorize", params))
25
29
 
30
+ return data if self.return?
31
+
26
32
  if self.id?
27
33
  puts data[:id]
28
34
  exit 0
@@ -9,6 +9,7 @@ module Kontena::Cli::Master::Users
9
9
 
10
10
  option ['-r', '--roles'], '[ROLES]', 'Comma separated list of roles to assign to the invited users'
11
11
  option ['-c', '--code'], :flag, 'Only output the invite code'
12
+ option '--return', :flag, 'Return the code', hidden: true
12
13
 
13
14
  requires_current_master
14
15
  requires_current_master_token
@@ -26,6 +27,8 @@ module Kontena::Cli::Master::Users
26
27
  response = client.post('/oauth2/authorize', data)
27
28
  if self.code?
28
29
  puts response['invite_code']
30
+ elsif self.return?
31
+ return response
29
32
  else
30
33
  puts "Invitation created for #{response['email']}".colorize(:green)
31
34
  puts " * code: #{response['invite_code']}"
@@ -35,7 +38,7 @@ module Kontena::Cli::Master::Users
35
38
  Kontena.run("master users role add #{role.shellescape} #{email.shellescape}")
36
39
  end
37
40
  rescue
38
- puts "Failed to invite #{email}".colorize(:red)
41
+ STDERR.puts "Failed to invite #{email}".colorize(:red)
39
42
  ENV["DEBUG"] && puts("#{$!} - #{$!.message} -- #{$!.backtrace}")
40
43
  end
41
44
  end
@@ -8,6 +8,8 @@ module Kontena::Cli::Master::Users
8
8
  parameter "ROLE", "Role name"
9
9
  parameter "USER ...", "List of users"
10
10
 
11
+ option '--silent', :flag, 'Reduce output verbosity'
12
+
11
13
  def execute
12
14
  require_api_url
13
15
  token = require_token
@@ -16,10 +18,9 @@ module Kontena::Cli::Master::Users
16
18
  user_list.each do |email|
17
19
  begin
18
20
  response = client(token).post("users/#{email}/roles", data)
19
- puts "Added role #{role} to #{email}"
21
+ puts "Added role #{role} to #{email}" unless running_silent?
20
22
  rescue => exc
21
- puts "Failed to add role #{role} to #{email}".colorize(:red)
22
- puts exc.message
23
+ abort "Failed to add role #{role} to #{email} : #{exc.message}".colorize(:red)
23
24
  end
24
25
  end
25
26
  end
@@ -1,5 +1,6 @@
1
1
  require_relative '../main_command'
2
2
  require_relative 'master/use_command'
3
+ require_relative 'master/remove_command'
3
4
  require_relative 'master/list_command'
4
5
  require_relative 'master/users_command'
5
6
  require_relative 'master/current_command'
@@ -9,16 +10,13 @@ require_relative 'master/logout_command'
9
10
  require_relative 'master/join_command'
10
11
  require_relative 'master/audit_log_command'
11
12
  require_relative 'master/token_command'
12
-
13
- if ENV["DEV"]
14
- begin
15
- require_relative 'master/create_command'
16
- rescue LoadError
17
- end
18
- end
13
+ require_relative 'master/init_cloud_command'
19
14
 
20
15
  class Kontena::Cli::MasterCommand < Kontena::Command
16
+ include Kontena::Util
17
+
21
18
  subcommand ["list", "ls"], "List masters where client has logged in", Kontena::Cli::Master::ListCommand
19
+ subcommand ["remove", "rm"], "Remove a master from configuration file", Kontena::Cli::Master::RemoveCommand
22
20
  subcommand ["config", "cfg"], "Configure master settings", Kontena::Cli::Master::ConfigCommand
23
21
  subcommand "use", "Switch to use selected master", Kontena::Cli::Master::UseCommand
24
22
  subcommand "users", "Users specific commands", Kontena::Cli::Master::UsersCommand
@@ -28,8 +26,9 @@ class Kontena::Cli::MasterCommand < Kontena::Command
28
26
  subcommand "token", "Manage Kontena Master access tokens", Kontena::Cli::Master::TokenCommand
29
27
  subcommand "join", "Join Kontena Master using an invitation code", Kontena::Cli::Master::JoinCommand
30
28
  subcommand "audit-log", "Show master audit logs", Kontena::Cli::Master::AuditLogCommand
31
-
32
- if ENV["DEV"]
29
+ subcommand "init-cloud", "Configure current master to use Kontena Cloud services", Kontena::Cli::Master::InitCloudCommand
30
+ if experimental?
31
+ require_relative 'master/create_command'
33
32
  subcommand "create", "Install a new Kontena Master", Kontena::Cli::Master::CreateCommand
34
33
  end
35
34
 
@@ -24,13 +24,14 @@ module Kontena::Cli::Services
24
24
  }.join(", ")
25
25
  health = health_status(service)
26
26
  vars = [
27
- "#{health_status_icon(health)} #{service['name']}",
27
+ health_status_icon(health),
28
+ "#{service['name']}",
28
29
  instances,
29
30
  stateful,
30
31
  service['state'],
31
32
  ports
32
33
  ]
33
- puts "%-74s %-10.10s %-8s %-10s %-50s" % vars
34
+ puts "%s %-58s %-10.10s %-8s %-10s %-50s" % vars
34
35
  end
35
36
  end
36
37
  end
@@ -424,7 +424,7 @@ module Kontena
424
424
  icon.colorize(:green)
425
425
  else
426
426
  icon = '⊝'.freeze
427
- icon.colorize(:clear)
427
+ icon.colorize(:dim)
428
428
  end
429
429
  end
430
430
 
@@ -130,22 +130,12 @@ module Kontena
130
130
  return false unless token['access_token']
131
131
  return false unless token_verify_path
132
132
 
133
- uri = URI.parse(token_verify_path)
134
- host_options = {}
135
- host_options[:host] = uri.host if uri.host
136
- host_options[:port] = uri.port if uri.port
137
-
138
- if uri.host
139
- client = Kontena::Client.new("#{uri.scheme}://#{uri.host}:#{uri.port}", token)
140
- else
141
- client = self
142
- end
143
-
144
- final_path = uri.path.gsub(/\:access\_token/, token['access_token'])
145
- logger.debug "Requesting user info from #{final_path} - #{host_options}"
146
- client.request(path: final_path)
133
+ final_path = token_verify_path.gsub(/\:access\_token/, token['access_token'])
134
+ logger.debug "Requesting user info from #{final_path}"
135
+ request(path: final_path)
147
136
  true
148
137
  rescue
138
+ ENV["DEBUG"] && puts("Authentication verification exception: #{$!} #{$!.message} #{$!.backtrace}")
149
139
  false
150
140
  end
151
141
 
@@ -154,35 +144,20 @@ module Kontena
154
144
  def exchange_code(code)
155
145
  return nil unless token_account
156
146
  return nil unless token_account['token_endpoint']
157
- uri = URI.parse(token_account['token_endpoint'])
158
- host_options = {}
159
- host_options[:host] = uri.host if uri.host
160
- host_options[:port] = uri.port if uri.port
161
147
 
162
- if uri.host
163
- client = Kontena::Client.new("#{uri.scheme}://#{uri.host}:#{uri.port}")
164
- else
165
- client = self
166
- end
167
-
168
- client.request(
169
- {
170
- http_method: token_account['token_method'].downcase.to_sym,
171
- path: uri.path,
172
- headers: { CONTENT_TYPE => token_account['token_post_content_type'] },
173
- body: {
174
- 'grant_type' => 'authorization_code',
175
- 'code' => code,
176
- 'client_id' => Kontena::Client::CLIENT_ID,
177
- 'client_secret' => Kontena::Client::CLIENT_SECRET
178
- },
179
- expects: [200,201],
180
- auth: false
181
- }
148
+ request(
149
+ http_method: token_account['token_method'].downcase.to_sym,
150
+ path: token_account['token_endpoint'],
151
+ headers: { CONTENT_TYPE => token_account['token_post_content_type'] },
152
+ body: {
153
+ 'grant_type' => 'authorization_code',
154
+ 'code' => code,
155
+ 'client_id' => Kontena::Client::CLIENT_ID,
156
+ 'client_secret' => Kontena::Client::CLIENT_SECRET
157
+ },
158
+ expects: [200,201],
159
+ auth: false
182
160
  )
183
- rescue
184
- logger.debug "Code exchange exception: #{$!} #{$!.message}\n#{$!.backtrace}"
185
- nil
186
161
  end
187
162
 
188
163
  # Return server version from a Kontena master by requesting '/'
@@ -317,14 +292,23 @@ module Kontena
317
292
  end
318
293
  request_headers.merge!('Content-Length' => body_content.bytesize)
319
294
 
295
+ uri = URI.parse(path)
320
296
  host_options = {}
321
- host_options[:host] = host if host
322
- host_options[:port] = port if port
297
+
298
+ if uri.host
299
+ host_options[:host] = uri.host
300
+ host_options[:port] = uri.port
301
+ host_options[:scheme] = uri.scheme
302
+ path = uri.request_uri
303
+ else
304
+ host_options[:host] = host if host
305
+ host_options[:port] = port if port
306
+ end
323
307
 
324
308
  request_options = {
325
309
  method: http_method,
326
310
  expects: Array(expects),
327
- path: path.start_with?('/') ? path : request_uri(path),
311
+ path: path_with_prefix(path),
328
312
  headers: request_headers,
329
313
  body: body_content,
330
314
  query: query
@@ -443,12 +427,12 @@ module Kontena
443
427
  end
444
428
 
445
429
 
446
- # Get full request uri
430
+ # Get prefixed request path unless path starts with /
447
431
  #
448
432
  # @param [String] path
449
433
  # @return [String]
450
- def request_uri(path)
451
- "#{path_prefix}#{path}"
434
+ def path_with_prefix(path)
435
+ path.to_s.start_with?('/') ? path : "#{path_prefix}#{path}"
452
436
  end
453
437
 
454
438
 
@@ -1,7 +1,7 @@
1
1
  ---
2
- oauth2.authorize_endpoint: https://cloud-beta.kontena.io/login/oauth/authorize
2
+ oauth2.authorize_endpoint: https://cloud.kontena.io/login/oauth/authorize
3
3
  oauth2.code_requires_basic_auth: false
4
- oauth2.token_endpoint: https://auth2.kontena.io/v1/oauth2/token
4
+ oauth2.token_endpoint: https://cloud-api.kontena.io/oauth2/token
5
5
  oauth2.token_method: post
6
6
  oauth2.token_post_content_type: application/x-www-form-urlencoded
7
7
  oauth2.userinfo_endpoint: https://cloud-api.kontena.io/user
data/lib/kontena_cli.rb CHANGED
@@ -15,7 +15,11 @@ module Kontena
15
15
  rescue SystemExit
16
16
  ENV["DEBUG"] && puts("Command completed with failure, result: #{result.inspect} status: #{$!.status}")
17
17
  returning == :status ? $!.status : nil
18
+ rescue
19
+ ENV["DEBUG"] && puts("Command raised #{$!} with message: #{$!.message}\n #{$!.backtrace.join(" \n")}")
20
+ returning == :status ? 1 : nil
18
21
  end
22
+
19
23
 
20
24
  def self.version
21
25
  "kontena-cli/#{Kontena::Cli::VERSION}"
@@ -47,6 +51,14 @@ class String
47
51
  end
48
52
  end
49
53
 
54
+ require 'retriable'
55
+ Retriable.configure do |c|
56
+ c.on_retry = Proc.new do |exception, try, elapsed_time, next_interval|
57
+ return true unless ENV["DEBUG"]
58
+ puts "Retriable retry: #{try} - Exception: #{exception} - #{exception.message}. Elapsed: #{elapsed_time} Next interval: #{next_interval}"
59
+ end
60
+ end
61
+
50
62
  require 'ruby_dig'
51
63
  require 'shellwords'
52
64
  require_relative 'kontena/cli/version'
@@ -46,6 +46,11 @@ describe Kontena::Cli::Apps::DockerHelper do
46
46
  }
47
47
  end
48
48
 
49
+ before :each do
50
+ # image does not exist
51
+ allow(subject).to receive(:image_exist?).with('test_service').and_return(false)
52
+ end
53
+
49
54
  describe '#validate_image_name' do
50
55
  context 'when image name is valid' do
51
56
  it 'returns true' do
@@ -109,7 +114,7 @@ describe Kontena::Cli::Apps::DockerHelper do
109
114
  'build' => { 'context' => '.' },
110
115
  'image' => 'test_service'
111
116
  }
112
- expect(subject).to receive(:system).with("docker build -t test_service ."). and_return(true)
117
+ expect(subject).to receive(:system).with('docker', 'build', '-t', 'test_service', '.'). and_return(true)
113
118
  subject.build_docker_image(service)
114
119
  end
115
120
 
@@ -118,7 +123,7 @@ describe Kontena::Cli::Apps::DockerHelper do
118
123
  'build' => { 'context' => '.' },
119
124
  'image' => 'test_service'
120
125
  }
121
- expect(subject).to receive(:system).with("docker build -t test_service --no-cache ."). and_return(true)
126
+ expect(subject).to receive(:system).with('docker', 'build', '-t', 'test_service', '--no-cache', '.'). and_return(true)
122
127
  subject.build_docker_image(service, true)
123
128
  end
124
129
 
@@ -128,7 +133,7 @@ describe Kontena::Cli::Apps::DockerHelper do
128
133
  'image' => 'test_service'
129
134
  }
130
135
  expected_path = File.join(File.expand_path('.'), 'other_dockerfile')
131
- expect(subject).to receive(:system).with("docker build -t test_service -f #{expected_path} ."). and_return(true)
136
+ expect(subject).to receive(:system).with('docker', 'build', '-t', 'test_service', '-f', expected_path, '.'). and_return(true)
132
137
  subject.build_docker_image(service)
133
138
  end
134
139
 
@@ -143,7 +148,7 @@ describe Kontena::Cli::Apps::DockerHelper do
143
148
  },
144
149
  'image' => 'test_service'
145
150
  }
146
- expect(subject).to receive(:system).with("docker build -t test_service --build-arg FOO=bar --build-arg BAR=foo ."). and_return(true)
151
+ expect(subject).to receive(:system).with('docker', 'build', '-t', 'test_service', '--build-arg=FOO=bar', '--build-arg=BAR=foo', '.'). and_return(true)
147
152
  subject.build_docker_image(service)
148
153
  end
149
154
  end
@@ -2,134 +2,129 @@ require_relative '../../../../spec_helper'
2
2
  require 'kontena/cli/apps/yaml/validator'
3
3
 
4
4
  describe Kontena::Cli::Apps::YAML::Validator do
5
- describe '#validate_keys' do
6
- it 'returns error on invalid key' do
7
- result = subject.validate_keys('name' => 'wordpress')
8
- expect(result['name'].size).to eq(1)
9
- end
10
- end
11
5
 
12
6
  describe '#validate_options' do
13
7
  context 'build' do
14
8
  it 'is optional' do
15
9
  result = subject.validate_options({})
16
- expect(result.success?).to be_truthy
17
- expect(result.messages.key?('build')).to be_falsey
10
+ expect(result.valid?).to be_truthy
11
+
12
+ expect(result.errors.key?('build')).to be_falsey
18
13
  end
19
14
 
20
15
  it 'must be string' do
21
16
  result = subject.validate_options('build' => 12345)
22
- expect(result.messages.key?('build')).to be_truthy
17
+ expect(result.errors.key?('build')).to be_truthy
23
18
 
24
19
  result = subject.validate_options('build' => '.')
25
- expect(result.messages.key?('build')).to be_falsey
20
+ expect(result.errors.key?('build')).to be_falsey
26
21
  end
27
22
  end
28
23
 
29
24
  context 'image' do
30
25
  it 'is optional' do
31
26
  result = subject.validate_options('build' => '.')
32
- expect(result.success?).to be_truthy
33
- expect(result.messages.key?('image')).to be_falsey
27
+ expect(result.valid?).to be_truthy
28
+ expect(result.errors.key?('image')).to be_falsey
34
29
  end
35
30
 
36
31
  it 'must be string' do
37
32
  result = subject.validate_options('image' => 10)
38
- expect(result.success?).to be_falsey
39
- expect(result.messages.key?('image')).to be_truthy
33
+ expect(result.valid?).to be_falsey
34
+ expect(result.errors.key?('image')).to be_truthy
40
35
  end
41
36
  end
42
37
 
43
38
  it 'validates stateful is boolean' do
44
39
  result = subject.validate_options('stateful' => 'bool')
45
- expect(result.messages.key?('stateful')).to be_truthy
40
+ expect(result.errors.key?('stateful')).to be_truthy
46
41
  end
47
42
 
48
43
  it 'validates net is host or bridge' do
49
44
  result = subject.validate_options('net' => 'invalid')
50
- expect(result.messages.key?('net')).to be_truthy
45
+ expect(result.errors.key?('net')).to be_truthy
51
46
 
52
47
  result = subject.validate_options('net' => 'bridge')
53
- expect(result.messages.key?('net')).to be_falsey
48
+ expect(result.errors.key?('net')).to be_falsey
54
49
 
55
50
  result = subject.validate_options('net' => 'host')
56
- expect(result.messages.key?('net')).to be_falsey
51
+ expect(result.errors.key?('net')).to be_falsey
57
52
  end
58
53
 
59
54
  context 'affinity' do
60
55
  it 'is optional' do
61
56
  result = subject.validate_options({})
62
- expect(result.messages.key?('affinity')).to be_falsey
57
+ expect(result.errors.key?('affinity')).to be_falsey
63
58
  end
64
59
 
65
60
  it 'must be array' do
66
61
  result = subject.validate_options('affinity' => 'node==node1')
67
- expect(result.messages.key?('affinity')).to be_truthy
62
+ expect(result.errors.key?('affinity')).to be_truthy
68
63
  result = subject.validate_options('affinity' => ['node==node1'])
69
- expect(result.messages.key?('affinity')).to be_falsey
64
+ expect(result.errors.key?('affinity')).to be_falsey
70
65
  end
71
66
 
72
67
  it 'validates format' do
73
68
  result = subject.validate_options('affinity' => ['node=node1'])
74
- expect(result.messages.key?('affinity')).to be_truthy
69
+ expect(result.errors.key?('affinity')).to be_truthy
75
70
 
76
71
  result = subject.validate_options('affinity' => ['node==node1', 'service!=mariadb'])
77
- expect(result.messages.key?('affinity')).to be_falsey
72
+ expect(result.errors.key?('affinity')).to be_falsey
78
73
  end
79
74
  end
80
75
 
81
76
  context 'deploy' do
82
77
  it 'is optional' do
83
78
  result = subject.validate_options({})
84
- expect(result.messages.key?('deploy')).to be_falsey
79
+ expect(result.errors.key?('deploy')).to be_falsey
85
80
  end
86
81
 
87
82
  context 'strategy' do
88
83
  it 'accepts daemon' do
89
84
  result = subject.validate_options('deploy' => {'strategy' => 'daemon'})
90
- expect(result.messages.key?('deploy')).to be_falsey
85
+ expect(result.errors.key?('deploy')).to be_falsey
91
86
  end
92
87
 
93
88
  it 'accepts random' do
94
89
  result = subject.validate_options('deploy' => {'strategy' => 'random'})
95
- expect(result.messages.key?('deploy')).to be_falsey
90
+ expect(result.errors.key?('deploy')).to be_falsey
96
91
  end
97
92
 
98
93
  it 'accepts ha' do
99
94
  result = subject.validate_options('deploy' => {'strategy' => 'ha'})
100
- expect(result.messages.key?('deploy')).to be_falsey
95
+ expect(result.errors.key?('deploy')).to be_falsey
101
96
  end
102
97
 
103
98
  it 'rejects invalid values' do
104
99
  result = subject.validate_options('deploy' => {'strategy' => 'global'})
105
- expect(result.messages.key?('deploy')).to be_truthy
100
+ expect(result.errors.key?('deploy')).to be_truthy
106
101
  end
107
102
  end
108
103
 
109
104
  context 'interval' do
110
105
  it 'rejects wrong format' do
111
106
  result = subject.validate_options('deploy' => {'interval' => '1xyz'})
112
- expect(result.messages.key?('deploy')).to be_truthy
107
+ expect(result.errors.key?('deploy')).to be_truthy
113
108
  end
114
109
 
115
110
  it 'accepts 1min as value' do
116
111
  result = subject.validate_options('deploy' => {'interval' => '1min'})
117
- expect(result.messages.key?('deploy')).to be_falsey
112
+ expect(result.errors.key?('deploy')).to be_falsey
118
113
  end
119
114
 
120
115
  it 'accepts 1h as value' do
121
116
  result = subject.validate_options('deploy' => {'interval' => '1h'})
122
- expect(result.messages.key?('deploy')).to be_falsey
117
+ expect(result.errors.key?('deploy')).to be_falsey
123
118
  end
124
119
 
125
120
  it 'accepts 1d as value' do
126
121
  result = subject.validate_options('deploy' => {'interval' => '1d'})
127
- expect(result.messages.key?('deploy')).to be_falsey
122
+ expect(result.errors.key?('deploy')).to be_falsey
128
123
  end
129
124
 
130
125
  it 'accepts integer as value' do
131
126
  result = subject.validate_options('deploy' => {'interval' => '100'})
132
- expect(result.messages.key?('deploy')).to be_falsey
127
+ expect(result.errors.key?('deploy')).to be_falsey
133
128
  end
134
129
  end
135
130
  end
@@ -137,59 +132,59 @@ describe Kontena::Cli::Apps::YAML::Validator do
137
132
  context 'command' do
138
133
  it 'is optional' do
139
134
  result = subject.validate_options({})
140
- expect(result.messages.key?('command')).to be_falsey
135
+ expect(result.errors.key?('command')).to be_falsey
141
136
  end
142
137
 
143
138
  it 'must be string or empty' do
144
139
  result = subject.validate_options('command' => 1234)
145
- expect(result.messages.key?('command')).to be_truthy
140
+ expect(result.errors.key?('command')).to be_truthy
146
141
 
147
142
  result = subject.validate_options('command' => nil)
148
- expect(result.messages.key?('command')).to be_falsey
143
+ expect(result.errors.key?('command')).to be_falsey
149
144
 
150
145
  result = subject.validate_options('command' => 'bundle exec rails s')
151
- expect(result.messages.key?('command')).to be_falsey
146
+ expect(result.errors.key?('command')).to be_falsey
152
147
  end
153
148
  end
154
149
 
155
150
  it 'validates cpu_shares is integer' do
156
151
  result = subject.validate_options('cpu_shares' => '1m')
157
- expect(result.messages.key?('cpu_shares')).to be_truthy
152
+ expect(result.errors.key?('cpu_shares')).to be_truthy
158
153
  result = subject.validate_options('cpu_shares' => 1024)
159
- expect(result.messages.key?('cpu_shares')).to be_falsey
154
+ expect(result.errors.key?('cpu_shares')).to be_falsey
160
155
  result = subject.validate_options({})
161
- expect(result.messages.key?('cpu_shares')).to be_falsey
156
+ expect(result.errors.key?('cpu_shares')).to be_falsey
162
157
  end
163
158
 
164
159
  it 'validates environment is array or hash' do
165
160
  result = subject.validate_options('environment' => 'KEY=VALUE')
166
- expect(result.messages.key?('environment')).to be_truthy
161
+ expect(result.errors.key?('environment')).to be_truthy
167
162
  result = subject.validate_options('environment' => ['KEY=VALUE'])
168
- expect(result.messages.key?('environment')).to be_falsey
163
+ expect(result.errors.key?('environment')).to be_falsey
169
164
  result = subject.validate_options('environment' => { 'KEY' => 'VALUE' })
170
- expect(result.messages.key?('environment')).to be_falsey
165
+ expect(result.errors.key?('environment')).to be_falsey
171
166
  end
172
167
 
173
168
  context 'validates secrets' do
174
169
  it 'must be array' do
175
170
  result = subject.validate_options('secrets' => {})
176
- expect(result.messages.key?('secrets')).to be_truthy
171
+ expect(result.errors.key?('secrets')).to be_truthy
177
172
  end
178
173
 
179
174
  context 'item' do
180
175
  it 'must contain secret' do
181
176
  result = subject.validate_options('secrets' => [{ 'name' => 'test', 'type' => 'env' }])
182
- expect(result.messages.key?('secrets')).to be_truthy
177
+ expect(result.errors.key?('secrets')).to be_truthy
183
178
  end
184
179
 
185
180
  it 'must contain name' do
186
181
  result = subject.validate_options('secrets' => [{ 'secret' => 'test', 'type' => 'env' }])
187
- expect(result.messages.key?('secrets')).to be_truthy
182
+ expect(result.errors.key?('secrets')).to be_truthy
188
183
  end
189
184
 
190
185
  it 'must contain type' do
191
186
  result = subject.validate_options('secrets' => [{ 'secret' => 'test', 'name' => 'test' }])
192
- expect(result.messages.key?('secrets')).to be_truthy
187
+ expect(result.errors.key?('secrets')).to be_truthy
193
188
  end
194
189
 
195
190
  it 'accepts valid input' do
@@ -201,7 +196,7 @@ describe Kontena::Cli::Apps::YAML::Validator do
201
196
  'type' => 'env'
202
197
  }
203
198
  ])
204
- expect(result.messages.key?('secrets')).to be_falsey
199
+ expect(result.errors.key?('secrets')).to be_falsey
205
200
  end
206
201
  end
207
202
  end
@@ -209,20 +204,20 @@ describe Kontena::Cli::Apps::YAML::Validator do
209
204
  context 'validates extends' do
210
205
  it 'accepts string value' do
211
206
  result = subject.validate_options('extends' => 'web')
212
- expect(result.messages.key?('extends')).to be_falsey
207
+ expect(result.errors.key?('extends')).to be_falsey
213
208
  end
214
209
 
215
210
  context 'when value is hash' do
216
211
  it 'must contain service' do
217
212
  result = subject.validate_options('extends' => { 'file' => 'docker_compose.yml'})
218
- expect(result.messages.key?('extends')).to be_truthy
213
+ expect(result.errors.key?('extends')).to be_truthy
219
214
  end
220
215
  end
221
216
 
222
217
  context 'when value is not string or hash' do
223
218
  it 'returns error' do
224
219
  result = subject.validate_options('extends' => ['array is invalid'])
225
- expect(result.messages.key?('extends')).to be_truthy
220
+ expect(result.errors.key?('extends')).to be_truthy
226
221
  end
227
222
  end
228
223
  end
@@ -230,7 +225,7 @@ describe Kontena::Cli::Apps::YAML::Validator do
230
225
  context 'validates pre_build' do
231
226
  it 'must be array' do
232
227
  result = subject.validate_options('hooks' => { 'pre_build' => {} })
233
- expect(result.messages.key?('hooks')).to be_truthy
228
+ expect(result.errors.key?('hooks')).to be_truthy
234
229
  data = {
235
230
  'hooks' => {
236
231
  'pre_build' => [
@@ -241,13 +236,13 @@ describe Kontena::Cli::Apps::YAML::Validator do
241
236
  }
242
237
  }
243
238
  result = subject.validate_options(data)
244
- expect(result.messages.key?('hooks')).to be_falsey
239
+ expect(result.errors.key?('hooks')).to be_falsey
245
240
  end
246
241
  end
247
242
  context 'validates post_start' do
248
243
  it 'must be array' do
249
244
  result = subject.validate_options('hooks' => { 'post_start' => {} })
250
- expect(result.messages.key?('hooks')).to be_truthy
245
+ expect(result.errors.key?('hooks')).to be_truthy
251
246
  data = {
252
247
  'hooks' => {
253
248
  'post_start' => [
@@ -260,7 +255,7 @@ describe Kontena::Cli::Apps::YAML::Validator do
260
255
  }
261
256
  }
262
257
  result = subject.validate_options(data)
263
- expect(result.messages.key?('hooks')).to be_falsey
258
+ expect(result.errors.key?('hooks')).to be_falsey
264
259
  end
265
260
 
266
261
  context 'item' do
@@ -274,7 +269,7 @@ describe Kontena::Cli::Apps::YAML::Validator do
274
269
  }
275
270
  ]
276
271
  })
277
- expect(result.messages.key?('hooks')).to be_truthy
272
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
278
273
  end
279
274
 
280
275
  it 'must contain cmd' do
@@ -287,7 +282,7 @@ describe Kontena::Cli::Apps::YAML::Validator do
287
282
  }
288
283
  ]
289
284
  })
290
- expect(result.messages.key?('hooks')).to be_truthy
285
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
291
286
  end
292
287
 
293
288
  it 'must contain instance number or *' do
@@ -299,7 +294,7 @@ describe Kontena::Cli::Apps::YAML::Validator do
299
294
  }
300
295
  ]
301
296
  })
302
- expect(result.messages.key?('hooks')).to be_truthy
297
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
303
298
  data = {
304
299
  'hooks' => {
305
300
  'post_start' => [
@@ -313,7 +308,7 @@ describe Kontena::Cli::Apps::YAML::Validator do
313
308
  }
314
309
  }
315
310
  result = subject.validate_options(data)
316
- expect(result.messages.key?('hooks')).to be_truthy
311
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
317
312
  end
318
313
 
319
314
  it 'may contain boolean oneshot' do
@@ -330,24 +325,24 @@ describe Kontena::Cli::Apps::YAML::Validator do
330
325
  }
331
326
  }
332
327
  result = subject.validate_options(data)
333
- expect(result.messages.key?('hooks')).to be_truthy
328
+ expect(result.errors.key?('hooks.post_start')).to be_truthy
334
329
  end
335
330
  end
336
331
 
337
332
  it 'validates volumes is array' do
338
333
  result = subject.validate_options('volumes' => '/app')
339
- expect(result.messages.key?('volumes')).to be_truthy
334
+ expect(result.errors.key?('volumes')).to be_truthy
340
335
 
341
336
  result = subject.validate_options('volumes' => ['/app'])
342
- expect(result.messages.key?('volumes')).to be_falsey
337
+ expect(result.errors.key?('volumes')).to be_falsey
343
338
  end
344
339
 
345
340
  it 'validates volumes_from is array' do
346
341
  result = subject.validate_options('volumes_from' => 'mysql_data')
347
- expect(result.messages.key?('volumes_from')).to be_truthy
342
+ expect(result.errors.key?('volumes_from')).to be_truthy
348
343
 
349
344
  result = subject.validate_options('volumes_from' => ['mysql_data'])
350
- expect(result.messages.key?('volumes_from')).to be_falsey
345
+ expect(result.errors.key?('volumes_from')).to be_falsey
351
346
  end
352
347
  end
353
348
  end
@@ -355,31 +350,31 @@ describe Kontena::Cli::Apps::YAML::Validator do
355
350
  context 'validates health_check' do
356
351
  it 'validates health_check' do
357
352
  result = subject.validate_options('health_check' => {})
358
- expect(result.messages.key?('health_check')).to be_truthy
353
+ expect(result.errors.key?('health_check')).to be_truthy
359
354
  end
360
355
 
361
356
  it 'validates health_check port ' do
362
357
  result = subject.validate_options('health_check' => { 'protocol' => 'http', 'port' => 'abc'})
363
- expect(result.messages.key?('health_check')).to be_truthy
358
+ expect(result.errors.key?('health_check')).to be_truthy
364
359
 
365
360
  result = subject.validate_options('health_check' => { 'protocol' => 'http', 'port' => 8080})
366
- expect(result.messages.key?('health_check')).to be_falsey
361
+ expect(result.errors.key?('health_check')).to be_falsey
367
362
  end
368
363
 
369
364
  it 'validates health_check uri' do
370
365
  result = subject.validate_options('health_check' => { 'protocol' => 'http', 'port' => 8080, 'uri' => 'foobar'})
371
- expect(result.messages.key?('health_check')).to be_truthy
366
+ expect(result.errors.key?('health_check')).to be_truthy
372
367
 
373
368
  result = subject.validate_options('health_check' => { 'protocol' => 'http', 'port' => 8080, 'uri' => '/health/foo/bar'})
374
- expect(result.messages.key?('health_check')).to be_falsey
369
+ expect(result.errors.key?('health_check')).to be_falsey
375
370
  end
376
371
 
377
372
  it 'validates health_check protocol' do
378
373
  result = subject.validate_options('health_check' => { 'protocol' => 'foo', 'port' => 8080, 'uri' => 'foobar'})
379
- expect(result.messages.key?('health_check')).to be_truthy
374
+ expect(result.errors.key?('health_check')).to be_truthy
380
375
 
381
376
  result = subject.validate_options('health_check' => { 'protocol' => 'tcp', 'port' => 3306 })
382
- expect(result.messages.key?('health_check')).to be_falsey
377
+ expect(result.errors.key?('health_check')).to be_falsey
383
378
  end
384
379
  end
385
380
  end