kontena-cli 1.1.0 → 1.1.1.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/kontena-cli.gemspec +1 -1
  4. data/lib/kontena/callbacks/master/deploy/70_invite_self_after_deploy.rb +9 -6
  5. data/lib/kontena/cli/common.rb +2 -0
  6. data/lib/kontena/cli/plugins/install_command.rb +4 -0
  7. data/lib/kontena/cli/services/link_command.rb +12 -4
  8. data/lib/kontena/cli/services/unlink_command.rb +11 -5
  9. data/lib/kontena/cli/stacks/service_generator.rb +10 -7
  10. data/lib/kontena/cli/stacks/yaml/opto.rb +15 -0
  11. data/lib/kontena/cli/stacks/yaml/opto/prompt_resolver.rb +13 -5
  12. data/lib/kontena/cli/stacks/yaml/opto/service_instances_resolver.rb +1 -1
  13. data/lib/kontena/cli/stacks/yaml/opto/service_link_resolver.rb +66 -35
  14. data/lib/kontena/cli/stacks/yaml/opto/vault_cert_prompt_resolver.rb +33 -10
  15. data/lib/kontena/cli/stacks/yaml/opto/vault_resolver.rb +1 -1
  16. data/lib/kontena/cli/stacks/yaml/opto/vault_setter.rb +1 -2
  17. data/lib/kontena/cli/stacks/yaml/reader.rb +7 -8
  18. data/lib/kontena/plugin_manager.rb +1 -2
  19. data/lib/kontena_cli.rb +1 -1
  20. data/spec/kontena/cli/cloud/master/add_command_spec.rb +0 -1
  21. data/spec/kontena/cli/common_spec.rb +28 -21
  22. data/spec/kontena/cli/master/init_cloud_command_spec.rb +28 -0
  23. data/spec/kontena/cli/services/link_command_spec.rb +2 -2
  24. data/spec/kontena/cli/services/unlink_command_spec.rb +1 -1
  25. data/spec/kontena/cli/stacks/service_generator_spec.rb +7 -3
  26. data/spec/kontena/cli/stacks/yaml/opto/prompt_resolver_spec.rb +27 -0
  27. data/spec/kontena/cli/stacks/yaml/opto/service_link_resolver_spec.rb +54 -0
  28. data/spec/kontena/cli/stacks/yaml/opto/vault_cert_prompt_resolver_spec.rb +73 -0
  29. data/spec/kontena/kontena_cli_spec.rb +15 -6
  30. data/spec/kontena/main_command_spec.rb +16 -0
  31. metadata +19 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e2dfaafb984f7f7c0ca63566203262c8692a33f
4
- data.tar.gz: '0280a6505d2d6568203b61221cc753dd297a4461'
3
+ metadata.gz: 76963be4ff231516ea09ffc1261b2945f7728f78
4
+ data.tar.gz: ea1f2e4fa944e33af0d7c5b9abea939485805346
5
5
  SHA512:
6
- metadata.gz: 537c7d6ee6d2aea359559e70ce0a8b593fd22c6a1da3c612a839b8adc32aed53f98d4e3f99625824afb900fc888005f34ff5f0a0faa06bbf563f795c4da56b58
7
- data.tar.gz: 8d1f092a778f4f0933cd785d6ea7e149dc2d9ca843b5172380aed4ff81654da622782402b85a80a838b7439306689e108e9346a03b7c266792d24157eedfc15a
6
+ metadata.gz: 66580e1af2b505487e82ae8458f5fed6f2965438df752e0ae8581108fe067932dc8205276117f3fc0a00bba46ddb17f3c5b19f8d224b82c78cd2e90af8853b0c
7
+ data.tar.gz: ce4aef539d02fb0ee56f6850a7657d8d0b2466bca5db2d7633ae4d01c28e75ce36330ef1df8a7c46644abd6df79cd8fa9ffbe7d716595bc0155ad71466e8e27f
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.1.1.rc1
data/kontena-cli.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_runtime_dependency "launchy", "~> 2.4.3"
30
30
  spec.add_runtime_dependency "hash_validator", "~> 0.7.0"
31
31
  spec.add_runtime_dependency "retriable", "~> 2.1.0"
32
- spec.add_runtime_dependency "opto", "~> 1.8.0"
32
+ spec.add_runtime_dependency "opto", "1.8.3"
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"
@@ -4,7 +4,7 @@ module Kontena
4
4
 
5
5
  include Kontena::Cli::Common
6
6
 
7
- matches_commands 'master create'
7
+ matches_commands 'master create', 'master init_cloud'
8
8
 
9
9
  def cloud_user_data
10
10
  return @cloud_user_data if @cloud_user_data
@@ -27,8 +27,7 @@ module Kontena
27
27
  def after
28
28
  return unless current_master
29
29
  return unless command.exit_code == 0
30
- return unless current_master.username.to_s == 'admin'
31
- return nil if command.skip_auth_provider?
30
+ return nil if command.respond_to?(:skip_auth_provider?) && command.skip_auth_provider?
32
31
  return nil unless cloud_user_data
33
32
 
34
33
  invite_response = nil
@@ -51,11 +50,15 @@ module Kontena
51
50
 
52
51
  return nil if role_status.to_i > 0
53
52
 
