emptyd 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d30169c9b6b2194aa9463c384bca30e153180adb
4
- data.tar.gz: 1cbb92a9adb7026e0753b540bc48229547e26da0
3
+ metadata.gz: 3fda361182421082855d749acd1a551aa6e91945
4
+ data.tar.gz: 8241b5977d1abedae42f7ca337a2a97edef3b23c
5
5
  SHA512:
6
- metadata.gz: 863c278fa138f2a375a7324f385684c609c253acb478d59a8a642aa9340bf4720bef71b4829ed5f634c7d3307f53717e5cc1d5e1403be5e174ca34eb59b855ac
7
- data.tar.gz: 721c1cfbcf09a778de8121062eef223292fc182d6f44c1f8eea7815a7e33f925ac747633de7d6ac41cb787323e0da7eea981b8339c6009025cbd4c2b9bee8bfd
6
+ metadata.gz: 3069fa9c80e7c57db53f45a3fa5a4831caab1ac53af6030fce57c7b1338aa976a1771f41388ddd51bfcbfaacaeb36829728df186c7a7597b7b186c92d1f82ff6
7
+ data.tar.gz: 418666a78701ae6ce58a443f8d0b8f9a3cac7acec237ea5d81b31254bf7e6b360aee176830fd3fd042e2c02f6c2cabb3a2207439901433370d3470ac5f09a5f6
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .*.*sw[ponm]
data/bin/emptyd CHANGED
@@ -13,6 +13,10 @@ require 'evma_httpserver'
13
13
  class MyHttpServer < EM::Connection
14
14
  include EM::HttpServer
15
15
 
16
+ def initialize(logger)
17
+ @logger = logger
18
+ end
19
+
16
20
  def post_init
17
21
  super
18
22
  no_environment_strings
@@ -21,7 +25,7 @@ class MyHttpServer < EM::Connection
21
25
  def process_http_request
22
26
  done = false
23
27
  port, ip = Socket.unpack_sockaddr_in(get_peername)
24
- Emptyd::LOG.info "#{ip}:#{port} #{@http_request_method} #{@http_path_info} #{@http_post_content.inspect}"
28
+ @logger.info "#{ip}:#{port} #{@http_request_method} #{@http_path_info} #{@http_post_content.inspect}"
25
29
 
26
30
  response = EM::DelegatedHttpResponse.new(self)
27
31
  response.status = 200
@@ -31,7 +35,7 @@ class MyHttpServer < EM::Connection
31
35
  begin
32
36
  case @http_path_info
33
37
  when %r{/session/new}
34
- session = Emptyd::Session.new JSON.load(@http_post_content)
38
+ session = Emptyd::Session.new JSON.parse(@http_post_content, :symbolize_names => true).merge(:logger => @logger)
35
39
  response.content = JSON.dump({id: session.uuid})
36
40
  when %r{/session/(.*)/run}
37
41
  Emptyd::Session[$1].run @http_post_content
@@ -46,6 +50,9 @@ class MyHttpServer < EM::Connection
46
50
  response.send_response
47
51
  end
48
52
  end
53
+ when %r{/session/(.*)/write}
54
+ session = Emptyd::Session[$1]
55
+ session << @http_post_content
49
56
  when %r{/session/(.*)/terminate}
50
57
  session = Emptyd::Session[$1]
51
58
  session.terminate @http_post_content
@@ -70,8 +77,24 @@ class MyHttpServer < EM::Connection
70
77
  end
71
78
  end
72
79
 
80
+ CONFIG = {
81
+ :server => "::1",
82
+ :server_port => 53353
83
+ }
84
+
85
+ %w{.emptyd.conf .empty.conf}.map{|name| File.expand_path("~/#{name}")}.each do |name|
86
+ next unless File.exist? name
87
+ File.open(name) do |file|
88
+ CONFIG.merge!(JSON.parse(file, :symbolize_names => true)) do |k, x, y|
89
+ x.respond_to?(:merge) ? x.merge(y) : y
90
+ end
91
+ end
92
+ end
93
+
73
94
  EM.run do
