vpsadmin-client 3.0.0.master.20220107.pre.0.57b7b680 → 3.0.0.master.20220121.pre.0.b5a81113
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/lib/vpsadmin/cli/commands/vps_remote_console.rb +184 -59
- data/lib/vpsadmin/client/version.rb +1 -1
- data/vpsadmin-client.gemspec +0 -2
- metadata +1 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce5635948bc213752357b413d4e92f8b7f2089306b9a3e02194b76f44f3617c3
|
4
|
+
data.tar.gz: 4304acd24bbecf0389a954d61ce680fece999e1128651f84e75306cf61889195
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3c0c9029f6d14639adb9f901f4b1cc133d1518360be85d8e506e8aa285fa5a1b3e8be5cfa475ae87d8147fadf78dd55bd168593e945f59c1dc237412f714705
|
7
|
+
data.tar.gz: 35e2c3b730bf246d1a14c0a6a7cdf77c08910efa4d460aeb878a52a9d4a0210058a7450c556c7e30a08199b011464a45b17b155b13d965c2da057e14a3878309
|
@@ -1,5 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'em-http'
|
1
|
+
require 'thread'
|
3
2
|
require 'time'
|
4
3
|
require 'json'
|
5
4
|
require 'base64'
|
@@ -11,16 +10,35 @@ module VpsAdmin::CLI::Commands
|
|
11
10
|
args 'VPS_ID'
|
12
11
|
desc 'Open VPS remote console'
|
13
12
|
|
14
|
-
class InputHandler
|
15
|
-
|
16
|
-
|
17
|
-
def initialize
|
13
|
+
class InputHandler
|
14
|
+
def initialize(http_client)
|
15
|
+
@http_client = http_client
|
18
16
|
@private_buffer = ''
|
19
|
-
@buffer = ''
|
20
17
|
@end_seq = ["\r", "\e", "."]
|
21
18
|
@end_i = 0
|
19
|
+
@stop = false
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_from(io)
|
23
|
+
begin
|
24
|
+
data = io.read_nonblock(4096)
|
25
|
+
rescue IO::WaitReadable
|
26
|
+
return
|
27
|
+
rescue Errno::EIO
|
28
|
+
stop
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
write(data)
|
22
33
|
end
|
23
34
|
|
35
|
+
def stop?
|
36
|
+
@stop
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
attr_reader :http_client
|
41
|
+
|
24
42
|
# Data is checked on the presence of the end sequence. The first character
|
25
43
|
# in the sequence (ENTER) can be read multiple times in a row and it is
|
26
44
|
# to be forwarded.
|
@@ -29,38 +47,160 @@ module VpsAdmin::CLI::Commands
|
|
29
47
|
# but stored in a private buffer. If the sequence is later broken, the private
|
30
48
|
# buffer is forwarded and reset.
|
31
49
|
#
|
32
|
-
# If the whole end sequence is read,
|
33
|
-
def
|
50
|
+
# If the whole end sequence is read, we exit.
|
51
|
+
def write(data)
|
52
|
+
buffer = ''
|
53
|
+
|
34
54
|
data.each_char do |char|
|
35
55
|
if char == @end_seq[ @end_i ]
|
36
|
-
if @end_i == @end_seq.size-1
|
37
|
-
|
56
|
+
if @end_i == @end_seq.size - 1
|
57
|
+
@stop = true
|
38
58
|
return
|
39
59
|
end
|
40
60
|
|
41
61
|
@end_i += 1
|
42
62
|
|
43
63
|
if @end_i == 1
|
44
|
-
|
45
|
-
|
64
|
+
buffer += char
|
46
65
|
else
|
47
66
|
@private_buffer += char
|
48
67
|
end
|
49
68
|
|
50
69
|
elsif char == @end_seq.first
|
51
|
-
|
52
|
-
|
70
|
+
buffer += char
|
53
71
|
else
|
54
72
|
@end_i = 0
|
55
73
|
|
56
74
|
unless @private_buffer.empty?
|
57
|
-
|
75
|
+
buffer += @private_buffer
|
58
76
|
@private_buffer.clear
|
59
77
|
end
|
60
78
|
|
61
|
-
|
79
|
+
buffer += char
|
62
80
|
end
|
63
81
|
end
|
82
|
+
|
83
|
+
http_client.write(buffer) unless buffer.empty?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class HttpClient
|
88
|
+
def initialize(vps, token, rate)
|
89
|
+
@vps = vps
|
90
|
+
@token = token
|
91
|
+
@rate = rate
|
92
|
+
@mutex = Mutex.new
|
93
|
+
@write_buffer = ''
|
94
|
+
@stop = false
|
95
|
+
@error = false
|
96
|
+
end
|
97
|
+
|
98
|
+
def start
|
99
|
+
@thread = Thread.new { run }
|
100
|
+
end
|
101
|
+
|
102
|
+
def stop
|
103
|
+
@stop = true
|
104
|
+
end
|
105
|
+
|
106
|
+
def stop?
|
107
|
+
@stop
|
108
|
+
end
|
109
|
+
|
110
|
+
def error?
|
111
|
+
@error != false
|
112
|
+
end
|
113
|
+
|
114
|
+
def error
|
115
|
+
@error
|
116
|
+
end
|
117
|
+
|
118
|
+
def join
|
119
|
+
stop
|
120
|
+
thread.join
|
121
|
+
end
|
122
|
+
|
123
|
+
def write(data)
|
124
|
+
mutex.synchronize do
|
125
|
+
write_buffer << data
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def resize(width, height)
|
130
|
+
@width = width
|
131
|
+
@height = height
|
132
|
+
end
|
133
|
+
|
134
|
+
protected
|
135
|
+
attr_reader :vps, :token, :rate, :width, :height, :write_buffer,
|
136
|
+
:mutex, :thread
|
137
|
+
|
138
|
+
def run
|
139
|
+
uri = URI("#{vps.node.location.remote_console_server}/console/feed/#{vps.id}")
|
140
|
+
start_args = [uri.host, uri.port, nil, nil, nil, nil, {
|
141
|
+
use_ssl: uri.scheme == 'https',
|
142
|
+
}]
|
143
|
+
|
144
|
+
Net::HTTP.start(*start_args) do |http|
|
145
|
+
loop do
|
146
|
+
break if stop? || error?
|
147
|
+
|
148
|
+
rate_limit { send_request(http, uri) }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def send_request(http, uri)
|
154
|
+
req = Net::HTTP::Post.new(uri)
|
155
|
+
|
156
|
+
keys =
|
157
|
+
mutex.synchronize do
|
158
|
+
s = write_buffer.dup
|
159
|
+
write_buffer.clear
|
160
|
+
s
|
161
|
+
end
|
162
|
+
|
163
|
+
req.set_form_data(
|
164
|
+
'session' => token,
|
165
|
+
'keys' => keys,
|
166
|
+
'width' => width,
|
167
|
+
'height' => height,
|
168
|
+
)
|
169
|
+
|
170
|
+
res = http.request(req)
|
171
|
+
|
172
|
+
unless res.is_a?(Net::HTTPSuccess)
|
173
|
+
set_error(
|
174
|
+
"Console server returned error: "+
|
175
|
+
"HTTP #{res.code} - #{res.message}\n\n#{res.body}"
|
176
|
+
)
|
177
|
+
stop
|
178
|
+
return
|
179
|
+
end
|
180
|
+
|
181
|
+
ret = JSON.parse(res.body, symbolize_names: true)
|
182
|
+
|
183
|
+
unless ret[:session]
|
184
|
+
$stdout.write(ret[:data])
|
185
|
+
set_error("Session closed.")
|
186
|
+
stop
|
187
|
+
return
|
188
|
+
end
|
189
|
+
|
190
|
+
$stdout.write(Base64.decode64(ret[:data]))
|
191
|
+
end
|
192
|
+
|
193
|
+
def rate_limit
|
194
|
+
t1 = Time.now
|
195
|
+
yield
|
196
|
+
t2 = Time.now
|
197
|
+
|
198
|
+
cooldown = rate - (t2 - t1)
|
199
|
+
sleep(cooldown) if cooldown > 0
|
200
|
+
end
|
201
|
+
|
202
|
+
def set_error(str)
|
203
|
+
@error = str
|
64
204
|
end
|
65
205
|
end
|
66
206
|
|
@@ -93,7 +233,7 @@ module VpsAdmin::CLI::Commands
|
|
93
233
|
end
|
94
234
|
|
95
235
|
puts " VPS is on #{vps.node.domain_name}, located in #{vps.node.location.label}."
|
96
|
-
puts "Console
|
236
|
+
puts "Console server URL is #{vps.node.location.remote_console_server}"
|
97
237
|
write "Obtaining authentication token..."
|
98
238
|
|
99
239
|
begin
|
@@ -105,31 +245,18 @@ module VpsAdmin::CLI::Commands
|
|
105
245
|
exit(false)
|
106
246
|
end
|
107
247
|
|
108
|
-
@token = t.token
|
109
|
-
|
110
248
|
puts
|
111
|
-
puts "Connecting to remote console..."
|
249
|
+
puts "Connecting to the remote console..."
|
112
250
|
puts "Press ENTER ESC . to exit"
|
113
251
|
puts
|
114
252
|
|
115
253
|
raw_mode do
|
116
|
-
|
117
|
-
@input = EventMachine.open_keyboard(InputHandler)
|
118
|
-
|
119
|
-
@http = EventMachine::HttpRequest.new(
|
120
|
-
"#{vps.node.location.remote_console_server}/console/feed/#{vps_id}",
|
121
|
-
ssl: {verify_peer: true},
|
122
|
-
)
|
123
|
-
communicate
|
124
|
-
end
|
254
|
+
run_console(vps, t.token)
|
125
255
|
end
|
126
256
|
end
|
127
257
|
|
128
258
|
protected
|
129
|
-
|
130
|
-
$stdout.write(s)
|
131
|
-
$stdout.flush
|
132
|
-
end
|
259
|
+
attr_reader :client
|
133
260
|
|
134
261
|
def raw_mode
|
135
262
|
state = `stty -g`
|
@@ -140,6 +267,7 @@ module VpsAdmin::CLI::Commands
|
|
140
267
|
|
141
268
|
Signal.trap('WINCH') do
|
142
269
|
@size = Terminal.size!
|
270
|
+
client.resize(@size[:width], @size[:height]) if client
|
143
271
|
end
|
144
272
|
|
145
273
|
yield
|
@@ -151,38 +279,35 @@ module VpsAdmin::CLI::Commands
|
|
151
279
|
puts
|
152
280
|
end
|
153
281
|
|
154
|
-
def
|
155
|
-
|
156
|
-
body: {
|
157
|
-
session: @token,
|
158
|
-
keys: @input.buffer,
|
159
|
-
width: @size[:width],
|
160
|
-
height: @size[:height],
|
161
|
-
},
|
162
|
-
keepalive: true
|
163
|
-
)
|
282
|
+
def run_console(vps, token)
|
283
|
+
Thread.abort_on_exception = true
|
164
284
|
|
165
|
-
@
|
285
|
+
@client = HttpClient.new(vps, token, @opts[:rate])
|
286
|
+
client.resize(@size[:width], @size[:height])
|
166
287
|
|
167
|
-
|
168
|
-
|
169
|
-
EventMachine.stop
|
170
|
-
end
|
288
|
+
input = InputHandler.new(client)
|
289
|
+
client.start
|
171
290
|
|
172
|
-
|
173
|
-
|
291
|
+
loop do
|
292
|
+
res = IO.select([$stdin], [], [], 1)
|
293
|
+
input.read_from($stdin) if res
|
174
294
|
|
175
|
-
|
176
|
-
|
177
|
-
puts "\nSession closed."
|
178
|
-
EM.stop
|
179
|
-
next
|
180
|
-
end
|
295
|
+
if input.stop? || client.stop?
|
296
|
+
client.join
|
181
297
|
|
182
|
-
|
298
|
+
if client.error?
|
299
|
+
write("\n")
|
300
|
+
write(client.error)
|
301
|
+
end
|
183
302
|
|
184
|
-
|
303
|
+
return
|
304
|
+
end
|
185
305
|
end
|
186
306
|
end
|
307
|
+
|
308
|
+
def write(s)
|
309
|
+
$stdout.write(s)
|
310
|
+
$stdout.flush
|
311
|
+
end
|
187
312
|
end
|
188
313
|
end
|
data/vpsadmin-client.gemspec
CHANGED
@@ -22,8 +22,6 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency 'rake'
|
23
23
|
|
24
24
|
spec.add_runtime_dependency 'haveapi-client', '~> 0.15.1'
|
25
|
-
spec.add_runtime_dependency 'eventmachine', '~> 1.2.7'
|
26
|
-
spec.add_runtime_dependency 'em-http-request', '~> 1.1.6'
|
27
25
|
spec.add_runtime_dependency 'json'
|
28
26
|
spec.add_runtime_dependency 'curses'
|
29
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vpsadmin-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.master.
|
4
|
+
version: 3.0.0.master.20220121.pre.0.b5a81113
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakub Skokan
|
@@ -52,34 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.15.1
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: eventmachine
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 1.2.7
|
62
|
-
type: :runtime
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 1.2.7
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: em-http-request
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 1.1.6
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 1.1.6
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
56
|
name: json
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|