54
- spinner "Adding #{cloud_user_data[:email]} to grid 'test'" do |spin|
55
- grid_add_status = Kontena.run("grid user add --grid test #{cloud_user_data[:email].shellescape}")
56
- spin.fail if grid_add_status.to_i > 0
53
+ if current_master.grid
54
+ spinner "Adding #{cloud_user_data[:email]} to grid '#{current_master.grid}'" do |spin|
55
+ grid_add_status = Kontena.run("grid user add --grid #{current_master.grid} #{cloud_user_data[:email].shellescape}")
56
+ spin.fail if grid_add_status.to_i > 0
57
+ end
57
58
  end
58
59
 
60
+ return unless current_master.username.to_s == 'admin'
61
+
59
62
  new_user_token = nil
60
63
  spinner "Creating an access token for #{cloud_user_data[:email]}" do |spin|
61
64
  new_user_token = Kontena.run("master token create -e 0 -s user --return -u #{cloud_user_data[:email].shellescape}", returning: :result)
@@ -247,6 +247,7 @@ module Kontena
247
247
  if self.respond_to?(:force?) && self.force?
248
248
  return
249
249
  end
250
+ exit_with_error 'Command requires --force' unless $stdout.tty? && $stdin.tty?
250
251
  puts message if message
251
252
  puts "Destructive command. To proceed, type \"#{name}\" or re-run this command with --force option."
252
253
 
@@ -257,6 +258,7 @@ module Kontena
257
258
  if self.respond_to?(:force?) && self.force?
258
259
  return
259
260
  end
261
+ exit_with_error 'Command requires --force' unless $stdout.tty? && $stdin.tty?
260
262
  prompt.yes?(message) || error('Aborted command.')
261
263
  end
262
264
 
@@ -23,6 +23,10 @@ module Kontena::Cli::Plugins
23
23
  spin.fail!
24
24
  end
25
25
  end
26
+
27
+ spinner "Running cleanup" do |spin|
28
+ Kontena::PluginManager.instance.cleanup_plugin(name)
29
+ end
26
30
  else
27
31
  installed = spinner "Installing plugin #{name.colorize(:cyan)}" do |spin|
28
32
  begin
@@ -13,12 +13,20 @@ module Kontena::Cli::Services
13
13
  def execute
14
14
  require_api_url
15
15
  token = require_token
16
+ target_service = target
17
+
18
+ target_service = "null/#{target_service}" unless target_service.include?('/')
16
19
 
17
20
  service = client(token).get("services/#{parse_service_id(name)}")
18
- existing_targets = service['links'].map{|l| l['grid_service_id'].split('/')[1]}
19
- exit_with_error("Service is already linked to #{target.to_s}") if existing_targets.include?(target.to_s)
20
- links = service['links'].map{|l| {name: l['grid_service_id'].split('/')[1], alias: l['alias']} }
21
- links << {name: target.to_s, alias: target.to_s}
21
+ existing_targets = service['links'].map{ |l| l['id'].split('/', 2)[1] }
22
+ if existing_targets.include?(target_service.to_s)
23
+ exit_with_error("Service is already linked to #{target.to_s}")
24
+ end
25
+ links = service['links'].map{ |l|
26
+ { name: l['id'].split('/', 2)[1], alias: l['alias'] }
27
+ }
28
+ links << {name: target_service.to_s, alias: target.to_s}
29
+ links.compact!
22
30
  data = {links: links}
23
31
  spinner "Linking #{name.colorize(:cyan)} to #{target.colorize(:cyan)} " do
24
32
  update_service(token, name, data)
@@ -13,13 +13,19 @@ module Kontena::Cli::Services
13
13
  def execute
14
14
  require_api_url
15
15
  token = require_token
16
-
16
+ target_service = target
17
+ target_service = "null/#{target_service}" unless target_service.include?('/')
18
+ target_id = "#{current_grid}/#{target_service}"
17
19
  service = client(token).get("services/#{parse_service_id(name)}")
18
- links = service['links'].map{|l| {name: l['grid_service_id'].split('/')[1], alias: l['alias']} }
19
- exit_with_error("Service is not linked to #{target.to_s}") unless links.find{|l| l[:name] == target.to_s}
20
- links.delete_if{|l| l[:name] == target.to_s}
20
+ links = service['links']
21
+ unless links.find { |l| l['id'] == target_id }
22
+ exit_with_error("Service is not linked to #{target.to_s}")
23
+ end
24
+ links.delete_if { |l| l['id'] == target_id }
21
25
  data = {links: links}
22
- update_service(token, name, data)
26
+ spinner "Unlinking #{name.colorize(:cyan)} from #{target.colorize(:cyan)} " do
27
+ update_service(token, name, data)
28
+ end
23
29
  end
24
30
  end
25
31
  end
@@ -49,13 +49,16 @@ module Kontena::Cli::Stacks
49
49
  data['log_opts'] = options['log_opt'] if options['log_opt'] && !options['log_opt'].empty?
50
50
  deploy_opts = options['deploy'] || {}
51
51
  data['strategy'] = deploy_opts['strategy'] if deploy_opts['strategy']
