kontena-cli 1.2.0.pre3 → 1.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/kontena-cli.gemspec +3 -3
  4. data/lib/kontena/cli/common.rb +13 -0
  5. data/lib/kontena/cli/grid_command.rb +1 -0
  6. data/lib/kontena/cli/grids/events_command.rb +50 -0
  7. data/lib/kontena/cli/helpers/log_helper.rb +2 -2
  8. data/lib/kontena/cli/master/config/import_command.rb +2 -2
  9. data/lib/kontena/cli/master/logout_command.rb +19 -17
  10. data/lib/kontena/cli/service_command.rb +1 -0
  11. data/lib/kontena/cli/services/events_command.rb +36 -0
  12. data/lib/kontena/cli/services/remove_command.rb +28 -4
  13. data/lib/kontena/cli/services/services_helper.rb +39 -4
  14. data/lib/kontena/cli/services/show_command.rb +8 -1
  15. data/lib/kontena/cli/stack_command.rb +1 -0
  16. data/lib/kontena/cli/stacks/events_command.rb +33 -0
  17. data/lib/kontena/cli/stacks/logs_command.rb +2 -0
  18. data/lib/kontena/cli/stacks/yaml/opto/prompt_resolver.rb +1 -1
  19. data/lib/kontena/cli/vault/import_command.rb +2 -2
  20. data/lib/kontena/cli/vault/update_command.rb +9 -12
  21. data/lib/kontena/cli/vault/write_command.rb +8 -15
  22. data/lib/kontena/cli/volume_command.rb +3 -0
  23. data/lib/kontena/cli/volumes/show_command.rb +38 -0
  24. data/lib/kontena/main_command.rb +1 -1
  25. data/lib/kontena_cli.rb +5 -1
  26. data/spec/kontena/cli/grids/events_command_spec.rb +15 -0
  27. data/spec/kontena/cli/services/events_command_spec.rb +22 -0
  28. data/spec/kontena/cli/stacks/events_command_spec.rb +22 -0
  29. data/spec/kontena/cli/stacks/logs_command_spec.rb +32 -0
  30. data/spec/kontena/cli/vault/update_command_spec.rb +79 -0
  31. data/spec/kontena/cli/vault/write_command_spec.rb +60 -0
  32. data/spec/kontena/kontena_cli_spec.rb +6 -3
  33. metadata +26 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fbe93b31f3b0185a1f79d87967898a48dd6c3a8b
4
- data.tar.gz: d94145ce8dbb0289a75785293802b25f8a29e48f
3
+ metadata.gz: d1303357caabb58711e52c64fef54b12a9afc989
4
+ data.tar.gz: 8ce9069dcf068450f990cb23fddac6792a7956c6
5
5
  SHA512:
6
- metadata.gz: b6f6a68a4ef272bdc3f8ef941e155d7536eb3c429fd726dc00d98fe8ee6cd15a39f1f600ad218f7d1041a33db88d01a23f4d6d692ef0af1f2ad9b25de3a54725
7
- data.tar.gz: 1e0552a145da0f8cebebe1ef1305052385f25077b7d903d58a4bc4f4fda7622e323e712af13b2bcce79319fc0950d55e59c01ae4cd63f97b8c5876d3ff2b0c2c
6
+ metadata.gz: 7df9880f68b96488d865427015884e99af34a56c9b4b0c52df1c4614e15e5e3686f386fa672ea1310c42801cb6e8b6d82a2387dca9439800f3b2e980e06ae01a
7
+ data.tar.gz: d1447a0ac64a834660c7b4857e50e533757247af83664d875644c812075b6622bdd53397e18c2fb1bb12cc4924319e17c9b9c788d46b6dbb6de1288d034e1399
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0.pre3
1
+ 1.2.0.rc1
data/kontena-cli.gemspec CHANGED
@@ -23,13 +23,13 @@ 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.11.0"
26
+ spec.add_runtime_dependency "tty-prompt", "0.12.0"
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"
30
- spec.add_runtime_dependency "hash_validator", "~> 0.7.0"
30
+ spec.add_runtime_dependency "hash_validator", "~> 0.7.1"
31
31
  spec.add_runtime_dependency "retriable", "~> 2.1.0"
32
- spec.add_runtime_dependency "opto", "1.8.4"
32
+ spec.add_runtime_dependency "opto", "1.8.5"
33
33
  spec.add_runtime_dependency "semantic", "~> 1.5"
34
34
  spec.add_runtime_dependency "safe_yaml", "~> 1.0"
35
35
  spec.add_runtime_dependency "liquid", "~> 4.0.0"
@@ -22,6 +22,19 @@ module Kontena
22
22
  @pastel ||= Pastel.new(enabled: $stdout.tty?)
23
23
  end
24
24
 
25
+ # Read from STDIN. If stdin is a console, use prompt to ask.
26
+ # @param [String] message
27
+ # @param [Symbol] mode (prompt method: :ask, :multiline, etc)
28
+ def stdin_input(message = nil, mode = :ask)
29
+ if $stdin.tty?
30
+ Array(prompt.send(mode, message)).join.chomp
31
+ elsif !$stdin.eof?
32
+ $stdin.read.chomp
33
+ else
34
+ exit_with_error 'Missing input'
35
+ end
36
+ end
37
+
25
38
  def running_silent?
26
39
  self.respond_to?(:silent?) && self.silent?
27
40
  end
@@ -6,6 +6,7 @@ class Kontena::Cli::GridCommand < Kontena::Command
6
6
  subcommand "use", "Switch to use specific grid", load_subcommand('grids/use_command')
7
7
  subcommand "show", "Show grid details", load_subcommand('grids/show_command')
8
8
  subcommand "logs", "Show logs from grid containers", load_subcommand('grids/logs_command')
