kontena-cli 1.3.0.rc2 → 1.3.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '09472d142a0b905453dae67fd9a5f773d8cacb78'
4
- data.tar.gz: 0fbe77649ccdb7fcb79a5d15b093235ea1dee186
3
+ metadata.gz: 56f2fe0867115aff48b183538824e13eb77810f8
4
+ data.tar.gz: 67d15d8bcf84c128a38b7c70ea65d65f8444d696
5
5
  SHA512:
6
- metadata.gz: 909d89da90b3b8101329c00bb5971200e8404368457e475620ff1a8f742ed4dcce1b1899fb6859bff861e0d7416f1b691d313ff532eb9675f3e68d2d1faa2b54
7
- data.tar.gz: 542ec9dce844b9aa9e4e420c3f6dfcf610e65140f144ee531a777f8cdc3aa418eb672894287b2ffd934a2bd8af4456ed01411d00bcb00f0cbf1dee899adad79a
6
+ metadata.gz: dcb08e8ae2a117aee8c7e7e47b332d8fc5cd493bafd1e12c02e70e14d2a47295c10e71f33772d5806a13fc01bf807d42c4314d160e8e54f97bdfb2b0bdda8980
7
+ data.tar.gz: bec4e80d3179561182a5d59bd09f5d5d8c8f7ba47812b57fca560a80cb9e33980d76f2c3c098776a2042fdde114e3a17c94c2c29492ad63f210c9aadc4623f1e
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0.rc2
1
+ 1.3.0.rc3
@@ -0,0 +1,18 @@
1
+ # stdlib
2
+ autoload :JSON, 'json'
3
+ autoload :YAML, 'safe_yaml'
4
+ autoload :URI, 'uri'
5
+ autoload :Logger, 'logger'
6
+ autoload :FileUtils, 'fileutils'
7
+ autoload :Forwardable, 'forwardable'
8
+ autoload :Singleton, 'singleton'
9
+ autoload :Observable, 'observer'
10
+ autoload :Pathname, 'pathname'
11
+ autoload :DateTime, 'date'
12
+ autoload :Date, 'date'
13
+ autoload :Base64, 'base64'
14
+ autoload :SecureRandom, 'securerandom'
15
+
16
+ # dependencies
17
+ autoload :Excon, 'excon'
18
+ autoload :Opto, 'opto'
@@ -50,6 +50,7 @@ module Kontena
50
50
  end
51
51
 
52
52
  def before
53
+ extend Kontena::Cli::Common
53
54
  unless_param(:name) do
54
55
  if command.cloud_master_id
55
56
  response = spinner "Receiving Master information from Kontena Cloud" do
@@ -37,7 +37,7 @@ module Kontena::Cli::Cloud::Master
37
37
  attrs["owner"] = self.owner if self.owner
38
38
 
39
39
  response = cloud_client.put(
40
- "master",
40
+ "user/masters/#{master_id}",
41
41
  { data: { attributes: attrs.reject{ |k, _| ['client-id', 'client-secret'].include?(k) } } }
42
42
  )
43
43
 
@@ -2,7 +2,13 @@ require 'forwardable'
2
2
  require 'kontena_cli'
3
3
 
4
4
  module Kontena
5
+ autoload :Client, 'kontena/client'
6
+
5
7
  module Cli
8
+ autoload :ShellSpinner, 'kontena/cli/spinner'
9
+ autoload :Spinner, 'kontena/cli/spinner'
10
+ autoload :Config, 'kontena/cli/config'
11
+
6
12
  module Common
7
13
  extend Forwardable
8
14
 
@@ -27,12 +33,10 @@ module Kontena
27
33
  end
28
34
 
29
35
  def spinner(msg, &block)
30
- require 'kontena/cli/spinner' unless Kontena::Cli.const_defined?(:Spinner)
31
36
  Kontena::Cli::Spinner.spin(msg, &block)
32
37
  end
33
38
 
34
39
  def config
35
- require 'kontena/cli/config' unless Kontena::Cli.const_defined?(:Config)
36
40
  Kontena::Cli::Config.instance
37
41
  end
38
42
 
@@ -10,36 +10,41 @@ module Kontena::Cli::Containers
10
10
  parameter "CMD ...", "Command"
11
11
 
12
12
  option ["--shell"], :flag, "Execute as a shell command"
