kontena-cli 1.0.6 → 1.1.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 +1 -1
- data/VERSION +1 -1
- data/bin/kontena +4 -1
- data/kontena-cli.gemspec +1 -1
- data/lib/kontena/callback.rb +1 -1
- 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 +5 -5
- data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +1 -1
- data/lib/kontena/callbacks/master/deploy/56_set_server_provider_after_deploy.rb +25 -0
- data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +1 -1
- data/lib/kontena/cli/common.rb +3 -3
- data/lib/kontena/cli/config.rb +1 -1
- data/lib/kontena/cli/grid_command.rb +2 -0
- data/lib/kontena/cli/grids/common.rb +12 -0
- data/lib/kontena/cli/grids/health_command.rb +69 -0
- data/lib/kontena/cli/helpers/health_helper.rb +53 -0
- data/lib/kontena/cli/localhost_web_server.rb +3 -3
- data/lib/kontena/cli/master/users/invite_command.rb +1 -1
- data/lib/kontena/cli/node_command.rb +2 -0
- data/lib/kontena/cli/nodes/health_command.rb +32 -0
- data/lib/kontena/cli/nodes/list_command.rb +40 -26
- data/lib/kontena/cli/nodes/show_command.rb +0 -1
- data/lib/kontena/cli/plugins/install_command.rb +28 -30
- data/lib/kontena/cli/plugins/search_command.rb +6 -14
- data/lib/kontena/cli/plugins/uninstall_command.rb +7 -11
- data/lib/kontena/cli/services/stats_command.rb +4 -2
- data/lib/kontena/cli/spinner.rb +20 -4
- data/lib/kontena/cli/stacks/show_command.rb +5 -1
- data/lib/kontena/cli/stacks/yaml/opto/service_instances_resolver.rb +22 -0
- data/lib/kontena/cli/stacks/yaml/opto/vault_setter.rb +1 -1
- data/lib/kontena/cli/stacks/yaml/reader.rb +1 -0
- data/lib/kontena/cli/vault/export_command.rb +22 -0
- data/lib/kontena/cli/vault/import_command.rb +80 -0
- data/lib/kontena/cli/vault/list_command.rb +4 -0
- data/lib/kontena/cli/vault/read_command.rb +8 -3
- data/lib/kontena/cli/vault/remove_command.rb +2 -1
- data/lib/kontena/cli/vault/update_command.rb +5 -7
- data/lib/kontena/cli/vault_command.rb +5 -1
- data/lib/kontena/client.rb +25 -2
- data/lib/kontena/command.rb +1 -1
- data/lib/kontena/debug_instrumentor.rb +70 -0
- data/lib/kontena/light_prompt.rb +103 -0
- data/lib/kontena/plugin_manager.rb +167 -6
- data/lib/kontena/stacks_cache.rb +1 -1
- data/lib/kontena_cli.rb +23 -6
- data/spec/kontena/cli/grids/health_command_spec.rb +390 -0
- data/spec/kontena/cli/nodes/health_command_spec.rb +206 -0
- data/spec/kontena/cli/nodes/list_command_spec.rb +205 -0
- data/spec/kontena/cli/vault/export_spec.rb +32 -0
- data/spec/kontena/cli/vault/import_spec.rb +69 -0
- data/spec/kontena/client_spec.rb +39 -0
- data/spec/kontena/plugin_manager_spec.rb +7 -7
- data/spec/spec_helper.rb +1 -0
- data/spec/support/output_helpers.rb +51 -0
- metadata +27 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fa4fdfff0c1b90c0954ec7e31f8f916c8636f16
|
4
|
+
data.tar.gz: 62e78df6ba2b65b1bfcaacde2290d81719d93e82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2fb585c59f687236071ec28640e487f32e4b4a1426ef9152249bcdd0651dfb96d0250b1ec91df171e9c0c1b650c257c9bb0e9f2039d8fda9b5a553ddda9c55f
|
7
|
+
data.tar.gz: 7408e60150cbd48e54549a490af795bb6688af0e01c9969f2f53a20fb8b59417228b3d1fc798e877377a39d28540c9ba8c614a99be110ae86bc74bf07c60716a
|
data/Dockerfile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.1.0.pre1
|
data/bin/kontena
CHANGED
@@ -12,7 +12,7 @@ require 'kontena_cli'
|
|
12
12
|
STDOUT.sync = true
|
13
13
|
|
14
14
|
begin
|
15
|
-
Kontena::PluginManager.instance.
|
15
|
+
Kontena::PluginManager.instance.init
|
16
16
|
Kontena::MainCommand.run
|
17
17
|
rescue Excon::Errors::SocketError => exc
|
18
18
|
if exc.message.include?('Unable to verify certificate')
|
@@ -27,6 +27,9 @@ rescue Kontena::Errors::StandardError => exc
|
|
27
27
|
raise exc if ENV['DEBUG']
|
28
28
|
puts " [#{Kontena.pastel.red('error')}] #{exc.message}"
|
29
29
|
abort
|
30
|
+
rescue Errno::EPIPE
|
31
|
+
# If user is piping the command outputs to some other command that might exit before CLI has outputted everything
|
32
|
+
abort
|
30
33
|
rescue => exc
|
31
34
|
raise exc if ENV['DEBUG']
|
32
35
|
$stderr.puts " [#{Kontena.pastel.red('error')}] #{exc.message}"
|
data/kontena-cli.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.7"
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
25
|
spec.add_runtime_dependency "excon", "~> 0.49.0"
|
26
|
-
spec.add_runtime_dependency "tty-prompt", "~> 0.
|
26
|
+
spec.add_runtime_dependency "tty-prompt", "~> 0.10"
|
27
27
|
spec.add_runtime_dependency "clamp", "~> 1.1.0"
|
28
28
|
spec.add_runtime_dependency "ruby_dig", "~> 0.0.2"
|
29
29
|
spec.add_runtime_dependency "launchy", "~> 2.4.3"
|
data/lib/kontena/callback.rb
CHANGED
@@ -44,7 +44,7 @@ class Kontena::Callback
|
|
44
44
|
if klass.instance_methods.include?(state)
|
45
45
|
cb = klass.new(obj)
|
46
46
|
if cb.send(state).kind_of?(FalseClass)
|
47
|
-
ENV["DEBUG"] && puts("Execution aborted by #{klass}")
|
47
|
+
ENV["DEBUG"] && STDERR.puts("Execution aborted by #{klass}")
|
48
48
|
exit 1
|
49
49
|
end
|
50
50
|
end
|
@@ -10,7 +10,7 @@ module Kontena
|
|
10
10
|
return unless command.exit_code == 0
|
11
11
|
return unless config.current_master
|
12
12
|
|
13
|
-
ENV["DEBUG"] && puts("Removing current master from config")
|
13
|
+
ENV["DEBUG"] && STDERR.puts("Removing current master from config")
|
14
14
|
config.servers.delete_at(config.find_server_index(config.current_master.name))
|
15
15
|
config.current_server = nil
|
16
16
|
config.write
|
@@ -8,8 +8,8 @@ module Kontena
|
|
8
8
|
matches_commands 'master create'
|
9
9
|
|
10
10
|
def after
|
11
|
-
ENV["DEBUG"] && puts("Command result: #{command.result.inspect}")
|
12
|
-
ENV["DEBUG"] && puts("Command exit code: #{command.exit_code.inspect}")
|
11
|
+
ENV["DEBUG"] && STDERR.puts("Command result: #{command.result.inspect}")
|
12
|
+
ENV["DEBUG"] && STDERR.puts("Command exit code: #{command.exit_code.inspect}")
|
13
13
|
return unless command.exit_code == 0
|
14
14
|
return unless command.result.kind_of?(Hash)
|
15
15
|
return unless command.result.has_key?(:public_ip)
|
@@ -35,11 +35,11 @@ module Kontena
|
|
35
35
|
|
36
36
|
# Figure out if HTTPS works, if not, try HTTP
|
37
37
|
begin
|
38
|
-
ENV["DEBUG"] && puts("Trying to request / from #{new_master.url}")
|
38
|
+
ENV["DEBUG"] && STDERR.puts("Trying to request / from #{new_master.url}")
|
39
39
|
client = Kontena::Client.new(new_master.url, nil, ignore_ssl_errors: true)
|
40
40
|
client.get('/')
|
41
41
|
rescue
|
42
|
-
ENV["DEBUG"] && puts("HTTPS test failed: #{$!} #{$!.message}")
|
42
|
+
ENV["DEBUG"] && STDERR.puts("HTTPS test failed: #{$!} #{$!.message}")
|
43
43
|
unless retried
|
44
44
|
new_master.url = "http://#{command.result[:public_ip]}"
|
45
45
|
retried = true
|
@@ -51,7 +51,7 @@ module Kontena
|
|
51
51
|
require 'shellwords'
|
52
52
|
cmd = "master login --no-login-info --skip-grid-auto-select --verbose --name #{command.result[:name].shellescape} --code #{command.result[:code].shellescape} #{new_master.url.shellescape}"
|
53
53
|
Retriable.retriable do
|
54
|
-
ENV["DEBUG"] && puts("Running: #{cmd}")
|
54
|
+
ENV["DEBUG"] && STDERR.puts("Running: #{cmd}")
|
55
55
|
Kontena.run(cmd)
|
56
56
|
end
|
57
57
|
end
|
@@ -12,7 +12,7 @@ module Kontena
|
|
12
12
|
return unless config.current_master.name == command.result[:name]
|
13
13
|
|
14
14
|
cmd = "grid create --silent test"
|
15
|
-
ENV["DEBUG"] && puts("Running: #{cmd}")
|
15
|
+
ENV["DEBUG"] && STDERR.puts("Running: #{cmd}")
|
16
16
|
Retriable.retriable do
|
17
17
|
Kontena.run(cmd)
|
18
18
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Kontena
|
2
|
+
module Callbacks
|
3
|
+
class SetServerProviderAfterDeploy < Kontena::Callback
|
4
|
+
|
5
|
+
include Kontena::Cli::Common
|
6
|
+
|
7
|
+
matches_commands 'master create'
|
8
|
+
|
9
|
+
def after
|
10
|
+
return unless command.exit_code == 0
|
11
|
+
return unless config.current_master
|
12
|
+
return unless config.current_master.name == command.result[:name]
|
13
|
+
return unless command.result[:provider]
|
14
|
+
|
15
|
+
require 'shellwords'
|
16
|
+
|
17
|
+
cmd = ['master', 'config', 'set', "server.provider=#{command.result[:provider]}"]
|
18
|
+
spinner "Setting Master configuration server.provider to '#{command.result[:provider]}'" do
|
19
|
+
Kontena.run(cmd.shelljoin)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
data/lib/kontena/cli/common.rb
CHANGED
@@ -12,7 +12,7 @@ module Kontena
|
|
12
12
|
|
13
13
|
def logger
|
14
14
|
return @logger if @logger
|
15
|
-
@logger = Logger.new(STDOUT)
|
15
|
+
@logger = Logger.new(ENV["DEBUG"] ? STDERR : STDOUT)
|
16
16
|
@logger.level = ENV["DEBUG"].nil? ? Logger::INFO : Logger::DEBUG
|
17
17
|
@logger.progname = 'COMMON'
|
18
18
|
@logger
|
@@ -129,10 +129,10 @@ module Kontena
|
|
129
129
|
return unless server.token.refresh_token
|
130
130
|
return if server.token.expired?
|
131
131
|
client = Kontena::Client.new(server.url, server.token)
|
132
|
-
|
132
|
+
logger.debug "Trying to invalidate refresh token on #{server.name}"
|
133
133
|
client.refresh_token
|
134
134
|
rescue
|
135
|
-
|
135
|
+
logger.debug "Refreshing failed: #{$!} : #{$!.message}"
|
136
136
|
end
|
137
137
|
|
138
138
|
def require_current_master
|
data/lib/kontena/cli/config.rb
CHANGED
@@ -26,7 +26,7 @@ module Kontena
|
|
26
26
|
|
27
27
|
def initialize
|
28
28
|
super
|
29
|
-
@logger = Logger.new(STDOUT)
|
29
|
+
@logger = Logger.new(ENV["DEBUG"] ? STDERR : STDOUT)
|
30
30
|
@logger.level = ENV["DEBUG"].nil? ? Logger::INFO : Logger::DEBUG
|
31
31
|
@logger.progname = 'CONFIG'
|
32
32
|
load_settings_from_env || load_settings_from_config_file
|
@@ -11,6 +11,7 @@ require_relative 'grids/audit_log_command'
|
|
11
11
|
require_relative 'grids/user_command'
|
12
12
|
require_relative 'grids/cloud_config_command'
|
13
13
|
require_relative 'grids/trusted_subnet_command'
|
14
|
+
require_relative 'grids/health_command'
|
14
15
|
|
15
16
|
class Kontena::Cli::GridCommand < Kontena::Command
|
16
17
|
|
@@ -27,6 +28,7 @@ class Kontena::Cli::GridCommand < Kontena::Command
|
|
27
28
|
subcommand "user", "User specific commands", Kontena::Cli::Grids::UserCommand
|
28
29
|
subcommand "cloud-config", "Generate cloud-config", Kontena::Cli::Grids::CloudConfigCommand
|
29
30
|
subcommand "trusted-subnet", "Trusted subnet related commands", Kontena::Cli::Grids::TrustedSubnetCommand
|
31
|
+
subcommand "health", "Check grid health", Kontena::Cli::Grids::HealthCommand
|
30
32
|
|
31
33
|
def execute
|
32
34
|
end
|
@@ -7,6 +7,7 @@ module Kontena::Cli::Grids
|
|
7
7
|
host = ENV['KONTENA_URL'] || self.current_master['url']
|
8
8
|
puts "#{grid['name']}:"
|
9
9
|
puts " uri: #{host.sub('http', 'ws')}"
|
10
|
+
puts " initial_size: #{grid['initial_size']}"
|
10
11
|
root_dir = grid['engine_root_dir']
|
11
12
|
nodes = client(require_token).get("grids/#{grid['name']}/nodes")
|
12
13
|
nodes = nodes['nodes'].select{|n| n['connected'] == true }
|
@@ -92,5 +93,16 @@ module Kontena::Cli::Grids
|
|
92
93
|
return 0.0 if amount.nil?
|
93
94
|
(amount.to_f / 1024 / 1024 / 1024).to_f.round(2)
|
94
95
|
end
|
96
|
+
|
97
|
+
# @return [Hash] /v1/grids/:grid JSON { ... }
|
98
|
+
def get_grid(name = nil)
|
99
|
+
if name
|
100
|
+
client(require_token).get("grids/#{name}")
|
101
|
+
elsif current_grid
|
102
|
+
client(require_token).get("grids/#{current_grid}")
|
103
|
+
else
|
104
|
+
exit_with_error "No grid given or selected"
|
105
|
+
end
|
106
|
+
end
|
95
107
|
end
|
96
108
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
require "kontena/cli/helpers/health_helper"
|
3
|
+
|
4
|
+
module Kontena::Cli::Grids
|
5
|
+
class HealthCommand < Kontena::Command
|
6
|
+
include Kontena::Cli::Common
|
7
|
+
include Kontena::Cli::Helpers::HealthHelper
|
8
|
+
include Common
|
9
|
+
|
10
|
+
parameter "[NAME]", "Grid name"
|
11
|
+
|
12
|
+
def execute
|
13
|
+
require_api_url
|
14
|
+
|
15
|
+
grid = get_grid(name)
|
16
|
+
grid_nodes = client(require_token).get("grids/#{grid['name']}/nodes")
|
17
|
+
|
18
|
+
return show_grid_health(grid, grid_nodes['nodes'])
|
19
|
+
end
|
20
|
+
|
21
|
+
# Validate grid/nodes configuration for grid operation
|
22
|
+
#
|
23
|
+
# @return [Boolean] false if unhealthy
|
24
|
+
def show_grid_health(grid, nodes)
|
25
|
+
initial_size = grid['initial_size']
|
26
|
+
minimum_size = grid['initial_size'] / 2 + 1 # a majority is required for etcd quorum
|
27
|
+
|
28
|
+
grid_health = grid_health(grid, nodes)
|
29
|
+
initial_nodes = nodes.select{|node| node['initial_member']}
|
30
|
+
online_nodes = initial_nodes.select{|node| node['connected']}
|
31
|
+
|
32
|
+
# configuration and status
|
33
|
+
if initial_nodes.length == 0
|
34
|
+
puts "#{health_icon :error} Grid does not have any initial nodes, and requires at least #{minimum_size} of #{initial_size} initial nodes for operation"
|
35
|
+
elsif online_nodes.empty?
|
36
|
+
puts "#{health_icon :error} Grid does not have any initial nodes online, and requires at least #{minimum_size} of #{initial_size} initial nodes for operation"
|
37
|
+
elsif initial_nodes.length < minimum_size
|
38
|
+
puts "#{health_icon :error} Grid only has #{initial_nodes.length} initial nodes, and requires at least #{minimum_size} of #{initial_size} initial nodes for operation"
|
39
|
+
elsif online_nodes.length < minimum_size
|
40
|
+
puts "#{health_icon :error} Grid only has #{online_nodes.length} initial nodes online, and requires at least #{minimum_size} of #{initial_size} initial nodes for operation"
|
41
|
+
elsif initial_nodes.length < initial_size
|
42
|
+
puts "#{health_icon :warning} Grid only has #{initial_nodes.length} initial nodes of #{initial_size} required for high-availability"
|
43
|
+
elsif online_nodes.length < initial_size
|
44
|
+
puts "#{health_icon :warning} Grid only has #{online_nodes.length} initial nodes online of #{initial_size} required for high-availability"
|
45
|
+
elsif initial_nodes.length == 2
|
46
|
+
puts "#{health_icon :warning} Grid only has #{initial_nodes.length} initial nodes, and is not high-availability"
|
47
|
+
elsif initial_nodes.length == 1
|
48
|
+
puts "#{health_icon :warning} Grid only has #{initial_nodes.length} initial node, and is not high-availability"
|
49
|
+
else
|
50
|
+
puts "#{health_icon :ok} Grid has all #{online_nodes.length} of #{initial_size} initial nodes online"
|
51
|
+
end
|
52
|
+
|
53
|
+
nodes.each do |node|
|
54
|
+
node_health = node_health(node, grid_health)
|
55
|
+
|
56
|
+
if node['connected']
|
57
|
+
|
58
|
+
elsif node['initial_member']
|
59
|
+
puts "#{health_icon grid_health} Initial node #{node['name']} is offline"
|
60
|
+
else
|
61
|
+
puts "#{health_icon node_health} Grid node #{node['name']} is offline"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# operational if we have etcd quorum
|
66
|
+
return online_nodes.length >= minimum_size
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Kontena::Cli::Helpers
|
2
|
+
module HealthHelper
|
3
|
+
def health_icon(health)
|
4
|
+
case health
|
5
|
+
when nil
|
6
|
+
" "
|
7
|
+
when :ok
|
8
|
+
pastel.green('⊛')
|
9
|
+
when :warning
|
10
|
+
pastel.yellow('⊙')
|
11
|
+
when :error
|
12
|
+
pastel.red('⊗')
|
13
|
+
when :offline
|
14
|
+
pastel.dark('⊝')
|
15
|
+
else
|
16
|
+
fail "Invalid health=#{health}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Validate grid nodes configuration and status
|
21
|
+
#
|
22
|
+
# @param grid [Hash] get(/grids/:grid) => { ... }
|
23
|
+
# @param nodes [Array<Hash>] get(/grids/:grid/nodes)[nodes] => [ { ... } ]
|
24
|
+
# @return [Symbol] health
|
25
|
+
def grid_health(grid, nodes)
|
26
|
+
initial = grid['initial_size']
|
27
|
+
minimum = grid['initial_size'] / 2 + 1 # a majority is required for etcd quorum
|
28
|
+
|
29
|
+
online = nodes.select{|node| node['initial_member'] && node['connected']}
|
30
|
+
|
31
|
+
if online.length < minimum
|
32
|
+
return :error
|
33
|
+
elsif online.length < initial
|
34
|
+
return :warning
|
35
|
+
else
|
36
|
+
return :ok
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Validate grid node status based on the grid health
|
41
|
+
#
|
42
|
+
# @param node [Hash] GET /nodes/:grid/:node
|
43
|
+
# @param grid_health [Symbol] @see #grid_health
|
44
|
+
# @return [Symbol] health
|
45
|
+
def node_health(node, grid_health)
|
46
|
+
if node['initial_member']
|
47
|
+
return node['connected'] ? grid_health : :offline
|
48
|
+
else
|
49
|
+
return node['connected'] ? :ok : :offline
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -39,7 +39,7 @@ module Kontena
|
|
39
39
|
#
|
40
40
|
# @return [Hash] query_params
|
41
41
|
def serve_one
|
42
|
-
ENV["DEBUG"] && puts("Waiting for connection on port #{port}..")
|
42
|
+
ENV["DEBUG"] && STDERR.puts("Waiting for connection on port #{port}..")
|
43
43
|
socket = server.accept
|
44
44
|
|
45
45
|
content = socket.recvfrom(2048).first.split(/(?:\r)?\n/)
|
@@ -56,7 +56,7 @@ module Kontena
|
|
56
56
|
|
57
57
|
body = content.join("\n")
|
58
58
|
|
59
|
-
ENV["DEBUG"] && puts("Got request: \"#{request.inspect}\n Headers: #{headers.inspect}\n Body: #{body}\"")
|
59
|
+
ENV["DEBUG"] && STDERR.puts("Got request: \"#{request.inspect}\n Headers: #{headers.inspect}\n Body: #{body}\"")
|
60
60
|
|
61
61
|
get_request = request[/GET (\/cb.+?) HTTP/, 1]
|
62
62
|
if get_request
|
@@ -81,7 +81,7 @@ module Kontena
|
|
81
81
|
socket.close
|
82
82
|
server.close
|
83
83
|
uri = URI.parse("http://localhost#{get_request}")
|
84
|
-
ENV["DEBUG"] && puts(" * Parsing params: \"#{uri.query}\"")
|
84
|
+
ENV["DEBUG"] && STDERR.puts(" * Parsing params: \"#{uri.query}\"")
|
85
85
|
params = {}
|
86
86
|
URI.decode_www_form(uri.query).each do |key, value|
|
87
87
|
if value.to_s == ''
|
@@ -43,7 +43,7 @@ module Kontena::Cli::Master::Users
|
|
43
43
|
end
|
44
44
|
rescue
|
45
45
|
STDERR.puts "Failed to invite #{email}".colorize(:red)
|
46
|
-
ENV["DEBUG"] && puts("#{$!} - #{$!.message} -- #{$!.backtrace}")
|
46
|
+
ENV["DEBUG"] && STDERR.puts("#{$!} - #{$!.message} -- #{$!.backtrace}")
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
@@ -4,6 +4,7 @@ require_relative 'nodes/show_command'
|
|
4
4
|
require_relative 'nodes/update_command'
|
5
5
|
require_relative 'nodes/ssh_command'
|
6
6
|
require_relative 'nodes/label_command'
|
7
|
+
require_relative 'nodes/health_command'
|
7
8
|
|
8
9
|
class Kontena::Cli::NodeCommand < Kontena::Command
|
9
10
|
|
@@ -13,6 +14,7 @@ class Kontena::Cli::NodeCommand < Kontena::Command
|
|
13
14
|
subcommand "update", "Update node", Kontena::Cli::Nodes::UpdateCommand
|
14
15
|
subcommand ["remove","rm"], "Remove node", Kontena::Cli::Nodes::RemoveCommand
|
15
16
|
subcommand "label", "Node label specific commands", Kontena::Cli::Nodes::LabelCommand
|
17
|
+
subcommand "health", "Check node health", Kontena::Cli::Nodes::HealthCommand
|
16
18
|
|
17
19
|
def execute
|
18
20
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative '../helpers/health_helper'
|
2
|
+
|
3
|
+
module Kontena::Cli::Nodes
|
4
|
+
class HealthCommand < Kontena::Command
|
5
|
+
include Kontena::Cli::Common
|
6
|
+
include Kontena::Cli::GridOptions
|
7
|
+
include Kontena::Cli::Helpers::HealthHelper
|
8
|
+
|
9
|
+
parameter "NODE_ID", "Node id"
|
10
|
+
|
11
|
+
def execute
|
12
|
+
require_api_url
|
13
|
+
require_current_grid
|
14
|
+
token = require_token
|
15
|
+
|
16
|
+
node = client(token).get("nodes/#{current_grid}/#{node_id}")
|
17
|
+
|
18
|
+
return show_node_health(node)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Boolean] true if healthy
|
22
|
+
def show_node_health(node)
|
23
|
+
if node['connected']
|
24
|
+
puts "#{health_icon(:ok)} Node is online"
|
25
|
+
return true
|
26
|
+
else
|
27
|
+
puts "#{health_icon(:offline)} Node is offline"
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,46 +1,60 @@
|
|
1
|
+
require_relative '../helpers/health_helper'
|
2
|
+
|
1
3
|
module Kontena::Cli::Nodes
|
2
4
|
class ListCommand < Kontena::Command
|
3
5
|
include Kontena::Cli::Common
|
4
6
|
include Kontena::Cli::GridOptions
|
7
|
+
include Kontena::Cli::Helpers::HealthHelper
|
5
8
|
|
6
9
|
option ["--all"], :flag, "List nodes for all grids", default: false
|
7
10
|
|
11
|
+
def node_initial(node, grid)
|
12
|
+
if node['initial_member']
|
13
|
+
return "#{node['node_number']} / #{grid['initial_size']}"
|
14
|
+
else
|
15
|
+
return "-"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def node_labels(node)
|
20
|
+
(node['labels'] || ['-']).join(",")
|
21
|
+
end
|
22
|
+
|
23
|
+
def show_grid_nodes(grid, nodes, multi: false)
|
24
|
+
grid_health = grid_health(grid, nodes)
|
25
|
+
|
26
|
+
nodes = nodes.sort_by{|n| n['node_number'] }
|
27
|
+
nodes.each do |node|
|
28
|
+
puts [
|
29
|
+
"%s" % health_icon(node_health(node, grid_health)),
|
30
|
+
"%-70.70s" % [multi ? "#{grid['name']}/#{node['name']}" : node['name']],
|
31
|
+
"%-10s" % node['agent_version'],
|
32
|
+
"%-10s" % (node['connected'] ? "online" : "offline"),
|
33
|
+
"%-10s" % node_initial(node, grid),
|
34
|
+
"%s" % [node_labels(node)],
|
35
|
+
].join ' '
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
8
39
|
def execute
|
9
40
|
require_api_url
|
10
41
|
require_current_grid
|
11
42
|
token = require_token
|
12
43
|
|
44
|
+
puts "%s %-70s %-10s %-10s %-10s %-s" % [health_icon(nil), "Name", "Version", "Status", "Initial", "Labels"]
|
45
|
+
|
13
46
|
if all?
|
14
47
|
grids = client(token).get("grids")
|
15
|
-
puts "%-70s %-10s %-40s" % [ 'Name', 'Status', 'Labels']
|
16
|
-
|
17
48
|
grids['grids'].each do |grid|
|
18
|
-
nodes = client(
|
19
|
-
|
20
|
-
|
21
|
-
status = 'online'
|
22
|
-
else
|
23
|
-
status = 'offline'
|
24
|
-
end
|
25
|
-
puts "%-70.70s %-10s %-40s" % [
|
26
|
-
"#{grid['name']}/#{node['name']}",
|
27
|
-
status,
|
28
|
-
(node['labels'] || ['-']).join(",")
|
29
|
-
]
|
30
|
-
end
|
49
|
+
nodes = client(require_token).get("grids/#{grid['id']}/nodes")['nodes']
|
50
|
+
|
51
|
+
show_grid_nodes(grid, nodes, multi: true)
|
31
52
|
end
|
32
53
|
else
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
nodes
|
37
|
-
puts "%-70.70s %-10s %-10s %-40s" % [
|
38
|
-
node['name'],
|
39
|
-
node['connected'] ? 'online' : 'offline',
|
40
|
-
node['initial_member'] ? 'yes' : 'no',
|
41
|
-
(node['labels'] || ['-']).join(",")
|
42
|
-
]
|
43
|
-
end
|
54
|
+
grid = client(token).get("grids/#{current_grid}")
|
55
|
+
nodes = client(require_token).get("grids/#{current_grid}/nodes")['nodes']
|
56
|
+
|
57
|
+
show_grid_nodes(grid, nodes)
|
44
58
|
end
|
45
59
|
end
|
46
60
|
end
|