52
- deploy = {}
53
- deploy['wait_for_port'] = deploy_opts['wait_for_port'] if deploy_opts.has_key?('wait_for_port')
54
- deploy['min_health'] = deploy_opts['min_health'] if deploy_opts.has_key?('min_health')
55
- deploy['interval'] = parse_relative_time(deploy_opts['interval']) if deploy_opts.has_key?('interval')
56
- unless deploy.empty?
57
- data['deploy_opts'] = deploy
58
- end
52
+ deploy = {
53
+ 'wait_for_port' => deploy_opts['wait_for_port'],
54
+ 'min_health' => deploy_opts['min_health']
55
+ }
56
+ if deploy_opts.has_key?('interval')
57
+ deploy['interval'] = parse_relative_time(deploy_opts['interval'])
58
+ else
59
+ deploy['interval'] = nil
60
+ end
61
+ data['deploy_opts'] = deploy
59
62
  data['hooks'] = options['hooks'] || {}
60
63
  data['secrets'] = options['secrets'] if options['secrets']
61
64
  data['build'] = parse_build_options(options) if options['build']
@@ -0,0 +1,15 @@
1
+ module Kontena::Cli::Stacks
2
+ module YAML
3
+ module Opto
4
+ module Resolvers; end
5
+ module Setters; end
6
+ end
7
+ end
8
+ end
9
+ require 'opto'
10
+ require_relative 'opto/vault_setter'
11
+ require_relative 'opto/vault_resolver'
12
+ require_relative 'opto/prompt_resolver'
13
+ require_relative 'opto/service_instances_resolver'
14
+ require_relative 'opto/vault_cert_prompt_resolver'
15
+ require_relative 'opto/service_link_resolver'
@@ -1,9 +1,12 @@
1
+ require 'kontena/cli/stacks/yaml/opto'
2
+ require 'kontena/cli/common'
3
+
1
4
  module Kontena::Cli::Stacks
2
5
  module YAML
3
- class Prompt < Opto::Resolver
6
+ class Prompt < ::Opto::Resolver
4
7
  include Kontena::Cli::Common
5
8
 
6
- using Opto::Extension::HashStringOrSymbolKey
9
+ using ::Opto::Extension::HashStringOrSymbolKey
7
10
 
8
11
  def enum?
9
12
  option.type == 'enum'
@@ -50,10 +53,16 @@ module Kontena::Cli::Stacks
50
53
  prompt.yes?(question_text, default: option.default == false ? false : true)
51
54
  end
52
55
 
53
- def ask
54
- prompt.ask(question_text, default: option.default)
56
+ def echo?
57
+ return true if option.handler.nil?
58
+ return true if option.handler.options.nil?
59
+ return true if option.handler.options[:echo].nil?
60
+ option.handler.options[:echo]
55
61
  end
56
62
 
63
+ def ask
64
+ prompt.ask(question_text, default: option.default, echo: echo?)
65
+ end
57
66
 
58
67
  def resolve
59
68
  return nil if option.skip?
@@ -68,4 +77,3 @@ module Kontena::Cli::Stacks
68
77
  end
69
78
  end
70
79
  end
71
-
@@ -1,6 +1,6 @@
1
1
  module Kontena::Cli::Stacks
2
2
  module YAML
3
- class Opto::Resolvers::ServiceInstances < Opto::Resolver
3
+ class Opto::Resolvers::ServiceInstances < ::Opto::Resolver
4
4
  def resolve
5
5
  read_command = Kontena::Cli::Stacks::ShowCommand.new([self.stack])
6
6
  stack = read_command.fetch_stack(self.stack)
@@ -1,45 +1,76 @@
1
- module Kontena::Cli::Stacks
2
- module YAML
3
- class Opto::Resolvers::ServiceLink < Opto::Resolver
4
- include Kontena::Cli::Common
5
-
6
- def resolve
7
- message = hint['prompt']
8
- name_filter = hint['name']
9
- image_filter = hint['image']
10
- raise "prompt missing" unless message
11
-
12
- services = client.get("grids/#{current_grid}/services")['services']
13
- services = filter_by_image(services, image_filter) if image_filter
14
- services = filter_by_name(services, name_filter) if name_filter
15
- prompt.select(message) do |menu|
16
- menu.choice "<none>", nil unless option.required?
17
- services.each do |s|
18
- if s.dig('stack', 'name') == 'null'
19
- name = s['name']
20
- else
21
- name = "#{s.dig('stack', 'name')}/#{s['name']}"
22
- end
23
- menu.choice name, "#{s.dig('stack', 'name')}/#{s['name']}"
24
- end
1
+ module Kontena::Cli::Stacks::YAML::Opto::Resolvers
2
+ class ServiceLink < ::Opto::Resolver
3
+ include Kontena::Cli::Common
4
+
5
+ def resolve
6
+ message = hint['prompt']
7
+ name_filter = hint['name']
8
+ image_filter = hint['image']
9
+ raise "prompt missing" unless message
10
+
11
+ services = get_services
12
+ services = filter_by_image(services, image_filter) if image_filter
13
+ services = filter_by_name(services, name_filter) if name_filter
14
+ return nil if services.size == 0
15
+ prompt.select(message) do |menu|
16
+ menu.default(default_index(services)) if option.default
17
+ menu.choice "<none>", nil unless option.required?
18
+ services.each do |s|
19
+ menu.choice service_name(s), service_link(s)
25
20
  end
26
21
  end
22
+ end
27
23
 
28
- def filter_by_image(services, image)
29
- services.select { |s|
30
- s['image'].include?(image)
31
- }
32
- end
24
+ # @return [Array<Hash>]
25
+ def get_services
26
+ client.get("grids/#{current_grid}/services")['services']
27
+ rescue
28
+ []
29
+ end
33
30
 