13
- option ["--interactive"], :flag, "Keep stdin open"
13
+ option ["-i", "--interactive"], :flag, "Keep stdin open"
14
+ option ["-t", "--tty"], :flag, "Allocate a pseudo-TTY"
14
15
 
15
16
  def execute
17
+ exit_with_error "the input device is not a TTY" if tty? && !STDIN.tty?
18
+
16
19
  require_api_url
17
20
  token = require_token
18
21
  cmd = JSON.dump({cmd: cmd_list})
19
- url = ws_url("#{current_grid}/#{container_id}")
20
- url << 'interactive=true&' if interactive?
21
- url << 'shell=true' if shell?
22
+ queue = Queue.new
23
+ stdin_reader = nil
24
+ url = ws_url("#{current_grid}/#{container_id}", interactive: interactive?, shell: shell?, tty: tty?)
22
25
  ws = connect(url, token)
23
-
24
26
  ws.on :message do |msg|
25
- self.handle_message(msg)
27
+ data = parse_message(msg)
28
+ queue << data if data.is_a?(Hash)
26
29
  end
27
30
  ws.on :open do
28
31
  ws.text(cmd)
32
+ stdin_reader = self.stream_stdin_to_ws(ws) if self.interactive?
29
33
  end
30
34
  ws.on :close do |e|
31
35
  if e.reason.include?('code: 404')
32
- exit_with_error('Not found')
36
+ queue << {'exit' => 1, 'message' => 'Not found'}
33
37
  else
34
- exit 1
38
+ queue << {'exit' => 1}
35
39
  end
36
40
  end
37
41
  ws.connect
38
- if interactive?
39
- stream_stdin_to_ws(ws).join
40
- else
41
- sleep
42
+ while msg = queue.pop
43
+ self.handle_message(msg)
42
44
  end
45
+ rescue SystemExit
46
+ stdin_reader.kill if stdin_reader
47
+ raise
43
48
  end
44
49
  end
45
50
  end
@@ -3,35 +3,41 @@ require_relative '../../websocket/client'
3
3
  module Kontena::Cli::Helpers
4
4
  module ExecHelper
5
5
 
6
- # @param [WebSocket::Client::Simple] ws
6
+ # @param [WebSocket::Client::Simple] ws
7
7
  # @return [Thread]
8
8
  def stream_stdin_to_ws(ws)
9
9
  require 'io/console'
10
10
  Thread.new {
11
- STDIN.raw {
12
- while char = STDIN.readpartial(1024)
11
+ if STDIN.tty?
12
+ STDIN.raw {
13
+ while char = STDIN.readpartial(1024)
14
+ ws.text(JSON.dump({ stdin: char }))
15
+ end
16
+ }
17
+ else
18
+ while char = STDIN.gets
13
19
  ws.text(JSON.dump({ stdin: char }))
14
20
  end
15
- }
21
+ ws.text(JSON.dump({ stdin: nil }))
22
+ end
16
23
  }
17
24
  end
18
25
 
19
- # @param [Websocket::Frame::Incoming] msg
26
+ # @param [Hash] msg
20
27
  def handle_message(msg)
21
- data = parse_message(msg)
22
- if data.is_a?(Hash)
23
- if data.has_key?('exit')
24
- exit data['exit'].to_i
25
- elsif data.has_key?('stream')
26
- if data['stream'] == 'stdout'
27
- $stdout << data['chunk']
28
- else
29
- $stderr << data['chunk']
30
- end
28
+ if msg.has_key?('exit')
29
+ if msg['message']
30
+ exit_with_error(msg['message'])
31
+ else
32
+ exit msg['exit'].to_i
33
+ end
34
+ elsif msg.has_key?('stream')
35
+ if msg['stream'] == 'stdout'
36
+ $stdout << msg['chunk']
37
+ else
38
+ $stderr << msg['chunk']
31
39
  end
32
40
  end
33
- rescue => exc
34
- $stderr << "#{exc.class.name}: #{exc.message}"
35
41
  end
36
42
 
37
43
  # @param [Websocket::Frame::Incoming] msg
@@ -41,12 +47,26 @@ module Kontena::Cli::Helpers
41
47
  nil
42
48
  end
43
49
 
44
- # @param [String] container_id
50
+ # @param container_id [String] The container id
51
+ # @param interactive [Boolean] Interactive TTY on/off
52
+ # @param shell [Boolean] Shell on/of
53
+ # @param tty [Boolean] TTY on/of
45
54
  # @return [String]
