kontena-cli 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/kontena +3 -2
  4. data/lib/kontena/cli/app_command.rb +6 -2
  5. data/lib/kontena/cli/apps/build_command.rb +0 -1
  6. data/lib/kontena/cli/apps/common.rb +1 -0
  7. data/lib/kontena/cli/apps/deploy_command.rb +17 -13
  8. data/lib/kontena/cli/apps/init_command.rb +1 -2
  9. data/lib/kontena/cli/apps/list_command.rb +13 -5
  10. data/lib/kontena/cli/apps/logs_command.rb +22 -13
  11. data/lib/kontena/cli/apps/monitor_command.rb +91 -0
  12. data/lib/kontena/cli/apps/remove_command.rb +0 -2
  13. data/lib/kontena/cli/apps/scale_command.rb +32 -0
  14. data/lib/kontena/cli/apps/show_command.rb +22 -0
  15. data/lib/kontena/cli/apps/start_command.rb +0 -1
  16. data/lib/kontena/cli/apps/stop_command.rb +0 -1
  17. data/lib/kontena/cli/common.rb +12 -0
  18. data/lib/kontena/cli/containers/inspect_command.rb +8 -3
  19. data/lib/kontena/cli/etcd/common.rb +8 -0
  20. data/lib/kontena/cli/etcd/get_command.rb +4 -0
  21. data/lib/kontena/cli/etcd/list_command.rb +4 -0
  22. data/lib/kontena/cli/etcd/mkdir_command.rb +5 -0
  23. data/lib/kontena/cli/etcd/remove_command.rb +5 -0
  24. data/lib/kontena/cli/etcd/set_command.rb +5 -0
  25. data/lib/kontena/cli/grid_command.rb +2 -0
  26. data/lib/kontena/cli/grids/env_command.rb +22 -0
  27. data/lib/kontena/cli/grids/logs_command.rb +12 -1
  28. data/lib/kontena/cli/node_command.rb +2 -0
  29. data/lib/kontena/cli/nodes/ssh_command.rb +30 -0
  30. data/lib/kontena/cli/service_command.rb +4 -0
  31. data/lib/kontena/cli/services/create_command.rb +9 -0
  32. data/lib/kontena/cli/services/deploy_command.rb +1 -7
  33. data/lib/kontena/cli/services/envs_command.rb +19 -0
  34. data/lib/kontena/cli/services/list_command.rb +15 -3
  35. data/lib/kontena/cli/services/monitor_command.rb +57 -0
  36. data/lib/kontena/cli/services/scale_command.rb +2 -6
  37. data/lib/kontena/cli/services/services_helper.rb +94 -43
  38. data/lib/kontena/cli/services/update_command.rb +12 -1
  39. data/lib/kontena/machine/aws/cloudinit_master.yml +2 -2
  40. data/lib/kontena/machine/azure/cloudinit_master.yml +2 -2
  41. data/lib/kontena/machine/digital_ocean/cloudinit_master.yml +2 -2
  42. data/lib/kontena/scripts/completer +4 -2
  43. data/spec/kontena/cli/app/deploy_command_spec.rb +24 -7
  44. data/spec/kontena/cli/app/scale_spec.rb +64 -0
  45. data/spec/kontena/cli/services/services_helper_spec.rb +0 -7
  46. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e969537b351069b7a4fcb5c64cfa116c87300f5d
4
- data.tar.gz: 13e84eb262c9f12338d9b2f0f1ed46882ba30a39
3
+ metadata.gz: 70b337e81cf3345b89451a5c86ac5fdd5de1389d
4
+ data.tar.gz: a18d8b80661384607d290267e5729a473c816a07
5
5
  SHA512:
6
- metadata.gz: 42ed8afb2c22070a405c20e6696a40ed43bc88a90c407c824901a68528c821979b4e9d9232ce5863dcf912425d5927074f73cff3e398cb5d47b969789812a979
7
- data.tar.gz: 9cf027136624b48923300814179b41187abafa9c6b4c68eaf8ff758aa2dde5640ae7e08c33353f46fc2e203242f70489d121952c31ceb13a9af679a5ada16235
6
+ metadata.gz: b9cc3f93437193fee63ae85aefbd44270044ebb70825a0eb1c7ca0c29c92f62589264b5f46ada6855f2b1116b508798a5b835964d02f481d9096ed2738674934
7
+ data.tar.gz: c151d8c9756635dcd2ed09e02365441ef78ef96b175c43b7cd8002bdcb16c38f5197a0e40c6fd7f33b5ca934ca84e7a44407e23b8400cba4b65419d95996efec
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.3
1
+ 0.10.0
data/bin/kontena CHANGED
@@ -66,8 +66,9 @@ begin
66
66
  MainCommand.run