9
+ subcommand "events", "Show events from grid", load_subcommand('grids/events_command')
9
10
  subcommand ["remove","rm"], "Remove a grid", load_subcommand('grids/remove_command')
10
11
  subcommand "current", "Show current grid details", load_subcommand('grids/current_command')
11
12
  subcommand "env", "Show the current grid environment details", load_subcommand('grids/env_command')
@@ -0,0 +1,50 @@
1
+ require_relative '../helpers/log_helper'
2
+
3
+ module Kontena::Cli::Grids
4
+ class EventsCommand < Kontena::Command
5
+ include Kontena::Cli::Common
6
+ include Kontena::Cli::Helpers::LogHelper
7
+
8
+ SKIP_TYPES = ['grid']
9
+
10
+ option "--node", "NODE", "Filter by node name", multivalued: true
11
+ option "--service", "SERVICE", "Filter by service name", multivalued: true
12
+
13
+ def execute
14
+ require_api_url
15
+
16
+ query_params = {}
17
+ query_params[:nodes] = node_list.join(",") unless node_list.empty?
18
+ query_params[:services] = service_list.join(",") unless service_list.empty?
19
+
20
+ titles = ['TIME', 'TYPE', 'RELATIONSHIPS', 'MESSAGE']
21
+ puts "%-25s %-25s %-40s %s" % titles
22
+ show_logs("grids/#{current_grid}/event_logs", query_params) do |log|
23
+ show_log(log)
24
+ end
25
+ end
26
+
27
+ def show_log(log)
28
+ msg = log['message']
29
+ rels = log['relationships'].
30
+ delete_if { |r| SKIP_TYPES.include?(r['type']) }.
31
+ map { |r|
32
+ id = r['id'].split('/')[1..-1].delete_if{ |s| s == 'null'}.join('/')
33
+ unless id.empty?
34
+ "#{r['type']}=#{id}"
35
+ end
36
+ }.compact
37
+
38
+ time = log['created_at']
39
+ if log['severity'] == 2
40
+ time = time.colorize(:yellow)
41
+ elsif log['severity'] >= 3
42
+ time = time.colorize(:red)
43
+ end
44
+
45
+ puts '%-25s %-25s %-40s %s' % [
46
+ time, log['type'], rels.join(','), msg
47
+ ]
48
+ end
49
+ end
50
+ end
@@ -52,7 +52,7 @@ module Kontena::Cli::Helpers
52
52
 
53
53
  begin
54
54
  query_params[:from] = last_seen if last_seen
55
- result = client(token).get_stream(url, streamer, query_params)
55
+ client(token).get_stream(url, streamer, query_params)
56
56
  rescue => exc
57
57
  retry if exc.cause.is_a?(EOFError) # Excon wraps the EOFerror into SocketError
58
58
  raise
@@ -74,7 +74,7 @@ module Kontena::Cli::Helpers
74
74
  end
75
75
  @buffer = ''
76
76
  log
77
- rescue => exc
77
+ rescue
78
78
  @buffer << orig_chunk
79
79
  nil
80
80
  end
@@ -8,7 +8,7 @@ module Kontena::Cli::Master::Config
8
8
 
9
9
  banner "Updates configuration from a file into Master"
10
10
 
11
- parameter '[PATH]', "Input from file in PATH, default: STDIN", required: false
11
+ parameter '[PATH]', "Input from file in PATH (default: STDIN)", required: false
12
12
 
13
13
  option ['--preset'], '[NAME]', 'Load preset', hidden: true
14
14
 
@@ -30,7 +30,7 @@ module Kontena::Cli::Master::Config
30
30
  path = File.join(Kontena.root, 'lib/kontena/presets', "#{self.preset}.yml")
31
31
  File.read(path)
32
32
  else
33
- STDIN.read
33
+ stdin_input("Enter master configuration as #{format.upcase}", :multiline)
34
34
  end
35
35
  end
36
36
 
@@ -1,23 +1,25 @@
1
- class Kontena::Cli::Master::LogoutCommand < Kontena::Command
2
- include Kontena::Cli::Common
1
+ module Kontena::Cli::Master
2
+ class LogoutCommand < Kontena::Command
3
+ include Kontena::Cli::Common
3
4
 
4
- option ['-A', '--all'], :flag, 'Log out from all masters. By default only log out from current master.'
5
+ option ['-A', '--all'], :flag, 'Log out from all masters. By default only log out from current master.'
5
6
 
6
- def execute
7
- if self.all?
8
- config.servers.each do |server|
9
- use_refresh_token(server)
10
- server.token = nil
11
- puts "Logged out of #{server.name.colorize(:green)}"
7
+ def execute
8
+ if self.all?
9
+ config.servers.each do |server|
10
+ use_refresh_token(server)
11
+ server.token = nil
12
+ puts "Logged out of #{server.name.colorize(:green)}"
13
+ end
14
+ elsif config.current_master
15
+ use_refresh_token(config.current_master)
16
+ config.current_master.token = nil
17
+ puts "Logged out of #{config.current_master.name.colorize(:green)}"
18
+ else
19
+ warn "Current master has not been selected"
20
+ exit 0 # exiting with 0 not 1, it's not really an error situation (kontena logout && kontena master login...)
12
21
  end
13
- elsif config.current_master
14
- use_refresh_token(config.current_master)
15
- config.current_master.token = nil
16
- puts "Logged out of #{config.current_master.name.colorize(:green)}"
17
- else
18
- warn "Current master has not been selected"
19
- exit 0 # exiting with 0 not 1, it's not really an error situation (kontena logout && kontena master login...)
22
+ config.write
20
23
  end
21
- config.write
22
24
  end
23
25
  end
