vpsadmin-client 3.0.0.master.20211209.pre.0.34bce62b → 3.0.0.master.20220809.pre.0.4bb004b0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7904eb80c6480049ca0bd7610d1271ca8190021cedee59e8035427513dcd90f0
4
- data.tar.gz: 88999438bbc0016133b931a8678ef2ab1aa9657e4cfa98d17b42ba98c63c12ac
3
+ metadata.gz: b441c7f4575b1c83f0b44e030e487f5a21314f28044379aa020bb65437315764
4
+ data.tar.gz: 83a6e16dfb20baf6188f09ec4d92e3887c957e0bbd884b4143d95a49f958f86a
5
5
  SHA512:
6
- metadata.gz: b6c410b2f35e56c6f438e2941a0a915439ab8e749869ad88c526fea5f7aedaaa339a23591e09eea1b81c2d3acb52fb89781eb9d45035d3a85a65203cda84b462
7
- data.tar.gz: 5e896b3ae63c5ab7c28a51713dafaf32936ce4e7ed3559b5380c9802fc093f6b8c244b1efb95966a3e162e9e931977d5812cf2db15761b35e173a785d518ba86
6
+ metadata.gz: a421f897c7a466b86914ecc77db3f077175b25118ae198e059f8a3c2213002d46e652734588da5f21401ec19b6d34ddd1f577d6244cc3de8631aae79267e0442
7
+ data.tar.gz: 16fd9d780cf499e8af47cb9dd21592ea01e6ac76717883a3d88cfe0f108d1c579396bd4b08450e5e2a1c5ea6d5aeca5cf6f23bba47a27e27a9c566f2fcd102e0
data/README.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # vpsAdmin client
2
2
 
3
- TODO: Write a gem description
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
- TODO: Write usage instructions here
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 'eventmachine'
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 < EventMachine::Connection
15
- attr_accessor :buffer
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, EM event loop is stopped.
33
- def receive_data(data)
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
- EM.stop
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
- @buffer += char
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
- @buffer += char
52
-
70
+ buffer += char
53
71
  else
54
72
  @end_i = 0
55
73
 
56
74
  unless @private_buffer.empty?
57
- @buffer += @private_buffer
75
+ buffer += @private_buffer
58
76
  @private_buffer.clear
59
77
  end
60
78
 
61
- @buffer += char
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 router URL is #{vps.node.location.remote_console_server}"
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
- EventMachine.run do
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
- def write(s)
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 communicate
155
- post = @http.post(
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
- @input.buffer = ''
285
+ @client = HttpClient.new(vps, token, @opts[:rate])
286
+ client.resize(@size[:width], @size[:height])
166
287
 
167
- post.errback do
168
- puts "Error: connection to console router failed"
169
- EventMachine.stop
170
- end
288
+ input = InputHandler.new(client)
289
+ client.start
171
290
 
172
- post.callback do
173
- ret = JSON.parse(post.response, symbolize_names: true)
291
+ loop do
292
+ res = IO.select([$stdin], [], [], 1)
293
+ input.read_from($stdin) if res
174
294
 
175
- unless ret[:session]
176
- $stdout.write(ret[:data])
177
- puts "\nSession closed."
178
- EM.stop
179
- next
180
- end
295
+ if input.stop? || client.stop?
296
+ client.join
181
297
 
182
- $stdout.write(Base64.decode64(ret[:data]))
298
+ if client.error?
299
+ write("\n")
300
+ write(client.error)
301
+ end
183
302
 
184
- EM.add_timer(@opts[:rate]) { communicate }
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,
@@ -1,5 +1,5 @@
1
1
  module VpsAdmin
2
2
  module Client
3
- VERSION = '3.0.0.master.20211209-0.34bce62b'
3
+ VERSION = '3.0.0.master.20220809-0.4bb004b0'
4
4
  end
5
5
  end
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
  }
@@ -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.14.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.20211209.pre.0.34bce62b
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.14.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.14.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