emptyd 0.0.1 → 0.0.2

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
  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