@@ -11,6 +11,7 @@ class Kontena::Cli::ServiceCommand < Kontena::Command
11
11
  subcommand ["remove", "rm"], "Remove service", load_subcommand('services/remove_command')
12
12
  subcommand "containers", "List service containers", load_subcommand('services/containers_command')
13
13
  subcommand "logs", "Show service logs", load_subcommand('services/logs_command')
14
+ subcommand "events", "Show service events", load_subcommand('services/events_command')
14
15
  subcommand "stats", "Show service statistics", load_subcommand('services/stats_command')
15
16
  subcommand "monitor", "Monitor", load_subcommand('services/monitor_command')
16
17
 
@@ -0,0 +1,36 @@
1
+ require_relative 'services_helper'
2
+ require_relative '../helpers/log_helper'
3
+
4
+ module Kontena::Cli::Services
5
+ class EventsCommand < Kontena::Command
6
+ include Kontena::Cli::Common
7
+ include Kontena::Cli::GridOptions
8
+ include Kontena::Cli::Helpers::LogHelper
9
+ include ServicesHelper
10
+
11
+ parameter "NAME", "Service name"
12
+
13
+ def execute
14
+ require_api_url
15
+
16
+ query_params = {}
17
+
18
+ titles = ['TIME', 'TYPE', 'MESSAGE']
19
+ puts "%-25s %-20s %s" % titles
20
+ show_logs("services/#{parse_service_id(name)}/event_logs", query_params) do |log|
21
+ show_log(log)
22
+ end
23
+ end
24
+
25
+ def show_log(log)
26
+ msg = log['message']
27
+ node = log['relationships'].find { |r| r['type'] == 'node' }
28
+ if node
29
+ msg = "#{msg} (#{node['id'].split('/')[-1]})"
30
+ end
31
+ puts '%-25s %-20s %s' % [
32
+ log['created_at'], log['type'].sub('service:'.freeze, ''.freeze), msg
33
+ ]
34
+ end
35
+ end
36
+ end
@@ -6,20 +6,32 @@ module Kontena::Cli::Services
6
6
  include ServicesHelper
7
7
 
8
8
  parameter "NAME", "Service name"
9
+ option "--instance", "INSTANCE", "Remove only given instance"
9
10
  option "--force", :flag, "Force remove", default: false, attribute_name: :forced
10
11
 
12
+ banner "Remove a service"
13
+
14
+ requires_current_master
15
+ requires_current_master_token
16
+
11
17
  def execute
12
- require_api_url
13
- token = require_token
18
+ if instance
19
+ remove_instance
20
+ else
21
+ remove
22
+ end
23
+ end
24
+
25
+ def remove
14
26
  confirm_command(name) unless forced?
15
27
 
16
28
  spinner "Removing service #{name.colorize(:cyan)} " do
17
- client(token).delete("services/#{parse_service_id(name)}")
29
+ client.delete("services/#{parse_service_id(name)}")
18
30
  removed = false
19
31
  until removed == true
20
32
  sleep 1
21
33
  begin
22
- client(token).get("services/#{parse_service_id(name)}")
34
+ client.get("services/#{parse_service_id(name)}")
23
35
  rescue Kontena::Errors::StandardError => exc
24
36
  if exc.status == 404
25
37
  removed = true
@@ -30,5 +42,17 @@ module Kontena::Cli::Services
30
42
  end
31
43
  end
32
44
  end
45
+
46
+ def remove_instance
47
+ instance_name = "#{name}/#{instance}"
48
+ confirm_command("#{name}/#{instance}") unless forced?
49
+ service_instance = client.get("services/#{parse_service_id(name)}/instances")['instances'].find{ |i|
50
+ i['instance_number'] == instance.to_i
51
+ }
52
+ exit_with_error("Instance not found") unless service_instance
53
+ spinner "Removing service instance #{instance_name.colorize(:cyan)} " do
54
+ client.delete("services/#{parse_service_id(name)}/instances/#{service_instance['id']}")
55
+ end
56
+ end
33
57
  end
34
58
  end
@@ -41,12 +41,11 @@ module Kontena
41
41
  # @param [String] service_id
42
42
  def show_service(token, service_id)
43
43
  service = get_service(token, service_id)
44
- grid = service['id'].split('/')[0]
45
44
  puts "#{service['id']}:"
46
45
  puts " created: #{service['created_at']}"
47
46
  puts " updated: #{service['updated_at']}"
48
47
  puts " stack: #{service['stack']['id'] }"
49
- puts " state: #{service['state'] }"
48
+ puts " desired_state: #{service['state'] }"
50
49
  puts " image: #{service['image']}"
51
50
  puts " revision: #{service['revision']}"
52
51
  puts " stack_revision: #{service['stack_revision']}" if service['stack_revision']
@@ -54,7 +53,9 @@ module Kontena
54
53
  puts " scaling: #{service['instances'] }"
55
54
  puts " strategy: #{service['strategy']}"
56
55
  puts " deploy_opts:"
57
- puts " min_health: #{service['deploy_opts']['min_health']}"
56
+ if service['deploy_opts']['min_health']
57
+ puts " min_health: #{service['deploy_opts']['min_health']}"
58
+ end
58
59
  if service['deploy_opts']['wait_for_port']
59
60
  puts " wait_for_port: #{service['deploy_opts']['wait_for_port']}"
60
61
  end
@@ -104,7 +105,7 @@ module Kontena
104
105
  end
105
106
  end
106
107
 
107
- unless service['net'].to_s.empty?
108
+ if service['net'].to_s != 'bridge'
108
109
  puts " net: #{service['net']}"
109
110
  end
110
111
 
@@ -188,6 +189,40 @@ module Kontena
188
189
  end
189
190
 
190
191
  def show_service_instances(token, service_id)
