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 +4 -4
- data/VERSION +1 -1
- data/lib/kontena/autoload_core.rb +18 -0
- data/lib/kontena/callbacks/master/deploy/05_before_deploy_configuration_wizard.rb +1 -0
- data/lib/kontena/cli/cloud/master/update_command.rb +1 -1
- data/lib/kontena/cli/common.rb +6 -2
- data/lib/kontena/cli/containers/exec_command.rb +17 -12
- data/lib/kontena/cli/helpers/exec_helper.rb +43 -23
- data/lib/kontena/cli/services/exec_command.rb +34 -20
- data/lib/kontena/plugin_manager.rb +11 -27
- data/lib/kontena/plugin_manager/rubygems_client.rb +40 -0
- data/lib/kontena/websocket/client/connection.rb +1 -1
- data/lib/kontena_cli.rb +2 -1
- data/spec/kontena/cli/helpers/exec_helper_spec.rb +44 -0
- data/spec/kontena/cli/services/exec_command_spec.rb +17 -17
- data/spec/kontena/plugin_manager/rubygems_client_spec.rb +50 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56f2fe0867115aff48b183538824e13eb77810f8
|
4
|
+
data.tar.gz: 67d15d8bcf84c128a38b7c70ea65d65f8444d696
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcb08e8ae2a117aee8c7e7e47b332d8fc5cd493bafd1e12c02e70e14d2a47295c10e71f33772d5806a13fc01bf807d42c4314d160e8e54f97bdfb2b0bdda8980
|
7
|
+
data.tar.gz: bec4e80d3179561182a5d59bd09f5d5d8c8f7ba47812b57fca560a80cb9e33980d76f2c3c098776a2042fdde114e3a17c94c2c29492ad63f210c9aadc4623f1e
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.3.0.
|
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'
|
@@ -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
|
-
"
|
40
|
+
"user/masters/#{master_id}",
|
41
41
|
{ data: { attributes: attrs.reject{ |k, _| ['client-id', 'client-secret'].include?(k) } } }
|
42
42
|
)
|
43
43
|
|
data/lib/kontena/cli/common.rb
CHANGED
@@ -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
|
-
|
20
|
-
|
21
|
-
url
|
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
|
-
|
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
|
-
|
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
|
-
|
39
|
-
|
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.
|
12
|
-
|
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 [
|
26
|
+
# @param [Hash] msg
|
20
27
|
def handle_message(msg)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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]
|
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
|
-
|
48
|
-
|
49
|
-
|
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 ["
|
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
|
-
|
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|
|
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
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/kontena_cli.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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.
|
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-
|
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
|