kontena-cli 1.4.3 → 1.5.0.pre1
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/Dockerfile +7 -3
- data/Gemfile +7 -3
- data/README.md +1 -1
- data/VERSION +1 -1
- data/bin/kontena +1 -0
- data/kontena-cli.gemspec +5 -6
- data/lib/kontena/cli/browser_launcher.rb +61 -0
- data/lib/kontena/cli/certificate/authorize_command.rb +40 -16
- data/lib/kontena/cli/certificate/get_command.rb +1 -1
- data/lib/kontena/cli/cloud/login_command.rb +3 -4
- data/lib/kontena/cli/cloud/master/add_command.rb +1 -1
- data/lib/kontena/cli/cloud/master/list_command.rb +1 -1
- data/lib/kontena/cli/cloud/master/remove_command.rb +1 -1
- data/lib/kontena/cli/cloud/master/update_command.rb +1 -1
- data/lib/kontena/cli/common.rb +2 -2
- data/lib/kontena/cli/etcd_command.rb +1 -1
- data/lib/kontena/cli/external_registries/add_command.rb +2 -2
- data/lib/kontena/cli/external_registries/remove_command.rb +1 -1
- data/lib/kontena/cli/grids/common.rb +14 -4
- data/lib/kontena/cli/grids/events_command.rb +2 -2
- data/lib/kontena/cli/grids/list_command.rb +1 -1
- data/lib/kontena/cli/grids/logs_command.rb +1 -1
- data/lib/kontena/cli/grids/remove_command.rb +12 -10
- data/lib/kontena/cli/grids/trusted_subnets/add_command.rb +1 -1
- data/lib/kontena/cli/grids/trusted_subnets/remove_command.rb +12 -10
- data/lib/kontena/cli/grids/use_command.rb +1 -1
- data/lib/kontena/cli/helpers/log_helper.rb +1 -1
- data/lib/kontena/cli/logout_command.rb +1 -1
- data/lib/kontena/cli/master/login_command.rb +2 -3
- data/lib/kontena/cli/master/logout_command.rb +2 -2
- data/lib/kontena/cli/master/token/common.rb +2 -1
- data/lib/kontena/cli/master/token/create_command.rb +5 -2
- data/lib/kontena/cli/master/token/current_command.rb +9 -4
- data/lib/kontena/cli/master/token/list_command.rb +1 -1
- data/lib/kontena/cli/master/token/show_command.rb +11 -1
- data/lib/kontena/cli/master/user/invite_command.rb +1 -1
- data/lib/kontena/cli/master_command.rb +0 -1
- data/lib/kontena/cli/nodes/create_command.rb +1 -1
- data/lib/kontena/cli/nodes/labels/remove_command.rb +17 -3
- data/lib/kontena/cli/nodes/remove_command.rb +12 -10
- data/lib/kontena/cli/nodes/reset_token_command.rb +1 -1
- data/lib/kontena/cli/nodes/update_command.rb +1 -1
- data/lib/kontena/cli/plugin_command.rb +2 -1
- data/lib/kontena/cli/plugins/install_command.rb +2 -2
- data/lib/kontena/cli/plugins/uninstall_command.rb +19 -10
- data/lib/kontena/cli/plugins/upgrade_command.rb +60 -0
- data/lib/kontena/cli/registry/create_command.rb +1 -1
- data/lib/kontena/cli/registry/remove_command.rb +2 -2
- data/lib/kontena/cli/services/containers_command.rb +1 -1
- data/lib/kontena/cli/services/create_command.rb +1 -1
- data/lib/kontena/cli/services/deploy_command.rb +1 -1
- data/lib/kontena/cli/services/envs/add_command.rb +1 -1
- data/lib/kontena/cli/services/envs/remove_command.rb +5 -3
- data/lib/kontena/cli/services/link_command.rb +1 -1
- data/lib/kontena/cli/services/logs_command.rb +1 -1
- data/lib/kontena/cli/services/monitor_command.rb +1 -1
- data/lib/kontena/cli/services/remove_command.rb +11 -9
- data/lib/kontena/cli/services/restart_command.rb +1 -1
- data/lib/kontena/cli/services/secrets/link_command.rb +1 -1
- data/lib/kontena/cli/services/services_helper.rb +6 -12
- data/lib/kontena/cli/services/start_command.rb +5 -3
- data/lib/kontena/cli/services/stop_command.rb +5 -3
- data/lib/kontena/cli/services/unlink_command.rb +1 -1
- data/lib/kontena/cli/services/update_command.rb +1 -1
- data/lib/kontena/cli/spinner.rb +10 -10
- data/lib/kontena/cli/stack_command.rb +1 -0
- data/lib/kontena/cli/stacks/build_command.rb +6 -6
- data/lib/kontena/cli/stacks/deploy_command.rb +12 -10
- data/lib/kontena/cli/stacks/inspect_command.rb +17 -0
- data/lib/kontena/cli/stacks/install_command.rb +15 -4
- data/lib/kontena/cli/stacks/list_command.rb +2 -3
- data/lib/kontena/cli/stacks/logs_command.rb +1 -1
- data/lib/kontena/cli/stacks/monitor_command.rb +2 -2
- data/lib/kontena/cli/stacks/remove_command.rb +28 -19
- data/lib/kontena/cli/stacks/restart_command.rb +5 -4
- data/lib/kontena/cli/stacks/stop_command.rb +6 -5
- data/lib/kontena/cli/stacks/upgrade_command.rb +84 -64
- data/lib/kontena/cli/stacks/yaml/reader.rb +9 -4
- data/lib/kontena/cli/vault/remove_command.rb +7 -5
- data/lib/kontena/cli/vault/update_command.rb +1 -1
- data/lib/kontena/cli/vault/write_command.rb +1 -1
- data/lib/kontena/cli/volumes/remove_command.rb +6 -4
- data/lib/kontena/cli/vpn/create_command.rb +1 -1
- data/lib/kontena/cli/vpn/remove_command.rb +1 -1
- data/lib/kontena/client.rb +23 -14
- data/lib/kontena/command.rb +2 -2
- data/lib/kontena/debug_instrumentor.rb +11 -2
- data/lib/kontena/plugin_manager/common.rb +5 -2
- data/lib/kontena/plugin_manager/installer.rb +34 -10
- data/lib/kontena/scripts/completer.rb +91 -43
- data/lib/kontena/{cli/stacks → stacks}/change_resolver.rb +38 -16
- data/lib/kontena/stacks/stack_data.rb +58 -0
- data/lib/kontena/stacks/stack_data_set.rb +51 -0
- data/lib/kontena_cli.rb +1 -0
- data/omnibus/Gemfile.lock +32 -22
- data/omnibus/config/projects/kontena.rb +2 -0
- data/omnibus/config/software/kontena-cli.rb +6 -4
- data/omnibus/package-scripts/kontena/postinstall +1 -1
- data/omnibus/wrappers/sh/kontena +1 -1
- data/spec/fixtures/kontena_v3_with_registry_extends.yml +20 -0
- data/spec/kontena/cli/certificates/authorize_command_spec.rb +81 -0
- data/spec/kontena/cli/cloud/login_command_spec.rb +4 -4
- data/spec/kontena/cli/common_spec.rb +8 -1
- data/spec/kontena/cli/grids/update_command_spec.rb +13 -0
- data/spec/kontena/cli/master/join_command_spec.rb +1 -4
- data/spec/kontena/cli/master/login_command_spec.rb +4 -4
- data/spec/kontena/cli/master/token/create_command_spec.rb +132 -0
- data/spec/kontena/cli/master/token/show_command_spec.rb +90 -0
- data/spec/kontena/cli/nodes/labels/remove_command_spec.rb +35 -5
- data/spec/kontena/cli/stacks/install_command_spec.rb +16 -6
- data/spec/kontena/cli/stacks/remove_command_spec.rb +23 -2
- data/spec/kontena/cli/stacks/validate_command_spec.rb +1 -1
- data/spec/kontena/cli/stacks/yaml/reader_spec.rb +33 -1
- data/spec/kontena/client_spec.rb +38 -1
- data/spec/kontena/stacks/change_resolver_spec.rb +44 -0
- data/spec/kontena/stacks/stack_data_set_spec.rb +59 -0
- metadata +36 -34
- data/lib/kontena/cli/master/users_command.rb +0 -13
@@ -213,7 +213,11 @@ module Kontena::Cli::Stacks
|
|
213
213
|
result['dependencies'] = dependencies
|
214
214
|
result['source'] = raw_content
|
215
215
|
result['variables'] = variable_values(without_defaults: true, without_vault: true)
|
216
|
-
|
216
|
+
end
|
217
|
+
if parent_name
|
218
|
+
result['parent'] = { 'name' => parent_name }
|
219
|
+
else
|
220
|
+
result['parent'] = nil
|
217
221
|
end
|
218
222
|
if service_name.nil?
|
219
223
|
result['services'].each do |service|
|
@@ -349,8 +353,8 @@ module Kontena::Cli::Stacks
|
|
349
353
|
@services ||= fully_interpolated_yaml.fetch('services', {})
|
350
354
|
end
|
351
355
|
|
352
|
-
def
|
353
|
-
external_reader =
|
356
|
+
def from_external_stack(name, service_name)
|
357
|
+
external_reader = StackFileLoader.for(name, loader).reader
|
354
358
|
variables.to_a(with_value: true).each do |var|
|
355
359
|
external_reader.variables.build_option(var)
|
356
360
|
end
|
@@ -445,7 +449,8 @@ module Kontena::Cli::Stacks
|
|
445
449
|
parent_config = process_config(services[extends])
|
446
450
|
when Hash
|
447
451
|
target = extends['file'] || extends['stack']
|
448
|
-
|
452
|
+
raise ("Service '#{extends}' does not define file: or stack: source") if target.nil?
|
453
|
+
parent_config = from_external_stack(target, extends['service'])
|
449
454
|
else
|
450
455
|
raise TypeError, "Extends must be a hash or string"
|
451
456
|
end
|
@@ -3,18 +3,20 @@ module Kontena::Cli::Vault
|
|
3
3
|
include Kontena::Cli::Common
|
4
4
|
include Kontena::Cli::GridOptions
|
5
5
|
|
6
|
-
parameter "NAME", "Secret name"
|
6
|
+
parameter "NAME ...", "Secret name", attribute_name: :names
|
7
7
|
option "--force", :flag, "Force remove", default: false, attribute_name: :forced
|
8
8
|
option "--silent", :flag, "Reduce output verbosity"
|
9
9
|
|
10
10
|
def execute
|
11
11
|
require_api_url
|
12
12
|
require_current_grid
|
13
|
-
|
13
|
+
names.each do |name|
|
14
|
+
confirm_command(name) unless forced?
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
token = require_token
|
17
|
+
vspinner "Removing #{pastel.cyan(name)} from the vault " do
|
18
|
+
client(token).delete("secrets/#{current_grid}/#{name}")
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
@@ -16,7 +16,7 @@ module Kontena::Cli::Vault
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def execute
|
19
|
-
vspinner "Updating #{
|
19
|
+
vspinner "Updating #{pastel.cyan(name)} value in the vault " do
|
20
20
|
client.put("secrets/#{current_grid}/#{name}", {name: name, value: value, upsert: upsert? })
|
21
21
|
end
|
22
22
|
end
|
@@ -15,7 +15,7 @@ module Kontena::Cli::Vault
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def execute
|
18
|
-
vspinner "Writing #{
|
18
|
+
vspinner "Writing #{pastel.cyan(name)} to the vault " do
|
19
19
|
client.post("grids/#{current_grid}/secrets", { name: name, value: value })
|
20
20
|
end
|
21
21
|
end
|
@@ -6,17 +6,19 @@ module Kontena::Cli::Volumes
|
|
6
6
|
|
7
7
|
|
8
8
|
banner "Removes a volume"
|
9
|
-
parameter 'VOLUME', 'Volume'
|
9
|
+
parameter 'VOLUME ...', 'Volume name', attribute_name: :volumes
|
10
10
|
option "--force", :flag, "Force remove", default: false, attribute_name: :forced
|
11
11
|
|
12
12
|
requires_current_master
|
13
13
|
requires_current_master_token
|
14
14
|
|
15
15
|
def execute
|
16
|
-
|
16
|
+
volumes.each do |volume|
|
17
|
+
confirm_command(volume) unless forced?
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
spinner "Removing volume #{pastel.cyan(volume)} " do
|
20
|
+
remove_volume(volume)
|
21
|
+
end
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
@@ -53,7 +53,7 @@ module Kontena::Cli::Vpn
|
|
53
53
|
spinner "Generating #{pastel.cyan(name)} keys (this will take a while) " do
|
54
54
|
wait_for_configuration_to_finish(token)
|
55
55
|
end
|
56
|
-
puts "#{
|
56
|
+
puts "#{pastel.cyan(name)} service is now started (udp://#{vpn_ip}:1194)."
|
57
57
|
puts "use 'kontena vpn config' to fetch OpenVPN client config to your machine."
|
58
58
|
end
|
59
59
|
|
@@ -14,7 +14,7 @@ module Kontena::Cli::Vpn
|
|
14
14
|
vpn = client(token).get("stacks/#{current_grid}/#{name}") rescue nil
|
15
15
|
exit_with_error("VPN stack does not exist") if vpn.nil?
|
16
16
|
|
17
|
-
spinner "Removing #{
|
17
|
+
spinner "Removing #{pastel.cyan(name)} service " do
|
18
18
|
client(token).delete("stacks/#{current_grid}/#{name}")
|
19
19
|
end
|
20
20
|
end
|
data/lib/kontena/client.rb
CHANGED
@@ -11,6 +11,8 @@ module Kontena
|
|
11
11
|
X_KONTENA_VERSION = 'X-Kontena-Version'.freeze
|
12
12
|
ACCEPT = 'Accept'.freeze
|
13
13
|
AUTHORIZATION = 'Authorization'.freeze
|
14
|
+
ACCEPT_ENCODING = 'Accept-Encoding'.freeze
|
15
|
+
GZIP = 'gzip'.freeze
|
14
16
|
|
15
17
|
attr_accessor :default_headers
|
16
18
|
attr_accessor :path_prefix
|
@@ -53,7 +55,8 @@ module Kontena
|
|
53
55
|
connect_timeout: ENV["EXCON_CONNECT_TIMEOUT"] ? ENV["EXCON_CONNECT_TIMEOUT"].to_i : 10,
|
54
56
|
read_timeout: ENV["EXCON_READ_TIMEOUT"] ? ENV["EXCON_READ_TIMEOUT"].to_i : 30,
|
55
57
|
write_timeout: ENV["EXCON_WRITE_TIMEOUT"] ? ENV["EXCON_WRITE_TIMEOUT"].to_i : 10,
|
56
|
-
ssl_verify_peer: ignore_ssl_errors? ? false : true
|
58
|
+
ssl_verify_peer: ignore_ssl_errors? ? false : true,
|
59
|
+
middlewares: Excon.defaults[:middlewares] + [Excon::Middleware::Decompress]
|
57
60
|
}
|
58
61
|
if Kontena.debug?
|
59
62
|
require 'kontena/debug_instrumentor'
|
@@ -248,7 +251,7 @@ module Kontena
|
|
248
251
|
# @param [Hash,NilClass] params
|
249
252
|
# @param [Hash] headers
|
250
253
|
def get_stream(path, response_block, params = nil, headers = {}, auth = true)
|
251
|
-
request(path: path, query: params, headers: headers, response_block: response_block, auth: auth)
|
254
|
+
request(path: path, query: params, headers: headers, response_block: response_block, auth: auth, gzip: false)
|
252
255
|
end
|
253
256
|
|
254
257
|
def token_expired?
|
@@ -279,15 +282,15 @@ module Kontena
|
|
279
282
|
# @param expects [Array] raises unless response status code matches this list.
|
280
283
|
# @param auth [Boolean] use token authentication default = true
|
281
284
|
# @return [Hash, String] response parsed response object
|
282
|
-
def request(http_method: :get, path:'/', body: nil, query: {}, headers: {}, response_block: nil, expects: [200, 201, 204], host: nil, port: nil, auth: true)
|
285
|
+
def request(http_method: :get, path:'/', body: nil, query: {}, headers: {}, response_block: nil, expects: [200, 201, 204], host: nil, port: nil, auth: true, gzip: true)
|
283
286
|
|
284
287
|
retried ||= false
|
285
288
|
|
286
289
|
if auth && token_expired?
|
287
|
-
raise Excon::
|
290
|
+
raise Excon::Error::Unauthorized, "Token expired or not valid, you need to login again, use: kontena #{token_is_for_master? ? "master" : "cloud"} login"
|
288
291
|
end
|
289
292
|
|
290
|
-
request_headers = request_headers(headers, auth)
|
293
|
+
request_headers = request_headers(headers, auth: auth, gzip: gzip)
|
291
294
|
|
292
295
|
if body.nil?
|
293
296
|
body_content = ''
|
@@ -325,7 +328,7 @@ module Kontena
|
|
325
328
|
@last_response = http_client.request(request_options)
|
326
329
|
|
327
330
|
parse_response(@last_response)
|
328
|
-
rescue Excon::
|
331
|
+
rescue Excon::Error::Unauthorized
|
329
332
|
if token
|
330
333
|
debug { 'Server reports access token expired' }
|
331
334
|
|
@@ -337,7 +340,11 @@ module Kontena
|
|
337
340
|
retry if refresh_token
|
338
341
|
end
|
339
342
|
raise Kontena::Errors::StandardError.new(401, 'Unauthorized')
|
340
|
-
rescue Excon::
|
343
|
+
rescue Excon::Error::HTTPStatus => error
|
344
|
+
if error.response.headers['Content-Encoding'] == 'gzip'
|
345
|
+
error.response.body = Zlib::GzipReader.new(StringIO.new(error.response.body)).read
|
346
|
+
end
|
347
|
+
|
341
348
|
debug { "Request #{error.request[:method].upcase} #{error.request[:path]}: #{error.response.status} #{error.response.reason_phrase}: #{error.response.body}" }
|
342
349
|
|
343
350
|
handle_error_response(error.response)
|
@@ -447,9 +454,10 @@ module Kontena
|
|
447
454
|
#
|
448
455
|
# @param [Hash] headers
|
449
456
|
# @return [Hash]
|
450
|
-
def request_headers(headers = {}, auth
|
457
|
+
def request_headers(headers = {}, auth: true, gzip: true)
|
451
458
|
headers = default_headers.merge(headers)
|
452
459
|
headers.merge!(bearer_authorization_header) if auth
|
460
|
+
headers[ACCEPT_ENCODING] = GZIP if gzip
|
453
461
|
headers.reject{|_,v| v.nil? || (v.respond_to?(:empty?) && v.empty?)}
|
454
462
|
end
|
455
463
|
|
@@ -479,7 +487,7 @@ module Kontena
|
|
479
487
|
check_version_and_warn(response.headers[X_KONTENA_VERSION])
|
480
488
|
|
481
489
|
if response.headers[CONTENT_TYPE] =~ JSON_REGEX
|
482
|
-
parse_json(response
|
490
|
+
parse_json(response)
|
483
491
|
else
|
484
492
|
response.body
|
485
493
|
end
|
@@ -503,13 +511,14 @@ module Kontena
|
|
503
511
|
|
504
512
|
# Parse json
|
505
513
|
#
|
506
|
-
# @param [
|
514
|
+
# @param response [Excon::Response]
|
507
515
|
# @return [Hash,Object,NilClass]
|
508
|
-
def parse_json(
|
509
|
-
|
516
|
+
def parse_json(response)
|
517
|
+
return nil if response.body.empty?
|
518
|
+
|
519
|
+
JSON.parse(response.body)
|
510
520
|
rescue => ex
|
511
|
-
|
512
|
-
nil
|
521
|
+
raise Kontena::Errors::StandardError.new(520, "Invalid response JSON from server for #{response.path}: #{ex.class.name}: #{ex.message}")
|
513
522
|
end
|
514
523
|
|
515
524
|
# Dump json
|
data/lib/kontena/command.rb
CHANGED
@@ -3,7 +3,7 @@ require 'kontena/cli/subcommand_loader'
|
|
3
3
|
require 'kontena/util'
|
4
4
|
require 'kontena/cli/bytes_helper'
|
5
5
|
require 'kontena/cli/grid_options'
|
6
|
-
require 'excon/
|
6
|
+
require 'excon/error'
|
7
7
|
|
8
8
|
class Kontena::Command < Clamp::Command
|
9
9
|
|
@@ -221,7 +221,7 @@ class Kontena::Command < Clamp::Command
|
|
221
221
|
run_callbacks :after unless help_requested?
|
222
222
|
exit(@exit_code) if @exit_code.to_i > 0
|
223
223
|
@result
|
224
|
-
rescue Excon::
|
224
|
+
rescue Excon::Error::Socket => ex
|
225
225
|
if ex.message.include?('Unable to verify certificate')
|
226
226
|
$stderr.puts " [#{Kontena.pastel.red('error')}] The server uses a certificate signed by an unknown authority."
|
227
227
|
$stderr.puts " You can trust this server by copying server CA pem file to: #{Kontena.pastel.yellow("~/.kontena/certs/<hostname>.pem")}"
|
@@ -20,7 +20,9 @@ module Kontena
|
|
20
20
|
str = "Headers: {"
|
21
21
|
heads = []
|
22
22
|
heads << "Accept: #{params[:headers]['Accept']}" if params[:headers]['Accept']
|
23
|
+
heads << "Accept-Encoding: #{params[:headers]['Accept-Encoding']}" if params[:headers]['Accept-Encoding']
|
23
24
|
heads << "Content-Type: #{params[:headers]['Content-Type']}" if params[:headers]['Content-Type']
|
25
|
+
heads << "Content-Encoding: #{params[:headers]['Content-Encoding']}" if params[:headers]['Content-Encoding']
|
24
26
|
heads << "Authorization: #{params[:headers]['Authorization'].split(' ', 2).first}" if params[:headers]['Authorization']
|
25
27
|
heads << "X-Kontena-Version: #{params[:headers]['X-Kontena-Version']}" if params[:headers]['X-Kontena-Version']
|
26
28
|
str << heads.join(', ')
|
@@ -39,12 +41,19 @@ module Kontena
|
|
39
41
|
end
|
40
42
|
|
41
43
|
if params[:body] && !params[:body].empty?
|
44
|
+
if params[:headers]['Content-Encoding'].to_s =~ /gzip/
|
45
|
+
body_content = Zlib::GzipReader.new(StringIO.new(params[:body])).read
|
46
|
+
body = "(GZIPPED 1:%d) %s" % [body_content.bytesize / params[:body].bytesize, body_content]
|
47
|
+
else
|
48
|
+
body = params[:body]
|
49
|
+
end
|
50
|
+
|
42
51
|
str = "Body: "
|
43
52
|
if ENV["DEBUG"] == "api"
|
44
53
|
str << "\n"
|
45
|
-
str <<
|
54
|
+
str << body
|
46
55
|
else
|
47
|
-
body =
|
56
|
+
body = body.inspect.strip
|
48
57
|
str << body[0,80]
|
49
58
|
if body.length > 80
|
50
59
|
str << "...\""
|
@@ -6,6 +6,8 @@ module Kontena
|
|
6
6
|
Gem.autoload :DefaultUserInteraction, 'rubygems/user_interaction'
|
7
7
|
Gem.autoload :StreamUI, 'rubygems/user_interaction'
|
8
8
|
|
9
|
+
KONTENA_PLUGIN = 'kontena-plugin-%s'
|
10
|
+
|
9
11
|
# @return [Boolean] is the CLI in plugin debugging mode?
|
10
12
|
def plugin_debug?
|
11
13
|
@plugin_debug ||= ENV['DEBUG'] == 'plugin'
|
@@ -26,8 +28,9 @@ module Kontena
|
|
26
28
|
|
27
29
|
# Prefix a plugin name into a gem name (hello to kontena-plugin-hello)
|
28
30
|
def prefix(plugin_name)
|
29
|
-
return
|
30
|
-
|
31
|
+
return KONTENA_PLUGIN % nil if plugin_name.nil? || plugin_name.empty?
|
32
|
+
return plugin_name if plugin_name.start_with?('kontena-plugin-') || plugin_name.include?('.')
|
33
|
+
KONTENA_PLUGIN % plugin_name
|
31
34
|
end
|
32
35
|
module_function :prefix
|
33
36
|
|
@@ -7,7 +7,8 @@ module Kontena
|
|
7
7
|
class Installer
|
8
8
|
include Common
|
9
9
|
|
10
|
-
attr_reader :plugin_name, :pre
|
10
|
+
attr_reader :plugin_name, :pre
|
11
|
+
attr_accessor :version
|
11
12
|
|
12
13
|
# Create a new instance of plugin installer
|
13
14
|
# @param plugin_name [String]
|
@@ -19,6 +20,10 @@ module Kontena
|
|
19
20
|
@version = version
|
20
21
|
end
|
21
22
|
|
23
|
+
def pre?
|
24
|
+
!!@pre
|
25
|
+
end
|
26
|
+
|
22
27
|
def command
|
23
28
|
@command ||= Gem::DependencyInstaller.new(
|
24
29
|
document: false,
|
@@ -30,23 +35,42 @@ module Kontena
|
|
30
35
|
|
31
36
|
# Install a plugin
|
32
37
|
def install
|
38
|
+
return install_uri if plugin_name.include?('://')
|
33
39
|
plugin_version = version.nil? ? Gem::Requirement.default : Gem::Requirement.new(version)
|
34
40
|
command.install(prefix(plugin_name), plugin_version)
|
35
41
|
command.installed_gems
|
36
42
|
end
|
37
43
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
def install_uri
|
45
|
+
require 'tempfile'
|
46
|
+
require 'open-uri'
|
47
|
+
file = Tempfile.new(['kontena_plugin', '.gem'])
|
48
|
+
open(plugin_name) do |input|
|
49
|
+
file.write input.read
|
50
|
+
file.close
|
51
|
+
end
|
52
|
+
self.class.new(file.path).install
|
53
|
+
ensure
|
54
|
+
file.unlink
|
55
|
+
end
|
56
|
+
|
57
|
+
def available_upgrade
|
43
58
|
installed = installed(plugin_name)
|
44
|
-
|
59
|
+
return false unless installed
|
45
60
|
|
46
|
-
|
47
|
-
latest = rubygems_client.latest_version(prefix(plugin_name), pre: pre)
|
61
|
+
pre = installed.version.prerelease?
|
62
|
+
latest = rubygems_client.latest_version(prefix(plugin_name), pre: pre? || pre)
|
48
63
|
if latest > installed.version
|
49
|
-
|
64
|
+
latest.to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Upgrade an installed plugin
|
69
|
+
def upgrade
|
70
|
+
return install if version
|
71
|
+
upgrade_to = available_upgrade
|
72
|
+
if upgrade_to
|
73
|
+
Installer.new(plugin_name, version: upgrade_to).install
|
50
74
|
end
|
51
75
|
end
|
52
76
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'kontena/cli/common'
|
2
|
+
require 'kontena/stacks_client'
|
2
3
|
|
3
4
|
class Helper
|
4
5
|
include Kontena::Cli::Common
|
@@ -27,12 +28,6 @@ class Helper
|
|
27
28
|
client_config['current_server']
|
28
29
|
end
|
29
30
|
|
30
|
-
def client
|
31
|
-
$VERSION_WARNING_ADDED=true
|
32
|
-
token = require_token
|
33
|
-
super(token)
|
34
|
-
end
|
35
|
-
|
36
31
|
def grids
|
37
32
|
client.get("grids")['grids'].map{|grid| grid['id']}
|
38
33
|
rescue => ex
|
@@ -57,6 +52,37 @@ class Helper
|
|
57
52
|
[]
|
58
53
|
end
|
59
54
|
|
55
|
+
def stack_registry_usable?
|
56
|
+
return false if current_account.nil? || current_account.stacks_url.nil?
|
57
|
+
return false if current_account.stacks_read_authentication && current_account.token.nil? || current_account.token.access_token.nil?
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
def stacks_client
|
62
|
+
Kontena::StacksClient.new(current_account.stacks_url, current_account.token, read_requires_token: current_account.stacks_read_authentication)
|
63
|
+
end
|
64
|
+
|
65
|
+
def registry_stacks(query = '')
|
66
|
+
return [] unless stack_registry_usable?
|
67
|
+
results = stacks_client.search(query).map { |s| s['stack'] }
|
68
|
+
if results.empty? && !query.empty? # this is here because old stack registry does not return anything for "org/"
|
69
|
+
results = stacks_client.search('').map { |s| s['stack'] }.select { |s| s.start_with?(query) }
|
70
|
+
end
|
71
|
+
results
|
72
|
+
rescue => ex
|
73
|
+
logger.debug ex
|
74
|
+
[]
|
75
|
+
end
|
76
|
+
|
77
|
+
def registry_stack_versions(stackname)
|
78
|
+
return [] unless stack_registry_usable?
|
79
|
+
logger.debug stackname.inspect
|
80
|
+
stacks_client.versions(stackname).map { |v| [stackname, v['version']].join(':') }
|
81
|
+
rescue => ex
|
82
|
+
logger.debug ex
|
83
|
+
[]
|
84
|
+
end
|
85
|
+
|
60
86
|
def services
|
61
87
|
services = client.get("grids/#{current_grid}/services")['services']
|
62
88
|
results = []
|
@@ -99,8 +125,21 @@ class Helper
|
|
99
125
|
[]
|
100
126
|
end
|
101
127
|
|
102
|
-
def
|
103
|
-
|
128
|
+
def directories(word)
|
129
|
+
if word && File.directory?(word) && !word.end_with?('/')
|
130
|
+
['%s/' % word]
|
131
|
+
else
|
132
|
+
Dir[File.join('.', '%s*' % word)].select { |file| File.directory?(file) }.map { |file| '%s/' % file.sub('./', '') }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def yml_files(word)
|
137
|
+
if word && File.directory?(word) && word.end_with?('/')
|
138
|
+
glob = File.join(word, '*.{yml,yaml}')
|
139
|
+
else
|
140
|
+
glob = File.join('.', '%s*.{yml,yaml}' % word)
|
141
|
+
end
|
142
|
+
Dir[glob].map { |file| file.sub('./', '') } + directories(word)
|
104
143
|
rescue => ex
|
105
144
|
logger.debug ex
|
106
145
|
[]
|
@@ -139,30 +178,30 @@ helper.logger.debug { "Completing #{words.inspect}" }
|
|
139
178
|
|
140
179
|
begin
|
141
180
|
completion = []
|
142
|
-
completion.push %w(cloud grid
|
181
|
+
completion.push %w(cloud grid service stack vault certificate node master vpn registry container etcd external-registry whoami plugin version) if words.size < 2
|
143
182
|
if words.size > 0
|
144
183
|
case words[0]
|
145
184
|
when 'plugin'
|
146
185
|
completion.clear
|
147
|
-
sub_commands = %w(list
|
186
|
+
sub_commands = %w(list search install uninstall)
|
148
187
|
if words[1]
|
149
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
188
|
+
completion.push(sub_commands) unless (sub_commands + %w(ls)).include?(words[1])
|
150
189
|
else
|
151
190
|
completion.push sub_commands
|
152
191
|
end
|
153
192
|
when 'etcd'
|
154
193
|
completion.clear
|
155
|
-
sub_commands = %w(get set mkdir
|
194
|
+
sub_commands = %w(get set mkdir list rm)
|
156
195
|
if words[1]
|
157
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
196
|
+
completion.push(sub_commands) unless (sub_commands + %w(ls)).include?(words[1])
|
158
197
|
else
|
159
198
|
completion.push sub_commands
|
160
199
|
end
|
161
200
|
when 'registry'
|
162
201
|
completion.clear
|
163
|
-
sub_commands = %w(create remove
|
202
|
+
sub_commands = %w(create remove)
|
164
203
|
if words[1]
|
165
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
204
|
+
completion.push(sub_commands) unless (sub_commands + %w(rm)).include?(words[1])
|
166
205
|
else
|
167
206
|
completion.push sub_commands
|
168
207
|
end
|
@@ -171,7 +210,7 @@ begin
|
|
171
210
|
sub_commands = %w(add-user audit-log create current list user remove show use)
|
172
211
|
if words[1] && words[1] == 'use'
|
173
212
|
completion.push helper.grids.reject { |g| g == helper.current_grid }
|
174
|
-
elsif words[1] && %w(update show
|
213
|
+
elsif words[1] && %w(update show remove env cloud-config health).include?(words[1])
|
175
214
|
completion.push helper.grids
|
176
215
|
else
|
177
216
|
completion.push sub_commands
|
@@ -186,7 +225,7 @@ begin
|
|
186
225
|
end
|
187
226
|
when 'master'
|
188
227
|
completion.clear
|
189
|
-
sub_commands = %w(list use user current remove
|
228
|
+
sub_commands = %w(list use user current remove config login logout token join audit-log init-cloud)
|
190
229
|
if words[1] && words[1] == 'use'
|
191
230
|
completion.push helper.master_names.reject { |n| n == helper.current_master_name }
|
192
231
|
elsif words[1] && %w(remove rm).include?(words[1])
|
@@ -194,8 +233,8 @@ begin
|
|
194
233
|
elsif words[1] && words[1] == 'user'
|
195
234
|
users_sub_commands = %w(invite list role)
|
196
235
|
if words[2] == 'role'
|
197
|
-
role_subcommands = %w(add remove
|
198
|
-
if !words[3] || !role_subcommands.include?(words[3])
|
236
|
+
role_subcommands = %w(add remove)
|
237
|
+
if !words[3] || !(role_subcommands + %w(rm)).include?(words[3])
|
199
238
|
completion.push role_subcommands
|
200
239
|
end
|
201
240
|
else
|
@@ -205,10 +244,10 @@ begin
|
|
205
244
|
config_sub_commands = %(set get dump load import export unset)
|
206
245
|
completion.push config_sub_commands
|
207
246
|
elsif words[1] && words[1] == 'token'
|
208
|
-
token_sub_commands = %(list
|
247
|
+
token_sub_commands = %(list remove show current create)
|
209
248
|
completion.push token_sub_commands
|
210
249
|
elsif words[1]
|
211
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
250
|
+
completion.push(sub_commands) unless (sub_commands + %w(ls rm)).include?(words[1])
|
212
251
|
else
|
213
252
|
completion.push sub_commands
|
214
253
|
end
|
@@ -216,10 +255,10 @@ begin
|
|
216
255
|
completion.clear
|
217
256
|
sub_commands = %w(login logout master)
|
218
257
|
if words[1] && words[1] == 'master'
|
219
|
-
cloud_master_sub_commands = %(list
|
258
|
+
cloud_master_sub_commands = %(list remove add show update)
|
220
259
|
completion.push cloud_master_sub_commands
|
221
260
|
elsif words[1]
|
222
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
261
|
+
completion.push(sub_commands) unless (sub_commands + %w(ls rm)).include?(words[1])
|
223
262
|
else
|
224
263
|
completion.push sub_commands
|
225
264
|
end
|
@@ -247,31 +286,40 @@ begin
|
|
247
286
|
when 'external-registry'
|
248
287
|
completion.clear
|
249
288
|
completion.push %w(add list delete)
|
250
|
-
when 'app'
|
251
|
-
completion.clear
|
252
|
-
sub_commands = %w(init build config deploy start stop remove rm ps list
|
253
|
-
logs monitor show)
|
254
|
-
if words[1] && sub_commands.include?(words[1])
|
255
|
-
completion.push helper.yml_services
|
256
|
-
else
|
257
|
-
completion.push sub_commands
|
258
|
-
end
|
259
289
|
when 'stack'
|
260
290
|
completion.clear
|
261
|
-
sub_commands = %w(build install upgrade deploy start stop remove
|
262
|
-
logs monitor show registry)
|
291
|
+
sub_commands = %w(build install upgrade deploy start stop remove restart list
|
292
|
+
logs monitor show registry inspect)
|
263
293
|
if words[1]
|
264
|
-
if words[1] == 'registry'
|
265
|
-
registry_sub_commands = %(push pull search show
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
294
|
+
if words[1] == 'registry' || words[1] == 'reg'
|
295
|
+
registry_sub_commands = %(push pull search show remove)
|
296
|
+
if words[2]
|
297
|
+
if words[2] == 'push'
|
298
|
+
completion.push helper.yml_files(words[3])
|
299
|
+
elsif %w(pull search show remove rm).include?(words[2]) && words[4].nil?
|
300
|
+
completion.push helper.registry_stacks(words[3].to_s)
|
301
|
+
else
|
302
|
+
completion.push registry_sub_commands
|
303
|
+
end
|
304
|
+
else
|
305
|
+
completion.push registry_sub_commands
|
306
|
+
end
|
307
|
+
elsif %w(install validate build).include?(words[1])
|
308
|
+
completion.push helper.yml_files(words[2])
|
309
|
+
if words[1] == 'install'
|
310
|
+
completion.push helper.registry_stacks(words[2].to_s)
|
311
|
+
end
|
312
|
+
elsif words[1] == 'upgrade'
|
313
|
+
if words[3]
|
314
|
+
completion.push helper.yml_files(words[4])
|
315
|
+
completion.push helper.registry_stacks(words[4].to_s)
|
316
|
+
else
|
317
|
+
completion.push helper.stacks
|
318
|
+
end
|
319
|
+
elsif %w(deploy start stop remove rm restart logs monitor show inspect).include?(words[1])
|
272
320
|
completion.push helper.stacks
|
273
321
|
else
|
274
|
-
completion.push(sub_commands)
|
322
|
+
completion.push(sub_commands) unless (sub_commands + %w(rm ls)).include?(words[1])
|
275
323
|
end
|
276
324
|
else
|
277
325
|
completion.push sub_commands
|