192
+ puts " instances:"
193
+ instances = client(token).get("services/#{parse_service_id(service_id)}/instances")['instances']
194
+ containers = client(token).get("services/#{parse_service_id(service_id)}/containers")['containers']
195
+ instances.each do |i|
196
+ puts " #{name}/#{i['instance_number']}:"
197
+ puts " scheduled_to: #{i.dig('node', 'name') || '-'}"
198
+ puts " deploy_rev: #{i['deploy_rev']}"
199
+ puts " rev: #{i['rev']}"
200
+ puts " state: #{i['state']}"
201
+ puts " error: #{i['error'] || '-'}" if i['error']
202
+ puts " containers:"
203
+ containers.select { |c|
204
+ c['instance_number'] == i['instance_number']
205
+ }.each do |container|
206
+ puts " #{container['name']} (on #{container.dig('node', 'name')}):"
207
+ puts " dns: #{container['hostname']}.#{container['domainname']}"
208
+ puts " ip: #{container['ip_address']}"
209
+ puts " public ip: #{container['node']['public_ip'] rescue 'unknown'}"
210
+ if container['health_status']
211
+ health_time = Time.now - Time.parse(container.dig('health_status', 'updated_at'))
212
+ puts " health: #{container.dig('health_status', 'status')} (#{health_time.to_i}s ago)"
213
+ end
214
+ puts " status: #{container['status']}"
215
+ if container.dig('state', 'error') != ''
216
+ puts " reason: #{container['state']['error']}"
217
+ end
218
+ if container.dig('state', 'exit_code').to_i != 0
219
+ puts " exit code: #{container['state']['exit_code']}"
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ def show_service_containers(token, service_id)
191
226
  puts " instances:"
192
227
  result = client(token).get("services/#{parse_service_id(service_id)}/containers")
193
228
  result['containers'].each do |container|
@@ -13,7 +13,14 @@ module Kontena::Cli::Services
13
13
  token = require_token
14
14
 
15
15
  show_service(token, name)
16
- show_service_instances(token, name)
16
+ begin
17
+ show_service_instances(token, name)
18
+ rescue Kontena::Errors::StandardError => exc
19
+ if exc.status == 404
20
+ # fallback to old behaviour
21
+ show_service_containers(token, name)
22
+ end
23
+ end
17
24
  end
18
25
  end
19
26
  end
@@ -6,6 +6,7 @@ class Kontena::Cli::StackCommand < Kontena::Command
6
6
  subcommand "upgrade", "Upgrade a stack in a grid", load_subcommand('stacks/upgrade_command')
7
7
  subcommand ["start", "deploy"], "Deploy an installed stack in a grid", load_subcommand('stacks/deploy_command')
8
8
  subcommand "logs", "Show logs from services in a stack", load_subcommand('stacks/logs_command')
9
+ subcommand "events", "Show events from services in a stack", load_subcommand('stacks/events_command')
9
10
  subcommand "monitor", "Monitor services in a stack", load_subcommand('stacks/monitor_command')
10
11
  subcommand "build", "Build images listed in a stack file and push them to an image registry", load_subcommand('stacks/build_command')
11
12
  subcommand ["reg", "registry"], "Stack registry related commands", load_subcommand('stacks/registry_command')
@@ -0,0 +1,33 @@
1
+ require_relative '../helpers/log_helper'
2
+
3
+ module Kontena::Cli::Stacks
4
+ class EventsCommand < Kontena::Command
5
+ include Kontena::Cli::Common
6
+ include Kontena::Cli::GridOptions
7
+ include Kontena::Cli::Helpers::LogHelper
8
+
9
+ parameter "NAME", "Service name"
10
+
11
+ def execute
12
+ require_api_url
13
+
14
+ query_params = {}
15
+ titles = ['TIME', 'TYPE', 'MESSAGE']
16
+ puts "%-25s %-25s %s" % titles
17
+ show_logs("stacks/#{current_grid}/#{name}/event_logs", query_params) do |log|
18
+ show_log(log)
19
+ end
20
+ end
21
+
22
+ def show_log(log)
23
+ msg = log['message']
24
+ node = log['relationships'].find { |r| r['type'] == 'node' }
25
+ if node
26
+ msg = "#{msg} (#{node['id'].split('/')[-1]})"
27
+ end
28
+ puts '%-25s %-25s %s' % [
29
+ log['created_at'], log['type'], msg
30
+ ]
31
+ end
32
+ end
33
+ end
@@ -1,3 +1,5 @@
1
+ require_relative '../helpers/log_helper'
2
+
1
3
  module Kontena::Cli::Stacks
2
4
  class LogsCommand < Kontena::Command
3
5
  include Kontena::Cli::Common
@@ -50,7 +50,7 @@ module Kontena::Cli::Stacks
50
50
  end
51
51
 
52
52
  def bool
53
- prompt.yes?(question_text, default: option.default == false ? false : true)
53
+ prompt.yes?(question_text, default: option.default.nil? || option.default)
54
54
  end
55
55
 
56
56
  def echo?
@@ -10,7 +10,7 @@ module Kontena::Cli::Vault
10
10
  option '--skip-null', :flag, "Do not remove keys with null values"
11
11
  option '--empty-is-null', :flag, "Treat empty values as null"
12
12
 
13
- parameter '[PATH]', "Input from file in PATH, default: STDIN"
13
+ parameter '[PATH]', "Input from file in PATH (default: STDIN)"
14
14
 
15
15
  requires_current_master
16
16
 
@@ -23,7 +23,7 @@ module Kontena::Cli::Vault
23
23
  end
24
24
 
25
25
  def input
26
- path ? File.read(path) : STDIN.read
26
+ path ? File.read(path) : stdin_input("Enter secrets YAML", :multiline)
27
27
  end
