kontena-cli 0.15.1 → 0.15.2
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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/kontena-cli.gemspec +6 -6
- data/lib/kontena/cli/apps/common.rb +2 -2
- data/lib/kontena/cli/apps/logs_command.rb +33 -42
- data/lib/kontena/cli/apps/yaml/reader.rb +16 -5
- data/lib/kontena/cli/apps/yaml/validations.rb +6 -3
- data/lib/kontena/cli/grids/logs_command.rb +17 -55
- data/lib/kontena/cli/helpers/log_helper.rb +68 -0
- data/lib/kontena/cli/services/logs_command.rb +12 -36
- data/lib/kontena/scripts/completer +3 -3
- data/spec/fixtures/kontena-internal-extend.yml +8 -0
- data/spec/fixtures/kontena_numeric_version.yml +9 -0
- data/spec/kontena/cli/app/common_spec.rb +12 -1
- data/spec/kontena/cli/app/logs_command_spec.rb +133 -0
- data/spec/kontena/cli/app/yaml/reader_spec.rb +47 -32
- data/spec/kontena/cli/app/yaml/validator_spec.rb +20 -0
- data/spec/kontena/cli/helpers/log_helper_spec.rb +58 -0
- metadata +32 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0799760c154ff891c9e67c2a893b47f47d9012c6
|
4
|
+
data.tar.gz: 075dcb4a30e995da47f86153b02d54b66e6576b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 943320e9d2f861ec68da9b0f4796bd268ec302bde7094ef440af6b578bc0f9b997ddf5dd913289cbe4fb40b78b049e0babdc52ec42de10cb3c64bec6543c449d
|
7
|
+
data.tar.gz: 74450cc8ab63055a6bd109f967d7c730e16c437236fb1c7b43825b73ade44dcad10fcbfdc9048eaa32d4c00e8f22585ea70a0035bea79898b973b1c1c92db018
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.15.
|
1
|
+
0.15.2
|
data/kontena-cli.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["info@kontena.io"]
|
11
11
|
spec.summary = %q{Kontena command line tool}
|
12
12
|
spec.description = %q{Kontena command line tool}
|
13
|
-
spec.homepage = "
|
13
|
+
spec.homepage = "https://www.kontena.io"
|
14
14
|
spec.license = "Apache-2.0"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -23,11 +23,11 @@ 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 "colorize"
|
27
|
-
spec.add_runtime_dependency "clamp"
|
28
|
-
spec.add_runtime_dependency "highline"
|
29
|
-
spec.add_runtime_dependency "shell-spinner"
|
30
|
-
spec.add_runtime_dependency "ruby_dig"
|
26
|
+
spec.add_runtime_dependency "colorize", "~> 0.8.1"
|
27
|
+
spec.add_runtime_dependency "clamp", "~> 1.0.0"
|
28
|
+
spec.add_runtime_dependency "highline", "~> 1.7.8"
|
29
|
+
spec.add_runtime_dependency "shell-spinner", "~> 1.0.4"
|
30
|
+
spec.add_runtime_dependency "ruby_dig", "~> 0.0.2"
|
31
31
|
spec.add_runtime_dependency "dry-validation", "~> 0.8.0"
|
32
32
|
spec.add_runtime_dependency "dry-configurable", "~> 0.1.6"
|
33
33
|
end
|
@@ -33,7 +33,7 @@ module Kontena::Cli::Apps
|
|
33
33
|
# @return [Hash]
|
34
34
|
def generate_services(yaml_services, version)
|
35
35
|
services = {}
|
36
|
-
if version ==
|
36
|
+
if version.to_i == 2
|
37
37
|
generator_klass = ServiceGeneratorV2
|
38
38
|
else
|
39
39
|
generator_klass = ServiceGenerator
|
@@ -63,7 +63,7 @@ module Kontena::Cli::Apps
|
|
63
63
|
def project_name_from_yaml(file)
|
64
64
|
reader = YAML::Reader.new(file, true)
|
65
65
|
outcome = reader.execute
|
66
|
-
if outcome[:version] ==
|
66
|
+
if outcome[:version].to_i == 2
|
67
67
|
outcome[:name]
|
68
68
|
else
|
69
69
|
nil
|
@@ -1,9 +1,12 @@
|
|
1
1
|
require_relative 'common'
|
2
|
+
require_relative '../helpers/log_helper'
|
2
3
|
|
3
4
|
module Kontena::Cli::Apps
|
4
5
|
class LogsCommand < Clamp::Command
|
5
6
|
include Kontena::Cli::Common
|
6
7
|
include Kontena::Cli::GridOptions
|
8
|
+
include Kontena::Cli::Helpers::LogHelper
|
9
|
+
|
7
10
|
include Common
|
8
11
|
|
9
12
|
option ['-f', '--file'], 'FILE', 'Specify an alternate Kontena compose file', attribute_name: :filename, default: 'kontena.yml'
|
@@ -13,60 +16,48 @@ module Kontena::Cli::Apps
|
|
13
16
|
option ["-t", "--tail"], :flag, "Tail (follow) logs", default: false
|
14
17
|
parameter "[SERVICE] ...", "Show only specified service logs"
|
15
18
|
|
16
|
-
attr_reader :services
|
17
|
-
|
18
19
|
def execute
|
19
20
|
require_config_file(filename)
|
20
21
|
|
21
|
-
|
22
|
-
if services.size > 0
|
23
|
-
show_logs(services)
|
24
|
-
elsif !service_list.empty?
|
25
|
-
puts "No such service: #{service_list.join(', ')}".colorize(:red)
|
26
|
-
end
|
22
|
+
services = services_from_yaml(filename, service_list, service_prefix)
|
27
23
|
|
28
|
-
|
24
|
+
if services.empty? && !service_list.empty?
|
25
|
+
signal_error "No such service: #{service_list.join(', ')}"
|
26
|
+
elsif services.empty?
|
27
|
+
signal_error "No services for application"
|
28
|
+
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
services
|
39
|
-
|
40
|
-
|
41
|
-
logs = logs + result['logs'] if result && result['logs']
|
42
|
-
end
|
43
|
-
logs.sort!{|x,y| DateTime.parse(x['created_at']) <=> DateTime.parse(y['created_at'])}
|
44
|
-
logs.each do |log|
|
45
|
-
color = color_for_container(log['name'])
|
46
|
-
prefix = "#{log['created_at']} #{log['name']}:".colorize(color)
|
47
|
-
puts "#{prefix} #{log['data']}"
|
48
|
-
last_id = log['id']
|
49
|
-
end
|
50
|
-
break unless tail?
|
51
|
-
sleep(2)
|
30
|
+
query_services = services.map{|service_name, opts| prefixed_name(service_name)}.join ','
|
31
|
+
query_params = {
|
32
|
+
services: query_services,
|
33
|
+
limit: lines,
|
34
|
+
}
|
35
|
+
query_params[:since] = since if since
|
36
|
+
|
37
|
+
if tail?
|
38
|
+
tail_logs(services, query_params)
|
39
|
+
else
|
40
|
+
show_logs(services, query_params)
|
52
41
|
end
|
53
42
|
end
|
54
43
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
44
|
+
def tail_logs(services, query_params)
|
45
|
+
stream_logs("grids/#{current_grid}/container_logs", query_params) do |log|
|
46
|
+
show_log(log)
|
47
|
+
end
|
58
48
|
end
|
59
49
|
|
60
|
-
def
|
61
|
-
|
50
|
+
def show_logs(services, query_params)
|
51
|
+
result = client(token).get("grids/#{current_grid}/container_logs", query_params)
|
52
|
+
result['logs'].each do |log|
|
53
|
+
show_log(log)
|
54
|
+
end
|
62
55
|
end
|
63
56
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
@colors
|
57
|
+
def show_log(log)
|
58
|
+
color = color_for_container(log['name'])
|
59
|
+
prefix = "#{log['created_at']} #{log['name']}:".colorize(color)
|
60
|
+
puts "#{prefix} #{log['data']}"
|
70
61
|
end
|
71
62
|
end
|
72
63
|
end
|
@@ -126,15 +126,26 @@ module Kontena::Cli::Apps
|
|
126
126
|
# @param [Hash] service_config
|
127
127
|
# @return [Hash] updated service config
|
128
128
|
def extend_config(service_config)
|
129
|
-
|
129
|
+
extended_service = extended_service(service_config['extends'])
|
130
|
+
return unless extended_service
|
130
131
|
filename = service_config['extends']['file']
|
131
132
|
if filename
|
132
|
-
|
133
|
+
parent_config = from_external_file(filename, extended_service)
|
133
134
|
else
|
134
|
-
abort("Service '#{
|
135
|
-
|
135
|
+
abort("Service '#{extended_service}' not found in #{file}".colorize(:red)) unless services.key?(extended_service)
|
136
|
+
parent_config = process_config(services[extended_service])
|
137
|
+
end
|
138
|
+
ServiceExtender.new(service_config).extend(parent_config)
|
139
|
+
end
|
140
|
+
|
141
|
+
def extended_service(extend_config)
|
142
|
+
if extend_config.is_a?(Hash)
|
143
|
+
extend_config['service']
|
144
|
+
elsif extend_config.is_a?(String)
|
145
|
+
extend_config
|
146
|
+
else
|
147
|
+
nil
|
136
148
|
end
|
137
|
-
ServiceExtender.new(service_config).extend(parent_service)
|
138
149
|
end
|
139
150
|
|
140
151
|
def from_external_file(filename, service_name)
|
@@ -4,9 +4,12 @@ module Kontena::Cli::Apps::YAML
|
|
4
4
|
def append_common_validations(base)
|
5
5
|
base.optional('image').maybe(:str?)
|
6
6
|
|
7
|
-
base.optional('extends')
|
8
|
-
|
9
|
-
|
7
|
+
base.optional('extends') { str? | type?(Hash) }
|
8
|
+
base.rule(when_extends_is_hash: ['extends']) do |extends|
|
9
|
+
extends.type?(Hash) > extends.schema do
|
10
|
+
required('service').filled(:str?)
|
11
|
+
optional('file').value(:str?)
|
12
|
+
end
|
10
13
|
end
|
11
14
|
|
12
15
|
base.optional('stateful') { bool? }
|
@@ -1,6 +1,9 @@
|
|
1
|
+
require_relative '../helpers/log_helper'
|
2
|
+
|
1
3
|
module Kontena::Cli::Grids
|
2
4
|
class LogsCommand < Clamp::Command
|
3
5
|
include Kontena::Cli::Common
|
6
|
+
include Kontena::Cli::Helpers::LogHelper
|
4
7
|
|
5
8
|
option ["-t", "--tail"], :flag, "Tail (follow) logs", default: false
|
6
9
|
option "--lines", "LINES", "Number of lines to show from the end of the logs"
|
@@ -9,9 +12,13 @@ module Kontena::Cli::Grids
|
|
9
12
|
option "--service", "SERVICE", "Filter by service name", multivalued: true
|
10
13
|
option ["-c", "--container"], "CONTAINER", "Filter by container", multivalued: true
|
11
14
|
|
15
|
+
# @return [String]
|
16
|
+
def token
|
17
|
+
@token ||= require_token
|
18
|
+
end
|
19
|
+
|
12
20
|
def execute
|
13
21
|
require_api_url
|
14
|
-
token = require_token
|
15
22
|
|
16
23
|
query_params = {}
|
17
24
|
query_params[:nodes] = node_list.join(",") unless node_list.empty?
|
@@ -21,15 +28,13 @@ module Kontena::Cli::Grids
|
|
21
28
|
query_params[:since] = since if since
|
22
29
|
|
23
30
|
if tail?
|
24
|
-
|
25
|
-
query_params[:follow] = 1
|
26
|
-
stream_logs(token, query_params)
|
31
|
+
tail_logs(query_params)
|
27
32
|
else
|
28
|
-
list_logs(
|
33
|
+
list_logs(query_params)
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
|
-
def list_logs(
|
37
|
+
def list_logs(query_params)
|
33
38
|
result = client(token).get("grids/#{current_grid}/container_logs", query_params)
|
34
39
|
result['logs'].each do |log|
|
35
40
|
color = color_for_container(log['name'])
|
@@ -41,56 +46,13 @@ module Kontena::Cli::Grids
|
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
unless chunk.empty?
|
51
|
-
log = JSON.parse(chunk)
|
52
|
-
end
|
53
|
-
@buffer = ''
|
54
|
-
rescue => exc
|
55
|
-
@buffer << chunk
|
56
|
-
end
|
57
|
-
if log
|
58
|
-
@last_seen = log['id']
|
59
|
-
color = color_for_container(log['name'])
|
60
|
-
puts "#{log['name'].colorize(color)} | #{log['data']}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
begin
|
65
|
-
if @last_seen
|
66
|
-
query_params[:from] = @last_seen
|
67
|
-
end
|
68
|
-
result = client(token).get_stream(
|
69
|
-
"grids/#{current_grid}/container_logs", streamer, query_params
|
70
|
-
)
|
71
|
-
rescue => exc
|
72
|
-
if exc.cause.is_a?(EOFError) # Excon wraps the EOFerror into SockerError
|
73
|
-
retry
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
def color_for_container(container_id)
|
80
|
-
color_maps[container_id] = colors.shift unless color_maps[container_id]
|
81
|
-
color_maps[container_id].to_sym
|
82
|
-
end
|
83
|
-
|
84
|
-
def color_maps
|
85
|
-
@color_maps ||= {}
|
86
|
-
end
|
87
|
-
|
88
|
-
def colors
|
89
|
-
if(@colors.nil? || @colors.size == 0)
|
90
|
-
@colors = [:green, :yellow, :magenta, :cyan, :red,
|
91
|
-
:light_green, :light_yellow, :ligh_magenta, :light_cyan, :light_red]
|
49
|
+
# @param [String] token
|
50
|
+
# @param [Hash] query_params
|
51
|
+
def tail_logs(query_params)
|
52
|
+
stream_logs("grids/#{current_grid}/container_logs", query_params) do |log|
|
53
|
+
color = color_for_container(log['name'])
|
54
|
+
puts "#{log['name'].colorize(color)} | #{log['data']}"
|
92
55
|
end
|
93
|
-
@colors
|
94
56
|
end
|
95
57
|
end
|
96
58
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Kontena::Cli::Helpers
|
2
|
+
module LogHelper
|
3
|
+
|
4
|
+
# @param [String] url
|
5
|
+
# @param [Hash] query_params
|
6
|
+
def stream_logs(url, query_params)
|
7
|
+
last_seen = nil
|
8
|
+
streamer = lambda do |chunk, remaining_bytes, total_bytes|
|
9
|
+
log = buffered_log_json(chunk)
|
10
|
+
if log
|
11
|
+
yield log
|
12
|
+
last_seen = log['id']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
query_params[:follow] = 1
|
18
|
+
query_params[:from] = last_seen if last_seen
|
19
|
+
result = client(token).get_stream(url, streamer, query_params)
|
20
|
+
rescue => exc
|
21
|
+
retry if exc.cause.is_a?(EOFError) # Excon wraps the EOFerror into SocketError
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [String] chunk
|
27
|
+
# @return [Hash,NilClass]
|
28
|
+
def buffered_log_json(chunk)
|
29
|
+
@buffer = '' if @buffer.nil?
|
30
|
+
return if @buffer.empty? && chunk.strip.empty?
|
31
|
+
begin
|
32
|
+
orig_chunk = chunk
|
33
|
+
unless @buffer.empty?
|
34
|
+
chunk = @buffer + chunk
|
35
|
+
end
|
36
|
+
unless chunk.empty?
|
37
|
+
log = JSON.parse(chunk)
|
38
|
+
end
|
39
|
+
@buffer = ''
|
40
|
+
log
|
41
|
+
rescue => exc
|
42
|
+
@buffer << orig_chunk
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param [String] container_id
|
48
|
+
# @return [Symbol]
|
49
|
+
def color_for_container(container_id)
|
50
|
+
color_maps[container_id] = colors.shift unless color_maps[container_id]
|
51
|
+
color_maps[container_id].to_sym
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Hash]
|
55
|
+
def color_maps
|
56
|
+
@color_maps ||= {}
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Array<Symbol>]
|
60
|
+
def colors
|
61
|
+
if(@colors.nil? || @colors.size == 0)
|
62
|
+
@colors = [:green, :yellow, :magenta, :cyan, :red,
|
63
|
+
:light_green, :light_yellow, :ligh_magenta, :light_cyan, :light_red]
|
64
|
+
end
|
65
|
+
@colors
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
require_relative 'services_helper'
|
2
|
+
require_relative '../helpers/log_helper'
|
2
3
|
|
3
4
|
module Kontena::Cli::Services
|
4
5
|
class LogsCommand < Clamp::Command
|
5
6
|
include Kontena::Cli::Common
|
6
7
|
include Kontena::Cli::GridOptions
|
8
|
+
include Kontena::Cli::Helpers::LogHelper
|
7
9
|
include ServicesHelper
|
8
10
|
|
9
11
|
parameter "NAME", "Service name"
|
@@ -15,7 +17,7 @@ module Kontena::Cli::Services
|
|
15
17
|
def execute
|
16
18
|
require_api_url
|
17
19
|
token = require_token
|
18
|
-
|
20
|
+
|
19
21
|
|
20
22
|
query_params = {}
|
21
23
|
query_params[:limit] = lines if lines
|
@@ -47,56 +49,30 @@ module Kontena::Cli::Services
|
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
52
|
+
# @param [String] token
|
53
|
+
# @param [Hash] query_params
|
50
54
|
def stream_logs(token, query_params)
|
55
|
+
last_seen = nil
|
51
56
|
streamer = lambda do |chunk, remaining_bytes, total_bytes|
|
52
|
-
|
53
|
-
unless @buffer.empty?
|
54
|
-
chunk = @buffer + chunk
|
55
|
-
end
|
56
|
-
unless chunk.empty?
|
57
|
-
log = JSON.parse(chunk)
|
58
|
-
end
|
59
|
-
@buffer = ''
|
60
|
-
rescue => exc
|
61
|
-
@buffer << chunk
|
62
|
-
end
|
57
|
+
log = buffered_log_json(chunk)
|
63
58
|
if log
|
64
|
-
|
59
|
+
last_seen = log['id']
|
65
60
|
render_log_line(log)
|
66
61
|
end
|
67
62
|
end
|
68
63
|
|
69
64
|
begin
|
70
65
|
query_params[:follow] = true
|
71
|
-
if
|
72
|
-
query_params[:from] =
|
66
|
+
if last_seen
|
67
|
+
query_params[:from] = last_seen
|
73
68
|
end
|
74
69
|
result = client(token).get_stream(
|
75
70
|
"services/#{current_grid}/#{name}/container_logs", streamer, query_params
|
76
71
|
)
|
77
72
|
rescue => exc
|
78
|
-
if exc.cause.is_a?(EOFError) # Excon wraps the EOFerror into
|
79
|
-
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
end
|
84
|
-
|
85
|
-
def color_for_container(container_id)
|
86
|
-
color_maps[container_id] = colors.shift unless color_maps[container_id]
|
87
|
-
color_maps[container_id].to_sym
|
88
|
-
end
|
89
|
-
|
90
|
-
def color_maps
|
91
|
-
@color_maps ||= {}
|
92
|
-
end
|
93
|
-
|
94
|
-
def colors
|
95
|
-
if(@colors.nil? || @colors.size == 0)
|
96
|
-
@colors = [:green, :yellow, :magenta, :cyan, :red,
|
97
|
-
:light_green, :light_yellow, :ligh_magenta, :light_cyan, :light_red]
|
73
|
+
retry if exc.cause.is_a?(EOFError) # Excon wraps the EOFerror into SocketError
|
74
|
+
raise
|
98
75
|
end
|
99
|
-
@colors
|
100
76
|
end
|
101
77
|
end
|
102
78
|
end
|
@@ -55,7 +55,7 @@ class Helper
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def yml_services
|
58
|
-
if File.exist?('kontena.yml')
|
58
|
+
if File.exist?('kontena.yml')
|
59
59
|
yaml = YAML.load(File.read('kontena.yml'))
|
60
60
|
if yaml['version'] == '2'
|
61
61
|
services = yaml['services']
|
@@ -100,7 +100,7 @@ if words.size > 0
|
|
100
100
|
end
|
101
101
|
when 'node'
|
102
102
|
completion.clear
|
103
|
-
sub_commands = %w(list show remove
|
103
|
+
sub_commands = %w(list show remove)
|
104
104
|
if words[1]
|
105
105
|
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
106
106
|
completion.push helper.nodes
|
@@ -109,7 +109,7 @@ if words.size > 0
|
|
109
109
|
end
|
110
110
|
when 'master'
|
111
111
|
completion.clear
|
112
|
-
sub_commands = %w(list use
|
112
|
+
sub_commands = %w(list use)
|
113
113
|
if words[1] && words[1] == 'use'
|
114
114
|
completion.push helper.master_names
|
115
115
|
elsif words[1] && words[1] == 'users'
|
@@ -13,6 +13,10 @@ describe Kontena::Cli::Apps::Common do
|
|
13
13
|
fixture('kontena.yml')
|
14
14
|
end
|
15
15
|
|
16
|
+
let(:kontena_numeric_version_yml) do
|
17
|
+
fixture('kontena_numeric_version.yml')
|
18
|
+
end
|
19
|
+
|
16
20
|
let(:kontena_v2_yml) do
|
17
21
|
fixture('kontena_v2.yml')
|
18
22
|
end
|
@@ -61,7 +65,7 @@ describe Kontena::Cli::Apps::Common do
|
|
61
65
|
end
|
62
66
|
end
|
63
67
|
|
64
|
-
describe '#
|
68
|
+
describe '#services_from_yaml' do
|
65
69
|
before(:each) do
|
66
70
|
allow(File).to receive(:read).with("#{Dir.getwd}/kontena.yml").and_return(kontena_yml)
|
67
71
|
allow(File).to receive(:read).with("#{Dir.getwd}/health.yml").and_return(health_yml)
|
@@ -96,5 +100,12 @@ describe Kontena::Cli::Apps::Common do
|
|
96
100
|
expect(services['web']).not_to be_nil
|
97
101
|
expect(services['web']['health_check']).not_to be_nil
|
98
102
|
end
|
103
|
+
|
104
|
+
it 'allows version to be numeric' do
|
105
|
+
allow(File).to receive(:read).with("#{Dir.getwd}/kontena-numeric-version.yml").and_return(kontena_numeric_version_yml)
|
106
|
+
services = subject.services_from_yaml('kontena-numeric-version.yml', [], '')
|
107
|
+
expect(services.dig('bar', 'build', 'context')).to eq(Dir.pwd)
|
108
|
+
expect(services.dig('bar', 'build', 'dockerfile')).to eq('Dockerfile')
|
109
|
+
end
|
99
110
|
end
|
100
111
|
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require_relative "../../../spec_helper"
|
2
|
+
require 'kontena/cli/grid_options'
|
3
|
+
require "kontena/cli/apps/logs_command"
|
4
|
+
|
5
|
+
describe Kontena::Cli::Apps::LogsCommand do
|
6
|
+
include FixturesHelpers
|
7
|
+
|
8
|
+
let(:subject) do
|
9
|
+
described_class.new(File.basename($0))
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:client) do
|
13
|
+
double("client")
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:token) do
|
17
|
+
"testtoken"
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:current_grid) do
|
21
|
+
'test-grid'
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:service_prefix) do
|
25
|
+
'test'
|
26
|
+
end
|
27
|
+
|
28
|
+
before (:each) do
|
29
|
+
allow(subject).to receive(:token) { token }
|
30
|
+
allow(subject).to receive(:client) { client }
|
31
|
+
allow(subject).to receive(:service_prefix) { service_prefix }
|
32
|
+
allow(subject).to receive(:current_grid) { current_grid }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with multiple services' do
|
36
|
+
let(:kontena_yml) do
|
37
|
+
fixture('kontena.yml')
|
38
|
+
end
|
39
|
+
|
40
|
+
let(:docker_compose_yml) do
|
41
|
+
fixture('docker-compose.yml')
|
42
|
+
end
|
43
|
+
|
44
|
+
# globally ordered logs across multiple services
|
45
|
+
let (:logs) do
|
46
|
+
[
|
47
|
+
{
|
48
|
+
'id' => '57cff2e8cfee65c8b6efc8bd',
|
49
|
+
'name' => 'test-mysql-1',
|
50
|
+
'created_at' => '2016-09-07T15:19:04.362690',
|
51
|
+
'data' => "mysql log message 1",
|
52
|
+
},
|
53
|
+
{
|
54
|
+
'id' => '57cff2e8cfee65c8b6efc8be',
|
55
|
+
'name' => 'test-mysql-1',
|
56
|
+
'created_at' => '2016-09-07T15:19:04.500000',
|
57
|
+
'data' => "mysql log message 2",
|
58
|
+
},
|
59
|
+
{
|
60
|
+
'id' => '57cff2e8cfee65c8b6efc8bf',
|
61
|
+
'name' => 'test-wordpress-1',
|
62
|
+
'created_at' => '2016-09-07T15:19:05.362690',
|
63
|
+
'data' => "wordpress log message 1-1",
|
64
|
+
},
|
65
|
+
{
|
66
|
+
'id' => '57cff2e8cfee65c8b6efc8c1',
|
67
|
+
'name' => 'test-mysql-1',
|
68
|
+
'created_at' => '2016-09-07T15:19:06.100000',
|
69
|
+
'data' => "mysql log message 3",
|
70
|
+
},
|
71
|
+
{
|
72
|
+
'id' => '57cff2e8cfee65c8b6efc8c2',
|
73
|
+
'name' => 'test-wordpress-1',
|
74
|
+
'created_at' => '2016-09-07T15:19:07.100000',
|
75
|
+
'data' => "wordpress log message 1-2",
|
76
|
+
},
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
80
|
+
before (:each) do
|
81
|
+
# mock kontena.yml services
|
82
|
+
expect(subject).to receive(:require_config_file).with("kontena.yml")
|
83
|
+
allow(File).to receive(:read).with("#{Dir.getwd}/kontena.yml").and_return(kontena_yml)
|
84
|
+
allow(File).to receive(:read).with("#{Dir.getwd}/docker-compose.yml").and_return(docker_compose_yml)
|
85
|
+
|
86
|
+
# collect show_log() output
|
87
|
+
@logs = []
|
88
|
+
|
89
|
+
allow(subject).to receive(:show_log) do |log|
|
90
|
+
@logs << log
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "shows all service logs" do
|
95
|
+
expect(client).to receive(:get).with('grids/test-grid/container_logs', {
|
96
|
+
services: 'test-wordpress,test-mysql',
|
97
|
+
limit: '100',
|
98
|
+
}) { { 'logs' => logs } }
|
99
|
+
|
100
|
+
subject.run([])
|
101
|
+
|
102
|
+
expect(@logs).to eq logs
|
103
|
+
end
|
104
|
+
|
105
|
+
it "shows logs for one service" do
|
106
|
+
mysql_logs = logs.select{|log| log['name'] =~ /test-mysql-/ }
|
107
|
+
|
108
|
+
expect(client).to receive(:get).with('grids/test-grid/container_logs', {
|
109
|
+
services: 'test-mysql',
|
110
|
+
limit: '100',
|
111
|
+
}) { { 'logs' => mysql_logs } }
|
112
|
+
|
113
|
+
subject.run(["mysql"])
|
114
|
+
|
115
|
+
expect(@logs).to eq mysql_logs
|
116
|
+
end
|
117
|
+
|
118
|
+
it "shows logs since time" do
|
119
|
+
since = '2016-09-07T15:19:05.362690'
|
120
|
+
since_logs = logs.select{|log| log['created_at'] > since }
|
121
|
+
|
122
|
+
expect(client).to receive(:get).with('grids/test-grid/container_logs', {
|
123
|
+
services: 'test-wordpress,test-mysql',
|
124
|
+
limit: '100',
|
125
|
+
since: since,
|
126
|
+
}) { { 'logs' => since_logs } }
|
127
|
+
|
128
|
+
subject.run(["--since=#{since}"])
|
129
|
+
|
130
|
+
expect(@logs).to eq since_logs
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -194,38 +194,53 @@ describe Kontena::Cli::Apps::YAML::Reader do
|
|
194
194
|
.and_return(fixture('docker-compose.yml'))
|
195
195
|
end
|
196
196
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
'
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
'
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
.
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
.
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
197
|
+
context 'when extending services' do
|
198
|
+
it 'extends services from external file' do
|
199
|
+
docker_compose_yml = YAML.load(fixture('docker-compose.yml') % { project: 'test' })
|
200
|
+
wordpress_options = {
|
201
|
+
'extends' => {
|
202
|
+
'file' => 'docker-compose.yml',
|
203
|
+
'service' => 'wordpress'
|
204
|
+
},
|
205
|
+
'stateful' => true,
|
206
|
+
'environment' => ['WORDPRESS_DB_PASSWORD=test_secret'],
|
207
|
+
'instances' => 2,
|
208
|
+
'deploy' => { 'strategy' => 'ha' }
|
209
|
+
}
|
210
|
+
mysql_options = {
|
211
|
+
'extends' => {
|
212
|
+
'file' => 'docker-compose.yml',
|
213
|
+
'service' => 'mysql'
|
214
|
+
},
|
215
|
+
'stateful' => true,
|
216
|
+
'environment' => ['MYSQL_ROOT_PASSWORD=test_secret']
|
217
|
+
}
|
218
|
+
expect(Kontena::Cli::Apps::YAML::ServiceExtender).to receive(:new)
|
219
|
+
.with(wordpress_options)
|
220
|
+
.once
|
221
|
+
.and_return(service_extender)
|
222
|
+
expect(Kontena::Cli::Apps::YAML::ServiceExtender).to receive(:new)
|
223
|
+
.with(mysql_options)
|
224
|
+
.once
|
225
|
+
.and_return(service_extender)
|
226
|
+
expect(service_extender).to receive(:extend).with(docker_compose_yml['wordpress'])
|
227
|
+
expect(service_extender).to receive(:extend).with(docker_compose_yml['mysql'])
|
228
|
+
|
229
|
+
subject.execute
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'extends services from the same file' do
|
233
|
+
allow(File).to receive(:read)
|
234
|
+
.with(absolute_yaml_path('kontena.yml'))
|
235
|
+
.and_return(fixture('kontena-internal-extend.yml'))
|
236
|
+
kontena_yml = YAML.load(fixture('kontena-internal-extend.yml') % { project: 'test' })
|
237
|
+
expect(Kontena::Cli::Apps::YAML::ServiceExtender).to receive(:new)
|
238
|
+
.with(kontena_yml['app'])
|
239
|
+
.once
|
240
|
+
.and_return(service_extender)
|
241
|
+
expect(service_extender).to receive(:extend).with(kontena_yml['base'])
|
242
|
+
subject.execute
|
243
|
+
end
|
229
244
|
end
|
230
245
|
|
231
246
|
context 'environment variables' do
|
@@ -206,6 +206,26 @@ describe Kontena::Cli::Apps::YAML::Validator do
|
|
206
206
|
end
|
207
207
|
end
|
208
208
|
|
209
|
+
context 'validates extends' do
|
210
|
+
it 'accepts string value' do
|
211
|
+
result = subject.validate_options('extends' => 'web')
|
212
|
+
expect(result.messages.key?('extends')).to be_falsey
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'when value is hash' do
|
216
|
+
it 'must contain service' do
|
217
|
+
result = subject.validate_options('extends' => { 'file' => 'docker_compose.yml'})
|
218
|
+
expect(result.messages.key?('extends')).to be_truthy
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context 'when value is not string or hash' do
|
223
|
+
it 'returns error' do
|
224
|
+
result = subject.validate_options('extends' => ['array is invalid'])
|
225
|
+
expect(result.messages.key?('extends')).to be_truthy
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
209
229
|
context 'validates hooks' do
|
210
230
|
context 'validates pre_build' do
|
211
231
|
it 'must be array' do
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative "../../../spec_helper"
|
2
|
+
require "kontena/cli/helpers/log_helper"
|
3
|
+
|
4
|
+
describe Kontena::Cli::Helpers::LogHelper do
|
5
|
+
|
6
|
+
let(:described_class) do
|
7
|
+
Class.new do
|
8
|
+
include Kontena::Cli::Helpers::LogHelper
|
9
|
+
|
10
|
+
def buffer
|
11
|
+
@buffer
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#buffered_log_json' do
|
17
|
+
it 'returns has on valid json' do
|
18
|
+
chunk = {"foo" => "bar"}
|
19
|
+
log = subject.buffered_log_json(chunk.to_json)
|
20
|
+
expect(log).to eq(chunk)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'combines multi part json chunks to valid json' do
|
24
|
+
chunk1 = '{"foo": "'
|
25
|
+
chunk2 = 'bar'
|
26
|
+
chunk3 = '"}'
|
27
|
+
log = subject.buffered_log_json(chunk1)
|
28
|
+
expect(log).to be_nil
|
29
|
+
log = subject.buffered_log_json(chunk2)
|
30
|
+
expect(log).to be_nil
|
31
|
+
log = subject.buffered_log_json(chunk3)
|
32
|
+
expect(log).to eq({"foo" => "bar"})
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'handles big log messages' do
|
36
|
+
chunk1 = '{"foo": "' << "lol" * 10000
|
37
|
+
chunk2 = 'lol'
|
38
|
+
chunk3 = 'lol"}'
|
39
|
+
log = subject.buffered_log_json(chunk1)
|
40
|
+
expect(log).to be_nil
|
41
|
+
log = subject.buffered_log_json(chunk2)
|
42
|
+
expect(log).to be_nil
|
43
|
+
log = subject.buffered_log_json(chunk3)
|
44
|
+
expect(log).to eq(JSON.parse(chunk1 + chunk2 + chunk3))
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not append to buffer if buffer is empty and chunk has just whitespace' do
|
48
|
+
log = subject.buffered_log_json(' ')
|
49
|
+
expect(log).to be_nil
|
50
|
+
expect(subject.buffer).to eq('')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns nil on invalid json' do
|
54
|
+
log = subject.buffered_log_json('{"foo": "')
|
55
|
+
expect(log).to be_nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kontena-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.15.
|
4
|
+
version: 0.15.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kontena, Inc
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-09-
|
11
|
+
date: 2016-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -56,72 +56,72 @@ dependencies:
|
|
56
56
|
name: colorize
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 0.8.1
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 0.8.1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: clamp
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 1.0.0
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 1.0.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: highline
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 1.7.8
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 1.7.8
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: shell-spinner
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 1.0.4
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 1.0.4
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: ruby_dig
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
117
|
+
version: 0.0.2
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
124
|
+
version: 0.0.2
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: dry-validation
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -248,6 +248,7 @@ files:
|
|
248
248
|
- lib/kontena/cli/grids/users/add_command.rb
|
249
249
|
- lib/kontena/cli/grids/users/list_command.rb
|
250
250
|
- lib/kontena/cli/grids/users/remove_command.rb
|
251
|
+
- lib/kontena/cli/helpers/log_helper.rb
|
251
252
|
- lib/kontena/cli/login_command.rb
|
252
253
|
- lib/kontena/cli/logout_command.rb
|
253
254
|
- lib/kontena/cli/master/current_command.rb
|
@@ -352,11 +353,13 @@ files:
|
|
352
353
|
- spec/fixtures/docker-compose_v2.yml
|
353
354
|
- spec/fixtures/health.yml
|
354
355
|
- spec/fixtures/kontena-build.yml
|
356
|
+
- spec/fixtures/kontena-internal-extend.yml
|
355
357
|
- spec/fixtures/kontena-invalid.yml
|
356
358
|
- spec/fixtures/kontena-with-env-file.yml
|
357
359
|
- spec/fixtures/kontena-with-variables.yml
|
358
360
|
- spec/fixtures/kontena.yml
|
359
361
|
- spec/fixtures/kontena_build_v2.yml
|
362
|
+
- spec/fixtures/kontena_numeric_version.yml
|
360
363
|
- spec/fixtures/kontena_v2.yml
|
361
364
|
- spec/fixtures/mysql.yml
|
362
365
|
- spec/fixtures/wordpress-scaled.yml
|
@@ -367,6 +370,7 @@ files:
|
|
367
370
|
- spec/kontena/cli/app/deploy_command_spec.rb
|
368
371
|
- spec/kontena/cli/app/docker_helper_spec.rb
|
369
372
|
- spec/kontena/cli/app/init_command_spec.rb
|
373
|
+
- spec/kontena/cli/app/logs_command_spec.rb
|
370
374
|
- spec/kontena/cli/app/scale_spec.rb
|
371
375
|
- spec/kontena/cli/app/service_generator_spec.rb
|
372
376
|
- spec/kontena/cli/app/service_generator_v2_spec.rb
|
@@ -379,6 +383,7 @@ files:
|
|
379
383
|
- spec/kontena/cli/grids/trusted_subnets/add_command_spec.rb
|
380
384
|
- spec/kontena/cli/grids/trusted_subnets/list_command_spec.rb
|
381
385
|
- spec/kontena/cli/grids/trusted_subnets/remove_command_spec.rb
|
386
|
+
- spec/kontena/cli/helpers/log_helper_spec.rb
|
382
387
|
- spec/kontena/cli/login_command_spec.rb
|
383
388
|
- spec/kontena/cli/master/current_command_spec.rb
|
384
389
|
- spec/kontena/cli/master/use_command_spec.rb
|
@@ -403,7 +408,7 @@ files:
|
|
403
408
|
- spec/support/client_helpers.rb
|
404
409
|
- spec/support/fixtures_helpers.rb
|
405
410
|
- tasks/rspec.rake
|
406
|
-
homepage:
|
411
|
+
homepage: https://www.kontena.io
|
407
412
|
licenses:
|
408
413
|
- Apache-2.0
|
409
414
|
metadata: {}
|
@@ -433,11 +438,13 @@ test_files:
|
|
433
438
|
- spec/fixtures/docker-compose_v2.yml
|
434
439
|
- spec/fixtures/health.yml
|
435
440
|
- spec/fixtures/kontena-build.yml
|
441
|
+
- spec/fixtures/kontena-internal-extend.yml
|
436
442
|
- spec/fixtures/kontena-invalid.yml
|
437
443
|
- spec/fixtures/kontena-with-env-file.yml
|
438
444
|
- spec/fixtures/kontena-with-variables.yml
|
439
445
|
- spec/fixtures/kontena.yml
|
440
446
|
- spec/fixtures/kontena_build_v2.yml
|
447
|
+
- spec/fixtures/kontena_numeric_version.yml
|
441
448
|
- spec/fixtures/kontena_v2.yml
|
442
449
|
- spec/fixtures/mysql.yml
|
443
450
|
- spec/fixtures/wordpress-scaled.yml
|
@@ -448,6 +455,7 @@ test_files:
|
|
448
455
|
- spec/kontena/cli/app/deploy_command_spec.rb
|
449
456
|
- spec/kontena/cli/app/docker_helper_spec.rb
|
450
457
|
- spec/kontena/cli/app/init_command_spec.rb
|
458
|
+
- spec/kontena/cli/app/logs_command_spec.rb
|
451
459
|
- spec/kontena/cli/app/scale_spec.rb
|
452
460
|
- spec/kontena/cli/app/service_generator_spec.rb
|
453
461
|
- spec/kontena/cli/app/service_generator_v2_spec.rb
|
@@ -460,6 +468,7 @@ test_files:
|
|
460
468
|
- spec/kontena/cli/grids/trusted_subnets/add_command_spec.rb
|
461
469
|
- spec/kontena/cli/grids/trusted_subnets/list_command_spec.rb
|
462
470
|
- spec/kontena/cli/grids/trusted_subnets/remove_command_spec.rb
|
471
|
+
- spec/kontena/cli/helpers/log_helper_spec.rb
|
463
472
|
- spec/kontena/cli/login_command_spec.rb
|
464
473
|
- spec/kontena/cli/master/current_command_spec.rb
|
465
474
|
- spec/kontena/cli/master/use_command_spec.rb
|