kontena-cli 1.4.0.pre1 → 1.4.0.pre2
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 +4 -4
- data/VERSION +1 -1
- data/kontena-cli.gemspec +1 -1
- data/lib/kontena/cli/common.rb +7 -2
- data/lib/kontena/cli/config.rb +33 -0
- data/lib/kontena/cli/containers/exec_command.rb +12 -33
- data/lib/kontena/cli/helpers/exec_helper.rb +165 -62
- data/lib/kontena/cli/services/exec_command.rb +39 -90
- data/lib/kontena/client.rb +2 -10
- data/lib/kontena/command.rb +4 -3
- data/omnibus/Gemfile +2 -15
- data/omnibus/Gemfile.lock +30 -123
- data/spec/kontena/cli/common_spec.rb +9 -9
- data/spec/kontena/cli/containers/exec_command_spec.rb +55 -0
- data/spec/kontena/cli/helpers/exec_helper_spec.rb +300 -18
- data/spec/kontena/cli/services/exec_command_spec.rb +82 -153
- data/spec/support/client_helpers.rb +6 -3
- metadata +9 -9
- data/lib/kontena/websocket/client.rb +0 -12
- data/lib/kontena/websocket/client/connection.rb +0 -65
@@ -1,57 +1,9 @@
|
|
1
|
-
require 'kontena/websocket/client'
|
2
1
|
require 'kontena/cli/services/exec_command'
|
3
2
|
|
4
3
|
describe Kontena::Cli::Services::ExecCommand do
|
5
4
|
include ClientHelpers
|
6
5
|
include OutputHelpers
|
7
6
|
|
8
|
-
let(:ws_client_class) do
|
9
|
-
Class.new do
|
10
|
-
|
11
|
-
Event = Struct.new(:data)
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
@callbacks = {}
|
15
|
-
end
|
16
|
-
|
17
|
-
def on(callback, &block)
|
18
|
-
@callbacks[callback] = block
|
19
|
-
if callback == :open
|
20
|
-
Thread.new {
|
21
|
-
sleep 0.01
|
22
|
-
@callbacks[:open].call
|
23
|
-
}
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def connect ; end
|
28
|
-
|
29
|
-
def receive_message(msg)
|
30
|
-
@callbacks[:message].call(Event.new(JSON.dump(msg)))
|
31
|
-
rescue => exc
|
32
|
-
STDERR.puts exc.message
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
let(:ws_client) do
|
38
|
-
ws_client_class.new
|
39
|
-
end
|
40
|
-
|
41
|
-
let(:master_url) do
|
42
|
-
subject.require_current_master.url.gsub('http', 'ws')
|
43
|
-
end
|
44
|
-
|
45
|
-
def respond_ok(ws_client)
|
46
|
-
ws_client.receive_message({'stream' => 'stdout', 'chunk' => "ok\n"})
|
47
|
-
ws_client.receive_message({'exit' => 0})
|
48
|
-
end
|
49
|
-
|
50
|
-
def respond_error(ws_client)
|
51
|
-
ws_client.receive_message({'stream' => 'stderr', 'chunk' => "error\n"})
|
52
|
-
ws_client.receive_message({'exit' => 1})
|
53
|
-
end
|
54
|
-
|
55
7
|
context "For a service with one running instance" do
|
56
8
|
let :service_containers do
|
57
9
|
{ 'containers' => [
|
@@ -69,21 +21,16 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
69
21
|
end
|
70
22
|
|
71
23
|
it "Executes on the running container by default" do
|
72
|
-
expect(
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
expect {
|
79
|
-
subject.run(['test-service', 'test'])
|
80
|
-
}.to output("ok\n").to_stdout
|
24
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-1', ['test'],
|
25
|
+
interactive: false, shell: false, tty: false,
|
26
|
+
).and_return(0)
|
27
|
+
expect{subject.run(['test-service', 'test'])}.to_not exit_with_error
|
81
28
|
end
|
82
29
|
end
|
83
30
|
|
84
31
|
context "For a service with multiple running instances" do
|
85
32
|
let :service_containers do
|
86
|
-
|
33
|
+
[
|
87
34
|
{
|
88
35
|
'id' => 'test-grid/host/test-service.container-1',
|
89
36
|
'name' => 'test-service.container-1',
|
@@ -102,126 +49,108 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
102
49
|
'instance_number' => 3,
|
103
50
|
'status' => 'running',
|
104
51
|
},
|
105
|
-
]
|
52
|
+
]
|
106
53
|
end
|
107
54
|
|
108
55
|
it "Executes on the first running container by default" do
|
109
|
-
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
|
110
|
-
|
111
|
-
expect(
|
112
|
-
|
56
|
+
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return('containers' => service_containers)
|
57
|
+
|
58
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-1', ['test'],
|
59
|
+
interactive: false, shell: false, tty: false,
|
60
|
+
) do
|
61
|
+
puts 'ok 1'
|
62
|
+
0
|
113
63
|
end
|
114
|
-
|
64
|
+
|
65
|
+
expect{
|
115
66
|
subject.run(['test-service', 'test'])
|
116
|
-
}.to
|
67
|
+
}.to output_lines [ 'ok 1' ]
|
117
68
|
end
|
118
69
|
|
119
70
|
it "Executes on the first running container, even if they are ordered differently" do
|
120
|
-
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(
|
121
|
-
|
122
|
-
expect(
|
123
|
-
|
124
|
-
|
125
|
-
expect
|
126
|
-
subject.run(['test-service', 'test'])
|
127
|
-
}.to output("ok\n").to_stdout
|
71
|
+
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return('containers' => service_containers.reverse)
|
72
|
+
|
73
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-1', ['test'],
|
74
|
+
interactive: false, shell: false, tty: false,
|
75
|
+
).and_return(0)
|
76
|
+
expect{subject.run(['test-service', 'test'])}.to_not exit_with_error
|
128
77
|
end
|
129
78
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
expect(ws_client).to receive(:text) do
|
134
|
-
respond_ok(ws_client)
|
79
|
+
context do
|
80
|
+
before do
|
81
|
+
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return('containers' => service_containers)
|
135
82
|
end
|
136
|
-
expect {
|
137
|
-
subject.run(['test-service', 'test'])
|
138
|
-
}.to output("ok\n").to_stdout
|
139
|
-
end
|
140
83
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
84
|
+
it "Executes on the first running container if given" do
|
85
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-1', ['test'],
|
86
|
+
interactive: false, shell: false, tty: false,
|
87
|
+
).and_return(0)
|
88
|
+
expect{subject.run(['--instance=1', 'test-service', 'test'])}.to_not exit_with_error
|
146
89
|
end
|
147
|
-
expect {
|
148
|
-
subject.run(['--instance', '2', 'test-service', 'test'])
|
149
|
-
}.to output("ok\n").to_stdout
|
150
|
-
end
|
151
90
|
|
152
|
-
|
153
|
-
|
91
|
+
it "Executes on the second running container if given" do
|
92
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-2', ['test'],
|
93
|
+
interactive: false, shell: false, tty: false,
|
94
|
+
).and_return(0)
|
95
|
+
expect{subject.run(['--instance=2', 'test-service', 'test'])}.to_not exit_with_error
|
96
|
+
end
|
154
97
|
|
155
|
-
|
156
|
-
|
98
|
+
it "Errors on a nonexistant container if given" do
|
99
|
+
expect{subject.run(['--instance=4', 'test-service', 'test'])}.to exit_with_error.and output(/Service test-service does not have container instance 4/).to_stderr
|
100
|
+
end
|
157
101
|
|
158
|
-
|
159
|
-
|
102
|
+
it "Executes on each running container" do
|
103
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-1', ['test'],
|
104
|
+
interactive: false, shell: false, tty: false,
|
105
|
+
).and_return(0)
|
106
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-2', ['test'],
|
107
|
+
interactive: false, shell: false, tty: false,
|
108
|
+
).and_return(0)
|
109
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-3', ['test'],
|
110
|
+
interactive: false, shell: false, tty: false,
|
111
|
+
).and_return(0)
|
160
112
|
|
161
|
-
|
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)
|
164
|
-
expect(ws_client).to receive(:text) do
|
165
|
-
ws_client.receive_message({'stream' => 'stdout', 'chunk' => "test#{i + 1}\n"})
|
166
|
-
ws_client.receive_message({'exit' => 0})
|
167
|
-
end
|
113
|
+
subject.run(['--silent', '--all', 'test-service', 'test'])
|
168
114
|
end
|
169
115
|
|
170
|
-
|
171
|
-
subject.
|
172
|
-
|
173
|
-
|
116
|
+
it "Stops if the first container fails" do
|
117
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-1', ['test'],
|
118
|
+
interactive: false, shell: false, tty: false,
|
119
|
+
) { $stderr << 'error'; 1 }
|
174
120
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
expect(ws_client).to receive(:text) do
|
179
|
-
respond_error(ws_client)
|
121
|
+
expect {
|
122
|
+
subject.run(['--silent', '--all', 'test-service', 'test'])
|
123
|
+
}.to exit_with_error.and output("error").to_stderr
|
180
124
|
end
|
181
|
-
expect {
|
182
|
-
subject.run(['--silent', '--all', 'test-service', 'test'])
|
183
|
-
}.to output("error\n").to_stderr
|
184
|
-
end
|
185
125
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
end
|
198
|
-
end
|
199
|
-
i += 1
|
126
|
+
it "Stops if the second container fails" do
|
127
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-1', ['test'],
|
128
|
+
interactive: false, shell: false, tty: false,
|
129
|
+
) { $stdout << 'ok 1'; 0 }
|
130
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-2', ['test'],
|
131
|
+
interactive: false, shell: false, tty: false,
|
132
|
+
) { $stderr << 'error 2'; 1 }
|
133
|
+
|
134
|
+
expect {
|
135
|
+
subject.run(['--silent', '--all', 'test-service', 'test'])
|
136
|
+
}.to exit_with_error.and output("ok 1").to_stdout.and output("error 2").to_stderr
|
200
137
|
end
|
201
|
-
expect {
|
202
|
-
subject.run(['--silent', '--all', 'test-service', 'test'])
|
203
|
-
}.to output("ok\n").to_stdout.and output("error\n").to_stderr
|
204
|
-
end
|
205
|
-
|
206
|
-
it "Keeps going if the second container fails when using --skip" do
|
207
|
-
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
|
208
138
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
expect(
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
139
|
+
it "Keeps going if the second container fails when using --skip" do
|
140
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-1', ['test'],
|
141
|
+
interactive: false, shell: false, tty: false,
|
142
|
+
) { puts 'ok 1'; 0}
|
143
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-2', ['test'],
|
144
|
+
interactive: false, shell: false, tty: false,
|
145
|
+
) { puts 'err 2'; 2 }
|
146
|
+
expect(subject).to receive(:container_exec).with('test-grid/host/test-service.container-3', ['test'],
|
147
|
+
interactive: false, shell: false, tty: false,
|
148
|
+
) { puts 'ok 3'; 0 }
|
149
|
+
|
150
|
+
expect {
|
151
|
+
subject.run(['--silent', '--all', '--skip', 'test-service', 'test'])
|
152
|
+
}.to exit_with_error.and output_lines ['ok 1', 'err 2', 'ok 3']
|
221
153
|
end
|
222
|
-
expect {
|
223
|
-
subject.run(['--silent', '--all', '--skip', 'test-service', 'test'])
|
224
|
-
}.to output("ok\nok\n").to_stdout.and output("error\n").to_stderr
|
225
154
|
end
|
226
155
|
end
|
227
156
|
end
|
@@ -9,6 +9,8 @@ module ClientHelpers
|
|
9
9
|
instance_double(Kontena::Client, :client)
|
10
10
|
end
|
11
11
|
|
12
|
+
base.let(:master_url) { 'http://someurl.example.com/' }
|
13
|
+
|
12
14
|
base.let(:token) do
|
13
15
|
'1234567'
|
14
16
|
end
|
@@ -21,17 +23,18 @@ module ClientHelpers
|
|
21
23
|
{'current_server' => 'alias',
|
22
24
|
'current_account' => 'kontena',
|
23
25
|
'servers' => [
|
24
|
-
{'name' => 'some_master', 'url' =>
|
25
|
-
{'name' => 'alias', 'url' =>
|
26
|
+
{'name' => 'some_master', 'url' => master_url },
|
27
|
+
{'name' => 'alias', 'url' => master_url, 'token' => token, 'account' => 'master', 'grid' => current_grid},
|
26
28
|
]
|
27
29
|
}
|
28
30
|
end
|
29
31
|
|
30
32
|
base.before(:each) do
|
33
|
+
master_host = URI(master_url).host
|
31
34
|
RSpec::Mocks.space.proxy_for(File).reset
|
32
35
|
allow(subject).to receive(:client).and_return(client)
|
33
36
|
allow(subject).to receive(:current_grid).and_return(current_grid)
|
34
|
-
allow(File).to receive(:exist?).with(File.join(Dir.home,
|
37
|
+
allow(File).to receive(:exist?).with(File.join(Dir.home, ".kontena/certs/#{master_host}.pem")).and_return(false)
|
35
38
|
allow(File).to receive(:exist?).with(File.join(Dir.home, '.kontena_client.json')).and_return(true)
|
36
39
|
allow(File).to receive(:readable?).with(File.join(Dir.home, '.kontena_client.json')).and_return(true)
|
37
40
|
allow(File).to receive(:read).and_call_original
|
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.4.0.
|
4
|
+
version: 1.4.0.pre2
|
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-07-
|
11
|
+
date: 2017-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -193,19 +193,19 @@ dependencies:
|
|
193
193
|
- !ruby/object:Gem::Version
|
194
194
|
version: 0.8.0
|
195
195
|
- !ruby/object:Gem::Dependency
|
196
|
-
name: websocket-
|
196
|
+
name: kontena-websocket-client
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
198
198
|
requirements:
|
199
|
-
- -
|
199
|
+
- - "~>"
|
200
200
|
- !ruby/object:Gem::Version
|
201
|
-
version: 0.
|
201
|
+
version: 0.1.0
|
202
202
|
type: :runtime
|
203
203
|
prerelease: false
|
204
204
|
version_requirements: !ruby/object:Gem::Requirement
|
205
205
|
requirements:
|
206
|
-
- -
|
206
|
+
- - "~>"
|
207
207
|
- !ruby/object:Gem::Version
|
208
|
-
version: 0.
|
208
|
+
version: 0.1.0
|
209
209
|
description: Command-line client for the Kontena container and microservices platform
|
210
210
|
email:
|
211
211
|
- info@kontena.io
|
@@ -517,8 +517,6 @@ files:
|
|
517
517
|
- lib/kontena/stacks_cache.rb
|
518
518
|
- lib/kontena/stacks_client.rb
|
519
519
|
- lib/kontena/util.rb
|
520
|
-
- lib/kontena/websocket/client.rb
|
521
|
-
- lib/kontena/websocket/client/connection.rb
|
522
520
|
- lib/kontena_cli.rb
|
523
521
|
- omnibus/.gitignore
|
524
522
|
- omnibus/.kitchen.yml
|
@@ -590,6 +588,7 @@ files:
|
|
590
588
|
- spec/kontena/cli/cloud/logout_command_spec.rb
|
591
589
|
- spec/kontena/cli/cloud/master/add_command_spec.rb
|
592
590
|
- spec/kontena/cli/common_spec.rb
|
591
|
+
- spec/kontena/cli/containers/exec_command_spec.rb
|
593
592
|
- spec/kontena/cli/containers/list_command_spec.rb
|
594
593
|
- spec/kontena/cli/containers/logs_command_spec.rb
|
595
594
|
- spec/kontena/cli/etcd/health_command_spec.rb
|
@@ -756,6 +755,7 @@ test_files:
|
|
756
755
|
- spec/kontena/cli/cloud/logout_command_spec.rb
|
757
756
|
- spec/kontena/cli/cloud/master/add_command_spec.rb
|
758
757
|
- spec/kontena/cli/common_spec.rb
|
758
|
+
- spec/kontena/cli/containers/exec_command_spec.rb
|
759
759
|
- spec/kontena/cli/containers/list_command_spec.rb
|
760
760
|
- spec/kontena/cli/containers/logs_command_spec.rb
|
761
761
|
- spec/kontena/cli/etcd/health_command_spec.rb
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'socket'
|
3
|
-
require 'openssl'
|
4
|
-
|
5
|
-
module Kontena
|
6
|
-
module Websocket
|
7
|
-
module Client
|
8
|
-
class Connection
|
9
|
-
extend Forwardable
|
10
|
-
|
11
|
-
FRAME_SIZE = 1024
|
12
|
-
|
13
|
-
attr_reader :url
|
14
|
-
|
15
|
-
# @param [String] url
|
16
|
-
# @param [Hash] options
|
17
|
-
def initialize(url, options = {})
|
18
|
-
@options = options
|
19
|
-
@url = url
|
20
|
-
@client = ::WebSocket::Driver.client(self)
|
21
|
-
if headers = options[:headers]
|
22
|
-
headers.each do |k, v|
|
23
|
-
@client.set_header(k, v)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def connect
|
29
|
-
uri = URI.parse(@url)
|
30
|
-
port = uri.port || (uri.scheme == "ws" ? 80 : 443)
|
31
|
-
@socket = ::TCPSocket.new(uri.host, port)
|
32
|
-
if uri.scheme == "wss"
|
33
|
-
ctx = ::OpenSSL::SSL::SSLContext.new
|
34
|
-
ctx.ssl_version = @options[:ssl_version] if @options[:ssl_version]
|
35
|
-
ctx.verify_mode = @options[:verify_mode] if @options[:verify_mode]
|
36
|
-
cert_store = ::OpenSSL::X509::Store.new
|
37
|
-
cert_store.set_default_paths
|
38
|
-
ctx.cert_store = cert_store
|
39
|
-
@socket = ::OpenSSL::SSL::SSLSocket.new(@socket, ctx)
|
40
|
-
@socket.connect
|
41
|
-
end
|
42
|
-
@client.start
|
43
|
-
Thread.new { self.read_socket }
|
44
|
-
end
|
45
|
-
|
46
|
-
def_delegators :@client, :text, :binary, :ping, :close, :protocol, :on
|
47
|
-
|
48
|
-
def write(buffer)
|
49
|
-
@socket.write(buffer)
|
50
|
-
end
|
51
|
-
|
52
|
-
def read_socket
|
53
|
-
loop do
|
54
|
-
begin
|
55
|
-
@client.parse(@socket.readpartial(FRAME_SIZE))
|
56
|
-
rescue EOFError
|
57
|
-
break
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|