28
28
 
29
29
  def execute
@@ -1,26 +1,23 @@
1
1
  module Kontena::Cli::Vault
2
2
  class UpdateCommand < Kontena::Command
3
3
  include Kontena::Cli::Common
4
+ include Kontena::Cli::GridOptions
4
5
 
5
6
  parameter 'NAME', 'Secret name'
6
- parameter '[VALUE]', 'Secret value'
7
+ parameter '[VALUE]', 'Secret value (default: STDIN)'
7
8
 
8
9
  option ['-u', '--upsert'], :flag, 'Create secret unless already exists', default: false
9
10
  option '--silent', :flag, "Reduce output verbosity"
10
11
 
11
- def execute
12
- require_api_url
13
- require_current_grid
12
+ requires_current_master
13
+
14
+ def default_value
15
+ stdin_input("Enter value for secret '#{name}'", :mask)
16
+ end
14
17
 
15
- token = require_token
16
- value ||= STDIN.read.chomp
17
- data = {
18
- name: name,
19
- value: value,
20
- upsert: upsert?
21
- }
18
+ def execute
22
19
  vspinner "Updating #{name.colorize(:cyan)} value in the vault " do
23
- client(token).put("secrets/#{current_grid}/#{name}", data)
20
+ client.put("secrets/#{current_grid}/#{name}", {name: name, value: value, upsert: upsert? })
24
21
  end
25
22
  end
26
23
  end
@@ -4,26 +4,19 @@ module Kontena::Cli::Vault
4
4
  include Kontena::Cli::GridOptions
5
5
 
6
6
  parameter 'NAME', 'Secret name'
7
- parameter '[VALUE]', 'Secret value'
7
+ parameter '[VALUE]', 'Secret value (default: STDIN)'
8
8
 
9
9
  option '--silent', :flag, "Reduce output verbosity"
10
10
 
11
- def execute
12
- require_api_url
13
- require_current_grid
11
+ requires_current_master
14
12
 
15
- token = require_token
16
- secret = value
17
- if secret.to_s == ''
18
- secret = STDIN.read
19
- end
20
- exit_with_error('No value provided') if secret.to_s == ''
21
- data = {
22
- name: name,
23
- value: secret
24
- }
13
+ def default_value
14
+ stdin_input("Enter value for secret '#{name}'", :mask)
15
+ end
16
+
17
+ def execute
25
18
  vspinner "Writing #{name.colorize(:cyan)} to the vault " do
26
- client(token).post("grids/#{current_grid}/secrets", data)
19
+ client.post("grids/#{current_grid}/secrets", { name: name, value: value })
27
20
  end
28
21
  end
29
22
  end
@@ -1,7 +1,10 @@
1
1
 
2
2
  class Kontena::Cli::VolumeCommand < Kontena::Command
3
3
 
4
+ warn Kontena.pastel.yellow("[EXPERIMENTAL] The `kontena volume` commands are still experimental in Kontena #{Kontena.minor_version}, and may change in future releases")
5
+
4
6
  subcommand "create", "Create a managed volume", load_subcommand('volumes/create_command')
7
+ subcommand "show", "Show details of a volume", load_subcommand('volumes/show_command')
5
8
  subcommand ["remove", "rm"], "Remove a managed volume", load_subcommand('volumes/remove_command')
6
9
  subcommand ["list", "ls"], "List managed volumes", load_subcommand('volumes/list_command')
7
10
 
@@ -0,0 +1,38 @@
1
+
2
+ module Kontena::Cli::Volumes
3
+ class ShowCommand < Kontena::Command
4
+ include Kontena::Cli::Common
5
+ include Kontena::Cli::GridOptions
6
+
7
+ banner "Show details of a volume"
8
+
9
+ parameter 'VOLUME', 'Volume'
10
+
11
+ requires_current_master
12
+ requires_current_master_token
13
+
14
+ def execute
15
+ vol = client.get("volumes/#{current_grid}/#{volume}")
16
+ puts "#{vol['name']}:"
17
+ puts " id: #{vol['id']}"
18
+ puts " created: #{vol['created_at']}"
19
+ puts " scope: #{vol['scope']}"
20
+ puts " driver: #{vol['driver']}"
21
+ puts " driver_opts:"
22
+ vol['driver_opts'].each do |k,v|
23
+ puts " #{k}: #{v}"
24
+ end
25
+ puts " instances:"
26
+ vol['instances'].each do |instance|
27
+ puts " - name: #{instance['name']}"
28
+ puts " node: #{instance['node']}"
29
+ end
30
+ puts " services:"
31
+ vol['services'].each do |service|
32
+ puts " - #{service['id']}"
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
@@ -36,7 +36,7 @@ class Kontena::MainCommand < Kontena::Command
36
36
  subcommand "whoami", "Shows current logged in user", load_subcommand('whoami_command')
37
37
  subcommand "plugin", "Plugin related commands", load_subcommand('plugin_command')
38
38
  subcommand "version", "Show CLI and current master version", load_subcommand('version_command')
39
- subcommand "volume", "Volume specific commands", load_subcommand('volume_command')
39
+ subcommand "volume", "Volume specific commands [EXPERIMENTAL]", load_subcommand('volume_command')
40
40
 
41
41
  def execute
42
42
  end
data/lib/kontena_cli.rb CHANGED
@@ -28,6 +28,11 @@ module Kontena
28
28
  end
29
29
 
30
30
 
31
+ # @return [String] x.y
32
+ def self.minor_version
33
+ Kontena::Cli::VERSION.split('.')[0..1].join('.')
34
+ end
35
+
31
36
  def self.version
32
37
  "kontena-cli/#{Kontena::Cli::VERSION}"