46
- def ws_url(container_id)
47
- url = require_current_master.url
48
- url << '/' unless url.end_with?('/')
49
- "#{url.sub('http', 'ws')}v1/containers/#{container_id}/exec?"
55
+ def ws_url(container_id, interactive: false, shell: false, tty: false)
56
+ require 'uri' unless Object.const_defined?(:URI)
57
+ extend Kontena::Cli::Common unless self.respond_to?(:require_current_master)
58
+
59
+ url = URI.parse(require_current_master.url)
60
+ url.scheme = url.scheme.sub('http', 'ws')
61
+ url.path = "/v1/containers/#{container_id}/exec"
62
+ if shell || interactive || tty
63
+ query = {}
64
+ query.merge!(interactive: true) if interactive
65
+ query.merge!(shell: true) if shell
66
+ query.merge!(tty: true) if tty
67
+ url.query = URI.encode_www_form(query)
68
+ end
69
+ url.to_s
50
70
  end
51
71
 
52
72
  # @param [String] url
@@ -64,4 +84,4 @@ module Kontena::Cli::Helpers
64
84
  Kontena::Websocket::Client.new(url, options)
65
85
  end
66
86
  end
67
- end
87
+ end
@@ -1,4 +1,5 @@
1
1
  require 'shellwords'
2
+ require 'json'
2
3
  require_relative 'services_helper'
3
4
  require_relative '../helpers/exec_helper'
4
5
 
@@ -12,10 +13,11 @@ module Kontena::Cli::Services
12
13
  parameter "NAME", "Service name"
13
14
  parameter "CMD ...", "Command"
14
15
 
15
- option ["-i", "--instance"], "INSTANCE", "Exec on given numbered instance, default first running" do |value| Integer(value) end
16
+ option ["--instance"], "INSTANCE", "Exec on given numbered instance, default first running" do |value| Integer(value) end
16
17
  option ["-a", "--all"], :flag, "Exec on all running instances"
17
18
  option ["--shell"], :flag, "Execute as a shell command"
18
- option ["--interactive"], :flag, "Keep stdin open"
19
+ option ["-i", "--interactive"], :flag, "Keep stdin open"
20
+ option ["-t", "--tty"], :flag, "Allocate a pseudo-TTY"
19
21
  option ["--skip"], :flag, "Skip failed instances when executing --all"
20
22
  option ["--silent"], :flag, "Do not show exec status"
21
23
  option ["--verbose"], :flag, "Show exec status"
@@ -24,12 +26,13 @@ module Kontena::Cli::Services
24
26
  requires_current_grid
25
27
 
26
28
  def execute
29
+ exit_with_error "the input device is not a TTY" if tty? && !STDIN.tty?
27
30
  exit_with_error "--interactive cannot be used with --all" if all? && interactive?
28
31
 
29
32
  service_containers = client.get("services/#{parse_service_id(name)}/containers")['containers']
30
33
  service_containers.sort_by! { |container| container['instance_number'] }
31
34
  running_containers = service_containers.select{|container| container['status'] == 'running' }
32
-
35
+
33
36
  exit_with_error "Service #{name} does not have any running containers" if running_containers.empty?
34
37
 
35
38
  if all?
@@ -51,13 +54,13 @@ module Kontena::Cli::Services
51
54
  exit_with_error "Service #{name} container #{container['name']} is not running, it is #{container['status']}"
52
55
  elsif interactive?
53
56
  interactive_exec(container)
54
- else
57
+ else
55
58
  exec_container(container)
56
59
  end
57
60
  else
58
61
  if interactive?
59
62
  interactive_exec(running_containers.first)
60
- else
63
+ else
61
64
  exec_container(running_containers.first)
62
65
  end
63
66
  end
@@ -89,17 +92,15 @@ module Kontena::Cli::Services
89
92
  cmd = JSON.dump({ cmd: cmd_list })
90
93
  exit_status = nil
91
94
  token = require_token
92
- url = ws_url(container['id'])
93
- url << 'shell=true' if shell?
94
- ws = connect(url, token)
95
+ ws = connect(url(container['id']), token)
95
96
  ws.on :message do |msg|
96
97
  data = base.parse_message(msg)
97
- if data
98
+ if data
98
99
  if data['exit']
