kontena-cli 1.3.0.rc1 → 1.3.0.rc2
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 +23 -4
- data/lib/kontena/cli/containers/exec_command.rb +13 -8
- data/lib/kontena/cli/helpers/exec_helper.rb +12 -6
- data/lib/kontena/cli/master/join_command.rb +2 -2
- data/lib/kontena/cli/master/use_command.rb +12 -1
- data/lib/kontena/cli/nodes/list_command.rb +1 -2
- data/lib/kontena/cli/services/exec_command.rb +9 -11
- data/lib/kontena/cli/services/list_command.rb +2 -3
- data/lib/kontena/cli/stacks/list_command.rb +1 -2
- data/lib/kontena/cli/table_generator.rb +5 -3
- data/lib/kontena/errors.rb +1 -0
- data/lib/kontena/scripts/completer.rb +183 -142
- data/lib/kontena/websocket/client.rb +12 -0
- data/lib/kontena/websocket/client/connection.rb +65 -0
- data/lib/kontena_cli.rb +1 -2
- data/spec/kontena/cli/app/build_command_spec.rb +0 -12
- data/spec/kontena/cli/master/join_command_spec.rb +33 -0
- data/spec/kontena/cli/master/use_command_spec.rb +10 -0
- data/spec/kontena/cli/services/exec_command_spec.rb +21 -19
- metadata +11 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09472d142a0b905453dae67fd9a5f773d8cacb78'
|
4
|
+
data.tar.gz: 0fbe77649ccdb7fcb79a5d15b093235ea1dee186
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 909d89da90b3b8101329c00bb5971200e8404368457e475620ff1a8f742ed4dcce1b1899fb6859bff861e0d7416f1b691d313ff532eb9675f3e68d2d1faa2b54
|
7
|
+
data.tar.gz: 542ec9dce844b9aa9e4e420c3f6dfcf610e65140f144ee531a777f8cdc3aa418eb672894287b2ffd934a2bd8af4456ed01411d00bcb00f0cbf1dee899adad79a
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.3.0.
|
1
|
+
1.3.0.rc2
|
data/kontena-cli.gemspec
CHANGED
@@ -34,5 +34,5 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_runtime_dependency "safe_yaml", "~> 1.0"
|
35
35
|
spec.add_runtime_dependency "liquid", "~> 4.0.0"
|
36
36
|
spec.add_runtime_dependency "tty-table", "~> 0.8.0"
|
37
|
-
spec.add_runtime_dependency "websocket-
|
37
|
+
spec.add_runtime_dependency "websocket-driver-kontena", "0.6.5"
|
38
38
|
end
|
data/lib/kontena/cli/common.rb
CHANGED
@@ -1,22 +1,41 @@
|
|
1
1
|
require 'forwardable'
|
2
|
+
require 'kontena_cli'
|
2
3
|
|
3
4
|
module Kontena
|
4
5
|
module Cli
|
5
6
|
module Common
|
6
7
|
extend Forwardable
|
7
8
|
|
8
|
-
def_delegators :Kontena, :pastel, :prompt, :logger
|
9
9
|
def_delegators :prompt, :ask, :yes?
|
10
10
|
def_delegators :config,
|
11
11
|
:current_grid=, :require_current_grid, :current_master,
|
12
12
|
:current_master=, :require_current_master, :require_current_account,
|
13
13
|
:current_account
|
14
|
-
def_delegator Kontena::Cli::Spinner, :spin, :spinner
|
15
|
-
def_delegator Kontena::Cli::Config, :instance, :config
|
16
|
-
def_delegator Kontena::Cli::Config, :instance, :settings
|
17
14
|
def_delegator :config, :config_filename, :settings_filename
|
18
15
|
def_delegator :client, :server_version, :api_url_version
|
19
16
|
|
17
|
+
def logger
|
18
|
+
Kontena.logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def prompt
|
22
|
+
Kontena.prompt
|
23
|
+
end
|
24
|
+
|
25
|
+
def pastel
|
26
|
+
Kontena.pastel
|
27
|
+
end
|
28
|
+
|
29
|
+
def spinner(msg, &block)
|
30
|
+
require 'kontena/cli/spinner' unless Kontena::Cli.const_defined?(:Spinner)
|
31
|
+
Kontena::Cli::Spinner.spin(msg, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def config
|
35
|
+
require 'kontena/cli/config' unless Kontena::Cli.const_defined?(:Config)
|
36
|
+
Kontena::Cli::Config.instance
|
37
|
+
end
|
38
|
+
|
20
39
|
# Read from STDIN. If stdin is a console, use prompt to ask.
|
21
40
|
# @param [String] message
|
22
41
|
# @param [Symbol] mode (prompt method: :ask, :multiline, etc)
|
@@ -13,23 +13,28 @@ module Kontena::Cli::Containers
|
|
13
13
|
option ["--interactive"], :flag, "Keep stdin open"
|
14
14
|
|
15
15
|
def execute
|
16
|
-
require 'websocket-client-simple'
|
17
|
-
|
18
16
|
require_api_url
|
19
17
|
token = require_token
|
20
18
|
cmd = JSON.dump({cmd: cmd_list})
|
21
|
-
|
22
|
-
|
19
|
+
url = ws_url("#{current_grid}/#{container_id}")
|
20
|
+
url << 'interactive=true&' if interactive?
|
21
|
+
url << 'shell=true' if shell?
|
22
|
+
ws = connect(url, token)
|
23
|
+
|
23
24
|
ws.on :message do |msg|
|
24
|
-
|
25
|
+
self.handle_message(msg)
|
25
26
|
end
|
26
27
|
ws.on :open do
|
27
|
-
ws.
|
28
|
+
ws.text(cmd)
|
28
29
|
end
|
29
30
|
ws.on :close do |e|
|
30
|
-
|
31
|
+
if e.reason.include?('code: 404')
|
32
|
+
exit_with_error('Not found')
|
33
|
+
else
|
34
|
+
exit 1
|
35
|
+
end
|
31
36
|
end
|
32
|
-
|
37
|
+
ws.connect
|
33
38
|
if interactive?
|
34
39
|
stream_stdin_to_ws(ws).join
|
35
40
|
else
|
@@ -1,13 +1,16 @@
|
|
1
|
+
require_relative '../../websocket/client'
|
2
|
+
|
1
3
|
module Kontena::Cli::Helpers
|
2
4
|
module ExecHelper
|
3
5
|
|
4
6
|
# @param [WebSocket::Client::Simple] ws
|
5
7
|
# @return [Thread]
|
6
8
|
def stream_stdin_to_ws(ws)
|
9
|
+
require 'io/console'
|
7
10
|
Thread.new {
|
8
11
|
STDIN.raw {
|
9
12
|
while char = STDIN.readpartial(1024)
|
10
|
-
ws.
|
13
|
+
ws.text(JSON.dump({ stdin: char }))
|
11
14
|
end
|
12
15
|
}
|
13
16
|
}
|
@@ -43,19 +46,22 @@ module Kontena::Cli::Helpers
|
|
43
46
|
def ws_url(container_id)
|
44
47
|
url = require_current_master.url
|
45
48
|
url << '/' unless url.end_with?('/')
|
46
|
-
"#{url.sub('http', 'ws')}v1/containers/#{container_id}/exec"
|
49
|
+
"#{url.sub('http', 'ws')}v1/containers/#{container_id}/exec?"
|
47
50
|
end
|
48
51
|
|
49
52
|
# @param [String] url
|
50
53
|
# @param [String] token
|
51
54
|
# @return [WebSocket::Client::Simple]
|
52
55
|
def connect(url, token)
|
53
|
-
|
56
|
+
options = {
|
54
57
|
headers: {
|
55
|
-
'Authorization' => "Bearer #{token.access_token}"
|
56
|
-
'Accept' => 'application/json'
|
58
|
+
'Authorization' => "Bearer #{token.access_token}"
|
57
59
|
}
|
58
|
-
}
|
60
|
+
}
|
61
|
+
if ENV['SSL_IGNORE_ERRORS'].to_s == 'true'
|
62
|
+
options[:verify_mode] = ::OpenSSL::SSL::VERIFY_NONE
|
63
|
+
end
|
64
|
+
Kontena::Websocket::Client.new(url, options)
|
59
65
|
end
|
60
66
|
end
|
61
67
|
end
|
@@ -9,9 +9,9 @@ module Kontena::Cli::Master
|
|
9
9
|
|
10
10
|
def execute
|
11
11
|
params = []
|
12
|
-
params
|
12
|
+
params += ["--join", self.invite_code]
|
13
13
|
params << "--remote" if self.remote?
|
14
|
-
params
|
14
|
+
params += ["--name", self.name] if self.name
|
15
15
|
params << "--verbose" if self.verbose?
|
16
16
|
|
17
17
|
cmd = ['master', 'login'] + params
|
@@ -2,9 +2,20 @@ module Kontena::Cli::Master
|
|
2
2
|
class UseCommand < Kontena::Command
|
3
3
|
include Kontena::Cli::Common
|
4
4
|
|
5
|
-
parameter "NAME", "Master name to use"
|
5
|
+
parameter "[NAME]", "Master name to use"
|
6
|
+
|
7
|
+
option '--clear', :flag, "Clear current master setting"
|
6
8
|
|
7
9
|
def execute
|
10
|
+
if clear?
|
11
|
+
config.current_master = nil
|
12
|
+
config.write
|
13
|
+
exit 0
|
14
|
+
elsif name.nil?
|
15
|
+
signal_usage_error Clamp.message(:parameter_argument_error, :param => 'NAME', :message => "missing")
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
8
19
|
master = config.find_server(name)
|
9
20
|
if master.nil?
|
10
21
|
exit_with_error p"Could not resolve master by name '#{name}'." +
|
@@ -34,7 +34,6 @@ module Kontena::Cli::Nodes
|
|
34
34
|
def fields
|
35
35
|
return ['name'] if quiet?
|
36
36
|
{
|
37
|
-
' ' => 'health_icon',
|
38
37
|
name: 'name',
|
39
38
|
version: 'agent_version',
|
40
39
|
status: 'status',
|
@@ -67,7 +66,7 @@ module Kontena::Cli::Nodes
|
|
67
66
|
unless quiet?
|
68
67
|
grid_health = grid_health(grid, grid_nodes)
|
69
68
|
grid_nodes.each do |node|
|
70
|
-
node['
|
69
|
+
node['name'] = health_icon(node_health(node, grid_health)) + " " + node['name']
|
71
70
|
end
|
72
71
|
end
|
73
72
|
|
@@ -24,8 +24,6 @@ module Kontena::Cli::Services
|
|
24
24
|
requires_current_grid
|
25
25
|
|
26
26
|
def execute
|
27
|
-
require 'websocket-client-simple'
|
28
|
-
|
29
27
|
exit_with_error "--interactive cannot be used with --all" if all? && interactive?
|
30
28
|
|
31
29
|
service_containers = client.get("services/#{parse_service_id(name)}/containers")['containers']
|
@@ -92,7 +90,7 @@ module Kontena::Cli::Services
|
|
92
90
|
exit_status = nil
|
93
91
|
token = require_token
|
94
92
|
url = ws_url(container['id'])
|
95
|
-
url << '
|
93
|
+
url << 'shell=true' if shell?
|
96
94
|
ws = connect(url, token)
|
97
95
|
ws.on :message do |msg|
|
98
96
|
data = base.parse_message(msg)
|
@@ -107,11 +105,12 @@ module Kontena::Cli::Services
|
|
107
105
|
end
|
108
106
|
end
|
109
107
|
ws.on :open do
|
110
|
-
ws.
|
108
|
+
ws.text(cmd)
|
111
109
|
end
|
112
|
-
ws.on :close do |e|
|
113
|
-
exit_status = 1
|
110
|
+
ws.on :close do |e|s
|
111
|
+
exit_status = 1 if exit_status.nil? && e.code != 1000
|
114
112
|
end
|
113
|
+
ws.connect
|
115
114
|
|
116
115
|
sleep 0.01 until !exit_status.nil?
|
117
116
|
|
@@ -120,23 +119,22 @@ module Kontena::Cli::Services
|
|
120
119
|
|
121
120
|
# @param [Hash] container
|
122
121
|
def interactive_exec(container)
|
123
|
-
require 'io/console'
|
124
|
-
|
125
122
|
token = require_token
|
126
123
|
cmd = JSON.dump({ cmd: cmd_list })
|
127
124
|
base = self
|
128
|
-
url = ws_url(container['id']) << '
|
125
|
+
url = ws_url(container['id']) << 'interactive=true'
|
129
126
|
url << '&shell=true' if shell?
|
130
127
|
ws = connect(url, token)
|
131
128
|
ws.on :message do |msg|
|
132
129
|
base.handle_message(msg)
|
133
130
|
end
|
134
131
|
ws.on :open do
|
135
|
-
ws.
|
132
|
+
ws.text(cmd)
|
136
133
|
end
|
137
134
|
ws.on :close do |e|
|
138
|
-
exit 1
|
135
|
+
exit 1 if e.code != 1000
|
139
136
|
end
|
137
|
+
ws.connect
|
140
138
|
|
141
139
|
stream_stdin_to_ws(ws).join
|
142
140
|
end
|
@@ -17,7 +17,7 @@ module Kontena::Cli::Services
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def fields
|
20
|
-
quiet? ? ['name'] : {
|
20
|
+
quiet? ? ['name'] : {name: 'name', instances: 'instances', stateful: 'stateful', state: 'state', "exposed ports" => 'ports' }
|
21
21
|
end
|
22
22
|
|
23
23
|
def service_port(port)
|
@@ -49,9 +49,8 @@ module Kontena::Cli::Services
|
|
49
49
|
|
50
50
|
def execute
|
51
51
|
print_table(services) do |row|
|
52
|
-
row['name'] = service_name(row)
|
52
|
+
row['name'] = quiet? ? service_name(row) : health_status_icon(health_status(row)) + " " + service_name(row)
|
53
53
|
next if quiet?
|
54
|
-
row['health_icon'] = health_status_icon(health_status(row))
|
55
54
|
row['stateful'] = row['stateful'] ? pastel.green('yes') : 'no'
|
56
55
|
row['ports'] = row['ports'].map(&method(:service_port)).join(',')
|
57
56
|
row['state'] = pastel.send(state_color(row['state']), row['state'])
|
@@ -26,7 +26,6 @@ module Kontena::Cli::Stacks
|
|
26
26
|
def fields
|
27
27
|
return ['name'] if quiet?
|
28
28
|
{
|
29
|
-
' ' => 'health_icon',
|
30
29
|
name: 'name',
|
31
30
|
stack: 'stack',
|
32
31
|
services: 'services_count',
|
@@ -38,7 +37,7 @@ module Kontena::Cli::Stacks
|
|
38
37
|
def execute
|
39
38
|
print_table(stacks) do |row|
|
40
39
|
next if quiet?
|
41
|
-
row['
|
40
|
+
row['name'] = health_icon(stack_health(row)) + " " + row['name']
|
42
41
|
row['stack'] = "#{row['stack']}:#{row['version']}"
|
43
42
|
row['services_count'] = row['services'].size
|
44
43
|
row['ports'] = stack_ports(row).join(',')
|
@@ -11,6 +11,8 @@ module Kontena
|
|
11
11
|
|
12
12
|
DEFAULT_HEADER_FORMAT_PROC = lambda { |header| header.to_s.capitalize }
|
13
13
|
|
14
|
+
DEFAULT_RENDER_OPTS = {padding: [0,2,0,0]}
|
15
|
+
|
14
16
|
module Helper
|
15
17
|
def self.included(base)
|
16
18
|
if base.respond_to?(:option)
|
@@ -28,8 +30,8 @@ module Kontena
|
|
28
30
|
array,
|
29
31
|
fields,
|
30
32
|
row_format_proc: block_given? ? block.to_proc : nil,
|
31
|
-
header_format_proc: lambda { |item| pastel.
|
32
|
-
render_options: self.respond_to?(:render_options) ? self.render_options :
|
33
|
+
header_format_proc: lambda { |item| pastel.bold(item.to_s.upcase) },
|
34
|
+
render_options: self.respond_to?(:render_options) ? DEFAULT_RENDER_OPTS.merge(self.render_options) : DEFAULT_RENDER_OPTS
|
33
35
|
).render
|
34
36
|
end
|
35
37
|
|
@@ -68,7 +70,7 @@ module Kontena
|
|
68
70
|
if data.empty?
|
69
71
|
fields.map(&method(:format_header_item)).join(' ')
|
70
72
|
else
|
71
|
-
table.render(render_mode, render_options)
|
73
|
+
table.render(render_mode, render_options).gsub(/\s+$/, '')
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
data/lib/kontena/errors.rb
CHANGED
@@ -1,18 +1,42 @@
|
|
1
|
-
require 'kontena/client'
|
2
1
|
require 'kontena/cli/common'
|
3
|
-
require 'yaml'
|
4
2
|
|
5
3
|
class Helper
|
6
4
|
include Kontena::Cli::Common
|
7
5
|
|
6
|
+
def client_config
|
7
|
+
require 'json'
|
8
|
+
config_file = File.expand_path('~/.kontena_client.json')
|
9
|
+
if(File.exist?(config_file))
|
10
|
+
JSON.parse(File.read(config_file))
|
11
|
+
else
|
12
|
+
{}
|
13
|
+
end
|
14
|
+
rescue => ex
|
15
|
+
logger.debug ex
|
16
|
+
{}
|
17
|
+
end
|
18
|
+
|
19
|
+
def current_grid
|
20
|
+
client_config['servers'].find { |s| s['name'] == client_config['current_server']}['grid']
|
21
|
+
rescue => ex
|
22
|
+
logger.debug ex
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def current_master_name
|
27
|
+
client_config['current_server']
|
28
|
+
end
|
29
|
+
|
8
30
|
def client
|
31
|
+
$VERSION_WARNING_ADDED=true
|
9
32
|
token = require_token
|
10
33
|
super(token)
|
11
34
|
end
|
12
35
|
|
13
36
|
def grids
|
14
37
|
client.get("grids")['grids'].map{|grid| grid['id']}
|
15
|
-
rescue
|
38
|
+
rescue => ex
|
39
|
+
logger.debug ex
|
16
40
|
[]
|
17
41
|
end
|
18
42
|
|
@@ -28,7 +52,8 @@ class Helper
|
|
28
52
|
results.push stacks.map{|s| s['name']}
|
29
53
|
results.delete('null')
|
30
54
|
results
|
31
|
-
rescue
|
55
|
+
rescue => ex
|
56
|
+
logger.debug ex
|
32
57
|
[]
|
33
58
|
end
|
34
59
|
|
@@ -44,7 +69,8 @@ class Helper
|
|
44
69
|
end
|
45
70
|
}
|
46
71
|
results
|
47
|
-
rescue
|
72
|
+
rescue => ex
|
73
|
+
logger.debug ex
|
48
74
|
[]
|
49
75
|
end
|
50
76
|
|
@@ -56,33 +82,34 @@ class Helper
|
|
56
82
|
results.push(containers.map{|c| c['id'] })
|
57
83
|
end
|
58
84
|
results
|
59
|
-
rescue
|
85
|
+
rescue => ex
|
86
|
+
logger.debug ex
|
60
87
|
[]
|
61
88
|
end
|
62
89
|
|
63
90
|
def yml_services
|
91
|
+
require 'yaml'
|
64
92
|
if File.exist?('kontena.yml')
|
65
93
|
yaml = YAML.safe_load(File.read('kontena.yml'))
|
66
94
|
services = yaml['services']
|
67
95
|
services.keys
|
68
96
|
end
|
69
|
-
rescue
|
97
|
+
rescue => ex
|
98
|
+
logger.debug ex
|
70
99
|
[]
|
71
100
|
end
|
72
101
|
|
73
102
|
def yml_files
|
74
103
|
Dir["./*.yml"].map{|file| file.sub('./', '')}
|
75
|
-
rescue
|
104
|
+
rescue => ex
|
105
|
+
logger.debug ex
|
76
106
|
[]
|
77
107
|
end
|
78
108
|
|
79
109
|
def master_names
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
return config['servers'].map{|s| s['name']}
|
84
|
-
end
|
85
|
-
rescue
|
110
|
+
client_config['servers'].map{|s| s['name']}
|
111
|
+
rescue => ex
|
112
|
+
logger.debug ex
|
86
113
|
[]
|
87
114
|
end
|
88
115
|
|
@@ -108,138 +135,152 @@ end
|
|
108
135
|
|
109
136
|
words.delete_at(0)
|
110
137
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
completion.
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
when 'etcd'
|
124
|
-
completion.clear
|
125
|
-
sub_commands = %w(get set mkdir mk list ls rm)
|
126
|
-
if words[1]
|
127
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
128
|
-
else
|
129
|
-
completion.push sub_commands
|
130
|
-
end
|
131
|
-
when 'registry'
|
132
|
-
completion.clear
|
133
|
-
sub_commands = %w(create remove rm)
|
134
|
-
if words[1]
|
135
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
136
|
-
else
|
137
|
-
completion.push sub_commands
|
138
|
-
end
|
139
|
-
when 'grid'
|
140
|
-
completion.clear
|
141
|
-
sub_commands = %w(add-user audit-log create current list user remove show use)
|
142
|
-
if words[1]
|
143
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
144
|
-
completion.push helper.grids
|
145
|
-
else
|
146
|
-
completion.push sub_commands
|
147
|
-
end
|
148
|
-
when 'node'
|
149
|
-
completion.clear
|
150
|
-
sub_commands = %w(list show remove)
|
151
|
-
if words[1]
|
152
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
153
|
-
completion.push helper.nodes
|
154
|
-
else
|
155
|
-
completion.push sub_commands
|
156
|
-
end
|
157
|
-
when 'master'
|
158
|
-
completion.clear
|
159
|
-
sub_commands = %w(list use users current remove rm config cfg login logout token join audit-log init-cloud)
|
160
|
-
if words[1] && words[1] == 'use'
|
161
|
-
completion.push helper.master_names
|
162
|
-
elsif words[1] && words[1] == 'users'
|
163
|
-
users_sub_commands = %(invite list role)
|
164
|
-
completion.push users_sub_commands
|
165
|
-
elsif words[1] && ['config', 'cfg'].include?(words[1])
|
166
|
-
config_sub_commands = %(set get dump load import export unset)
|
167
|
-
completion.push config_sub_commands
|
168
|
-
elsif words[1] && words[1] == 'token'
|
169
|
-
token_sub_commands = %(list ls rm remove show current create)
|
170
|
-
completion.push token_sub_commands
|
171
|
-
elsif words[1]
|
172
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
173
|
-
else
|
174
|
-
completion.push sub_commands
|
175
|
-
end
|
176
|
-
when 'cloud'
|
177
|
-
completion.clear
|
178
|
-
sub_commands = %w(login logout master)
|
179
|
-
if words[1] && words[1] == 'master'
|
180
|
-
cloud_master_sub_commands = %(list ls remove rm add show update)
|
181
|
-
completion.push cloud_master_sub_commands
|
182
|
-
elsif words[1]
|
183
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
184
|
-
else
|
185
|
-
completion.push sub_commands
|
186
|
-
end
|
187
|
-
when 'service'
|
188
|
-
completion.clear
|
189
|
-
sub_commands = %w(containers create delete deploy list logs restart
|
190
|
-
scale show start stats stop update monitor env
|
191
|
-
secret link unlink)
|
192
|
-
if words[1]
|
193
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
194
|
-
completion.push helper.services
|
195
|
-
else
|
196
|
-
completion.push sub_commands
|
197
|
-
end
|
198
|
-
when 'container'
|
199
|
-
completion.clear
|
200
|
-
sub_commands = %w(exec inspect logs)
|
201
|
-
if words[1]
|
202
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
203
|
-
completion.push helper.containers
|
204
|
-
else
|
205
|
-
completion.push sub_commands
|
206
|
-
end
|
207
|
-
when 'vpn'
|
208
|
-
completion.clear
|
209
|
-
completion.push %w(config create delete)
|
210
|
-
when 'external-registry'
|
211
|
-
completion.clear
|
212
|
-
completion.push %w(add list delete)
|
213
|
-
when 'app'
|
214
|
-
completion.clear
|
215
|
-
sub_commands = %w(init build config deploy start stop remove rm ps list
|
216
|
-
logs monitor show)
|
217
|
-
if words[1]
|
218
|
-
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
219
|
-
completion.push helper.yml_services
|
220
|
-
else
|
221
|
-
completion.push sub_commands
|
222
|
-
end
|
223
|
-
when 'stack'
|
224
|
-
completion.clear
|
225
|
-
sub_commands = %w(build install upgrade deploy start stop remove rm ls list
|
226
|
-
logs monitor show registry)
|
227
|
-
if words[1]
|
228
|
-
if words[1] == 'registry'
|
229
|
-
registry_sub_commands = %(push pull search show rm)
|
230
|
-
completion.push registry_sub_commands
|
231
|
-
elsif %w(install).include?(words[1])
|
232
|
-
completion.push helper.yml_files
|
233
|
-
elsif words[1] == 'upgrade' && words[3]
|
234
|
-
completion.push helper.yml_files
|
138
|
+
helper.logger.debug { "Completing #{words.inspect}" }
|
139
|
+
|
140
|
+
begin
|
141
|
+
completion = []
|
142
|
+
completion.push %w(cloud logout grid app service stack vault certificate node master vpn registry container etcd external-registry whoami plugin version) if words.size < 2
|
143
|
+
if words.size > 0
|
144
|
+
case words[0]
|
145
|
+
when 'plugin'
|
146
|
+
completion.clear
|
147
|
+
sub_commands = %w(list ls search install uninstall)
|
148
|
+
if words[1]
|
149
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
235
150
|
else
|
151
|
+
completion.push sub_commands
|
152
|
+
end
|
153
|
+
when 'etcd'
|
154
|
+
completion.clear
|
155
|
+
sub_commands = %w(get set mkdir mk list ls rm)
|
156
|
+
if words[1]
|
236
157
|
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
237
|
-
|
158
|
+
else
|
159
|
+
completion.push sub_commands
|
238
160
|
end
|
239
|
-
|
240
|
-
completion.
|
241
|
-
|
161
|
+
when 'registry'
|
162
|
+
completion.clear
|
163
|
+
sub_commands = %w(create remove rm)
|
164
|
+
if words[1]
|
165
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
166
|
+
else
|
167
|
+
completion.push sub_commands
|
168
|
+
end
|
169
|
+
when 'grid'
|
170
|
+
completion.clear
|
171
|
+
sub_commands = %w(add-user audit-log create current list user remove show use)
|
172
|
+
if words[1] && words[1] == 'use'
|
173
|
+
completion.push helper.grids.reject { |g| g == helper.current_grid }
|
174
|
+
elsif words[1] && %w(update show rm remove env cloud-config health).include?(words[1])
|
175
|
+
completion.push helper.grids
|
176
|
+
else
|
177
|
+
completion.push sub_commands
|
178
|
+
end
|
179
|
+
when 'node'
|
180
|
+
completion.clear
|
181
|
+
sub_commands = %w(list show remove)
|
182
|
+
if words[1] && sub_commands.include?(words[1])
|
183
|
+
completion.push helper.nodes
|
184
|
+
else
|
185
|
+
completion.push sub_commands
|
186
|
+
end
|
187
|
+
when 'master'
|
188
|
+
completion.clear
|
189
|
+
sub_commands = %w(list use user current remove rm config cfg login logout token join audit-log init-cloud)
|
190
|
+
if words[1] && words[1] == 'use'
|
191
|
+
completion.push helper.master_names.reject { |n| n == helper.current_master_name }
|
192
|
+
elsif words[1] && %w(remove rm).include?(words[1])
|
193
|
+
completion.push helper.master_names
|
194
|
+
elsif words[1] && words[1] == 'user'
|
195
|
+
users_sub_commands = %w(invite list role)
|
196
|
+
if words[2] == 'role'
|
197
|
+
role_subcommands = %w(add remove rm)
|
198
|
+
if !words[3] || !role_subcommands.include?(words[3])
|
199
|
+
completion.push role_subcommands
|
200
|
+
end
|
201
|
+
else
|
202
|
+
completion.push users_sub_commands
|
203
|
+
end
|
204
|
+
elsif words[1] && ['config', 'cfg'].include?(words[1])
|
205
|
+
config_sub_commands = %(set get dump load import export unset)
|
206
|
+
completion.push config_sub_commands
|
207
|
+
elsif words[1] && words[1] == 'token'
|
208
|
+
token_sub_commands = %(list ls rm remove show current create)
|
209
|
+
completion.push token_sub_commands
|
210
|
+
elsif words[1]
|
211
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
212
|
+
else
|
213
|
+
completion.push sub_commands
|
214
|
+
end
|
215
|
+
when 'cloud'
|
216
|
+
completion.clear
|
217
|
+
sub_commands = %w(login logout master)
|
218
|
+
if words[1] && words[1] == 'master'
|
219
|
+
cloud_master_sub_commands = %(list ls remove rm add show update)
|
220
|
+
completion.push cloud_master_sub_commands
|
221
|
+
elsif words[1]
|
222
|
+
completion.push(sub_commands) unless sub_commands.include?(words[1])
|
223
|
+
else
|
224
|
+
completion.push sub_commands
|
225
|
+
end
|
226
|
+
when 'service'
|
227
|
+
completion.clear
|
228
|
+
sub_commands = %w(containers create delete deploy list logs restart
|
229
|
+
scale show start stats stop update monitor env
|
230
|
+
secret link unlink)
|
231
|
+
if words[1] && sub_commands.include?(words[1])
|
232
|
+
completion.push helper.services
|
233
|
+
else
|
234
|
+
completion.push sub_commands
|
235
|
+
end
|
236
|
+
when 'container'
|
237
|
+
completion.clear
|
238
|
+
sub_commands = %w(exec inspect logs)
|
239
|
+
if words[1] && sub_commands.include?(words[1])
|
240
|
+
completion.push helper.containers
|
241
|
+
else
|
242
|
+
completion.push sub_commands
|
243
|
+
end
|
244
|
+
when 'vpn'
|
245
|
+
completion.clear
|
246
|
+
completion.push %w(config create delete)
|
247
|
+
when 'external-registry'
|
248
|
+
completion.clear
|
249
|
+
completion.push %w(add list delete)
|
250
|
+
when 'app'
|
251
|
+
completion.clear
|
252
|
+
sub_commands = %w(init build config deploy start stop remove rm ps list
|
253
|
+
logs monitor show)
|
254
|
+
if words[1] && sub_commands.include?(words[1])
|
255
|
+
completion.push helper.yml_services
|
256
|
+
else
|
257
|
+
completion.push sub_commands
|
258
|
+
end
|
259
|
+
when 'stack'
|
260
|
+
completion.clear
|
261
|
+
sub_commands = %w(build install upgrade deploy start stop remove rm ls list
|
262
|
+
logs monitor show registry)
|
263
|
+
if words[1]
|
264
|
+
if words[1] == 'registry'
|
265
|
+
registry_sub_commands = %(push pull search show rm)
|
266
|
+
completion.push registry_sub_commands
|
267
|
+
elsif %w(install).include?(words[1])
|
268
|
+
completion.push helper.yml_files
|
269
|
+
elsif words[1] == 'upgrade' && words[3]
|
270
|
+
completion.push helper.yml_files
|
271
|
+
elsif words[1] && sub_commands.include?(words[1])
|
272
|
+
completion.push helper.stacks
|
273
|
+
else
|
274
|
+
completion.push(sub_commands)
|
275
|
+
end
|
276
|
+
else
|
277
|
+
completion.push sub_commands
|
278
|
+
end
|
279
|
+
end
|
242
280
|
end
|
281
|
+
rescue => ex
|
282
|
+
helper.logger.debug ex
|
243
283
|
end
|
284
|
+
helper.logger.debug { "Returning completions: #{completion.inspect}" }
|
244
285
|
|
245
286
|
puts completion
|
@@ -0,0 +1,65 @@
|
|
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
|
+
|
data/lib/kontena_cli.rb
CHANGED
@@ -173,8 +173,7 @@ end
|
|
173
173
|
require 'retriable'
|
174
174
|
Retriable.configure do |c|
|
175
175
|
c.on_retry = Proc.new do |exception, try, elapsed_time, next_interval|
|
176
|
-
|
177
|
-
puts "Retriable retry: #{try} - Exception: #{exception.class.name} - #{exception.message}. Elapsed: #{elapsed_time} Next interval: #{next_interval}"
|
176
|
+
Kontena.logger.debug { "Retriable retry: #{try} - Exception: #{exception.class.name} - #{exception.message}. Elapsed: #{elapsed_time} Next interval: #{next_interval}" }
|
178
177
|
end
|
179
178
|
end
|
180
179
|
|
@@ -19,20 +19,8 @@ describe Kontena::Cli::Apps::BuildCommand do
|
|
19
19
|
fixture('docker-compose.yml')
|
20
20
|
end
|
21
21
|
|
22
|
-
let(:settings) do
|
23
|
-
{'current_server' => 'alias',
|
24
|
-
'servers' => [
|
25
|
-
{
|
26
|
-
'name' => 'some_master',
|
27
|
-
'url' => 'some_master'
|
28
|
-
}
|
29
|
-
]
|
30
|
-
}
|
31
|
-
end
|
32
|
-
|
33
22
|
describe '#execute' do
|
34
23
|
before(:each) do
|
35
|
-
allow(subject).to receive(:settings).and_return(settings)
|
36
24
|
allow(subject).to receive(:current_dir).and_return("kontena-test")
|
37
25
|
allow(File).to receive(:exists?).and_return(true)
|
38
26
|
allow(File).to receive(:read).with("#{Dir.getwd}/kontena.yml").and_return(kontena_yml)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'kontena/cli/master/join_command'
|
2
|
+
require 'kontena/cli/localhost_web_server'
|
3
|
+
require 'launchy'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
describe Kontena::Cli::Master::JoinCommand do
|
7
|
+
|
8
|
+
include ClientHelpers
|
9
|
+
|
10
|
+
let(:subject) do
|
11
|
+
described_class.new(File.basename($0))
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'calls master login with proper join options' do
|
15
|
+
expect(Kontena).to receive(:run!).with(%w(master login --join xyz someurl))
|
16
|
+
subject.run(%w(someurl xyz))
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'calls master login with remote option' do
|
20
|
+
expect(Kontena).to receive(:run!).with(%w(master login --join xyz --remote someurl))
|
21
|
+
subject.run(%w(--remote someurl xyz))
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'calls master login with name option' do
|
25
|
+
expect(Kontena).to receive(:run!).with(%w(master login --join xyz --name somename someurl))
|
26
|
+
subject.run(%w(--name somename someurl xyz))
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'calls master login with verbose option' do
|
30
|
+
expect(Kontena).to receive(:run!).with(%w(master login --join xyz --verbose someurl))
|
31
|
+
subject.run(%w(--verbose someurl xyz))
|
32
|
+
end
|
33
|
+
end
|
@@ -18,5 +18,15 @@ describe Kontena::Cli::Master::UseCommand do
|
|
18
18
|
it 'should abort with error message if master is not configured' do
|
19
19
|
expect { subject.run(['not_existing']) }.to output(/Could not resolve master with name: 'not_existing'/).to_stderr
|
20
20
|
end
|
21
|
+
|
22
|
+
it 'should abort with error message if master is not given' do
|
23
|
+
expect { subject.run([]) }.to raise_error Clamp::UsageError
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should clear current master when --clear given' do
|
27
|
+
expect(subject.config).to receive(:write).and_return(true)
|
28
|
+
subject.run(['--clear'])
|
29
|
+
expect(subject.config.current_server).to be_nil
|
30
|
+
end
|
21
31
|
end
|
22
32
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'websocket
|
1
|
+
require 'kontena/websocket/client'
|
2
2
|
require 'kontena/cli/services/exec_command'
|
3
3
|
|
4
4
|
describe Kontena::Cli::Services::ExecCommand do
|
@@ -24,6 +24,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
def connect ; end
|
28
|
+
|
27
29
|
def receive_message(msg)
|
28
30
|
@callbacks[:message].call(Event.new(JSON.dump(msg)))
|
29
31
|
rescue => exc
|
@@ -67,8 +69,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
67
69
|
end
|
68
70
|
|
69
71
|
it "Executes on the running container by default" do
|
70
|
-
expect(
|
71
|
-
expect(ws_client).to receive(:
|
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
|
+
expect(ws_client).to receive(:text) do |foo|
|
72
74
|
ws_client.receive_message({'stream' => 'stdout', 'chunk' => "ok\n"})
|
73
75
|
ws_client.receive_message({'exit' => 0})
|
74
76
|
end
|
@@ -105,8 +107,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
105
107
|
|
106
108
|
it "Executes on the first running container by default" do
|
107
109
|
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
|
108
|
-
expect(
|
109
|
-
expect(ws_client).to receive(:
|
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
|
+
expect(ws_client).to receive(:text) do
|
110
112
|
respond_ok(ws_client)
|
111
113
|
end
|
112
114
|
expect {
|
@@ -116,8 +118,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
116
118
|
|
117
119
|
it "Executes on the first running container, even if they are ordered differently" do
|
118
120
|
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return({'containers' => service_containers['containers'].reverse })
|
119
|
-
expect(
|
120
|
-
expect(ws_client).to receive(:
|
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
|
+
expect(ws_client).to receive(:text) do
|
121
123
|
respond_ok(ws_client)
|
122
124
|
end
|
123
125
|
expect {
|
@@ -127,8 +129,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
127
129
|
|
128
130
|
it "Executes on the first running container if given" do
|
129
131
|
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
|
130
|
-
expect(
|
131
|
-
expect(ws_client).to receive(:
|
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
|
+
expect(ws_client).to receive(:text) do
|
132
134
|
respond_ok(ws_client)
|
133
135
|
end
|
134
136
|
expect {
|
@@ -138,8 +140,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
138
140
|
|
139
141
|
it "Executes on the second running container if given" do
|
140
142
|
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
|
141
|
-
expect(
|
142
|
-
expect(ws_client).to receive(:
|
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
|
+
expect(ws_client).to receive(:text) do
|
143
145
|
respond_ok(ws_client)
|
144
146
|
end
|
145
147
|
expect {
|
@@ -158,8 +160,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
158
160
|
|
159
161
|
3.times do |i|
|
160
162
|
ws_client = ws_client_class.new
|
161
|
-
expect(
|
162
|
-
expect(ws_client).to receive(:
|
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
|
163
165
|
ws_client.receive_message({'stream' => 'stdout', 'chunk' => "test#{i + 1}\n"})
|
164
166
|
ws_client.receive_message({'exit' => 0})
|
165
167
|
end
|
@@ -172,8 +174,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
172
174
|
|
173
175
|
it "Stops if the first container fails" do
|
174
176
|
expect(client).to receive(:get).with('services/test-grid/null/test-service/containers').and_return(service_containers)
|
175
|
-
expect(
|
176
|
-
expect(ws_client).to receive(:
|
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
|
+
expect(ws_client).to receive(:text) do
|
177
179
|
respond_error(ws_client)
|
178
180
|
end
|
179
181
|
expect {
|
@@ -186,8 +188,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
186
188
|
i = 1
|
187
189
|
[:ok, :err].each do |status|
|
188
190
|
ws_client = ws_client_class.new
|
189
|
-
expect(
|
190
|
-
expect(ws_client).to receive(:
|
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
|
+
expect(ws_client).to receive(:text) do
|
191
193
|
if status == :ok
|
192
194
|
respond_ok(ws_client)
|
193
195
|
else
|
@@ -207,8 +209,8 @@ describe Kontena::Cli::Services::ExecCommand do
|
|
207
209
|
i = 1
|
208
210
|
[:ok, :err, :ok].each do |status|
|
209
211
|
ws_client = ws_client_class.new
|
210
|
-
expect(
|
211
|
-
expect(ws_client).to receive(:
|
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
|
+
expect(ws_client).to receive(:text) do
|
212
214
|
if status == :ok
|
213
215
|
respond_ok(ws_client)
|
214
216
|
else
|
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.rc2
|
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-
|
11
|
+
date: 2017-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -207,19 +207,19 @@ dependencies:
|
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: 0.8.0
|
209
209
|
- !ruby/object:Gem::Dependency
|
210
|
-
name: websocket-
|
210
|
+
name: websocket-driver-kontena
|
211
211
|
requirement: !ruby/object:Gem::Requirement
|
212
212
|
requirements:
|
213
|
-
- -
|
213
|
+
- - '='
|
214
214
|
- !ruby/object:Gem::Version
|
215
|
-
version: 0.
|
215
|
+
version: 0.6.5
|
216
216
|
type: :runtime
|
217
217
|
prerelease: false
|
218
218
|
version_requirements: !ruby/object:Gem::Requirement
|
219
219
|
requirements:
|
220
|
-
- -
|
220
|
+
- - '='
|
221
221
|
- !ruby/object:Gem::Version
|
222
|
-
version: 0.
|
222
|
+
version: 0.6.5
|
223
223
|
description: Command-line client for the Kontena container and microservices platform
|
224
224
|
email:
|
225
225
|
- info@kontena.io
|
@@ -526,6 +526,8 @@ files:
|
|
526
526
|
- lib/kontena/stacks_cache.rb
|
527
527
|
- lib/kontena/stacks_client.rb
|
528
528
|
- lib/kontena/util.rb
|
529
|
+
- lib/kontena/websocket/client.rb
|
530
|
+
- lib/kontena/websocket/client/connection.rb
|
529
531
|
- lib/kontena_cli.rb
|
530
532
|
- omnibus/.gitignore
|
531
533
|
- omnibus/.kitchen.yml
|
@@ -610,6 +612,7 @@ files:
|
|
610
612
|
- spec/kontena/cli/main_command_spec.rb
|
611
613
|
- spec/kontena/cli/master/current_command_spec.rb
|
612
614
|
- spec/kontena/cli/master/init_cloud_command_spec.rb
|
615
|
+
- spec/kontena/cli/master/join_command_spec.rb
|
613
616
|
- spec/kontena/cli/master/login_command_spec.rb
|
614
617
|
- spec/kontena/cli/master/logout_command_spec.rb
|
615
618
|
- spec/kontena/cli/master/use_command_spec.rb
|
@@ -762,6 +765,7 @@ test_files:
|
|
762
765
|
- spec/kontena/cli/main_command_spec.rb
|
763
766
|
- spec/kontena/cli/master/current_command_spec.rb
|
764
767
|
- spec/kontena/cli/master/init_cloud_command_spec.rb
|
768
|
+
- spec/kontena/cli/master/join_command_spec.rb
|
765
769
|
- spec/kontena/cli/master/login_command_spec.rb
|
766
770
|
- spec/kontena/cli/master/logout_command_spec.rb
|
767
771
|
- spec/kontena/cli/master/use_command_spec.rb
|