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 +4 -4
- data/.gitignore +1 -0
- data/bin/emptyd +27 -4
- data/lib/emptyd/version.rb +1 -1
- data/lib/emptyd.rb +99 -56
- metadata +14 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fda361182421082855d749acd1a551aa6e91945
|
4
|
+
data.tar.gz: 8241b5977d1abedae42f7ca337a2a97edef3b23c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3069fa9c80e7c57db53f45a3fa5a4831caab1ac53af6030fce57c7b1338aa976a1771f41388ddd51bfcbfaacaeb36829728df186c7a7597b7b186c92d1f82ff6
|
7
|
+
data.tar.gz: 418666a78701ae6ce58a443f8d0b8f9a3cac7acec237ea5d81b31254bf7e6b360aee176830fd3fd042e2c02f6c2cabb3a2207439901433370d3470ac5f09a5f6
|
data/.gitignore
CHANGED
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
|
-
|
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.
|
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
|
-
|
75
|
-
|
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
|
|
data/lib/emptyd/version.rb
CHANGED
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
90
|
-
|
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
|
-
|
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
|
-
|
155
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
-
|
180
|
+
ch.on_request "exit-status" do |ch, data|
|
181
|
+
EM.next_tick do
|
182
182
|
@updated_at = Time.now
|
183
|
-
|
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
|
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
|
-
|
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
|
-
|
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,
|
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
|
-
|
352
|
+
@logger.debug "run is done."
|
310
353
|
else
|
311
|
-
|
354
|
+
@logger.debug "#{@running.size} connections pending"
|
312
355
|
end
|
313
356
|
else
|
314
|
-
|
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.
|
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-
|
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.
|
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
|