67
67
  rescue Excon::Errors::SocketError => exc
68
68
  if exc.message.include?('Unable to verify certificate')
69
- puts "The server uses a certificate signed by an unknown authority.".colorize(:red)
70
- puts "Protip: you can bypass the certificate check by setting #{'SSL_IGNORE_ERRORS=true'.colorize(:yellow)} env variable, but any data you send to the server could be intercepted by others."
69
+ $stderr.puts "The server uses a certificate signed by an unknown authority.".colorize(:red)
70
+ $stderr.puts "You can trust this server by copying server CA pem file to: #{"~/.kontena/certs/<hostname>.pem".colorize(:light_black)}"
71
+ $stderr.puts "Protip: you can bypass the certificate check by setting #{'SSL_IGNORE_ERRORS=true'.colorize(:yellow)} env variable, but any data you send to the server could be intercepted by others."
71
72
  exit(1)
72
73
  else
73
74
  abort(exc.message)
@@ -6,6 +6,8 @@ require_relative 'apps/stop_command'
6
6
  require_relative 'apps/remove_command'
7
7
  require_relative 'apps/list_command'
8
8
  require_relative 'apps/logs_command'
9
+ require_relative 'apps/monitor_command'
10
+ require_relative 'apps/show_command'
9
11
 
10
12
  class Kontena::Cli::AppCommand < Clamp::Command
11
13
 
@@ -14,9 +16,11 @@ class Kontena::Cli::AppCommand < Clamp::Command
14
16
  subcommand "deploy", "Deploy Kontena services", Kontena::Cli::Apps::DeployCommand
15
17
  subcommand "start", "Start services", Kontena::Cli::Apps::StartCommand
16
18
  subcommand "stop", "Stop services", Kontena::Cli::Apps::StopCommand
17
- subcommand ["remove","rm"], "Remove services", Kontena::Cli::Apps::RemoveCommand
19
+ subcommand "show", "Show service details", Kontena::Cli::Apps::ShowCommand
18
20
  subcommand ["ps", "list"], "List services", Kontena::Cli::Apps::ListCommand
19
21
  subcommand ["logs"], "Show service logs", Kontena::Cli::Apps::LogsCommand
22
+ subcommand "monitor", "Monitor services", Kontena::Cli::Apps::MonitorCommand
23
+ subcommand ["remove","rm"], "Remove services", Kontena::Cli::Apps::RemoveCommand
20
24
  def execute
21
25
  end
22
- end
26
+ end
@@ -1,4 +1,3 @@
1
- require 'yaml'
2
1
  require_relative 'common'
3
2
  require_relative 'docker_helper'
4
3
 
@@ -1,3 +1,4 @@
1
+ require 'yaml'
1
2
  require_relative '../services/services_helper'
2
3
 
3
4
  module Kontena::Cli::Apps
@@ -38,13 +38,10 @@ module Kontena::Cli::Apps
38
38
 
39
39
  def deploy_services(queue)
40
40
  queue.each do |service|
41
- puts "deploying #{service['id'].colorize(:cyan)}"
42
- data = {}
43
- if service['deploy']
44
- data[:strategy] = service['deploy']['strategy'] if service['deploy']['strategy']
45
- data[:wait_for_port] = service['deploy']['wait_for_port'] if service['deploy']['wait_for_port']
46
- end
47
- deploy_service(token, service['id'].split('/').last, data)
41
+ name = service['id'].split('/').last
42
+ short_name = name.sub("#{service_prefix}-", "")
43
+ puts "deploying #{short_name.colorize(:cyan)}"
44
+ deploy_service(token, name, {})
48
45
  end
49
46
  end
50
47
 
@@ -57,7 +54,6 @@ module Kontena::Cli::Apps
57
54
  parse_links(options['links']).each_with_index do |linked_service, index|
58
55
  # change prefixed service name also to links options
59
56
  options['links'][index] = "#{prefixed_name(linked_service[:name])}:#{linked_service[:alias]}"
60
-
61
57
  create_or_update_service(linked_service[:name], services[linked_service[:name]]) unless in_deploy_queue?(linked_service[:name])
62
58
  end
63
59
  end
@@ -71,9 +67,6 @@ module Kontena::Cli::Apps
71
67
  service = create(name, options)
72
68
  end
73
69
 
74
- # add deploy options to service
75
- service['deploy'] = options['deploy']
76
-
77
70
  deploy_queue.push service