74
- EM.start_server '0.0.0.0', 8080, MyHttpServer
75
- Emptyd::LOG.info "Server started on port 8080."
95
+ $0 = "emptyd"
96
+ logger = Logger.new(STDOUT)
97
+ EM.start_server CONFIG[:server], CONFIG[:server_port], MyHttpServer, logger
98
+ logger.info "Server started on #{CONFIG[:server]}, port #{CONFIG[:server_port]}."
76
99
  end
77
100
 
@@ -1,3 +1,3 @@
1
1
  module Emptyd
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/emptyd.rb CHANGED
@@ -7,9 +7,6 @@ require 'securerandom'
7
7
  require 'json'
8
8
 
9
9
  module Emptyd
10
- $0 = "emptyd"
11
- LOG = Logger.new(STDERR)
12
-
13
10
  class Connection
14
11
  attr_reader :key, :updated_at, :failed_at, :error
15
12
  EXPIRE_INTERVAL = 600 # 10min
@@ -18,13 +15,14 @@ module Emptyd
18
15
  @@connections = {}
19
16
  @@count = {}
20
17
 
21
- def self.[](key)
22
- @@connections[key] or Connection.new(key)
18
+ def self.[](key, logger)
19
+ @@connections[key] or Connection.new(key, logger)
23
20
  end
24
21
 
25
- def initialize(key)
22
+ def initialize(key, logger)
26
23
  raise IOError, "already registered" if @@connections[key]
27
24
  @key = key
25
+ @logger = logger
28
26
  @sessions = []
29
27
  @user, @host = key.split('@', 2)
30
28
  @user, @host = "root", @user if @host.nil?
@@ -50,7 +48,7 @@ module Emptyd
50
48
  conn.close
51
49
  end.resume
52
50
  end
53
- LOG.debug "Destroying connection #{@key}"
51
+ @logger.debug "Destroying connection #{@key}"
54
52
  end
55
53
 
56
54
  def start
@@ -63,7 +61,7 @@ module Emptyd
63
61
  if c
64
62
  c.destroy
65
63
  else
66
- LOG.debug "pressure: no free connections: #{@@count.keys}"
64
+ @logger.debug "pressure: no free connections: #{@@count.keys}"
67
65
  end
68
66
  end
69
67
  end
@@ -72,31 +70,22 @@ module Emptyd
72
70
  begin
73
71
  pressure[]
74
72
  if @@count.size >= MAX_CONNECTIONS
75
- LOG.debug "Quota exceeded by #{@key}: #{@@count.size}"
73
+ @logger.debug "Quota exceeded by #{@key}: #{@@count.size}"
76
74
  @start_timer = EM::PeriodicTimer.new(rand(1..10)) do
77
75
  pressure[]
78
76
  if @@count.size < MAX_CONNECTIONS
79
77
  @start_timer.cancel
80
78
  EM.next_tick starter
81
79
  else
82
- LOG.debug "No more connection quota, deferring #{@key}..."
80
+ @logger.debug "No more connection quota, deferring #{@key}..."
83
81
  end
84
82
  end
85
83
  else
86
84
  @@count[self.key] = self
87
- LOG.debug "Created new conn: #{key}, quota = #{@@count.size}"
85
+ @logger.debug "Created new conn: #{key}, quota = #{@@count.size}"
88
86
  EM::Ssh.start(@host, @user, user_known_hosts_file: []) do |conn|
89
- conn.errback do |err|
90
- @@count.delete self.key
91
- @conn = nil
92
- STDERR.puts "Connection to #{@key} is broken: #{err}"
93
- @error = err
94
- @failed_at = Time.now
95
- @connecting = false
96
- @run_queue.each do |cmd,session,callback|
97
- callback.call self, :error
98
- end
99
- end
87
+ conn.errback { |err| errback err }
88
+ conn.on(:closed) { errback "closed" }
100
89
  conn.callback do |ssh|
101
90
  @conn = ssh
102
91
  @error = nil
@@ -105,7 +94,7 @@ module Emptyd
105
94
  @connecting = false
106
95
  @run_queue.each do |cmd,session,callback|