33
38
  end
@@ -111,4 +116,3 @@ require_relative 'kontena/stacks_cache'
111
116
  require_relative 'kontena/plugin_manager'
112
117
  require_relative 'kontena/main_command'
113
118
  require_relative 'kontena/cli/spinner'
114
-
@@ -0,0 +1,15 @@
1
+ require 'kontena/cli/grids/events_command'
2
+
3
+ describe Kontena::Cli::Grids::EventsCommand do
4
+ include ClientHelpers
5
+ include OutputHelpers
6
+
7
+ describe '#execute' do
8
+ it 'requests events from master' do
9
+ expect(client).to receive(:get).with(
10
+ 'grids/test-grid/event_logs', {limit: 100}
11
+ ).and_return({'logs' => []})
12
+ subject.run([])
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ require_relative "../../../spec_helper"
2
+ require "kontena/cli/services/events_command"
3
+
4
+ describe Kontena::Cli::Services::EventsCommand do
5
+
6
+ include ClientHelpers
7
+
8
+ describe '#execute' do
9
+ before(:each) do
10
+ allow(client).to receive(:get).and_return({
11
+ 'logs' => []
12
+ })
13
+ end
14
+
15
+ it 'requests logs from master' do
16
+ expect(client).to receive(:get).with(
17
+ 'services/test-grid/null/service-a/event_logs', {limit: 100}
18
+ )
19
+ subject.run(['service-a'])
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ require_relative "../../../spec_helper"
2
+ require "kontena/cli/stacks/events_command"
3
+
4
+ describe Kontena::Cli::Stacks::EventsCommand do
5
+
6
+ include ClientHelpers
7
+
8
+ describe '#execute' do
9
+ before(:each) do
10
+ allow(client).to receive(:get).and_return({
11
+ 'logs' => []
12
+ })
13
+ end
14
+
15
+ it 'requests logs from master' do
16
+ expect(client).to receive(:get).with(
17
+ 'stacks/test-grid/redish/event_logs', {limit: 100}
18
+ )
19
+ subject.run(['redish'])
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ require "kontena/cli/stacks/logs_command"
2
+
3
+ describe Kontena::Cli::Stacks::LogsCommand do
4
+ include ClientHelpers
5
+ include OutputHelpers
6
+
7
+ let (:logs) do
8
+ [
9
+ {
10
+ 'id' => '57cff2e8cfee65c8b6efc8bd',
11
+ 'name' => 'test-stack.mysql-1',
12
+ 'created_at' => '2016-09-07T15:19:04.362690',
13
+ 'data' => "mysql log message 1",
14
+ },
15
+ ]
16
+ end
17
+
18
+ before(:each) do
19
+ Kontena.pastel.resolver.color.disable!
20
+ end
21
+
22
+ it "shows stack logs" do
23
+ expect(client).to receive(:get).with('stacks/test-grid/test-stack/container_logs', {
24
+ limit: 100,
25
+ }) { { 'logs' => logs } }
26
+
27
+ expect{subject.run(['test-stack'])}.to output_lines [
28
+ "2016-09-07T15:19:04.362690 [test-stack.mysql-1]: mysql log message 1",
29
+ ]
30
+ end
31
+
32
+ end
@@ -0,0 +1,79 @@
1
+ require_relative "../../../spec_helper"
2
+ require 'kontena/cli/vault/update_command'
3
+
4
+ describe Kontena::Cli::Vault::UpdateCommand do
5
+
6
+ include RequirementsHelper
7
+ include ClientHelpers
8
+
9
+ let(:subject) { described_class.new(File.basename($0)) }
10
+
11
+ describe '#execute' do
12
+
13
+ context 'without value parameter' do
14
+
15
+ let(:stdin) { double(:stdin) }
16
+
17
+ before(:each) do
18
+ @old_stdin = $stdin
19
+ $stdin = stdin
20
+ end
21
+
22
+ after(:each) { $stdin = @old_stdin }
23
+
24
+ context 'without tty' do
25
+ before(:each) { allow(stdin).to receive(:tty?).and_return(false) }
26
+ after(:each) { $stdin = @old_stdin }
27
+
28
+ context 'nothing in stdin' do
29
+ before(:each) do
30
+ allow(stdin).to receive(:eof?).and_return(true)
31
+ end
32
+
33
+ it 'returns error if value not provided' do
34
+ expect{subject.run(['mysql_password'])}.to exit_with_error.and output(/Missing/).to_stderr
35
+ end
36
+ end
37
+
38
+ context 'value in stdin' do
39
+ it 'sends update request' do
40
+ expect(stdin).to receive(:eof?).and_return(false)
41
+ expect(stdin).to receive(:read).and_return('secret')
42
+ expect(client).to receive(:put).with('secrets/test-grid/mysql_password', { name: 'mysql_password', value: 'secret', upsert: false})
43
+ expect{subject.run(['mysql_password'])}.not_to exit_with_error
44
+ end
45
+ end
46
+ end
47
+
48
+ context 'with tty' do
49
+ before(:each) do
50
+ allow(stdin).to receive(:tty?).and_return(true)
51
+ end
52
+
53
+ context 'when value not given' do
54
+ let(:prompt) { double(:prompt) }
55
+ it 'prompts for value' do
56
+ expect(subject).to receive(:prompt).and_return(prompt)
57
+ expect(prompt).to receive(:mask).once.and_return('very-secret')
58
+ expect(client).to receive(:put).with('secrets/test-grid/mysql_password', { name: 'mysql_password', value: 'very-secret', upsert: false})
59
+ expect{subject.run(['mysql_password'])}.not_to exit_with_error
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'with value parameter' do
66
+ it 'sends update request' do
67
+ expect(client).to receive(:put).with('secrets/test-grid/mysql_password', { name: 'mysql_password', value: 'secret', upsert: false})
68
+ expect{subject.run(['mysql_password', 'secret'])}.not_to exit_with_error
69
+ end
70
+
71
+ context 'when giving --upsert flag' do
72
+ it 'sets upsert true' do
73
+ expect(client).to receive(:put).with('secrets/test-grid/mysql_password', { name: 'mysql_password', value: 'secret', upsert: true})
74
+ subject.run(['-u', 'mysql_password', 'secret'])
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,60 @@
1
+ require_relative "../../../spec_helper"
2
+ require 'kontena/cli/vault/write_command'
3
+
4
+ describe Kontena::Cli::Vault::WriteCommand do
5
+
6
+ include RequirementsHelper
7
+ include ClientHelpers
8
+
9
+ let(:subject) { described_class.new(File.basename($0)) }
10
+
11
+ describe '#execute' do
12
+ context 'without value parameter' do
13
+
14
+ let(:stdin) { double(:stdin) }
15
+
16
+ before(:each) do
17
+ @old_stdin = $stdin
18
+ $stdin = stdin
19
+ end
20
+
21
+ after(:each) { $stdin = @old_stdin }
22
+
23
+ context 'no tty' do
24
+ context 'stdin empty' do
25
+ it 'returns an error' do
26
+ expect(stdin).to receive(:tty?).and_return(false)
27
+ expect(stdin).to receive(:eof?).and_return(true)
28
+ expect{subject.run(['mysql_password'])}.to exit_with_error.and output(/Missing/).to_stderr
29
+ end
30
+ end
31
+
32
+ context 'stdin has a value' do
33
+ it 'sends create request' do
34
+ expect(stdin).to receive(:tty?).and_return(false)
35
+ expect(stdin).to receive(:eof?).and_return(false)
36
+ expect(stdin).to receive(:read).and_return('secret')
37
+ expect(client).to receive(:post).with('grids/test-grid/secrets', { name: 'mysql_password', value: 'secret'})
38
+ expect{subject.run(['mysql_password'])}.not_to exit_with_error
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'with tty' do
44
+ let(:prompt) { double(:prompt) }
45
+ it 'prompts value from STDIN' do
46
+ expect(stdin).to receive(:tty?).and_return(true)
47
+ expect(subject).to receive(:prompt).and_return(prompt)
48
+ expect(prompt).to receive(:mask).and_return('secret')
49
+ expect(client).to receive(:post).with('grids/test-grid/secrets', { name: 'mysql_password', value: 'secret'})
50
+ expect{subject.run(['mysql_password'])}.not_to exit_with_error
51
+ end
52
+ end
53
+ end
54
+
55
+ it 'sends create request' do
56
+ expect(client).to receive(:post).with('grids/test-grid/secrets', { name: 'mysql_password', value: 'secret'})
57
+ subject.run(['mysql_password', 'secret'])
58
+ end
59
+ end
60
+ end
@@ -18,6 +18,12 @@ describe Kontena do
18
18
  end