34
- def filter_by_name(services, name)
35
- services.select { |s|
36
- s['name'].include?(name)
37
- }
31
+ # @param [Array<Hash>] services
32
+ # @return [Integer]
33
+ def default_index(services)
34
+ index = services.index {|s| service_link(s) == option.default }
35
+ if index
36
+ index.to_i + 1
37
+ else
38
+ 0
38
39
  end
40
+ end
41
+
42
+ # @param [Hash] service
43
+ # @return [String]
44
+ def service_link(service)
45
+ grid, stack, service = service['id'].split('/')
46
+ "#{stack}/#{service}"
47
+ end
39
48
 
40
- def stack
41
- ENV['STACK']
49
+ # @param [Hash] service
50
+ # @return [String]
51
+ def service_name(service)
52
+ grid, stack, service = service['id'].split('/')
53
+ if stack == 'null'.freeze
54
+ service
55
+ else
56
+ "#{stack}/#{service}"
42
57
  end
43
58
  end
59
+
60
+ def filter_by_image(services, image)
61
+ services.select { |s|
62
+ s['image'].include?(image)
63
+ }
64
+ end
65
+
66
+ def filter_by_name(services, name)
67
+ services.select { |s|
68
+ s['name'].include?(name)
69
+ }
70
+ end
71
+
72
+ def stack
73
+ ENV['STACK']
74
+ end
44
75
  end
45
76
  end
@@ -1,15 +1,38 @@
1
- module Kontena::Cli::Stacks
2
- module YAML
3
- class Opto::Resolvers::VaultCertPrompt < Opto::Resolver
4
- include Kontena::Cli::Common
1
+ module Kontena::Cli::Stacks::YAML::Opto::Resolvers
2
+ class VaultCertPrompt < ::Opto::Resolver
3
+ include Kontena::Cli::Common
5
4
 
6
- def resolve
7
- message = hint || 'Select SSL certs'
8
- secrets = client.get("grids/#{current_grid}/secrets")['secrets'].select{ |s|
9
- s['name'].match(/(ssl|cert)/i)
10
- }
11
- prompt.multi_select(hint, secrets.map{ |s| s['name'] })
5
+ def resolve
6
+ message = hint || 'Select SSL certs'
7
+ secrets = get_secrets.select{ |s|
8
+ s['name'].match(/(ssl|cert)/i)
9
+ }
10
+ if secrets.size > 0
11
+ prompt.multi_select(hint) do |menu|
12
+ menu.default(*default_indexes(secrets)) if option.default
13
+ secrets.each do |s|
14
+ menu.choice s['name']
15
+ end
16
+ end
12
17
  end
13
18
  end
19
+
20
+ # @return [Array<Hash>] secrets
21
+ def get_secrets
22
+ client.get("grids/#{current_grid}/secrets")['secrets']
23
+ rescue
24
+ []
25
+ end
26
+
27
+ # @param [Array<Hash>] secrets
28
+ # @return [Array<Integer>]
29
+ def default_indexes(secrets)
30
+ indexes = []
31
+ option.default.to_a.each do |name|
32
+ index = secrets.index { |s| s['name'] == name }
33
+ indexes << index.to_i + 1 if index
34
+ end
35
+ indexes
36
+ end
14
37
  end
15
38
  end
@@ -1,6 +1,6 @@
1
1
  module Kontena::Cli::Stacks
2
2
  module YAML
3
- class Opto::Resolvers::Vault < Opto::Resolver
3
+ class Opto::Resolvers::Vault < ::Opto::Resolver
4
4
  def resolve
5
5
  raise RuntimeError, "Missing or empty vault secret name" if hint.to_s.empty?
6
6
  require 'shellwords'
@@ -1,6 +1,6 @@
1
1
  module Kontena::Cli::Stacks
2
2
  module YAML
3
- class Opto::Setters::Vault < Opto::Setter
3
+ class Opto::Setters::Vault < ::Opto::Setter
4
4
  def set(value)
5
5
  require 'shellwords'
6
6
  ENV["DEBUG"] && STDERR.puts("Setting to vault: #{hint}")
@@ -9,4 +9,3 @@ module Kontena::Cli::Stacks
9
9
  end
10
10
  end
11
11
  end
12
-
@@ -2,6 +2,11 @@ require_relative '../../../util'
2
2
 
3
3
  module Kontena::Cli::Stacks
4
4
  module YAML
5
+ module Opto
6
+ module Resolvers; end
7
+ module Setters; end
8
+ end
9
+
5
10
  class Reader
6
11
  include Kontena::Util
7
12
  include Kontena::Cli::Common
@@ -12,13 +17,7 @@ module Kontena::Cli::Stacks
12
17
  require 'yaml'
13
18
  require_relative 'service_extender'
14
19
  require_relative 'validator_v3'
15
- require 'opto'
16
- require_relative 'opto/vault_setter'
17
- require_relative 'opto/vault_resolver'
18
- require_relative 'opto/prompt_resolver'
19
- require_relative 'opto/service_instances_resolver'
20
- require_relative 'opto/vault_cert_prompt_resolver'
21
- require_relative 'opto/service_link_resolver'
20
+ require_relative 'opto'
22
21
  require 'liquid'
23
22
 
24
23
  @file = file
@@ -88,7 +87,7 @@ module Kontena::Cli::Stacks
88
87
  # @return [Opto::Group]
89
88
  def variables
90
89
  return @variables if @variables
