kontena-cli 1.2.0.pre1 → 1.2.0.pre2
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/lib/kontena/callback.rb +2 -2
- data/lib/kontena/callbacks/master/01_clear_current_master_after_terminate.rb +1 -1
- data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +6 -6
- data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +1 -1
- data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +1 -1
- data/lib/kontena/callbacks/master/deploy/90_proptip_after_deploy.rb +1 -1
- data/lib/kontena/cli/apps/common.rb +6 -6
- data/lib/kontena/cli/apps/yaml/reader.rb +2 -2
- data/lib/kontena/cli/cloud/login_command.rb +4 -4
- data/lib/kontena/cli/cloud/master/add_command.rb +1 -1
- data/lib/kontena/cli/common.rb +5 -5
- data/lib/kontena/cli/config.rb +8 -8
- data/lib/kontena/cli/containers/exec_command.rb +1 -1
- data/lib/kontena/cli/localhost_web_server.rb +3 -3
- data/lib/kontena/cli/master/{users → user}/invite_command.rb +5 -5
- data/lib/kontena/cli/master/{users → user}/list_command.rb +1 -1
- data/lib/kontena/cli/master/{users → user}/remove_command.rb +3 -4
- data/lib/kontena/cli/master/{users/roles → user/role}/add_command.rb +4 -4
- data/lib/kontena/cli/master/{users/roles → user/role}/remove_command.rb +3 -4
- data/lib/kontena/cli/master/{users → user}/role_command.rb +3 -5
- data/lib/kontena/cli/master/user_command.rb +11 -0
- data/lib/kontena/cli/master/users_command.rb +8 -5
- data/lib/kontena/cli/master_command.rb +2 -1
- data/lib/kontena/cli/nodes/show_command.rb +3 -1
- data/lib/kontena/cli/plugins/install_command.rb +4 -4
- data/lib/kontena/cli/plugins/uninstall_command.rb +3 -2
- data/lib/kontena/cli/services/services_helper.rb +31 -9
- data/lib/kontena/cli/spinner.rb +4 -8
- data/lib/kontena/cli/stacks/build_command.rb +3 -1
- data/lib/kontena/cli/stacks/common.rb +44 -37
- data/lib/kontena/cli/stacks/install_command.rb +2 -2
- data/lib/kontena/cli/stacks/upgrade_command.rb +3 -1
- data/lib/kontena/cli/stacks/validate_command.rb +12 -10
- data/lib/kontena/cli/stacks/yaml/opto/service_instances_resolver.rb +1 -0
- data/lib/kontena/cli/stacks/yaml/opto/service_link_resolver.rb +1 -0
- data/lib/kontena/cli/stacks/yaml/opto/vault_cert_prompt_resolver.rb +1 -0
- data/lib/kontena/cli/stacks/yaml/opto/vault_resolver.rb +4 -2
- data/lib/kontena/cli/stacks/yaml/opto/vault_setter.rb +4 -3
- data/lib/kontena/cli/stacks/yaml/reader.rb +30 -7
- data/lib/kontena/cli/stacks/yaml/validations.rb +10 -0
- data/lib/kontena/cli/stacks/yaml/validator_v3.rb +88 -8
- data/lib/kontena/client.rb +14 -12
- data/lib/kontena/command.rb +10 -13
- data/lib/kontena/errors.rb +36 -0
- data/lib/kontena/plugin_manager.rb +8 -10
- data/lib/kontena/stacks_cache.rb +1 -1
- data/lib/kontena_cli.rb +5 -5
- data/spec/fixtures/stack-with-volumes.yml +33 -0
- data/spec/kontena/cli/master/{users → user}/invite_command_spec.rb +3 -3
- data/spec/kontena/cli/master/{users → user}/remove_command_spec.rb +3 -3
- data/spec/kontena/cli/master/{users/roles → user/role}/add_command_spec.rb +3 -3
- data/spec/kontena/cli/master/{users/roles → user/role}/remove_command_spec.rb +3 -3
- data/spec/kontena/cli/stacks/build_command_spec.rb +2 -2
- data/spec/kontena/cli/stacks/install_command_spec.rb +3 -3
- data/spec/kontena/cli/stacks/upgrade_command_spec.rb +6 -6
- data/spec/kontena/cli/stacks/yaml/opto/service_link_resolver_spec.rb +5 -0
- data/spec/kontena/cli/stacks/yaml/opto/vault_cert_prompt_resolver_spec.rb +6 -0
- data/spec/kontena/cli/stacks/yaml/reader_spec.rb +4 -2
- data/spec/kontena/client_spec.rb +19 -1
- data/spec/spec_helper.rb +2 -0
- metadata +19 -16
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
require 'kontena/cli/common'
|
|
2
|
+
require 'kontena/util'
|
|
2
3
|
|
|
3
4
|
module Kontena::Cli::Stacks
|
|
4
5
|
module YAML
|
|
@@ -73,8 +74,8 @@ module Kontena::Cli::Stacks
|
|
|
73
74
|
)
|
|
74
75
|
)
|
|
75
76
|
)
|
|
76
|
-
rescue Psych::SyntaxError =>
|
|
77
|
-
raise "Error while parsing #{file}
|
|
77
|
+
rescue Psych::SyntaxError => ex
|
|
78
|
+
raise ex, "Error while parsing #{file} : #{ex.message}"
|
|
78
79
|
end
|
|
79
80
|
|
|
80
81
|
def fully_interpolated_yaml
|
|
@@ -92,8 +93,8 @@ module Kontena::Cli::Stacks
|
|
|
92
93
|
)
|
|
93
94
|
)
|
|
94
95
|
)
|
|
95
|
-
rescue Psych::SyntaxError =>
|
|
96
|
-
raise "Error while parsing #{file}
|
|
96
|
+
rescue Psych::SyntaxError => ex
|
|
97
|
+
raise ex, "Error while parsing #{file} : #{ex.message}"
|
|
97
98
|
end
|
|
98
99
|
|
|
99
100
|
def raw_yaml
|
|
@@ -141,12 +142,13 @@ module Kontena::Cli::Stacks
|
|
|
141
142
|
result[:expose] = fully_interpolated_yaml['expose']
|
|
142
143
|
result[:errors] = errors unless skip_validation?
|
|
143
144
|
result[:notifications] = notifications
|
|
144
|
-
result[:services] = errors.count
|
|
145
|
+
result[:services] = errors.count.zero? ? parse_services(service_name) : {}
|
|
145
146
|
unless skip_variables?
|
|
146
147
|
result[:variables] = variables.to_h(values_only: true).reject do |k,_|
|
|
147
148
|
k == 'GRID' || k == 'STACK' || variables.option(k).to.has_key?(:vault) || variables.option(k).from.has_key?(:vault)
|
|
148
149
|
end
|
|
149
150
|
end
|
|
151
|
+
result[:volumes] = errors.count.zero? ? parse_volumes : {}
|
|
150
152
|
end
|
|
151
153
|
result
|
|
152
154
|
end
|
|
@@ -208,6 +210,19 @@ module Kontena::Cli::Stacks
|
|
|
208
210
|
@validator ||= YAML::ValidatorV3.new
|
|
209
211
|
end
|
|
210
212
|
|
|
213
|
+
def parse_volumes
|
|
214
|
+
volumes.each do |name, config|
|
|
215
|
+
if process_hash?(config)
|
|
216
|
+
volumes[name].delete('only_if')
|
|
217
|
+
volumes[name].delete('skip_if')
|
|
218
|
+
volumes[name] = process_volume(config)
|
|
219
|
+
else
|
|
220
|
+
volumes.delete(name)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
volumes
|
|
224
|
+
end
|
|
225
|
+
|
|
211
226
|
##
|
|
212
227
|
# @param [String] service_name - optional service to parse
|
|
213
228
|
# @return [Hash]
|
|
@@ -264,9 +279,17 @@ module Kontena::Cli::Stacks
|
|
|
264
279
|
service_config
|
|
265
280
|
end
|
|
266
281
|
|
|
282
|
+
def process_volume(volume_config)
|
|
283
|
+
volume_config
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def volumes
|
|
287
|
+
@volumes ||= fully_interpolated_yaml['volumes'] || {}
|
|
288
|
+
end
|
|
289
|
+
|
|
267
290
|
# @return [Hash] - services from YAML file
|
|
268
291
|
def services
|
|
269
|
-
@services ||= fully_interpolated_yaml['services']
|
|
292
|
+
@services ||= fully_interpolated_yaml['services'] || {}
|
|
270
293
|
end
|
|
271
294
|
|
|
272
295
|
def from_external_file(filename, service_name)
|
|
@@ -69,5 +69,15 @@ module Kontena::Cli::Stacks::YAML
|
|
|
69
69
|
def validate_options(service_config)
|
|
70
70
|
HashValidator.validate(service_config, @schema, true)
|
|
71
71
|
end
|
|
72
|
+
|
|
73
|
+
def validate_volume_options(volume_config)
|
|
74
|
+
HashValidator.validate(volume_config, volume_schema, true)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def volume_schema
|
|
78
|
+
{
|
|
79
|
+
'external' => optional(-> (value) { value.is_a?(TrueClass) || (value.is_a?(Hash) && value['name'].is_a?(String)) })
|
|
80
|
+
}
|
|
81
|
+
end
|
|
72
82
|
end
|
|
73
83
|
end
|
|
@@ -6,6 +6,19 @@ module Kontena::Cli::Stacks
|
|
|
6
6
|
require_relative 'validations'
|
|
7
7
|
include Validations
|
|
8
8
|
|
|
9
|
+
KNOWN_TOP_LEVEL_KEYS = %w(
|
|
10
|
+
services
|
|
11
|
+
errors
|
|
12
|
+
volumes
|
|
13
|
+
networks
|
|
14
|
+
variables
|
|
15
|
+
stack
|
|
16
|
+
version
|
|
17
|
+
data
|
|
18
|
+
description
|
|
19
|
+
expose
|
|
20
|
+
)
|
|
21
|
+
|
|
9
22
|
def initialize
|
|
10
23
|
@schema = common_validations
|
|
11
24
|
@schema['build'] = optional('stacks_valid_build')
|
|
@@ -27,24 +40,91 @@ module Kontena::Cli::Stacks
|
|
|
27
40
|
errors: [],
|
|
28
41
|
notifications: []
|
|
29
42
|
}
|
|
43
|
+
|
|
44
|
+
result[:notifications] += (yaml.keys - KNOWN_TOP_LEVEL_KEYS).map do |key|
|
|
45
|
+
{ key => "unknown top level key" }
|
|
46
|
+
end
|
|
47
|
+
|
|
30
48
|
if yaml.key?('services')
|
|
31
|
-
yaml['services'].
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
if yaml['services'].is_a?(Hash)
|
|
50
|
+
yaml['services'].each do |service, options|
|
|
51
|
+
unless options.is_a?(Hash)
|
|
52
|
+
result[:errors] << { 'services' => { service => { 'options' => "must be a mapping not a #{options.class}"} } }
|
|
53
|
+
next
|
|
54
|
+
end
|
|
55
|
+
option_errors = validate_options(options)
|
|
56
|
+
result[:errors] << { 'services' => { service => option_errors.errors } } unless option_errors.valid?
|
|
57
|
+
if options['volumes']
|
|
58
|
+
mount_points = options['volumes'].inject(Hash.new(0)) { |hsh, vol| hsh[vol.split(':').last] += 1; hsh }
|
|
59
|
+
mount_points.each do |mount_point, occurences|
|
|
60
|
+
next unless occurences > 1
|
|
61
|
+
result[:errors] << { 'services' => { service => { 'volumes' => { mount_point => "mount point defined #{occurences} times" } } } }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
options['volumes'].each do |volume|
|
|
65
|
+
if volume.include?(':')
|
|
66
|
+
volume_name, mount_point = volume.split(':', 2)
|
|
67
|
+
unless mount_point
|
|
68
|
+
result[:errors] << { 'services' => { service => { 'volumes' => { volume => 'mount point missing' } } } }
|
|
69
|
+
end
|
|
70
|
+
if volume_name
|
|
71
|
+
if yaml.key?('volumes')
|
|
72
|
+
unless yaml['volumes'][volume_name]
|
|
73
|
+
result[:errors] << { 'services' => { service => { 'volumes' => { volume_name => 'not found in top level volumes list' } } } }
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
result[:errors] << { 'services' => { service => { 'volumes' => { volume => 'defines volume name, but file does not contain volumes definitions' } } } }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
35
82
|
end
|
|
36
|
-
|
|
37
|
-
result[:errors] << {
|
|
83
|
+
else
|
|
84
|
+
result[:errors] << { 'services' => "must be a mapping, not #{yaml['services'].class}" }
|
|
38
85
|
end
|
|
39
86
|
else
|
|
40
|
-
result[:
|
|
87
|
+
result[:notifications] << { 'file' => 'does not define any services' }
|
|
41
88
|
end
|
|
89
|
+
|
|
42
90
|
if yaml.key?('volumes')
|
|
43
|
-
|
|
91
|
+
if yaml['volumes'].is_a?(Hash)
|
|
92
|
+
yaml['volumes'].each do |volume, options|
|
|
93
|
+
if options.is_a?(Hash)
|
|
94
|
+
option_errors = validate_volume_options(options)
|
|
95
|
+
if option_errors.valid?
|
|
96
|
+
if !options.key?('driver') && options.key?('driver_opts')
|
|
97
|
+
result[:errors] << { 'volumes' => { volume => { 'driver_opts' => 'defined without defining driver' } } }
|
|
98
|
+
end
|
|
99
|
+
if options.key?('external')
|
|
100
|
+
unless options['external'].is_a?(FalseClass)
|
|
101
|
+
['driver', 'driver_opts', 'scope'].each do |key|
|
|
102
|
+
result[:errors] << { 'volumes' => { volume => { key => 'specified together with external' } } } if options.key?(key)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
if options.key?('driver') && !options.key?('scope')
|
|
107
|
+
result[:errors] << { 'volumes' => { volume => { 'scope' => 'required value missing' } } }
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
result[:errors] << { 'volumes' => { volume => option_errors.errors } }
|
|
111
|
+
end
|
|
112
|
+
else
|
|
113
|
+
result[:errors] << { 'volumes' => { volume => { 'options' => "must be a mapping, not #{options.class}" } } }
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
else
|
|
117
|
+
result[:errors] << { 'volumes' => "must be a mapping, not #{yaml['volumes'].class}" }
|
|
118
|
+
end
|
|
44
119
|
end
|
|
120
|
+
|
|
45
121
|
if yaml.key?('networks')
|
|
46
122
|
result[:notifications] << { 'networks' => 'Kontena does not support multiple networks yet. You can reference services with Kontena\'s internal DNS (service_name.kontena.local)' }
|
|
47
123
|
end
|
|
124
|
+
|
|
125
|
+
if (yaml['volumes'].nil? || yaml['volumes'].empty?) && (yaml['services'].nil? || yaml['services'].empty?)
|
|
126
|
+
result[:errors] << { 'file' => 'does not list any services or volumes' }
|
|
127
|
+
end
|
|
48
128
|
result
|
|
49
129
|
end
|
|
50
130
|
end
|
data/lib/kontena/client.rb
CHANGED
|
@@ -47,7 +47,7 @@ module Kontena
|
|
|
47
47
|
uri = URI.parse(@api_url)
|
|
48
48
|
@host = uri.host
|
|
49
49
|
|
|
50
|
-
@logger = Logger.new(ENV["DEBUG"] ?
|
|
50
|
+
@logger = Logger.new(ENV["DEBUG"] ? $stderr : $stdout)
|
|
51
51
|
@logger.level = ENV["DEBUG"].nil? ? Logger::INFO : Logger::DEBUG
|
|
52
52
|
@logger.progname = 'CLIENT'
|
|
53
53
|
|
|
@@ -139,8 +139,8 @@ module Kontena
|
|
|
139
139
|
logger.debug "Requesting user info from #{final_path}"
|
|
140
140
|
request(path: final_path)
|
|
141
141
|
true
|
|
142
|
-
rescue
|
|
143
|
-
logger.debug "Authentication verification exception: #{
|
|
142
|
+
rescue => ex
|
|
143
|
+
logger.debug "Authentication verification exception: #{ex.class.name} : #{ex.message}\n#{ex.backtrace.join("\n ")}"
|
|
144
144
|
false
|
|
145
145
|
end
|
|
146
146
|
|
|
@@ -172,8 +172,8 @@ module Kontena
|
|
|
172
172
|
# @return [String] version_string
|
|
173
173
|
def server_version
|
|
174
174
|
request(auth: false, expects: 200)['version']
|
|
175
|
-
rescue
|
|
176
|
-
logger.debug "Server version exception: #{
|
|
175
|
+
rescue => ex
|
|
176
|
+
logger.debug "Server version exception: #{ex.class.name} : #{ex.message}\n#{ex.backtrace.join("\n ")}"
|
|
177
177
|
nil
|
|
178
178
|
end
|
|
179
179
|
|
|
@@ -369,8 +369,8 @@ module Kontena
|
|
|
369
369
|
else
|
|
370
370
|
{}
|
|
371
371
|
end
|
|
372
|
-
rescue
|
|
373
|
-
logger.debug "Access token refresh exception: #{
|
|
372
|
+
rescue => ex
|
|
373
|
+
logger.debug "Access token refresh exception: #{ex.class.name} : #{ex.message}\n#{ex.backtrace.join("\n ")}"
|
|
374
374
|
false
|
|
375
375
|
end
|
|
376
376
|
|
|
@@ -418,8 +418,8 @@ module Kontena
|
|
|
418
418
|
logger.debug "Got null or bad response to refresh request: #{last_response.inspect}"
|
|
419
419
|
false
|
|
420
420
|
end
|
|
421
|
-
rescue
|
|
422
|
-
logger.debug "Access token refresh exception: #{
|
|
421
|
+
rescue => ex
|
|
422
|
+
logger.debug "Access token refresh exception: #{ex.class.name} : #{ex.message}\n#{ex.backtrace.join("\n ")}"
|
|
423
423
|
false
|
|
424
424
|
end
|
|
425
425
|
|
|
@@ -509,8 +509,8 @@ module Kontena
|
|
|
509
509
|
# @return [Hash,Object,NilClass]
|
|
510
510
|
def parse_json(json)
|
|
511
511
|
JSON.parse(json)
|
|
512
|
-
rescue
|
|
513
|
-
logger.debug "JSON parse exception: #{
|
|
512
|
+
rescue => ex
|
|
513
|
+
logger.debug "JSON parse exception: #{ex.class.name} : #{ex.message}"
|
|
514
514
|
nil
|
|
515
515
|
end
|
|
516
516
|
|
|
@@ -531,7 +531,9 @@ module Kontena
|
|
|
531
531
|
def handle_error_response(response)
|
|
532
532
|
data = parse_response(response)
|
|
533
533
|
|
|
534
|
-
if data.is_a?(Hash) && data.has_key?('error')
|
|
534
|
+
if data.is_a?(Hash) && data.has_key?('error') && data['error'].is_a?(Hash)
|
|
535
|
+
raise Kontena::Errors::StandardErrorHash.new(response.status, response.reason_phrase, data['error'])
|
|
536
|
+
elsif data.is_a?(Hash) && data.has_key?('error')
|
|
535
537
|
raise Kontena::Errors::StandardError.new(response.status, data['error'])
|
|
536
538
|
elsif data.is_a?(String) && !data.empty?
|
|
537
539
|
raise Kontena::Errors::StandardError.new(response.status, data)
|
data/lib/kontena/command.rb
CHANGED
|
@@ -178,7 +178,7 @@ class Kontena::Command < Clamp::Command
|
|
|
178
178
|
end
|
|
179
179
|
|
|
180
180
|
def run(arguments)
|
|
181
|
-
ENV["DEBUG"] &&
|
|
181
|
+
ENV["DEBUG"] && $stderr.puts("Running #{self} -- callback matcher = '#{self.class.callback_matcher.nil? ? "nil" : self.class.callback_matcher.map(&:to_s).join(' ')}'")
|
|
182
182
|
@arguments = arguments
|
|
183
183
|
|
|
184
184
|
run_callbacks :before_parse unless help_requested?
|
|
@@ -202,29 +202,26 @@ class Kontena::Command < Clamp::Command
|
|
|
202
202
|
run_callbacks :after unless help_requested?
|
|
203
203
|
exit(@exit_code) if @exit_code.to_i > 0
|
|
204
204
|
@result
|
|
205
|
-
rescue Excon::Errors::SocketError =>
|
|
206
|
-
if
|
|
205
|
+
rescue Excon::Errors::SocketError => ex
|
|
206
|
+
if ex.message.include?('Unable to verify certificate')
|
|
207
207
|
$stderr.puts " [#{Kontena.pastel.red('error')}] The server uses a certificate signed by an unknown authority."
|
|
208
208
|
$stderr.puts " You can trust this server by copying server CA pem file to: #{Kontena.pastel.yellow("~/.kontena/certs/<hostname>.pem")}"
|
|
209
209
|
$stderr.puts " Protip: you can bypass the certificate check by setting #{Kontena.pastel.yellow('SSL_IGNORE_ERRORS=true')} env variable, but any data you send to the server could be intercepted by others."
|
|
210
210
|
abort
|
|
211
211
|
else
|
|
212
|
-
abort(
|
|
212
|
+
abort(ex.message)
|
|
213
213
|
end
|
|
214
|
-
rescue Kontena::Errors::StandardError =>
|
|
215
|
-
raise
|
|
216
|
-
|
|
217
|
-
abort
|
|
214
|
+
rescue Kontena::Errors::StandardError => ex
|
|
215
|
+
raise ex if ENV['DEBUG']
|
|
216
|
+
abort(" [#{Kontena.pastel.red('error')}] #{ex.class.name} : #{ex.message}")
|
|
218
217
|
rescue Errno::EPIPE
|
|
219
218
|
# If user is piping the command outputs to some other command that might exit before CLI has outputted everything
|
|
220
219
|
abort
|
|
221
220
|
rescue Clamp::HelpWanted, Clamp::UsageError
|
|
222
221
|
raise
|
|
223
|
-
rescue =>
|
|
224
|
-
raise
|
|
225
|
-
|
|
226
|
-
$stderr.puts " Rerun the command with environment DEBUG=true set to get the full exception"
|
|
227
|
-
abort
|
|
222
|
+
rescue => ex
|
|
223
|
+
raise ex if ENV['DEBUG']
|
|
224
|
+
abort(" [#{Kontena.pastel.red('error')}] #{ex.class.name} : #{ex.message}\n Rerun the command with environment DEBUG=true set to get the full exception")
|
|
228
225
|
end
|
|
229
226
|
end
|
|
230
227
|
|
data/lib/kontena/errors.rb
CHANGED
|
@@ -4,10 +4,46 @@ module Kontena
|
|
|
4
4
|
|
|
5
5
|
attr_reader :status
|
|
6
6
|
|
|
7
|
+
# @param status [Fixnum] HTTP response status
|
|
8
|
+
# @param message [String] short error message
|
|
7
9
|
def initialize(status, message)
|
|
8
10
|
@status = status
|
|
9
11
|
super(message)
|
|
10
12
|
end
|
|
11
13
|
end
|
|
14
|
+
|
|
15
|
+
# The normal {error: {foo: "invalid foo"}} error response format used by the API
|
|
16
|
+
class StandardErrorHash < StandardError
|
|
17
|
+
attr_reader :errors
|
|
18
|
+
|
|
19
|
+
# @param errors [Hash]
|
|
20
|
+
def initialize(status, message, errors)
|
|
21
|
+
super(status, message)
|
|
22
|
+
@errors = errors
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Render as indented YAML
|
|
26
|
+
def errors_message(indent: "\t")
|
|
27
|
+
@errors.to_yaml.lines[1..-1].map{|line| "#{indent}#{line}" }.join
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Render the full multi-line message including YAML-formatted errors
|
|
31
|
+
def message
|
|
32
|
+
"#{super}:\n#{errors_message}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# An error with an array of additional details
|
|
37
|
+
class StandardErrorArray < Kontena::Errors::StandardError
|
|
38
|
+
# @param details [Array<String>]
|
|
39
|
+
def initialize(status, message, details)
|
|
40
|
+
super(status, message)
|
|
41
|
+
@details = details
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def message
|
|
45
|
+
"#{super}:\n#{@details.map{|msg| "\t" + msg}.join("\n")}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
12
48
|
end
|
|
13
49
|
end
|
|
@@ -171,23 +171,21 @@ module Kontena
|
|
|
171
171
|
plugins << spec
|
|
172
172
|
else
|
|
173
173
|
plugin_name = spec.name.sub('kontena-plugin-', '')
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
end
|
|
177
|
-
rescue LoadError => exc
|
|
178
|
-
STDERR.puts " [#{Kontena.pastel.red('error')}] Failed to load plugin: #{spec.name}"
|
|
179
|
-
if ENV['DEBUG']
|
|
180
|
-
STDERR.puts exc.message
|
|
181
|
-
STDERR.puts exc.backtrace.join("\n")
|
|
174
|
+
$stderr.puts " [#{Kontena.pastel.red('error')}] Plugin #{Kontena.pastel.cyan(plugin_name)} (#{spec.version}) is not compatible with the current cli version."
|
|
175
|
+
$stderr.puts " To update the plugin, run 'kontena plugin install #{plugin_name}'"
|
|
182
176
|
end
|
|
177
|
+
rescue LoadError => ex
|
|
178
|
+
$stderr.puts " [#{Kontena.pastel.red('error')}] Failed to load plugin: #{spec.name}"
|
|
179
|
+
ENV['DEBUG'] && $stderr.puts("#{ex.class.name} : #{ex.message}\n#{ex.backtrace.join("\n ")}")
|
|
183
180
|
exit 1
|
|
184
181
|
end
|
|
185
182
|
end
|
|
186
183
|
end
|
|
187
184
|
end
|
|
188
185
|
plugins
|
|
189
|
-
rescue =>
|
|
190
|
-
|
|
186
|
+
rescue => ex
|
|
187
|
+
$stderr.puts Kontena.pastel.red(ex.message)
|
|
188
|
+
ENV['DEBUG'] && $stderr.puts("#{ex.class.name} : #{ex.message}\n#{ex.backtrace.join("\n ")}")
|
|
191
189
|
end
|
|
192
190
|
|
|
193
191
|
def prefix(plugin_name)
|
data/lib/kontena/stacks_cache.rb
CHANGED
data/lib/kontena_cli.rb
CHANGED
|
@@ -19,11 +19,11 @@ module Kontena
|
|
|
19
19
|
ENV["DEBUG"] && puts("Command completed, result: #{result.inspect} status: 0")
|
|
20
20
|
return 0 if returning == :status
|
|
21
21
|
return result if returning == :result
|
|
22
|
-
rescue SystemExit
|
|
23
|
-
ENV["DEBUG"] &&
|
|
22
|
+
rescue SystemExit => ex
|
|
23
|
+
ENV["DEBUG"] && $stderr.puts("Command completed with failure, result: #{result.inspect} status: #{ex.status}")
|
|
24
24
|
returning == :status ? $!.status : nil
|
|
25
|
-
rescue
|
|
26
|
-
ENV["DEBUG"] &&
|
|
25
|
+
rescue => ex
|
|
26
|
+
ENV["DEBUG"] && $stderr.puts("Command raised #{ex} with message: #{ex.message}\n#{ex.backtrace.join("\n ")}")
|
|
27
27
|
returning == :status ? 1 : nil
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -95,7 +95,7 @@ require 'retriable'
|
|
|
95
95
|
Retriable.configure do |c|
|
|
96
96
|
c.on_retry = Proc.new do |exception, try, elapsed_time, next_interval|
|
|
97
97
|
return true unless ENV["DEBUG"]
|
|
98
|
-
puts "Retriable retry: #{try} - Exception: #{exception} - #{exception.message}. Elapsed: #{elapsed_time} Next interval: #{next_interval}"
|
|
98
|
+
puts "Retriable retry: #{try} - Exception: #{exception.class.name} - #{exception.message}. Elapsed: #{elapsed_time} Next interval: #{next_interval}"
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
|