kontena-cli 0.9.3 → 0.10.0

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 (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