99
100
  exit_status = data['exit'].to_i
100
101
  elsif data['stream'] == 'stdout'
101
102
  $stdout << data['chunk']
102
- else
103
+ else
103
104
  $stderr << data['chunk']
104
105
  end
105
106
  end
@@ -107,7 +108,7 @@ module Kontena::Cli::Services
107
108
  ws.on :open do
108
109
  ws.text(cmd)
109
110
  end
110
- ws.on :close do |e|s
111
+ ws.on :close do |e|
111
112
  exit_status = 1 if exit_status.nil? && e.code != 1000
112
113
  end
113
114
  ws.connect
@@ -121,22 +122,35 @@ module Kontena::Cli::Services
121
122
  def interactive_exec(container)
122
123
  token = require_token
123
124
  cmd = JSON.dump({ cmd: cmd_list })
124
- base = self
125
- url = ws_url(container['id']) << 'interactive=true'
126
- url << '&shell=true' if shell?
127
- ws = connect(url, token)
125
+ queue = Queue.new
126
+ stdin_stream = nil
127
+ ws = connect(url(container['id']), token)
128
128
  ws.on :message do |msg|
129
- base.handle_message(msg)
129
+ data = self.parse_message(msg)
130
+ queue << data if data.is_a?(Hash)
130
131
  end
131
132
  ws.on :open do
132
133
  ws.text(cmd)
134
+ stdin_stream = self.stream_stdin_to_ws(ws)
133
135
  end
134
136
  ws.on :close do |e|
135
- exit 1 if e.code != 1000
137
+ if e.code != 1000
138
+ queue << {'exit' => 1}
139
+ else
140
+ queue << {'exit' => 0}
141
+ end
136
142
  end
137
143
  ws.connect
138
-
139
- stream_stdin_to_ws(ws).join
144
+ while msg = queue.pop
145
+ self.handle_message(msg)
146
+ end
147
+ rescue SystemExit
148
+ stdin_stream.kill if stdin_stream
149
+ raise
150
+ end
151
+
152
+ def url(container_id)
153
+ ws_url(container_id, shell: shell?, interactive: interactive?, tty: tty?)
140
154
  end
141
155
  end
142
- end
156
+ end
@@ -2,6 +2,14 @@ require 'singleton'
2
2
 
3
3
  module Kontena
4
4
  class PluginManager
5
+ autoload :RubygemsClient, 'kontena/plugin_manager/rubygems_client'
6
+ Gem.autoload :DependencyInstaller, 'rubygems/dependency_installer'
7
+ Gem.autoload :Requirement, 'rubygems/requirement'
8
+ Gem.autoload :Uninstaller, 'rubygems/uninstaller'
9
+ Gem.autoload :Commands, 'rubygems/command'
10
+ Gem.autoload :DefaultUserInteraction, 'rubygems/user_interaction'
11
+ Gem.autoload :StreamUI, 'rubygems/user_interaction'
12
+ Gem::Commands.autoload :CleanupCommand, 'rubygems/commands/cleanup_command'
5
13
 
6
14
  include Singleton
7
15
 
@@ -22,9 +30,6 @@ module Kontena
22
30
  # @param pre [Boolean] install a prerelease version if available
23
31
  # @param version [String] install a specific version
24
32
  def install_plugin(plugin_name, pre: false, version: nil)
