kontena-cli 1.4.0.pre1 → 1.4.0.pre2
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/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
|
-
|