kontena-cli 1.0.6 → 1.1.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/VERSION +1 -1
  4. data/bin/kontena +4 -1
  5. data/kontena-cli.gemspec +1 -1
  6. data/lib/kontena/callback.rb +1 -1
  7. data/lib/kontena/callbacks/master/01_clear_current_master_after_terminate.rb +1 -1
  8. data/lib/kontena/callbacks/master/deploy/50_authenticate_after_deploy.rb +5 -5
  9. data/lib/kontena/callbacks/master/deploy/55_create_initial_grid_after_deploy.rb +1 -1
  10. data/lib/kontena/callbacks/master/deploy/56_set_server_provider_after_deploy.rb +25 -0
  11. data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +1 -1
  12. data/lib/kontena/cli/common.rb +3 -3
  13. data/lib/kontena/cli/config.rb +1 -1
  14. data/lib/kontena/cli/grid_command.rb +2 -0
  15. data/lib/kontena/cli/grids/common.rb +12 -0
  16. data/lib/kontena/cli/grids/health_command.rb +69 -0
  17. data/lib/kontena/cli/helpers/health_helper.rb +53 -0
  18. data/lib/kontena/cli/localhost_web_server.rb +3 -3
  19. data/lib/kontena/cli/master/users/invite_command.rb +1 -1
  20. data/lib/kontena/cli/node_command.rb +2 -0
  21. data/lib/kontena/cli/nodes/health_command.rb +32 -0
  22. data/lib/kontena/cli/nodes/list_command.rb +40 -26
  23. data/lib/kontena/cli/nodes/show_command.rb +0 -1
  24. data/lib/kontena/cli/plugins/install_command.rb +28 -30
  25. data/lib/kontena/cli/plugins/search_command.rb +6 -14
  26. data/lib/kontena/cli/plugins/uninstall_command.rb +7 -11
  27. data/lib/kontena/cli/services/stats_command.rb +4 -2
  28. data/lib/kontena/cli/spinner.rb +20 -4
  29. data/lib/kontena/cli/stacks/show_command.rb +5 -1
  30. data/lib/kontena/cli/stacks/yaml/opto/service_instances_resolver.rb +22 -0
  31. data/lib/kontena/cli/stacks/yaml/opto/vault_setter.rb +1 -1
  32. data/lib/kontena/cli/stacks/yaml/reader.rb +1 -0
  33. data/lib/kontena/cli/vault/export_command.rb +22 -0
  34. data/lib/kontena/cli/vault/import_command.rb +80 -0
  35. data/lib/kontena/cli/vault/list_command.rb +4 -0
  36. data/lib/kontena/cli/vault/read_command.rb +8 -3
  37. data/lib/kontena/cli/vault/remove_command.rb +2 -1
  38. data/lib/kontena/cli/vault/update_command.rb +5 -7
  39. data/lib/kontena/cli/vault_command.rb +5 -1
  40. data/lib/kontena/client.rb +25 -2
  41. data/lib/kontena/command.rb +1 -1
  42. data/lib/kontena/debug_instrumentor.rb +70 -0
  43. data/lib/kontena/light_prompt.rb +103 -0
  44. data/lib/kontena/plugin_manager.rb +167 -6
  45. data/lib/kontena/stacks_cache.rb +1 -1
  46. data/lib/kontena_cli.rb +23 -6
  47. data/spec/kontena/cli/grids/health_command_spec.rb +390 -0
  48. data/spec/kontena/cli/nodes/health_command_spec.rb +206 -0
  49. data/spec/kontena/cli/nodes/list_command_spec.rb +205 -0
  50. data/spec/kontena/cli/vault/export_spec.rb +32 -0
  51. data/spec/kontena/cli/vault/import_spec.rb +69 -0
  52. data/spec/kontena/client_spec.rb +39 -0
  53. data/spec/kontena/plugin_manager_spec.rb +7 -7
  54. data/spec/spec_helper.rb +1 -0
  55. data/spec/support/output_helpers.rb +51 -0
  56. metadata +27 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 617ed6269bb5d4ebdfa64661b2c5d57513cb05f3
4
- data.tar.gz: 6d9483d2d5d8b7994ebffcc19adcb3505a1c9b0f
3
+ metadata.gz: 3fa4fdfff0c1b90c0954ec7e31f8f916c8636f16
4
+ data.tar.gz: 62e78df6ba2b65b1bfcaacde2290d81719d93e82
5
5
  SHA512:
6
- metadata.gz: 78b0b49d108cc919d604b46f83389bb17bd075d8693940f247f853073d2f422c30afeb32c065f1df06637204dca010c185165e58da03fc733587ef9d8a3bcd13
7
- data.tar.gz: 5d11bfbb81dc4c39fbb42f94f3d1f7b21ed455ab5d05cebfde7a9bd917d1ce894e6d4684688ea5d7ebfecc01e1f0554aa43312167822d968f611b694b6c450c9
6
+ metadata.gz: e2fb585c59f687236071ec28640e487f32e4b4a1426ef9152249bcdd0651dfb96d0250b1ec91df171e9c0c1b650c257c9bb0e9f2039d8fda9b5a553ddda9c55f
7
+ data.tar.gz: 7408e60150cbd48e54549a490af795bb6688af0e01c9969f2f53a20fb8b59417228b3d1fc798e877377a39d28540c9ba8c614a99be110ae86bc74bf07c60716a
data/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM alpine:3.4
1
+ FROM alpine:3.5
2
2
  MAINTAINER Kontena, Inc. <info@kontena.io>
3
3
 
4
4
  ADD https://get.docker.com/builds/Linux/x86_64/docker-1.10.3 /usr/local/bin/docker
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.6
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.load_plugins
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.7.1"
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"
@@ -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
+
@@ -40,7 +40,7 @@ module Kontena
40
40
  end
41
41
 
42
42
  return nil unless invite_response
43
- ENV["DEBUG"] && puts("Got invite code: #{invite_response['invite_code']}")
43
+ ENV["DEBUG"] && STDERR.puts("Got invite code: #{invite_response['invite_code']}")
44
44
 
45
45
  role_status = nil
46
46
 
@@ -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
- ENV["DEBUG"] && puts("Trying to invalidate refresh token on #{server.name}")
132
+ logger.debug "Trying to invalidate refresh token on #{server.name}"
133
133
  client.refresh_token
134
134
  rescue
135
- ENV["DEBUG"] && puts("Refreshing failed: #{$!} : #{$!.message}")
135
+ logger.debug "Refreshing failed: #{$!} : #{$!.message}"
136
136
  end
137
137
 
138
138
  def require_current_master
@@ -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(token).get("grids/#{grid['name']}/nodes")
19
- nodes['nodes'].each do |node|
20
- if node['connected']
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
- nodes = client(token).get("grids/#{current_grid}/nodes")
34
- puts "%-70s %-10s %-10s %-40s" % ['Name', 'Status', 'Initial', 'Labels']
35
- nodes = nodes['nodes'].sort_by{|n| n['node_number'] }
36
- nodes.each do |node|
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