107
96
  if session.dead?
108
- LOG.debug "Dropping pending run request from a dead session"
97
+ @logger.debug "Dropping pending run request from a dead session"
109
98
  @error = "session is dead"
110
99
  else
111
100
  EM.next_tick { run cmd, session, &callback }
@@ -134,6 +123,25 @@ module Emptyd
134
123
  EM.next_tick starter
135
124
  end
136
125
 
126
+ def errback err
127
+ @@count.delete self.key
128
+ had_valid_conn = !!@conn
129
+ @conn = nil
130
+ STDERR.puts "Connection to #{@key} is broken: #{err}"
131
+ @error = err
132
+ @failed_at = Time.now
133
+ @connecting = false
134
+ if had_valid_conn
135
+ @sessions.each do |session|
136
+ session.queue.push [self, :error, nil]
137
+ end
138
+ else
139
+ @run_queue.each do |cmd,session,callback|
140
+ callback.call self, :error
141
+ end
142
+ end
143
+ end
144
+
137
145
  def bind(session)
138
146
  raise IOError, "already bound" if @sessions.include? session
139
147
  @sessions << session
@@ -151,36 +159,56 @@ module Emptyd
151
159
  return
152
160
  end
153
161
 
154
- @conn.open_channel do |ch|
155
- ch.exec cmd do |ch, success|
156
- callback.call self, :init, ch
157
-
158
- STDERR.puts "#{h}: exec failed: #{cmd}" unless success
159
- ch.on_data do |c, data|
160
- EM.next_tick do
161
- @updated_at = Time.now
162
- session.queue.push [@key,nil,data]
163
- end
164
- end
162
+ setup = proc do |ch|
163
+ callback.call self, :init, ch
165
164
 
166
- ch.on_extended_data do |c, type, data|
167
- EM.next_tick do
168
- @updated_at = Time.now
169
- session.queue.push [@key,type,data]
170
- LOG.debug [type,data]
171
- end
165
+ ch.on_data do |c, data|
166
+ EM.next_tick do
167
+ @updated_at = Time.now
168
+ session.queue.push [@key,nil,data]
172
169
  end
170
+ end
173
171
 
174
- ch.on_request "exit-status" do |ch, data|
175
- EM.next_tick do
176
- @updated_at = Time.now
177
- session.queue.push [@key,:exit,data.read_long]
178
- end
172
+ ch.on_extended_data do |c, type, data|
173
+ EM.next_tick do
174
+ @updated_at = Time.now
175
+ session.queue.push [@key,type,data]
176
+ @logger.debug [type,data]
179
177
  end
178
+ end
180
179
 
181
- ch.on_close do
180
+ ch.on_request "exit-status" do |ch, data|
181
+ EM.next_tick do
182
182
  @updated_at = Time.now
183
- callback.call self, :close
183
+ session.queue.push [@key,:exit,data.read_long]
184
+ end
185
+ end
186
+
187
+ ch.on_close do
188
+ @updated_at = Time.now
189
+ p "closed"
190
+ callback.call self, :close
191
+ end
192
+
193
+ ch.on_open_failed do |ch, code, desc|
194
+ EM.next_tick do
195
+ callback.call self, :error, desc
196
+ end
197
+ end
198
+ end
199
+
200
+ @conn.open_channel do |ch|
201
+ if session.interactive?
202
+ ch.request_pty do |ch, success|
203
+ ch.exec cmd do |ch, success|
204
+ STDERR.puts "exec failed: #{cmd}" unless success
205
+ setup[ch]
206
+ end
207
+ end
208
+ else
209
+ ch.exec cmd do |ch, success|
210
+ STDERR.puts "exec failed: #{cmd}" unless success
211
+ setup[ch]
184
212
  end
185
213
  end
186
214
  end
@@ -204,7 +232,7 @@ module Emptyd
204
232
  end
205
233
 
206
234
  class Session
207
- attr_reader :uuid, :queue
235
+ attr_reader :uuid, :queue, :logger
208
236
  @@sessions = {}
209
237
 
