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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/kontena-cli.gemspec +2 -5
- data/lib/kontena/callbacks/master/deploy/05_before_deploy_configuration_wizard.rb +15 -21
- data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +5 -3
- data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +3 -1
- data/lib/kontena/callbacks/master/deploy/60_configure_auth_provider_after_deploy.rb +7 -18
- data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +93 -0
- data/lib/kontena/callbacks/master/deploy/90_proptip_after_deploy.rb +33 -0
- data/lib/kontena/cli/apps/build_command.rb +2 -1
- data/lib/kontena/cli/apps/common.rb +3 -2
- data/lib/kontena/cli/apps/config_command.rb +3 -3
- data/lib/kontena/cli/apps/deploy_command.rb +1 -0
- data/lib/kontena/cli/apps/docker_helper.rb +7 -7
- data/lib/kontena/cli/apps/list_command.rb +1 -1
- data/lib/kontena/cli/apps/logs_command.rb +1 -1
- data/lib/kontena/cli/apps/monitor_command.rb +2 -2
- data/lib/kontena/cli/apps/remove_command.rb +1 -1
- data/lib/kontena/cli/apps/restart_command.rb +1 -1
- data/lib/kontena/cli/apps/scale_command.rb +1 -1
- data/lib/kontena/cli/apps/start_command.rb +1 -1
- data/lib/kontena/cli/apps/stop_command.rb +1 -1
- data/lib/kontena/cli/apps/yaml/custom_validators/affinities_validator.rb +19 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/build_validator.rb +22 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/extends_validator.rb +21 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/hooks_validator.rb +54 -0
- data/lib/kontena/cli/apps/yaml/custom_validators/secrets_validator.rb +22 -0
- data/lib/kontena/cli/apps/yaml/validations.rb +60 -90
- data/lib/kontena/cli/apps/yaml/validator.rb +9 -31
- data/lib/kontena/cli/apps/yaml/validator_v2.rb +13 -40
- data/lib/kontena/cli/cloud/login_command.rb +2 -2
- data/lib/kontena/cli/cloud/master/add_command.rb +114 -34
- data/lib/kontena/cli/cloud/master/list_command.rb +5 -2
- data/lib/kontena/cli/cloud/master/remove_command.rb +69 -0
- data/lib/kontena/cli/cloud/master_command.rb +2 -2
- data/lib/kontena/cli/common.rb +10 -17
- data/lib/kontena/cli/config.rb +4 -3
- data/lib/kontena/cli/grids/env_command.rb +5 -5
- data/lib/kontena/cli/localhost_web_server.rb +1 -1
- data/lib/kontena/cli/master/init_cloud_command.rb +21 -0
- data/lib/kontena/cli/master/login_command.rb +28 -29
- data/lib/kontena/cli/master/remove_command.rb +58 -0
- data/lib/kontena/cli/master/token/create_command.rb +6 -0
- data/lib/kontena/cli/master/users/invite_command.rb +4 -1
- data/lib/kontena/cli/master/users/roles/add_command.rb +4 -3
- data/lib/kontena/cli/master_command.rb +8 -9
- data/lib/kontena/cli/services/list_command.rb +3 -2
- data/lib/kontena/cli/services/services_helper.rb +1 -1
- data/lib/kontena/client.rb +31 -47
- data/lib/kontena/presets/kontena_auth_provider.yml +2 -2
- data/lib/kontena_cli.rb +12 -0
- data/spec/kontena/cli/app/docker_helper_spec.rb +9 -4
- data/spec/kontena/cli/app/yaml/validator_spec.rb +66 -71
- data/spec/kontena/cli/app/yaml/validator_v2_spec.rb +51 -58
- metadata +25 -59
- data/lib/kontena/callbacks/master/deploy/90_suggest_inviting_yourself_after_deploy.rb +0 -24
- 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
|
-
|
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
|
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
|
-
|
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 "%-
|
34
|
+
puts "%s %-58s %-10.10s %-8s %-10s %-50s" % vars
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
data/lib/kontena/client.rb
CHANGED
@@ -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
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
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
|
-
|
322
|
-
|
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:
|
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
|
430
|
+
# Get prefixed request path unless path starts with /
|
447
431
|
#
|
448
432
|
# @param [String] path
|
449
433
|
# @return [String]
|
450
|
-
def
|
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
|
2
|
+
oauth2.authorize_endpoint: https://cloud.kontena.io/login/oauth/authorize
|
3
3
|
oauth2.code_requires_basic_auth: false
|
4
|
-
oauth2.token_endpoint: https://
|
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(
|
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(
|
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(
|
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(
|
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.
|
17
|
-
|
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.
|
17
|
+
expect(result.errors.key?('build')).to be_truthy
|
23
18
|
|
24
19
|
result = subject.validate_options('build' => '.')
|
25
|
-
expect(result.
|
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.
|
33
|
-
expect(result.
|
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.
|
39
|
-
expect(result.
|
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.
|
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.
|
45
|
+
expect(result.errors.key?('net')).to be_truthy
|
51
46
|
|
52
47
|
result = subject.validate_options('net' => 'bridge')
|
53
|
-
expect(result.
|
48
|
+
expect(result.errors.key?('net')).to be_falsey
|
54
49
|
|
55
50
|
result = subject.validate_options('net' => 'host')
|
56
|
-
expect(result.
|
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.
|
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.
|
62
|
+
expect(result.errors.key?('affinity')).to be_truthy
|
68
63
|
result = subject.validate_options('affinity' => ['node==node1'])
|
69
|
-
expect(result.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
140
|
+
expect(result.errors.key?('command')).to be_truthy
|
146
141
|
|
147
142
|
result = subject.validate_options('command' => nil)
|
148
|
-
expect(result.
|
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.
|
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.
|
152
|
+
expect(result.errors.key?('cpu_shares')).to be_truthy
|
158
153
|
result = subject.validate_options('cpu_shares' => 1024)
|
159
|
-
expect(result.
|
154
|
+
expect(result.errors.key?('cpu_shares')).to be_falsey
|
160
155
|
result = subject.validate_options({})
|
161
|
-
expect(result.
|
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.
|
161
|
+
expect(result.errors.key?('environment')).to be_truthy
|
167
162
|
result = subject.validate_options('environment' => ['KEY=VALUE'])
|
168
|
-
expect(result.
|
163
|
+
expect(result.errors.key?('environment')).to be_falsey
|
169
164
|
result = subject.validate_options('environment' => { 'KEY' => 'VALUE' })
|
170
|
-
expect(result.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
334
|
+
expect(result.errors.key?('volumes')).to be_truthy
|
340
335
|
|
341
336
|
result = subject.validate_options('volumes' => ['/app'])
|
342
|
-
expect(result.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
377
|
+
expect(result.errors.key?('health_check')).to be_falsey
|
383
378
|
end
|
384
379
|
end
|
385
380
|
end
|