19
19
  end
20
20
 
21
+ describe '#minor_version' do
22
+ it "returns a version string" do
23
+ expect(Kontena.minor_version).to match /^\d+\.\d+$/
24
+ end
25
+ end
26
+
21
27
  describe '#run' do
22
28
  let(:whoami) { double(:whoami) }
23
29
 
@@ -39,6 +45,3 @@ describe Kontena do
39
45
  end
40
46
  end
41
47
  end
42
-
43
-
44
-
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: 1.2.0.pre3
4
+ version: 1.2.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kontena, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-06 00:00:00.000000000 Z
11
+ date: 2017-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: tty-prompt
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 0.11.0
61
+ version: 0.12.0
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: 0.11.0
68
+ version: 0.12.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: clamp
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 0.7.0
117
+ version: 0.7.1
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: 0.7.0
124
+ version: 0.7.1
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: retriable
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +142,14 @@ dependencies:
142
142
  requirements:
143
143
  - - '='
144
144
  - !ruby/object:Gem::Version
145
- version: 1.8.4
145
+ version: 1.8.5
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - '='
151
151
  - !ruby/object:Gem::Version
152
- version: 1.8.4
152
+ version: 1.8.5
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: semantic
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -309,6 +309,7 @@ files:
309
309
  - lib/kontena/cli/grids/create_command.rb
310
310
  - lib/kontena/cli/grids/current_command.rb
311
311
  - lib/kontena/cli/grids/env_command.rb
312
+ - lib/kontena/cli/grids/events_command.rb
312
313
  - lib/kontena/cli/grids/health_command.rb
313
314
  - lib/kontena/cli/grids/list_command.rb
314
315
  - lib/kontena/cli/grids/logs_command.rb
@@ -390,6 +391,7 @@ files:
390
391
  - lib/kontena/cli/services/envs/add_command.rb
391
392
  - lib/kontena/cli/services/envs/list_command.rb
392
393
  - lib/kontena/cli/services/envs/remove_command.rb
394
+ - lib/kontena/cli/services/events_command.rb
393
395
  - lib/kontena/cli/services/exec_command.rb
394
396
  - lib/kontena/cli/services/link_command.rb
395
397
  - lib/kontena/cli/services/list_command.rb
@@ -413,6 +415,7 @@ files:
413
415
  - lib/kontena/cli/stacks/build_command.rb
414
416
  - lib/kontena/cli/stacks/common.rb
415
417
  - lib/kontena/cli/stacks/deploy_command.rb
418
+ - lib/kontena/cli/stacks/events_command.rb
416
419
  - lib/kontena/cli/stacks/install_command.rb
417
420
  - lib/kontena/cli/stacks/list_command.rb
