gilmour 0.3.0 → 0.3.1
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/gilmour.gemspec +18 -18
- data/lib/gilmour/responder.rb +126 -28
- data/lib/gilmour/stdhijack.rb +49 -0
- data/lib/gilmour/waiter.rb +35 -0
- data/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 762241aef84bf02c572d89ac9f22c16b710911dc
|
4
|
+
data.tar.gz: 50f90851b8bbe757576118d44480b4d102aa6d0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d91f45f9377ff883ff8c371c21212e2702323649c4d45d819bf405bbb570aa3ccd0dff4c401a85d5d59e6a745b2a2398f456947bdc83183c51dc99a0ed31e573
|
7
|
+
data.tar.gz: 8202e63ba3940829ccac7a89aeba02336f0b2f6b18001c73d3549da496a4576ddb54d01b6ded31472bc2af8891bab5d24b8512bed8dda696065b35c63a1a2b7c
|
data/gilmour.gemspec
CHANGED
@@ -3,24 +3,24 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
require "./version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
s.name = "gilmour"
|
7
|
+
s.version = Gilmour::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Aditya Godbole", "Piyush Verma"]
|
10
|
+
s.email = ["code.aa@gdbl.me", "piyush@piyushverma.net"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{A Sinatra like DSL for implementing AMQP services}
|
13
|
+
s.description = %q{This gem provides a Sinatra like DSL and a simple protocol to enable writing services that communicate over AMQP}
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
s.add_development_dependency "rspec"
|
16
|
+
s.add_development_dependency "rspec-given"
|
17
|
+
s.add_dependency "mash"
|
18
|
+
s.add_dependency "redis"
|
19
|
+
s.add_dependency "gilmour-em-hiredis"
|
20
|
+
s.add_dependency "amqp"
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
26
|
end
|
data/lib/gilmour/responder.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'json'
|
4
4
|
require 'logger'
|
5
|
+
require_relative './stdhijack'
|
6
|
+
require_relative './waiter'
|
5
7
|
|
6
8
|
# Top level module
|
7
9
|
module Gilmour
|
@@ -20,9 +22,23 @@ module Gilmour
|
|
20
22
|
end
|
21
23
|
|
22
24
|
class Responder
|
25
|
+
CAPTURE_STDOUT = false
|
26
|
+
LOG_SEPERATOR = '%%'
|
27
|
+
LOG_PREFIX = "#{LOG_SEPERATOR}gilmour#{LOG_SEPERATOR}"
|
28
|
+
|
23
29
|
attr_reader :logger
|
24
30
|
attr_reader :request
|
25
31
|
|
32
|
+
def fork_logger
|
33
|
+
logger = Logger.new(STDERR)
|
34
|
+
loglevel = ENV["LOG_LEVEL"] ? ENV["LOG_LEVEL"].to_sym : :warn
|
35
|
+
logger.level = Gilmour::LoggerLevels[loglevel] || Logger::WARN
|
36
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
37
|
+
"#{LOG_PREFIX}#{severity}#{LOG_SEPERATOR}#{msg}"
|
38
|
+
end
|
39
|
+
logger
|
40
|
+
end
|
41
|
+
|
26
42
|
def make_logger
|
27
43
|
logger = Logger.new(STDERR)
|
28
44
|
original_formatter = Logger::Formatter.new
|
@@ -82,53 +98,129 @@ module Gilmour
|
|
82
98
|
end
|
83
99
|
end
|
84
100
|
|
101
|
+
def pub_relay(waiter)
|
102
|
+
Thread.new {
|
103
|
+
waiter.add 1
|
104
|
+
loop {
|
105
|
+
begin
|
106
|
+
data = @read_publish_pipe.readline
|
107
|
+
destination, message = JSON.parse(data)
|
108
|
+
@backend.publish(message, destination)
|
109
|
+
rescue EOFError
|
110
|
+
waiter.done
|
111
|
+
rescue Exception => e
|
112
|
+
GLogger.debug e.message
|
113
|
+
GLogger.debug e.backtrace
|
114
|
+
end
|
115
|
+
}
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def io_readers(parent_io, waiter)
|
120
|
+
io_threads = []
|
121
|
+
|
122
|
+
parent_io.each do |reader|
|
123
|
+
io_threads << Thread.new {
|
124
|
+
waiter.add 1
|
125
|
+
loop {
|
126
|
+
begin
|
127
|
+
data = reader.readline.chomp
|
128
|
+
if data.empty?
|
129
|
+
next
|
130
|
+
end
|
131
|
+
|
132
|
+
if data.start_with?(LOG_PREFIX)
|
133
|
+
data.split(LOG_PREFIX).each do |msg|
|
134
|
+
msg_grp = msg.split(LOG_SEPERATOR, 2)
|
135
|
+
|
136
|
+
if msg_grp.length > 1
|
137
|
+
data = msg_grp[1]
|
138
|
+
case msg_grp[0]
|
139
|
+
when 'INFO'
|
140
|
+
logger.info data
|
141
|
+
when 'UNKNOWN'
|
142
|
+
logger.unknown data
|
143
|
+
when 'WARN'
|
144
|
+
logger.warn data
|
145
|
+
when 'ERROR'
|
146
|
+
logger.error data
|
147
|
+
when 'FATAL'
|
148
|
+
logger.fatal data
|
149
|
+
else
|
150
|
+
logger.debug data
|
151
|
+
end
|
152
|
+
else
|
153
|
+
logger.debug msg
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
next
|
158
|
+
end
|
159
|
+
|
160
|
+
logger.debug data
|
161
|
+
rescue EOFError
|
162
|
+
waiter.done
|
163
|
+
rescue Exception => e
|
164
|
+
GLogger.error e.message
|
165
|
+
GLogger.error e.backtrace
|
166
|
+
end
|
167
|
+
}
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
io_threads
|
172
|
+
end
|
173
|
+
|
85
174
|
# Called by parent
|
86
175
|
# :nodoc:
|
87
176
|
def execute(handler)
|
88
177
|
if @multi_process
|
89
178
|
GLogger.debug "Executing #{@sender} in forked moode"
|
90
|
-
@read_pipe = @pipe[0]
|
91
|
-
@write_pipe = @pipe[1]
|
92
179
|
|
93
|
-
@
|
94
|
-
@write_publish_pipe = @publish_pipe
|
180
|
+
@read_pipe, @write_pipe = @pipe
|
181
|
+
@read_publish_pipe, @write_publish_pipe = @publish_pipe
|
182
|
+
|
183
|
+
out_r, out_w = IO.pipe
|
184
|
+
parent_io = [out_r]
|
185
|
+
child_io = [out_w]
|
186
|
+
|
187
|
+
if CAPTURE_STDOUT == true
|
188
|
+
err_r, err_w = IO.pipe
|
189
|
+
child_io << err_w
|
190
|
+
parent_io << err_r
|
191
|
+
end
|
95
192
|
|
96
193
|
pid = Process.fork do
|
97
194
|
@backend.stop
|
98
195
|
EventMachine.stop_event_loop
|
196
|
+
|
197
|
+
#Close the parent channels in forked process
|
99
198
|
@read_pipe.close
|
100
199
|
@read_publish_pipe.close
|
200
|
+
parent_io.each{|io| io.close}
|
201
|
+
|
101
202
|
@response_sent = false
|
102
|
-
|
203
|
+
@logger = fork_logger
|
204
|
+
|
205
|
+
capture_output(child_io, CAPTURE_STDOUT) {
|
206
|
+
_execute(handler)
|
207
|
+
}
|
103
208
|
end
|
104
209
|
|
210
|
+
# Cleanup the writers in Parent process.
|
211
|
+
child_io.each {|io| io.close }
|
212
|
+
|
105
213
|
@write_pipe.close
|
106
214
|
@write_publish_pipe.close
|
107
215
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
loop {
|
112
|
-
begin
|
113
|
-
data = @read_publish_pipe.readline
|
114
|
-
pub_mutex.synchronize do
|
115
|
-
destination, message = JSON.parse(data)
|
116
|
-
@backend.publish(message, destination)
|
117
|
-
end
|
118
|
-
rescue EOFError
|
119
|
-
# awkward blank rescue block
|
120
|
-
rescue Exception => e
|
121
|
-
GLogger.debug e.message
|
122
|
-
GLogger.debug e.backtrace
|
123
|
-
end
|
124
|
-
}
|
125
|
-
}
|
216
|
+
wg = Gilmour::Waiter.new
|
217
|
+
io_threads = io_readers(parent_io, wg)
|
218
|
+
io_threads << pub_relay(wg)
|
126
219
|
|
127
220
|
begin
|
128
221
|
receive_data(@read_pipe.readline)
|
129
222
|
rescue EOFError => e
|
130
223
|
logger.debug e.message
|
131
|
-
logger.debug "EOFError caught in responder.rb, because of nil response"
|
132
224
|
end
|
133
225
|
|
134
226
|
pid, status = Process.waitpid2(pid)
|
@@ -146,12 +238,18 @@ module Gilmour
|
|
146
238
|
write_response(@sender, msg, 500)
|
147
239
|
end
|
148
240
|
|
149
|
-
|
150
|
-
|
241
|
+
@read_pipe.close
|
242
|
+
|
243
|
+
wg.wait do
|
244
|
+
io_threads.each { |th|
|
245
|
+
th.kill
|
246
|
+
}
|
151
247
|
end
|
152
248
|
|
153
|
-
|
249
|
+
# Cleanup.
|
154
250
|
@read_publish_pipe.close
|
251
|
+
parent_io.each{|io| io.close unless io.closed?}
|
252
|
+
|
155
253
|
else
|
156
254
|
_execute(handler)
|
157
255
|
end
|
@@ -162,7 +260,7 @@ module Gilmour
|
|
162
260
|
# supplied at setup.
|
163
261
|
def emit_error(message, code = 500, extra = {})
|
164
262
|
opts = {
|
165
|
-
topic: @request.topic,
|
263
|
+
topic: @request.topic,
|
166
264
|
request_data: @request.body,
|
167
265
|
userdata: JSON.generate(extra || {}),
|
168
266
|
sender: @sender,
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# StringIO also do as IO, but IO#reopen fails.
|
2
|
+
# The problem is that a StringIO cannot exist in the O/S's file descriptor
|
3
|
+
# table. STDERR.reopen(...) at the low level does a dup() or dup2() to
|
4
|
+
# copy one file descriptor to another.
|
5
|
+
#
|
6
|
+
# I have two options:
|
7
|
+
#
|
8
|
+
# (1) $stderr = StringIO.new
|
9
|
+
# Then any program which writes to $stderr will be fine. But anything
|
10
|
+
# which writes to STDERR will still go to file descriptor 2.
|
11
|
+
#
|
12
|
+
# (2) reopen STDERR with something which exists in the O/S file descriptor
|
13
|
+
# table: e.g. a file or a pipe.
|
14
|
+
#
|
15
|
+
# I canot use a file, hence a Pipe.
|
16
|
+
|
17
|
+
def capture_output(pipes, capture_stdout=false)
|
18
|
+
streams = []
|
19
|
+
|
20
|
+
if capture_stdout == true
|
21
|
+
streams << $stdout
|
22
|
+
end
|
23
|
+
|
24
|
+
streams << $stderr
|
25
|
+
|
26
|
+
# Save the streams to be reassigned later.
|
27
|
+
# Actually it doesn't matter because the child process would be killed
|
28
|
+
# anyway after the work is done.
|
29
|
+
saved = streams.each do |stream|
|
30
|
+
stream.dup
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
streams.each_with_index do |stream, ix|
|
35
|
+
# Probably I should not use IX, otherwise stdout and stderr can arrive
|
36
|
+
# out of order, which they should?
|
37
|
+
# If I reopen both of them on the same PIPE, they are guaranteed to
|
38
|
+
# arrive in order.
|
39
|
+
stream.reopen(pipes[ix])
|
40
|
+
end
|
41
|
+
yield
|
42
|
+
ensure
|
43
|
+
# This is sort of meaningless, just makes sense aesthetically.
|
44
|
+
# To return what was borrowed.
|
45
|
+
streams.each_with_index do |stream, i|
|
46
|
+
stream.reopen(saved[i])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/gilmour/waiter.rb
CHANGED
@@ -1,11 +1,25 @@
|
|
1
1
|
module Gilmour
|
2
2
|
class Waiter
|
3
3
|
def initialize
|
4
|
+
@count = 0
|
4
5
|
@done = false
|
5
6
|
@waiter_m = Mutex.new
|
6
7
|
@waiter_c = ConditionVariable.new
|
7
8
|
end
|
8
9
|
|
10
|
+
def add n = 1
|
11
|
+
synchronize { @count += n }
|
12
|
+
end
|
13
|
+
|
14
|
+
def done
|
15
|
+
synchronize do
|
16
|
+
@count -= n
|
17
|
+
if @count == 0
|
18
|
+
signal
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
9
23
|
def synchronize(&blk)
|
10
24
|
@waiter_m.synchronize(&blk)
|
11
25
|
end
|
@@ -13,12 +27,33 @@ module Gilmour
|
|
13
27
|
def signal
|
14
28
|
synchronize do
|
15
29
|
@done = true
|
30
|
+
@count = 0
|
16
31
|
@waiter_c.signal
|
17
32
|
end
|
18
33
|
end
|
19
34
|
|
20
35
|
def wait(timeout=nil)
|
21
36
|
synchronize { @waiter_c.wait(@waiter_m, timeout) unless @done }
|
37
|
+
yield if block_given?
|
22
38
|
end
|
23
39
|
end
|
24
40
|
end
|
41
|
+
|
42
|
+
def test
|
43
|
+
wg = Gilmour::Waiter.new
|
44
|
+
wg.add 3
|
45
|
+
|
46
|
+
3.times do
|
47
|
+
Thread.new {
|
48
|
+
t = rand(10000) / 10000.0
|
49
|
+
sleep(t)
|
50
|
+
puts "done\n"
|
51
|
+
wg.done
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
wg.wait do
|
56
|
+
puts "All jobs done"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gilmour
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aditya Godbole
|
8
|
+
- Piyush Verma
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
12
|
+
date: 2015-07-28 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: rspec
|
@@ -98,6 +99,7 @@ description: This gem provides a Sinatra like DSL and a simple protocol to enabl
|
|
98
99
|
writing services that communicate over AMQP
|
99
100
|
email:
|
100
101
|
- code.aa@gdbl.me
|
102
|
+
- piyush@piyushverma.net
|
101
103
|
executables: []
|
102
104
|
extensions: []
|
103
105
|
extra_rdoc_files: []
|
@@ -117,6 +119,7 @@ files:
|
|
117
119
|
- lib/gilmour/base.rb
|
118
120
|
- lib/gilmour/protocol.rb
|
119
121
|
- lib/gilmour/responder.rb
|
122
|
+
- lib/gilmour/stdhijack.rb
|
120
123
|
- lib/gilmour/waiter.rb
|
121
124
|
- test/spec/helpers/common.rb
|
122
125
|
- test/spec/helpers/connection.rb
|