kontena-cli 0.16.0.pre7 → 0.16.0.pre8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|