418
421
  - lib/kontena/cli/stacks/logs_command.rb
@@ -461,6 +464,7 @@ files:
461
464
  - lib/kontena/cli/volumes/create_command.rb
462
465
  - lib/kontena/cli/volumes/list_command.rb
463
466
  - lib/kontena/cli/volumes/remove_command.rb
467
+ - lib/kontena/cli/volumes/show_command.rb
464
468
  - lib/kontena/cli/vpn/config_command.rb
465
469
  - lib/kontena/cli/vpn/create_command.rb
466
470
  - lib/kontena/cli/vpn/remove_command.rb
@@ -561,6 +565,7 @@ files:
561
565
  - spec/kontena/cli/containers/list_command_spec.rb
562
566
  - spec/kontena/cli/containers/logs_command_spec.rb
563
567
  - spec/kontena/cli/etcd/health_command_spec.rb
568
+ - spec/kontena/cli/grids/events_command_spec.rb
564
569
  - spec/kontena/cli/grids/health_command_spec.rb
565
570
  - spec/kontena/cli/grids/trusted_subnets/add_command_spec.rb
566
571
  - spec/kontena/cli/grids/trusted_subnets/list_command_spec.rb
@@ -581,6 +586,7 @@ files:
581
586
  - spec/kontena/cli/nodes/health_command_spec.rb
582
587
  - spec/kontena/cli/nodes/list_command_spec.rb
583
588
  - spec/kontena/cli/services/containers_command_spec.rb
589
+ - spec/kontena/cli/services/events_command_spec.rb
584
590
  - spec/kontena/cli/services/exec_command_spec.rb
585
591
  - spec/kontena/cli/services/link_command_spec.rb
586
592
  - spec/kontena/cli/services/logs_command_spec.rb
@@ -592,8 +598,10 @@ files:
592
598
  - spec/kontena/cli/services/update_command_spec.rb
593
599
  - spec/kontena/cli/stacks/build_command_spec.rb
594
600
  - spec/kontena/cli/stacks/deploy_command_spec.rb
601
+ - spec/kontena/cli/stacks/events_command_spec.rb
595
602
  - spec/kontena/cli/stacks/install_command_spec.rb
596
603
  - spec/kontena/cli/stacks/list_command_spec.rb
604
+ - spec/kontena/cli/stacks/logs_command_spec.rb
597
605
  - spec/kontena/cli/stacks/remove_command_spec.rb
598
606
  - spec/kontena/cli/stacks/service_generator_spec.rb
599
607
  - spec/kontena/cli/stacks/service_generator_v2_spec.rb
@@ -607,6 +615,8 @@ files:
607
615
  - spec/kontena/cli/stacks/yaml/validator_v3_spec.rb
608
616
  - spec/kontena/cli/vault/export_spec.rb
609
617
  - spec/kontena/cli/vault/import_spec.rb
618
+ - spec/kontena/cli/vault/update_command_spec.rb
619
+ - spec/kontena/cli/vault/write_command_spec.rb
610
620
  - spec/kontena/cli/version_command_spec.rb
611
621
  - spec/kontena/cli/vpn/create_command_spec.rb
612
622
  - spec/kontena/client_spec.rb
@@ -700,6 +710,7 @@ test_files:
700
710
  - spec/kontena/cli/containers/list_command_spec.rb
701
711
  - spec/kontena/cli/containers/logs_command_spec.rb
702
712
  - spec/kontena/cli/etcd/health_command_spec.rb
713
+ - spec/kontena/cli/grids/events_command_spec.rb
703
714
  - spec/kontena/cli/grids/health_command_spec.rb
704
715
  - spec/kontena/cli/grids/trusted_subnets/add_command_spec.rb
705
716
  - spec/kontena/cli/grids/trusted_subnets/list_command_spec.rb
@@ -720,6 +731,7 @@ test_files:
720
731
  - spec/kontena/cli/nodes/health_command_spec.rb
721
732
  - spec/kontena/cli/nodes/list_command_spec.rb
722
733
  - spec/kontena/cli/services/containers_command_spec.rb
734
+ - spec/kontena/cli/services/events_command_spec.rb
723
735
  - spec/kontena/cli/services/exec_command_spec.rb
724
736
  - spec/kontena/cli/services/link_command_spec.rb
725
737
  - spec/kontena/cli/services/logs_command_spec.rb
@@ -731,8 +743,10 @@ test_files:
731
743
  - spec/kontena/cli/services/update_command_spec.rb
732
744
  - spec/kontena/cli/stacks/build_command_spec.rb
733
745
  - spec/kontena/cli/stacks/deploy_command_spec.rb
746
+ - spec/kontena/cli/stacks/events_command_spec.rb
734
747
  - spec/kontena/cli/stacks/install_command_spec.rb
735
748
  - spec/kontena/cli/stacks/list_command_spec.rb
749
+ - spec/kontena/cli/stacks/logs_command_spec.rb
736
750
  - spec/kontena/cli/stacks/remove_command_spec.rb
737
751
  - spec/kontena/cli/stacks/service_generator_spec.rb
738
752
  - spec/kontena/cli/stacks/service_generator_v2_spec.rb
@@ -746,6 +760,8 @@ test_files:
746
760
  - spec/kontena/cli/stacks/yaml/validator_v3_spec.rb
747
761
  - spec/kontena/cli/vault/export_spec.rb
748
762
  - spec/kontena/cli/vault/import_spec.rb
763
+ - spec/kontena/cli/vault/update_command_spec.rb
764
+ - spec/kontena/cli/vault/write_command_spec.rb
749
765
  - spec/kontena/cli/version_command_spec.rb
750
766
  - spec/kontena/cli/vpn/create_command_spec.rb
751
767
  - spec/kontena/client_spec.rb