78
71
  end
79
72
 
@@ -82,17 +75,17 @@ module Kontena::Cli::Apps
82
75
  end
83
76
 
84
77
  def create(name, options)
85
- name = prefixed_name(name)
86
78
  puts "creating #{name.colorize(:cyan)}"
79
+ name = prefixed_name(name)
87
80
  data = {name: name}
88
81
  data.merge!(parse_data(options))
89
82
  create_service(token, current_grid, data)
90
83
  end
91
84
 
92
85
  def update(id, options)
86
+ puts "updating #{id.colorize(:cyan)}"
93
87
  id = prefixed_name(id)
94
88
  data = parse_data(options)
95
- puts "updating #{id.colorize(:cyan)}"
96
89
  update_service(token, id, data)
97
90
  end
98
91
 
@@ -150,6 +143,17 @@ module Kontena::Cli::Apps
150
143
  data[:log_driver] = options['log_driver'] if options['log_driver']
151
144
  data[:log_opts] = options['log_opt'] if options['log_opt'] && !options['log_opt'].empty?
152
145
 
146
+ deploy_opts = options['deploy'] || {}
147
+ data[:strategy] = deploy_opts['strategy'] if deploy_opts['strategy']
148
+ deploy = {}
149
+ deploy[:wait_for_port] = deploy_opts['wait_for_port'] if deploy_opts.has_key?('wait_for_port')
150
+ deploy[:min_health] = deploy_opts['min_health'] if deploy_opts.has_key?('min_health')
151
+ unless deploy.empty?
152
+ data[:deploy_opts] = deploy
153
+ end
154
+
155
+ data[:hooks] = options['hooks'] || {}
156
+
153
157
  data
154
158
  end
155
159
 
@@ -1,4 +1,3 @@
1
- require 'yaml'
2
1
  require 'securerandom'
3
2
  require_relative 'common'
4
3
  require_relative 'dockerfile_generator'
@@ -93,4 +92,4 @@ module Kontena::Cli::Apps
93
92
  end
94
93
 
95
94
  end
96
- end
95
+ end
@@ -1,4 +1,3 @@
1
- require 'yaml'
2
1
  require_relative 'common'
3
2
 
4
3
  module Kontena::Cli::Apps
@@ -27,16 +26,25 @@ module Kontena::Cli::Apps
27
26
  end
28
27
 
29
28
  def show_services(services)
30
- puts "%-30.30s %-50.50s %-15s %-10.10s %-15.20s %-50s" % ['NAME', 'IMAGE', 'INSTANCES', 'STATEFUL', 'STATE', 'PORTS']
29
+ titles = ['NAME', 'IMAGE', 'INSTANCES', 'STATEFUL', 'STATE', 'PORTS']
30
+ puts "%-30.30s %-50.50s %-15s %-10.10s %-15.20s %-50s" % titles
31
31
 
32
32
  services.each do |service_name, opts|
33
33
  service = get_service(token, prefixed_name(service_name)) rescue false
34
34
  if service
35
+ name = service['name'].sub("#{@service_prefix}-", '')
35
36
  state = service['stateful'] ? 'yes' : 'no'
36
-
37
- ports = service['ports'].map{|p| "#{p['ip']}:#{p['node_port']}->#{p['container_port']}/#{p['protocol']}"}.join(", ")
38
- puts "%-30.30s %-50.50s %-15.10s %-10.10s %-15.20s %-50s" % [service['name'], service['image'], service['container_count'], state, service['state'], ports]
37
+ ports = service['ports'].map{|p|
38
+ "#{p['ip']}:#{p['node_port']}->#{p['container_port']}/#{p['protocol']}"
39
+ }.join(", ")
40
+ running = service['instances']['running']
41
+ desired = service['container_count']
42
+ instances = "#{running} / #{desired}"
43
+ vars = [name, service['image'], instances, state, service['state'], ports]
44
+ else
45
+ vars = [service_name, '-', '-', '-', '-', '-']
39
46
  end
47
+ puts "%-30.30s %-50.50s %-15.10s %-10.10s %-15.20s %-50s" % vars
40
48
  end
41
49
  end
42
50
  end
@@ -1,4 +1,3 @@
1
- require 'yaml'
2
1
  require_relative 'common'
3
2
 
4
3
  module Kontena::Cli::Apps
@@ -8,8 +7,9 @@ module Kontena::Cli::Apps
8
7
 
9
8
  option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
10
9
  option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
