vpsadmin-client 3.0.0.master.20211209.pre.0.34bce62b → 3.0.0.master.20220809.pre.0.4bb004b0
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/README.md +13 -11
- data/lib/vpsadmin/cli/commands/vps_remote_console.rb +184 -59
- data/lib/vpsadmin/cli/stream_downloader.rb +2 -2
- data/lib/vpsadmin/client/version.rb +1 -1
- data/shell.nix +1 -1
- data/vpsadmin-client.gemspec +1 -3
- metadata +3 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b441c7f4575b1c83f0b44e030e487f5a21314f28044379aa020bb65437315764
|
4
|
+
data.tar.gz: 83a6e16dfb20baf6188f09ec4d92e3887c957e0bbd884b4143d95a49f958f86a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a421f897c7a466b86914ecc77db3f077175b25118ae198e059f8a3c2213002d46e652734588da5f21401ec19b6d34ddd1f577d6244cc3de8631aae79267e0442
|
7
|
+
data.tar.gz: 16fd9d780cf499e8af47cb9dd21592ea01e6ac76717883a3d88cfe0f108d1c579396bd4b08450e5e2a1c5ea6d5aeca5cf6f23bba47a27e27a9c566f2fcd102e0
|
data/README.md
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
# vpsAdmin client
|
2
2
|
|
3
|
-
|
3
|
+
vpsAdmin client is a Ruby CLI and client library for vpsAdmin API. It is based
|
4
|
+
on [haveapi-client](https://github.com/vpsfreecz/haveapi/tree/master/clients/ruby).
|
5
|
+
|
6
|
+
vpsAdmin client extends `haveapi-client` with several command-line operations
|
7
|
+
specific to vpsAdmin, including:
|
8
|
+
|
9
|
+
- VPS remote console
|
10
|
+
- Snapshot downloads, either ZFS streams or tar archives
|
11
|
+
- Automated utility for local backups using ZFS
|
12
|
+
- Live IP traffic monitor
|
4
13
|
|
5
14
|
## Installation
|
6
15
|
|
@@ -17,13 +26,6 @@ Or install it yourself as:
|
|
17
26
|
$ gem install vpsadmin-client
|
18
27
|
|
19
28
|
## Usage
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
## Contributing
|
24
|
-
|
25
|
-
1. Fork it ( https://github.com/[my-github-username]/vpsadminctl/fork )
|
26
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
-
5. Create a new Pull Request
|
29
|
+
See
|
30
|
+
[haveapi-client](https://github.com/vpsfreecz/haveapi/tree/master/clients/ruby)
|
31
|
+
for usage information.
|
@@ -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
|
@@ -7,8 +7,8 @@ module VpsAdmin::CLI
|
|
7
7
|
class DownloadError < StandardError ; end
|
8
8
|
|
9
9
|
class StreamDownloader
|
10
|
-
def self.download(*args)
|
11
|
-
new(*args)
|
10
|
+
def self.download(*args, **kwargs)
|
11
|
+
new(*args, **kwargs)
|
12
12
|
end
|
13
13
|
|
14
14
|
def initialize(api, dl, io, progress: STDOUT, position: 0, max_rate: nil,
|
data/shell.nix
CHANGED
@@ -17,7 +17,6 @@ in stdenv.mkDerivation rec {
|
|
17
17
|
mkdir -p /tmp/dev-ruby-gems
|
18
18
|
export GEM_HOME="/tmp/dev-ruby-gems"
|
19
19
|
export GEM_PATH="$GEM_HOME:$PWD/lib"
|
20
|
-
export PATH="$GEM_HOME/bin:$PATH"
|
21
20
|
|
22
21
|
BUNDLE="$GEM_HOME/bin/bundle"
|
23
22
|
|
@@ -29,5 +28,6 @@ in stdenv.mkDerivation rec {
|
|
29
28
|
$BUNDLE install
|
30
29
|
|
31
30
|
export RUBYOPT=-rbundler/setup
|
31
|
+
export PATH="$(ruby -e 'puts Gem.bindir'):$PATH"
|
32
32
|
'';
|
33
33
|
}
|
data/vpsadmin-client.gemspec
CHANGED
@@ -21,9 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency 'bundler'
|
22
22
|
spec.add_development_dependency 'rake'
|
23
23
|
|
24
|
-
spec.add_runtime_dependency 'haveapi-client', '~> 0.
|
25
|
-
spec.add_runtime_dependency 'eventmachine', '~> 1.2.7'
|
26
|
-
spec.add_runtime_dependency 'em-http-request', '~> 1.1.6'
|
24
|
+
spec.add_runtime_dependency 'haveapi-client', '~> 0.15.1'
|
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.20220809.pre.0.4bb004b0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakub Skokan
|
@@ -44,42 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.15.1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
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
|
54
|
+
version: 0.15.1
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
56
|
name: json
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|