91
- @variables = Opto::Group.new(
90
+ @variables = ::Opto::Group.new(
92
91
  (internals_interpolated_yaml['variables'] || {}).merge('STACK' => { type: :string, value: env['STACK']}, 'GRID' => {type: :string, value: env['GRID']}),
93
92
  defaults: {
94
93
  from: :env,
@@ -33,7 +33,6 @@ module Kontena
33
33
  )
34
34
  plugin_version = version.nil? ? Gem::Requirement.default : Gem::Requirement.new(version)
35
35
  without_safe { cmd.install(prefix(plugin_name), plugin_version) }
36
- cleanup_plugin(plugin_name)
37
36
  cmd.installed_gems
38
37
  end
39
38
 
@@ -123,7 +122,7 @@ module Kontena
123
122
  def cleanup_plugin(plugin_name)
124
123
  require 'rubygems/commands/cleanup_command'
125
124
  cmd = Gem::Commands::CleanupCommand.new
126
- options = ['--norc']
125
+ options = []
127
126
  options += ['-q', '--no-verbose'] unless ENV["DEBUG"]
128
127
  cmd.handle_options options
129
128
  without_safe { cmd.execute }
data/lib/kontena_cli.rb CHANGED
@@ -41,7 +41,7 @@ module Kontena
41
41
  end
42
42
 
43
43
  def self.simple_terminal?
44
- ENV['KONTENA_SIMPLE_TERM'] || !$stdout.tty?
44
+ on_windows? || ENV['KONTENA_SIMPLE_TERM'] || !$stdout.tty?
45
45
  end
46
46
 
47
47
  def self.pastel
@@ -115,7 +115,6 @@ describe Kontena::Cli::Cloud::Master::AddCommand do
115
115
 
116
116
  subject.register_current
117
117
  end
118
-
119
118
  end
120
119
  end
121
120
 
@@ -128,35 +128,42 @@ describe Kontena::Cli::Common do
128
128
  end
129
129
  end
130
130
 
131
- describe '#confirm_command' do
132
- it 'returns true if input matches' do
133
- allow(subject).to receive(:ask).and_return('name-to-confirm')
134
-
135
- expect(subject.confirm_command('name-to-confirm')).to be_truthy
136
- expect{subject.confirm_command('name-to-confirm')}.to_not raise_error
131
+ context 'confirm' do
132
+ before(:each) do
133
+ expect($stdout).to receive(:tty?).at_least(:once).and_return(true)
134
+ expect($stdin).to receive(:tty?).at_least(:once).and_return(true)
137
135
  end
138
136
 
139
- it 'raises error unless input matches' do
140
- expect(subject).to receive(:ask).and_return('wrong-name')
141
- expect(subject).to receive(:error).with(/did not match/)
137
+ describe '#confirm_command' do
138
+ it 'returns true if input matches' do
139
+ allow(subject).to receive(:ask).and_return('name-to-confirm')
142
140
 
143
- subject.confirm_command('name-to-confirm')
144
- end
145
- end
141
+ expect(subject.confirm_command('name-to-confirm')).to be_truthy
142
+ expect{subject.confirm_command('name-to-confirm')}.to_not raise_error
143
+ end
146
144
 
147
- describe '#confirm' do
148
- it 'returns true if confirmed' do
149
- allow(subject.prompt).to receive(:yes?).and_return(true)
145
+ it 'raises error unless input matches' do
146
+ expect(subject).to receive(:ask).and_return('wrong-name')
147
+ expect(subject).to receive(:error).with(/did not match/)
150
148
 
151
- expect(subject.confirm).to be_truthy
152
- expect{subject.confirm}.to_not raise_error
149
+ subject.confirm_command('name-to-confirm')
150
+ end
153
151
  end
154
152
 
155
- it 'raises error unless confirmed' do
156
- expect(subject.prompt).to receive(:yes?).and_return(false)
157
- expect(subject).to receive(:error).with(/Aborted/)
153
+ describe '#confirm' do
154
+ it 'returns true if confirmed' do
155
+ allow(subject.prompt).to receive(:yes?).and_return(true)
158
156
 
159
- subject.confirm
157
+ expect(subject.confirm).to be_truthy
158
+ expect{subject.confirm}.to_not raise_error
159
+ end
160
+
161
+ it 'raises error unless confirmed' do
162
+ expect(subject.prompt).to receive(:yes?).and_return(false)
163
+ expect(subject).to receive(:error).with(/Aborted/)
164
+
165
+ subject.confirm
166
+ end
160
167
  end
161
168
  end
162
169
 
@@ -0,0 +1,28 @@
1
+ require_relative "../../../spec_helper"
2
+ require "kontena/cli/master/init_cloud_command"
3
+
4
+ describe Kontena::Cli::Master::InitCloudCommand do
5
+
6
+ include ClientHelpers
7
+ include RequirementsHelper
8
+
9
+ mock_current_master
10
+
11
+ let(:cloud_client) { double(:cc) }
12
+
13
+ before(:each) do
14
+ allow(subject).to receive(:current_account).and_return('foo')
15
+ allow(subject).to receive(:cloud_auth?).and_return(true)
16
+ end
17
+
18
+ describe '#execute' do
19
+ expect_to_require_current_master
20
+ expect_to_require_current_master_token
21
+ end
22
+
23
+ it 'runs the invite self after deploy callback' do
24
+ expect(Kontena).to receive(:run).with('cloud master add --current --force').and_return(true)
25
+ expect_any_instance_of(Kontena::Callbacks::InviteSelfAfterDeploy).to receive(:after).and_return(true)
26
+ subject.run(['--force'])
27
+ end
28
+ end
@@ -25,7 +25,7 @@ describe Kontena::Cli::Services::LinkCommand do
25
25
  it 'aborts if service is already linked' do
26
26
  allow(client).to receive(:get).and_return({
27
27
  'links' => [
28
- {'alias' => 'service-b', 'grid_service_id' => "grid/service-b"}
28
+ {'alias' => 'service-b', 'id' => "grid/null/service-b"}
29
29
  ]
30
30
  })
31
31
  expect {
@@ -35,7 +35,7 @@ describe Kontena::Cli::Services::LinkCommand do
35
35
 
36
36
  it 'sends link to master' do
37
37
  expect(client).to receive(:put).with(
38
- 'services/test-grid/null/service-a', {links: [{name: 'service-b', alias: 'service-b'}]}
38
+ 'services/test-grid/null/service-a', {links: [{name: 'null/service-b', alias: 'service-b'}]}
39
39
  )
40
40
  subject.run(['service-a', 'service-b'])
41
41
  end
@@ -9,7 +9,7 @@ describe Kontena::Cli::Services::UnlinkCommand do
9
9
  before(:each) do
10
10
  allow(client).to receive(:get).and_return({
11
11
  'links' => [
12
- {'alias' => 'service-b', 'grid_service_id' => "grid/service-b"}
12
+ {'alias' => 'service-b', 'id' => "test-grid/null/service-b", 'name' => 'service-b'}
13
13
  ]
14
14
  })
15
15
  end
@@ -335,12 +335,16 @@ describe Kontena::Cli::Stacks::ServiceGenerator do
335
335
  expect(result['deploy_opts']['interval']).to eq(60)
336
336
  end
337
337
 
338
- it 'does not return deploy_opts if no deploy options are defined' do
338
+ it 'returns nil values if no deploy options are defined' do
339
339
  data = {
340
340
  'image' => 'foo/bar:latest'
341
341
  }
342
342
  result = subject.send(:parse_data, data)
343
- expect(result['deploy_opts']).to be_nil
343
+ expect(result['deploy_opts']).to eq({
344
+ 'interval' => nil,
345
+ 'min_health' => nil,
346
+ 'wait_for_port' => nil
347
+ })
344
348
  end
345
349
  end
346
350
 
@@ -381,5 +385,5 @@ describe Kontena::Cli::Stacks::ServiceGenerator do
381
385
  expect(result['secrets']).to be_nil
382
386
  end
383
387
  end
384
- end
388
+ end
385
389
  end
@@ -0,0 +1,27 @@
1
+ require 'opto'
2
+ require 'kontena_cli'
3
+ require 'kontena/cli/stacks/yaml/opto'
4
+
5
+ describe Kontena::Cli::Stacks::YAML::Prompt do
6
+
7
+ describe 'echoing' do
8
+ let(:option_without_echo) { Opto::Option.new(type: 'string', name: 'foo', from: 'prompt', echo: false) }
9
+ let(:option_with_echo) { Opto::Option.new(type: 'string', name: 'foo', from: 'prompt') }
10
+ let(:prompt) { double(:prompt) }
11
+
12
+ before(:each) do
13
+ allow(Kontena).to receive(:prompt).and_return(prompt)
14
+ end
15
+
16
+ it 'turns echo off when variable has "echo: false"' do
17
+ expect(prompt).to receive(:ask).with(/Enter/, hash_including(echo: false))
18
+ option_without_echo.value
19
+ end
20
+
21
+ it 'keeps echo on when variable does not have "echo: false"' do
22
+ expect(prompt).to receive(:ask).with(/Enter/, hash_not_including(echo: false))
23
+ option_with_echo.value
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,54 @@
1
+ require 'opto'
2
+ require 'kontena/cli/stacks/yaml/opto/service_link_resolver'
3
+
4
+ describe Kontena::Cli::Stacks::YAML::Opto::Resolvers::ServiceLink do
5
+ let(:subject) do
6
+ described_class.new({'prompt' => 'foo'})
7
+ end
8
+
9
+ describe '#resolve' do
10
+ it 'returns nil if no matching services' do
11
+ expect(subject).to receive(:get_services).and_return([])
12
+ expect(subject).not_to receive(:prompt)
13
+ expect(subject.resolve).to be_nil
14
+ end
15
+
16
+ it 'prompts user if matching services' do
17
+ prompt = double(:prompt)
18
+ allow(subject).to receive(:prompt).and_return(prompt)
19
+ expect(prompt).to receive(:select).and_return('null/bar')
20
+ expect(subject).to receive(:get_services).and_return([
21
+ {'id' => 'foo/null/bar'}
22
+ ])
23
+ expect(subject.resolve).to eq('null/bar')
24
+ end
25
+ end
26
+
27
+ describe '#default_index' do
28
+ let(:option) do
29
+ ::Opto::Option.new(default: 'foo/bar')
30
+ end
31
+ let(:subject) do
32
+ described_class.new({'prompt' => 'foo'}, option)
33
+ end
34
+
35
+ it 'returns matching index' do
36
+ services = [
37
+ {'id' => 'test/foo/foo'},
38
+ {'id' => 'test/foo/bar'},
39
+ {'id' => 'test/asd/asd'}
40
+ ]
41
+ index = subject.default_index(services)
42
+ expect(index).to eq(2)
43
+ end
44
+
45
+ it 'returns 0 if no matches' do
46
+ services = [
47
+ {'id' => 'test/foo/foo'},
48
+ {'id' => 'test/asd/asd'}
49
+ ]
50
+ index = subject.default_index(services)
51
+ expect(index).to eq(0)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,73 @@
1
+ require 'opto'
2
+ require 'kontena/cli/stacks/yaml/opto/vault_cert_prompt_resolver'
3
+
4
+ describe Kontena::Cli::Stacks::YAML::Opto::Resolvers::VaultCertPrompt do
5
+ describe '#resolve' do
6
+ it 'returns nil if no matching secrets' do
7
+ expect(subject).to receive(:get_secrets).and_return([])
8
+ expect(subject).not_to receive(:prompt)
9
+ expect(subject.resolve).to be_nil
10
+ end
11
+
12
+ it 'prompts user if matching secrets' do
13
+ prompt = double(:prompt)
14
+ allow(subject).to receive(:prompt).and_return(prompt)
15
+ expect(prompt).to receive(:multi_select).and_return('ssl-cert')
16
+ expect(subject).to receive(:get_secrets).and_return([{'name' => 'ssl-cert'}])
17
+ subject.resolve
18
+ end
19
+ end
20
+
21
+ describe '#default_indexes' do
22
+ let(:option) do
23
+ Opto::Option.new(default: ['ssl-cert-1', 'ssl-cert-3'])
24
+ end
25
+
26
+ let(:subject) do
27
+ described_class.new('foo', option)
28
+ end
29
+
30
+ it 'returns all default indexes if found' do
31
+ secrets = [
32
+ {'name' => 'ssl-cert-1'},
33
+ {'name' => 'ssl-cert-2'},
34
+ {'name' => 'ssl-cert-3'}
35
+ ]
36
+ indexes = subject.default_indexes(secrets)
37
+ expect(indexes).to eq([1, 3])
38
+ end
39
+
40
+ it 'returns partially found defaults' do
41
+ secrets = [
42
+ {'name' => 'ssl-cert-a'},
43
+ {'name' => 'ssl-cert-b'},
44
+ {'name' => 'ssl-cert-3'}
45
+ ]
46
+ indexes = subject.default_indexes(secrets)
47
+ expect(indexes).to eq([3])
48
+ end
49
+
50
+ it 'returns empty array if no matches' do
51
+ secrets = [
52
+ {'name' => 'ssl-cert-a'}
53
+ ]
54
+ indexes = subject.default_indexes(secrets)
55
+ expect(indexes).to eq([])
56
+ end
57
+
58
+ it 'returns empty array if no secrets' do
59
+ secrets = []
60
+ indexes = subject.default_indexes(secrets)
61
+ expect(indexes).to eq([])
62
+ end
63
+
64
+ it 'returns empty array if no defaults' do
65
+ secrets = [
66
+ {'name' => 'ssl-cert-a'}
67
+ ]
68
+ allow(subject.option).to receive(:default).and_return([])
69
+ indexes = subject.default_indexes(secrets)
70
+ expect(indexes).to eq([])
71
+ end
72
+ end
73
+ end
@@ -1,28 +1,37 @@
1
1
  require_relative '../spec_helper'
2
2
  require 'kontena_cli'
3
+ require 'kontena/light_prompt'
3
4
 
4
5
  describe Kontena do
5
- let(:subject) { described_class }
6
+ context 'prompt' do
7
+ it 'uses light prompt on windows' do
8
+ allow(ENV).to receive(:[]).with('OS').and_return('Windows_NT')
9
+ expect(Kontena.prompt).to be_kind_of(Kontena::LightPrompt)
10
+ end
11
+ end
6
12
 
7
13
  describe '#run' do
8
- let(:whoami) { double(:whoami_command) }
14
+ let(:whoami) { double(:whoami) }
9
15
 
10
16
  before(:each) do
11
- expect(Kontena::MainCommand).to receive(:new).and_call_original
12
17
  expect(Kontena::Cli::WhoamiCommand).to receive(:new).and_return(whoami)
13
18
  expect(whoami).to receive(:run).with(['--bash-completion-path']).and_return(true)
14
19
  end
15
20
 
16
21
  it 'accepts a command line as string' do
17
- subject.run('whoami --bash-completion-path')
22
+
23
+ Kontena.run('whoami --bash-completion-path')
18
24
  end
19
25
 
20
26
  it 'accepts a command line as a list of parameters' do
21
- subject.run('whoami', '--bash-completion-path')
27
+ Kontena.run('whoami', '--bash-completion-path')
22
28
  end
23
29
 
24
30
  it 'accepts a command line as an array' do
25
- subject.run(['whoami', '--bash-completion-path'])
31
+ Kontena.run(['whoami', '--bash-completion-path'])
26
32
  end
27
33
  end
28
34
  end
35
+
36
+
37
+
@@ -0,0 +1,16 @@
1
+ require_relative '../spec_helper'
2
+ require 'kontena/main_command'
3
+
4
+ describe Kontena::MainCommand do
5
+ let(:subject) { described_class.new('kontena') }
6
+
7
+ describe '--version' do
8
+ it 'outputs the version number and exits' do
9
+ expect do
10
+ expect{subject.run(['--version'])}.to output(/kontena-cli #{Kontena::Cli::VERSION}/).to_stdout
11
+ end.to raise_error(SystemExit) do |exc|
12
+ expect(exc.status).to eq 0
13
+ end
14
+ end
15
+ end
16
+ 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: 1.1.0
4
+ version: 1.1.1.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-02-03 00:00:00.000000000 Z
11
+ date: 2017-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -140,16 +140,16 @@ dependencies:
140
140
  name: opto
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - "~>"
143
+ - - '='
144
144
  - !ruby/object:Gem::Version
145
- version: 1.8.0
145
+ version: 1.8.3
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.0
152
+ version: 1.8.3
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: semantic
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -433,6 +433,7 @@ files:
433
433
  - lib/kontena/cli/stacks/yaml/custom_validators/extends_validator.rb
434
434
  - lib/kontena/cli/stacks/yaml/custom_validators/hooks_validator.rb
435
435
  - lib/kontena/cli/stacks/yaml/custom_validators/secrets_validator.rb
436
+ - lib/kontena/cli/stacks/yaml/opto.rb
436
437
  - lib/kontena/cli/stacks/yaml/opto/prompt_resolver.rb
437
438
  - lib/kontena/cli/stacks/yaml/opto/service_instances_resolver.rb
438
439
  - lib/kontena/cli/stacks/yaml/opto/service_link_resolver.rb
@@ -555,6 +556,7 @@ files:
555
556
  - spec/kontena/cli/helpers/log_helper_spec.rb
556
557
  - spec/kontena/cli/main_command_spec.rb
557
558
  - spec/kontena/cli/master/current_command_spec.rb
559
+ - spec/kontena/cli/master/init_cloud_command_spec.rb
558
560
  - spec/kontena/cli/master/login_command_spec.rb
559
561
  - spec/kontena/cli/master/logout_command_spec.rb
560
562
  - spec/kontena/cli/master/use_command_spec.rb
@@ -582,6 +584,9 @@ files:
582
584
  - spec/kontena/cli/stacks/service_generator_v2_spec.rb
583
585
  - spec/kontena/cli/stacks/show_command_spec.rb
584
586
  - spec/kontena/cli/stacks/upgrade_command_spec.rb
587
+ - spec/kontena/cli/stacks/yaml/opto/prompt_resolver_spec.rb
588
+ - spec/kontena/cli/stacks/yaml/opto/service_link_resolver_spec.rb
589
+ - spec/kontena/cli/stacks/yaml/opto/vault_cert_prompt_resolver_spec.rb
585
590
  - spec/kontena/cli/stacks/yaml/reader_spec.rb
586
591
  - spec/kontena/cli/stacks/yaml/service_extender_spec.rb
587
592
  - spec/kontena/cli/stacks/yaml/validator_v3_spec.rb
@@ -592,6 +597,7 @@ files:
592
597
  - spec/kontena/client_spec.rb
593
598
  - spec/kontena/config_spec.rb
594
599
  - spec/kontena/kontena_cli_spec.rb
600
+ - spec/kontena/main_command_spec.rb
595
601
  - spec/kontena/plugin_manager_spec.rb
596
602
  - spec/spec_helper.rb
597
603
  - spec/support/client_helpers.rb
@@ -615,9 +621,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
615
621
  version: 2.1.0
616
622
  required_rubygems_version: !ruby/object:Gem::Requirement
617
623
  requirements:
618
- - - ">="
624
+ - - ">"
619
625
  - !ruby/object:Gem::Version
620
- version: '0'
626
+ version: 1.3.1
621
627
  requirements: []
622
628
  rubyforge_project:
623
629
  rubygems_version: 2.6.8
@@ -683,6 +689,7 @@ test_files:
683
689
  - spec/kontena/cli/helpers/log_helper_spec.rb
684
690
  - spec/kontena/cli/main_command_spec.rb
685
691
  - spec/kontena/cli/master/current_command_spec.rb
692
+ - spec/kontena/cli/master/init_cloud_command_spec.rb
686
693
  - spec/kontena/cli/master/login_command_spec.rb
687
694
  - spec/kontena/cli/master/logout_command_spec.rb
688
695
  - spec/kontena/cli/master/use_command_spec.rb
@@ -710,6 +717,9 @@ test_files:
710
717
  - spec/kontena/cli/stacks/service_generator_v2_spec.rb
711
718
  - spec/kontena/cli/stacks/show_command_spec.rb
712
719
  - spec/kontena/cli/stacks/upgrade_command_spec.rb
720
+ - spec/kontena/cli/stacks/yaml/opto/prompt_resolver_spec.rb
721
+ - spec/kontena/cli/stacks/yaml/opto/service_link_resolver_spec.rb
722
+ - spec/kontena/cli/stacks/yaml/opto/vault_cert_prompt_resolver_spec.rb
713
723
  - spec/kontena/cli/stacks/yaml/reader_spec.rb
714
724
  - spec/kontena/cli/stacks/yaml/service_extender_spec.rb
715
725
  - spec/kontena/cli/stacks/yaml/validator_v3_spec.rb
@@ -720,6 +730,7 @@ test_files:
720
730
  - spec/kontena/client_spec.rb
721
731
  - spec/kontena/config_spec.rb
722
732
  - spec/kontena/kontena_cli_spec.rb
733
+ - spec/kontena/main_command_spec.rb
723
734
  - spec/kontena/plugin_manager_spec.rb
724
735
  - spec/spec_helper.rb
725
736
  - spec/support/client_helpers.rb