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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/bin/kontena +3 -2
- data/lib/kontena/cli/app_command.rb +6 -2
- data/lib/kontena/cli/apps/build_command.rb +0 -1
- data/lib/kontena/cli/apps/common.rb +1 -0
- data/lib/kontena/cli/apps/deploy_command.rb +17 -13
- data/lib/kontena/cli/apps/init_command.rb +1 -2
- data/lib/kontena/cli/apps/list_command.rb +13 -5
- data/lib/kontena/cli/apps/logs_command.rb +22 -13
- data/lib/kontena/cli/apps/monitor_command.rb +91 -0
- data/lib/kontena/cli/apps/remove_command.rb +0 -2
- data/lib/kontena/cli/apps/scale_command.rb +32 -0
- data/lib/kontena/cli/apps/show_command.rb +22 -0
- data/lib/kontena/cli/apps/start_command.rb +0 -1
- data/lib/kontena/cli/apps/stop_command.rb +0 -1
- data/lib/kontena/cli/common.rb +12 -0
- data/lib/kontena/cli/containers/inspect_command.rb +8 -3
- data/lib/kontena/cli/etcd/common.rb +8 -0
- data/lib/kontena/cli/etcd/get_command.rb +4 -0
- data/lib/kontena/cli/etcd/list_command.rb +4 -0
- data/lib/kontena/cli/etcd/mkdir_command.rb +5 -0
- data/lib/kontena/cli/etcd/remove_command.rb +5 -0
- data/lib/kontena/cli/etcd/set_command.rb +5 -0
- data/lib/kontena/cli/grid_command.rb +2 -0
- data/lib/kontena/cli/grids/env_command.rb +22 -0
- data/lib/kontena/cli/grids/logs_command.rb +12 -1
- data/lib/kontena/cli/node_command.rb +2 -0
- data/lib/kontena/cli/nodes/ssh_command.rb +30 -0
- data/lib/kontena/cli/service_command.rb +4 -0
- data/lib/kontena/cli/services/create_command.rb +9 -0
- data/lib/kontena/cli/services/deploy_command.rb +1 -7
- data/lib/kontena/cli/services/envs_command.rb +19 -0
- data/lib/kontena/cli/services/list_command.rb +15 -3
- data/lib/kontena/cli/services/monitor_command.rb +57 -0
- data/lib/kontena/cli/services/scale_command.rb +2 -6
- data/lib/kontena/cli/services/services_helper.rb +94 -43
- data/lib/kontena/cli/services/update_command.rb +12 -1
- data/lib/kontena/machine/aws/cloudinit_master.yml +2 -2
- data/lib/kontena/machine/azure/cloudinit_master.yml +2 -2
- data/lib/kontena/machine/digital_ocean/cloudinit_master.yml +2 -2
- data/lib/kontena/scripts/completer +4 -2
- data/spec/kontena/cli/app/deploy_command_spec.rb +24 -7
- data/spec/kontena/cli/app/scale_spec.rb +64 -0
- data/spec/kontena/cli/services/services_helper_spec.rb +0 -7
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70b337e81cf3345b89451a5c86ac5fdd5de1389d
|
4
|
+
data.tar.gz: a18d8b80661384607d290267e5729a473c816a07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9cc3f93437193fee63ae85aefbd44270044ebb70825a0eb1c7ca0c29c92f62589264b5f46ada6855f2b1116b508798a5b835964d02f481d9096ed2738674934
|
7
|
+
data.tar.gz: c151d8c9756635dcd2ed09e02365441ef78ef96b175c43b7cd8002bdcb16c38f5197a0e40c6fd7f33b5ca934ca84e7a44407e23b8400cba4b65419d95996efec
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
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 "
|
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
|
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
|
@@ -38,13 +38,10 @@ module Kontena::Cli::Apps
|
|
38
38
|
|
39
39
|
def deploy_services(queue)
|
40
40
|
queue.each do |service|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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_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
|
-
|
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
|
-
|
38
|
-
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
data/lib/kontena/cli/common.rb
CHANGED
@@ -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
|
-
|
12
|
-
|
13
|
-
|
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
|