210
238
  def self.ids
@@ -215,11 +243,18 @@ module Emptyd
215
243
  @@sessions[uuid] or raise KeyError, "no such session"
216
244
  end
217
245
 
218
- def initialize keys, &callback
246
+ def interactive?
247
+ @interactive
248
+ end
249
+
250
+ def initialize options, &callback
251
+ keys = options[:keys]
252
+ @interactive = !!options[:interactive]
253
+ @logger = options[:logger]
219
254
  @uuid = SecureRandom.uuid
220
255
  @@sessions[@uuid] = self
221
256
  @keys = keys
222
- @connections = Hash[keys.map{|h| [h, Connection[h]]}]
257
+ @connections = Hash[keys.map{|h| [h, Connection[h, @logger]]}]
223
258
  @connections.each_value{|h| h.bind self}
224
259
  @queue = EM::Queue.new
225
260
  @running = {}
@@ -228,7 +263,7 @@ module Emptyd
228
263
  end
229
264
 
230
265
  def destroy
231
- LOG.debug "Destroying session #{@uuid}"
266
+ @logger.debug "Destroying session #{@uuid}"
232
267
  p @running.map{|h,v| [h, v.class.name]}
233
268
  @running.each do |h,v|
234
269
  if v.respond_to? :close
@@ -278,6 +313,14 @@ module Emptyd
278
313
  }
279
314
  end
280
315
 
316
+ def << data
317
+ @running.each do |k,v|
318
+ if v.respond_to? :send_data
319
+ v.send_data data
320
+ end
321
+ end
322
+ end
323
+
281
324
  def terminate key
282
325
  chan = @running[key]
283
326
  conn = @connections[key]
@@ -293,7 +336,7 @@ module Emptyd
293
336
 
294
337
  def callback(h,e,c=nil)
295
338
  EM.next_tick do
296
- LOG.debug [h.key,e,c.nil?]
339
+ @logger.debug [h.key,e,c.nil?]
297
340
  case e
298
341
  when :init
299
342
  @queue.push [h.key,:start,nil]
@@ -301,17 +344,17 @@ module Emptyd
301
344
  when :close, :error
302
345
  h.unbind self unless @dead or not @connections.include? h.key
303
346
  @connections.delete h.key
304
- @queue.push [h.key,:dead,nil] if e == :error
347
+ @queue.push [h.key,:dead,c] if e == :error
305
348
  @queue.push [h.key,:done,nil]
306
349
  @running.delete h.key
307
350
  if done?
308
351
  @queue.push [nil,:done,nil]
309
- LOG.debug "run is done."
352
+ @logger.debug "run is done."
310
353
  else
311
- LOG.debug "#{@running.size} connections pending"
354
+ @logger.debug "#{@running.size} connections pending"
312
355
  end
313
356
  else
314
- LOG.error "Session#run: unexpected callback #{e}"
357
+ @logger.error "Session#run: unexpected callback #{e}"
315
358
  end
316
359
  end
317
360
  end
metadata CHANGED
@@ -1,69 +1,69 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: emptyd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - kmeaw
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-14 00:00:00.000000000 Z
11
+ date: 2014-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.3'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: em-ssh
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
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
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: eventmachine_httpserver
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  description: An HTTP interface to run a single command on a cluster.
@@ -74,7 +74,7 @@ executables:
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
- - .gitignore
77
+ - ".gitignore"
78
78
  - Gemfile
79
79
  - LICENSE.txt
80
80
  - README.md
@@ -93,17 +93,17 @@ require_paths:
93
93
  - lib
94
94
  required_ruby_version: !ruby/object:Gem::Requirement
95
95
  requirements:
96
- - - '>='
96
+ - - ">="
97
97
  - !ruby/object:Gem::Version
98
98
  version: '0'
99
99
  required_rubygems_version: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - '>='
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  requirements: []
105
105
  rubyforge_project:
106
- rubygems_version: 2.1.9
106
+ rubygems_version: 2.0.14
107
107
  signing_key:
108
108
  specification_version: 4
109
109
  summary: Run commands on multiple hosts over SSH