kontena-cli 1.3.0.rc2 → 1.3.0.rc3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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