11
-
12
- parameter "[SERVICE] ...", "Services to start"
10
+ option ["-s", "--search"], "SEARCH", "Search from logs"
11
+ option ["-t", "--follow"], :flag, "Follow (tail) logs", default: false
12
+ parameter "[SERVICE] ...", "Show only specified service logs"
13
13
 
14
14
  attr_reader :services, :service_prefix
15
15
 
@@ -27,16 +27,25 @@ module Kontena::Cli::Apps
27
27
  end
28
28
 
29
29
  def show_logs(services)
30
- logs = []
31
- services.each do |service_name, opts|
32
- service = get_service(token, prefixed_name(service_name)) rescue false
33
- result = client(token).get("services/#{service['id']}/container_logs")
34
- logs = logs + result['logs']
35
- end
36
- logs.sort!{|x,y| DateTime.parse(x['created_at']) <=> DateTime.parse(y['created_at'])}
37
- logs.each do |log|
38
- color = color_for_container(log['name'])
39
- puts "#{log['name'].colorize(color)} | #{log['data']}"
30
+ last_id = nil
31
+ loop do
32
+ query_params = []
33
+ query_params << "from=#{last_id}" unless last_id.nil?
34
+ query_params << "search=#{search}" if search
35
+ logs = []
36
+ services.each do |service_name, opts|
37
+ service = get_service(token, prefixed_name(service_name)) rescue false
38
+ result = client(token).get("services/#{service['id']}/container_logs?#{query_params.join('&')}") if service
39
+ logs = logs + result['logs']
40
+ end
41
+ logs.sort!{|x,y| DateTime.parse(x['created_at']) <=> DateTime.parse(y['created_at'])}
42
+ logs.each do |log|
43
+ color = color_for_container(log['name'])
44
+ puts "#{log['name'].colorize(color)} | #{log['data']}"
45
+ last_id = log['id']
46
+ end
47
+ break unless follow?
48
+ sleep(2)
40
49
  end
41
50
  end
42
51
 
@@ -0,0 +1,91 @@
1
+ require_relative 'common'
2
+
3
+ module Kontena::Cli::Apps
4
+ class MonitorCommand < Clamp::Command
5
+ include Kontena::Cli::Common
6
+ include Common
7
+
8
+ option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
9
+ option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
10
+
11
+ parameter "[SERVICE] ...", "Services to start"
12
+
13
+ attr_reader :services, :service_prefix
14
+
15
+ def execute
16
+ require_config_file(filename)
17
+
18
+ @service_prefix = project_name || current_dir
19
+ @services = load_services(filename, service_list, service_prefix)
20
+ if services.size > 0
21
+ show_monitor(services)
22
+ elsif !service_list.empty?
23
+ puts "No such service: #{service_list.join(', ')}".colorize(:red)
24
+ end
25
+ end
26
+
27
+ def show_monitor(services)
28
+ require_api_url
29
+ token = require_token
30
+ loop do
31
+ nodes = {}
32
+ services.each do |name, data|
33
+ service = prefixed_name(name)
34
+ result = client(token).get("services/#{current_grid}/#{service}/containers") rescue nil
35
+ if result
36
+ services[name]['instances'] = result['containers'].size
37
+ result['containers'].each do |container|
38
+ container['service'] = name
39
+ nodes[container['node']['name']] ||= []
40
+ nodes[container['node']['name']] << container
41
+ end
42
+ end
43
+ end
44
+ clear_terminal
45
+ puts "services:"
46
+ services.each do |name, data|
47
+ color = color_for_service(name)
48
+ puts " #{"■".colorize(color)} #{name} (#{data['instances']} instances)"
49
+ end
50
+ puts "nodes:"
51
+ node_names = nodes.keys.sort
52
+ node_names.each do |name|
53
+ containers = nodes[name]
54
+ puts " #{name} (#{containers.size} instances)"
55
+ print " "
56
+ containers.each do |container|
57
+ icon = "■"
58
+ if container['status'] != 'running'
59
+ icon = "□"
60
+ end
61
+ color = color_for_service(container['service'])
62
+ print icon.colorize(color)
63
+ end
64
+ puts ''
65
+ end
66
+ sleep 1
67
+ end
68
+ end
69
+
70
+ def color_for_service(service)
71
+ color_maps[service] = colors.shift unless color_maps[service]
72
+ color_maps[service].to_sym
73
+ end
74
+
75
+ def color_maps
76
+ @color_maps ||= {}
77
+ end
78
+
79
+ def colors
80
+ if(@colors.nil? || @colors.size == 0)
81
+ @colors = [:green, :magenta, :yellow, :cyan, :red,
82
+ :light_green, :light_yellow, :ligh_magenta, :light_cyan, :light_red]
83
+ end
84
+ @colors
85
+ end
86
+
87
+ def clear_terminal
88
+ print "\e[H\e[2J"
89
+ end
90
+ end
91
+ end
@@ -1,4 +1,3 @@
1
- require 'yaml'
2
1
  require_relative 'common'