25
- require 'rubygems/dependency_installer'
26
- require 'rubygems/requirement'
27
-
28
33
  cmd = Gem::DependencyInstaller.new(
29
34
  document: false,
30
35
  force: true,
@@ -42,7 +47,6 @@ module Kontena
42
47
  installed = installed(plugin_name)
43
48
  raise "Plugin #{plugin_name} not installed" unless installed
44
49
 
45
- require 'rubygems/uninstaller'
46
50
  cmd = Gem::Uninstaller.new(
47
51
  installed.name,
48
52
  all: true,
@@ -56,31 +60,13 @@ module Kontena
56
60
  # Search rubygems for kontena plugins
57
61
  # @param pattern [String] optional search pattern
58
62
  def search_plugins(pattern = nil)
59
- client = Excon.new('https://rubygems.org')
60
- response = client.get(
61
- path: "/api/v1/search.json?query=#{prefix(pattern)}",
62
- headers: {
63
- 'Content-Type' => 'application/json',
64
- 'Accept' => 'application/json'
65
- }
66
- )
67
-
68
- JSON.parse(response.body) rescue nil
63
+ RubygemsClient.new.search(prefix(pattern))
69
64
  end
70
65
 
71
66
  # Retrieve plugin versions from rubygems
72
67
  # @param plugin_name [String]
73
68
  def gem_versions(plugin_name)
74
- client = Excon.new('https://rubygems.org')
75
- response = client.get(
76
- path: "/api/v1/versions/#{prefix(plugin_name)}.json",
77
- headers: {
78
- 'Content-Type' => 'application/json',
79
- 'Accept' => 'application/json'
80
- }
81
- )
82
- versions = JSON.parse(response.body)
83
- versions.map { |version| Gem::Version.new(version["number"]) }.sort.reverse
69
+ RubygemsClient.new.versions(prefix(plugin_name))
84
70
  end
85
71
 
86
72
  # Get the latest version number from rubygems
@@ -120,7 +106,6 @@ module Kontena
120
106
  # Runs gem cleanup, removes remains from previous versions
121
107
  # @param plugin_name [String]
122
108
  def cleanup_plugin(plugin_name)
123
- require 'rubygems/commands/cleanup_command'
124
109
  cmd = Gem::Commands::CleanupCommand.new
125
110
  options = []
126
111
  options += ['-q', '--no-verbose'] unless ENV["DEBUG"]
@@ -199,7 +184,7 @@ module Kontena
199
184
  $stderr.puts " To update the plugin, run 'kontena plugin install #{plugin_name}'"
200
185
  end
201
186
  rescue ScriptError, StandardError => ex
202
- warn " [#{Kontena.pastel.red('error')}] Failed to load plugin: #{spec.name}\n\tRerun the command with environment DEBUG=true set to get the full exception."
187
+ warn " [#{Kontena.pastel.red('error')}] Failed to load plugin: #{spec.name} from #{spec.gem_dir}\n\tRerun the command with environment DEBUG=true set to get the full exception."
203
188
  Kontena.logger.error(ex)
204
189
  end
205
190
  end
@@ -221,7 +206,6 @@ module Kontena
221
206
  end
222
207
 
223
208
  def use_dummy_ui
224
- require 'rubygems/user_interaction'
225
209
  Gem::DefaultUserInteraction.ui = dummy_ui
226
210
  end
227
211
 
@@ -0,0 +1,40 @@
1
+ require 'excon'
2
+ require 'json'
3
+
4
+ module Kontena
5
+ class PluginManager
6
+ class RubygemsClient
7
+
8
+ RUBYGEMS_URL = 'https://rubygems.org'
9
+ HEADERS = {
10
+ 'Content-Type' => 'application/json',
11
+ 'Accept' => 'application/json'
12
+ }
13
+
14
+ attr_reader :client
15
+
16
+ def initialize
17
+ @client = Excon.new(RUBYGEMS_URL)
18
+ end
19
+
20
+ def search(pattern = nil)
21
+ response = client.get(
22
+ path: "/api/v1/search.json?query=#{pattern}",
23
+ headers: HEADERS
24
+ )
25
+
26
+ JSON.parse(response.body)
27
+ end
28
+
29
+ def versions(gem_name)
30
+ response = client.get(
31
+ path: "/api/v1/versions/#{gem_name}.json",
32
+ headers: HEADERS
33
+ )
34
+ versions = JSON.parse(response.body)
35
+ versions.map { |version| Gem::Version.new(version["number"]) }.sort.reverse
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -46,7 +46,7 @@ module Kontena
46
46
  def_delegators :@client, :text, :binary, :ping, :close, :protocol, :on
47
47
 
48
48
  def write(buffer)
49
- @socket.write buffer
49
+ @socket.write(buffer)
50
50
  end
51
51
 
52
52
  def read_socket
@@ -1,4 +1,4 @@
1
- require 'logger'
1
+ require 'kontena/autoload_core'
2
2
 
3
3
  $KONTENA_START_TIME = Time.now.to_f
4
4
  at_exit do
@@ -9,6 +9,7 @@ end
9
9
  module Kontena
10
10
  module Cli
11
11
  autoload :Config, 'kontena/cli/config'
12
+ autoload :ShellSpinner, 'kontena/cli/spinner'
12
13
  autoload :Spinner, 'kontena/cli/spinner'
13
14
  autoload :Common, 'kontena/cli/common'
14
15
  autoload :TableGenerator, 'kontena/cli/table_generator'
@@ -0,0 +1,44 @@
1
+ require "kontena/cli/helpers/exec_helper"
2
+
3
+ describe Kontena::Cli::Helpers::ExecHelper do
4
+
5
+ include ClientHelpers
6
+
7
+ let(:described_class) do
8
+ Class.new do
9
+ include Kontena::Cli::Helpers::ExecHelper
10
+ def initialize(*args)
11
+ end
12
+ end
13
+ end
14
+
15
+ describe '#ws_url' do
16
+ it 'returns an exec url for a container id' do
17
+ expect(subject).to receive(:require_current_master).and_return(double(url: 'http://someurl/'))
18
+ expect(subject.ws_url('abcd1234')).to eq 'ws://someurl/v1/containers/abcd1234/exec'
19
+ end
20
+
21
+ it 'also works when the url does not have a trailing slash' do
22
+ expect(subject).to receive(:require_current_master).and_return(double(url: 'http://someurl'))
23
+ expect(subject.ws_url('abcd1234')).to eq 'ws://someurl/v1/containers/abcd1234/exec'
24
+ end
25
+
26
+ context 'query params' do
27
+ before(:each) do
28
+ allow(subject).to receive(:require_current_master).and_return(double(url: 'http://someurl'))
29
+ end
30
+
31
+ it 'can add the interactive query param' do
32
+ expect(subject.ws_url('abcd1234', interactive: true)).to eq 'ws://someurl/v1/containers/abcd1234/exec?interactive=true'
33
+ end
34
+
35
+ it 'can add the shell query param' do
36
+ expect(subject.ws_url('abcd1234', shell: true)).to eq 'ws://someurl/v1/containers/abcd1234/exec?shell=true'
37
+ end
38
+
39
+ it 'can add both query params' do
40
+ expect(subject.ws_url('abcd1234', shell: true, interactive: true)).to eq 'ws://someurl/v1/containers/abcd1234/exec?interactive=true&shell=true'
41
+ end
42
+ end
43
+ end
44
+ end
@@ -17,9 +17,9 @@ describe Kontena::Cli::Services::ExecCommand do
17
17
  def on(callback, &block)
18
18
  @callbacks[callback] = block
19
19
  if callback == :open
20
- Thread.new {
21
- sleep 0.01
22
- @callbacks[:open].call
20
+ Thread.new {
21
+ sleep 0.01
22
+ @callbacks[:open].call
23
23
  }
24
24
  end
25
25
  end
@@ -28,7 +28,7 @@ describe Kontena::Cli::Services::ExecCommand do
28
28
 
29
29
  def receive_message(msg)
30
30
  @callbacks[:message].call(Event.new(JSON.dump(msg)))
31
- rescue => exc
31
+ rescue => exc
32
32
  STDERR.puts exc.message
33
33
  end
34
34
  end
@@ -69,12 +69,12 @@ describe Kontena::Cli::Services::ExecCommand do
69
69
  end
70
70
 
71
71
  it "Executes on the running container by default" do
72
- expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec?", anything).and_return(ws_client)
72
+ expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec", anything).and_return(ws_client)
73
73
  expect(ws_client).to receive(:text) do |foo|
74
74
  ws_client.receive_message({'stream' => 'stdout', 'chunk' => "ok\n"})
75
75
  ws_client.receive_message({'exit' => 0})
76
76
  end
77
-
77
+
78
78
  expect {
79
79
  subject.run(['test-service', 'test'])
80
80
  }.to output("ok\n").to_stdout
@@ -107,7 +107,7 @@ describe Kontena::Cli::Services::ExecCommand do
107
107
 
108
108
  it "Executes on the first running container by default" do
109
109
  expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
110
- expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec?", anything).and_return(ws_client)
110
+ expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec", anything).and_return(ws_client)
111
111
  expect(ws_client).to receive(:text) do
112
112
  respond_ok(ws_client)
113
113
  end
@@ -118,7 +118,7 @@ describe Kontena::Cli::Services::ExecCommand do
118
118
 
119
119
  it "Executes on the first running container, even if they are ordered differently" do
120
120
  expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return({'containers' => service_containers['containers'].reverse })
121
- expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec?", anything).and_return(ws_client)
121
+ expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec", anything).and_return(ws_client)
122
122
  expect(ws_client).to receive(:text) do
123
123
  respond_ok(ws_client)
124
124
  end
@@ -129,7 +129,7 @@ describe Kontena::Cli::Services::ExecCommand do
129
129
 
130
130
  it "Executes on the first running container if given" do
131
131
  expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
132
- expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec?", anything).and_return(ws_client)
132
+ expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec", anything).and_return(ws_client)
133
133
  expect(ws_client).to receive(:text) do
134
134
  respond_ok(ws_client)
135
135
  end
@@ -140,7 +140,7 @@ describe Kontena::Cli::Services::ExecCommand do
140
140
 
141
141
  it "Executes on the second running container if given" do
142
142
  expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
143
- expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-2/exec?", anything).and_return(ws_client)
143
+ expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-2/exec", anything).and_return(ws_client)
144
144
  expect(ws_client).to receive(:text) do
145
145
  respond_ok(ws_client)
146
146
  end
@@ -160,13 +160,13 @@ describe Kontena::Cli::Services::ExecCommand do
160
160
 
161
161
  3.times do |i|
162
162
  ws_client = ws_client_class.new
163
- expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-#{i + 1}/exec?", anything).and_return(ws_client)
163
+ expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-#{i + 1}/exec", anything).and_return(ws_client)
164
164
  expect(ws_client).to receive(:text) do
165
165
  ws_client.receive_message({'stream' => 'stdout', 'chunk' => "test#{i + 1}\n"})
166
166
  ws_client.receive_message({'exit' => 0})
167
167
  end
168
168
  end
169
-
169
+
170
170
  expect {
171
171
  subject.run(['--silent', '--all', 'test-service', 'test'])
172
172
  }.to output("test1\ntest2\ntest3\n").to_stdout
@@ -174,7 +174,7 @@ describe Kontena::Cli::Services::ExecCommand do
174
174
 
175
175
  it "Stops if the first container fails" do
176
176
  expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
177
- expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec?", anything).and_return(ws_client)
177
+ expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-1/exec", anything).and_return(ws_client)
178
178
  expect(ws_client).to receive(:text) do
179
179
  respond_error(ws_client)
180
180
  end
@@ -188,11 +188,11 @@ describe Kontena::Cli::Services::ExecCommand do
188
188
  i = 1
189
189
  [:ok, :err].each do |status|
190
190
  ws_client = ws_client_class.new
191
- expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-#{i}/exec?", anything).and_return(ws_client)
191
+ expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-#{i}/exec", anything).and_return(ws_client)
192
192
  expect(ws_client).to receive(:text) do
193
193
  if status == :ok
194
194
  respond_ok(ws_client)
195
- else
195
+ else
196
196
  respond_error(ws_client)
197
197
  end
198
198
  end
@@ -209,11 +209,11 @@ describe Kontena::Cli::Services::ExecCommand do
209
209
  i = 1
210
210
  [:ok, :err, :ok].each do |status|
211
211
  ws_client = ws_client_class.new
212
- expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-#{i}/exec?", anything).and_return(ws_client)
212
+ expect(Kontena::Websocket::Client).to receive(:new).with("#{master_url}v1/containers/test-grid/host/test-service.container-#{i}/exec", anything).and_return(ws_client)
213
213
  expect(ws_client).to receive(:text) do
214
214
  if status == :ok
215
215
  respond_ok(ws_client)
216
- else
216
+ else
217
217
  respond_error(ws_client)
218
218
  end
219
219
  end
@@ -0,0 +1,50 @@
1
+ require 'kontena/plugin_manager/rubygems_client'
2
+ require 'json'
3
+
4
+ describe Kontena::PluginManager::RubygemsClient do
5
+ let(:subject) { described_class.new }
6
+ let(:client) { double }
7
+
8
+ before(:each) do
9
+ allow(subject).to receive(:client).and_return(client)
10
+ end
11
+
12
+ context '#search' do
13
+ it 'searches rubygems and returns a hash' do
14
+ expect(client)
15
+ .to receive(:get)
16
+ .with(
17
+ hash_including(
18
+ path: "/api/v1/search.json?query=foofoo",
19
+ headers: hash_including(
20
+ 'Content-Type' => 'application/json',
21
+ 'Accept' => 'application/json'
22
+ )
23
+ )
24
+ )
25
+ .and_return(double(body: JSON.dump(foo: 'bar')))
26
+ expect(subject.search('foofoo')['foo']).to eq 'bar'
27
+ end
28
+ end
29
+
30
+ context '#versions' do
31
+ it 'fetches version list from rubygems and returns an array of Gem::Versions' do
32
+ expect(client)
33
+ .to receive(:get)
34
+ .with(
35
+ hash_including(
36
+ path: "/api/v1/versions/foofoo.json",
37
+ headers: hash_including(
38
+ 'Content-Type' => 'application/json',
39
+ 'Accept' => 'application/json'
40
+ )
41
+ )
42
+ )
43
+ .and_return(double(body: JSON.dump([{'number' => '0.1.0'}, {'number' => '0.2.0'}])))
44
+ versions = subject.versions('foofoo')
45
+ expect(versions.first).to be_kind_of Gem::Version
46
+ expect(versions.first.to_s).to eq '0.2.0'
47
+ expect(versions.last.to_s).to eq '0.1.0'
48
+ end
49
+ end
50
+ 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.3.0.rc2
4
+ version: 1.3.0.rc3
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-06-02 00:00:00.000000000 Z
11
+ date: 2017-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -251,6 +251,7 @@ files:
251
251
  - examples/kontena-plugin-hello/lib/kontena_cli_plugin.rb
252
252
  - kontena-cli.gemspec
253
253
  - kontena-docker.sh
254
+ - lib/kontena/autoload_core.rb
254
255
  - lib/kontena/callback.rb
255
256
  - lib/kontena/callbacks/.gitkeep
256
257
  - lib/kontena/callbacks/auth/01_list_and_select_grid_after_master_auth.rb
@@ -516,6 +517,7 @@ files:
516
517
  - lib/kontena/machine/random_name.rb
517
518
  - lib/kontena/main_command.rb
518
519
  - lib/kontena/plugin_manager.rb
520
+ - lib/kontena/plugin_manager/rubygems_client.rb
519
521
  - lib/kontena/presets/github_auth_provider.yml
520
522
  - lib/kontena/presets/kontena_auth_provider.yml
521
523
  - lib/kontena/scripts/completer
@@ -608,6 +610,7 @@ files:
608
610
  - spec/kontena/cli/grids/trusted_subnets/remove_command_spec.rb
609
611
  - spec/kontena/cli/grids/update_command_spec.rb
610
612
  - spec/kontena/cli/grids/use_command_spec.rb
613
+ - spec/kontena/cli/helpers/exec_helper_spec.rb
611
614
  - spec/kontena/cli/helpers/log_helper_spec.rb
612
615
  - spec/kontena/cli/main_command_spec.rb
613
616
  - spec/kontena/cli/master/current_command_spec.rb
@@ -667,6 +670,7 @@ files:
667
670
  - spec/kontena/config_spec.rb
668
671
  - spec/kontena/kontena_cli_spec.rb
669
672
  - spec/kontena/main_command_spec.rb
673
+ - spec/kontena/plugin_manager/rubygems_client_spec.rb
670
674
  - spec/kontena/plugin_manager_spec.rb
671
675
  - spec/spec_helper.rb
672
676
  - spec/support/client_helpers.rb
@@ -761,6 +765,7 @@ test_files:
761
765
  - spec/kontena/cli/grids/trusted_subnets/remove_command_spec.rb
762
766
  - spec/kontena/cli/grids/update_command_spec.rb
763
767
  - spec/kontena/cli/grids/use_command_spec.rb
768
+ - spec/kontena/cli/helpers/exec_helper_spec.rb
764
769
  - spec/kontena/cli/helpers/log_helper_spec.rb
765
770
  - spec/kontena/cli/main_command_spec.rb
766
771
  - spec/kontena/cli/master/current_command_spec.rb
@@ -820,6 +825,7 @@ test_files:
820
825
  - spec/kontena/config_spec.rb
821
826
  - spec/kontena/kontena_cli_spec.rb
822
827
  - spec/kontena/main_command_spec.rb
828
+ - spec/kontena/plugin_manager/rubygems_client_spec.rb
823
829
  - spec/kontena/plugin_manager_spec.rb
824
830
  - spec/spec_helper.rb
825
831
  - spec/support/client_helpers.rb