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