3
2
 
4
3
  module Kontena::Cli::Apps
@@ -19,7 +18,6 @@ module Kontena::Cli::Apps
19
18
  require_config_file(filename)
20
19
 
21
20
  @service_prefix = project_name || current_dir
22
- #Dir.chdir(File.dirname(filename))
23
21
  @services = load_services(filename, service_list, service_prefix)
24
22
  if services.size > 0
25
23
  remove_services(services)
@@ -0,0 +1,32 @@
1
+ require_relative 'common'
2
+
3
+ module Kontena::Cli::Apps
4
+ class ScaleCommand < Clamp::Command
5
+ include Kontena::Cli::Common
6
+ include Common
7
+
8
+ option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
9
+ option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
10
+
11
+ parameter "SERVICE", "Service to show"
12
+ parameter "INSTANCES", "Scales service to given number of instances"
13
+
14
+ attr_reader :services, :service_prefix
15
+
16
+ def execute
17
+ require_config_file(filename)
18
+ @service_prefix = project_name || current_dir
19
+ yml_service = load_services(filename, [service], service_prefix)
20
+ if yml_service[service]
21
+ options = yml_service[service]
22
+ abort("Service has already instances defined in #{filename}. Please update #{filename} and deploy service instead") if options['instances']
23
+ @service_prefix = project_name || current_dir
24
+ scale_service(require_token, prefixed_name(service), instances)
25
+ else
26
+ abort("Service not found")
27
+ end
28
+ end
29
+
30
+
31
+ end
32
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'common'
2
+
3
+ module Kontena::Cli::Apps
4
+ class ShowCommand < Clamp::Command
5
+ include Kontena::Cli::Common
6
+ include Common
7
+
8
+ option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
9
+ option ['-p', '--project-name'], 'NAME', 'Specify an alternate project name (default: directory name)'
10
+
11
+ parameter "SERVICE", "Service to show"
12
+
13
+ attr_reader :services, :service_prefix
14
+
15
+ def execute
16
+ require_config_file(filename)
17
+
18
+ @service_prefix = project_name || current_dir
19
+ show_service(require_token, prefixed_name(service))
20
+ end
21
+ end
22
+ end
@@ -1,4 +1,3 @@
1
- require 'yaml'
2
1
  require_relative 'common'
3
2
 
4
3
  module Kontena::Cli::Apps
@@ -1,4 +1,3 @@
1
- require 'yaml'
2
1
  require_relative 'common'
3
2
 
4
3
  module Kontena::Cli::Apps
@@ -1,4 +1,5 @@
1
1
  require 'colorize'
2
+ require 'uri'
2
3
 
3
4
  module Kontena
4
5
  module Cli
@@ -52,9 +53,20 @@ module Kontena
52
53
  unless url
53
54
  raise ArgumentError.new("It seem's that you are not logged into Kontena master, please login with: kontena login")
54
55
  end
56
+ ensure_custom_ssl_ca(url)
55
57
  url
56
58
  end
57
59
 
60
+ def ensure_custom_ssl_ca(url)
61
+ return if Excon.defaults[:ssl_ca_file]
62
+
63
+ uri = URI::parse(url)
64
+ cert_file = File.join(Dir.home, "/.kontena/certs/#{uri.host}.pem")
65
+ if File.exist?(cert_file)
66
+ Excon.defaults[:ssl_ca_file] = cert_file
67
+ end
68
+ end
69
+
58
70
  def current_grid=(grid)
59
71
  settings['server']['grid'] = grid['id']
60
72
  save_settings
@@ -8,9 +8,14 @@ module Kontena::Cli::Containers
8
8
  require_api_url
9
9
  token = require_token
10
10
 
11
- service_name = container_id.match(/(.+)-(\d+)/)[1]
12
- result = client(token).get("containers/#{current_grid}/#{service_name}/#{container_id}/inspect")
13
- puts JSON.pretty_generate(result)
11
+ match = container_id.match(/(.+)-(\d+)/)
12
+ if match
13
+ service_name = match[1]
14
+ result = client(token).get("containers/#{current_grid}/#{service_name}/#{container_id}/inspect")
15
+ puts JSON.pretty_generate(result)
16
+ else
17
+ abort("Cannot resolve container service")
18
+ end
14
19
  end
15
20
  end
16
21
  end