pmux 0.1.0
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.
- data/.gitignore +8 -0
- data/README.md +36 -0
- data/Rakefile +4 -0
- data/bin/pmux +5 -0
- data/lib/pmux/application.rb +166 -0
- data/lib/pmux/cleaner.rb +28 -0
- data/lib/pmux/fiber18.rb +64 -0
- data/lib/pmux/fixcmd.rb +25 -0
- data/lib/pmux/gatherer.rb +23 -0
- data/lib/pmux/handler.rb +262 -0
- data/lib/pmux/job.rb +101 -0
- data/lib/pmux/joblogger.rb +46 -0
- data/lib/pmux/mapper.rb +151 -0
- data/lib/pmux/mros.rb +207 -0
- data/lib/pmux/multi_session.rb +309 -0
- data/lib/pmux/pipeio.rb +19 -0
- data/lib/pmux/plugin.rb +23 -0
- data/lib/pmux/q.rb +3 -0
- data/lib/pmux/reducer.rb +90 -0
- data/lib/pmux/storage_adapter.rb +105 -0
- data/lib/pmux/task_dispatcher.rb +167 -0
- data/lib/pmux/task_queue.rb +11 -0
- data/lib/pmux/task_scheduler.rb +166 -0
- data/lib/pmux/util_daemon.rb +18 -0
- data/lib/pmux/util_logger.rb +137 -0
- data/lib/pmux/version.rb +3 -0
- data/lib/pmux/worker.rb +91 -0
- data/lib/pmux/writer.rb +19 -0
- data/lib/pmux.rb +27 -0
- data/pmux.gemspec +24 -0
- data/test/mock_mros.rb +284 -0
- data/test/mock_pipeio.rb +26 -0
- data/test/mock_world.rb +193 -0
- data/test/mock_xattr.rb +10 -0
- data/test/runner.rb +10 -0
- data/test/test_application.rb +13 -0
- data/test/test_fixcmd.rb +17 -0
- data/test/test_handler.rb +15 -0
- data/test/test_i_mapreduce.rb +169 -0
- data/test/test_i_mros.rb +28 -0
- data/test/test_i_msession.rb +27 -0
- data/test/test_job.rb +35 -0
- data/test/test_joblogger.rb +16 -0
- data/test/test_mapper.rb +60 -0
- data/test/test_pipeio.rb +24 -0
- data/test/test_storage_adapter.rb +63 -0
- data/test/test_task_queue.rb +87 -0
- data/test/test_task_scheduler.rb +39 -0
- data/test/txt/0.log +105 -0
- data/test/txt/1.log +105 -0
- data/test/txt/2.log +105 -0
- data/test/txt/3.log +105 -0
- data/test/txt/4.log +105 -0
- data/test/txt/5.log +105 -0
- data/test/txt/6.log +105 -0
- data/test/txt/7.log +105 -0
- data/test/txt/8.log +105 -0
- data/test/unittest_helper.rb +57 -0
- metadata +153 -0
data/test/mock_mros.rb
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
#require 'mros'
|
3
|
+
#require 'net/scp'
|
4
|
+
|
5
|
+
module Net::SSH
|
6
|
+
class MockChannel
|
7
|
+
@@handler_class = nil
|
8
|
+
def self.handler_class=(klass)
|
9
|
+
@@handler_class = klass
|
10
|
+
end
|
11
|
+
|
12
|
+
include MockWorld::BufferedSendable
|
13
|
+
attr_accessor :host
|
14
|
+
|
15
|
+
alias :send_data :buffered_send_data
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
init_buffer
|
19
|
+
end
|
20
|
+
|
21
|
+
def exec cmd
|
22
|
+
start_mock_cmd cmd
|
23
|
+
@out_buffer = []
|
24
|
+
yield self, true
|
25
|
+
end
|
26
|
+
|
27
|
+
def receive_data data
|
28
|
+
@cb_on_data.call self, data
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_data &block
|
32
|
+
@cb_on_data = block
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_extended_data &block
|
36
|
+
@cb_on_extended_data = block
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_close &block
|
40
|
+
@cb_on_close = block
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_eof &block
|
44
|
+
@cb_on_eof = block
|
45
|
+
end
|
46
|
+
|
47
|
+
def do_eof
|
48
|
+
@cb_on_eof.call(self) if @cb_on_eof
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_open_failed &block
|
52
|
+
end
|
53
|
+
|
54
|
+
def start_mock_cmd cmd
|
55
|
+
if cmd =~ /^pmux/ #XXX
|
56
|
+
tmp_dir = "#{$test_dir}/.t"
|
57
|
+
options = {:tmp_dir=>"#{tmp_dir}/#{host}",
|
58
|
+
:log_dir=>"#{tmp_dir}/#{host}",
|
59
|
+
:fs_dir=>"#{tmp_dir}/#{host}/fs"}
|
60
|
+
FileUtils.mkdir_p options[:tmp_dir]
|
61
|
+
FileUtils.mkdir_p options[:fs_dir]
|
62
|
+
server = MR::Server.new
|
63
|
+
handler_class = @@handler_class || Pmux::Handler
|
64
|
+
handler = handler_class.new server, options
|
65
|
+
|
66
|
+
pipe_transport = @world.new_connection MR::MockPipeTransport
|
67
|
+
server.listen pipe_transport, handler
|
68
|
+
pipe_transport.mock_receiver = self
|
69
|
+
@mock_receiver = pipe_transport
|
70
|
+
|
71
|
+
unix_transport = @world.new_connection MR::MockTransport
|
72
|
+
unix_transport.address = "/unixsocket"
|
73
|
+
server.listen unix_transport, handler
|
74
|
+
server
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class MockSession
|
80
|
+
attr_reader :host
|
81
|
+
attr_accessor :coolio_loop
|
82
|
+
|
83
|
+
def initialize args
|
84
|
+
host, user = args
|
85
|
+
@host = host
|
86
|
+
end
|
87
|
+
|
88
|
+
def open_channel
|
89
|
+
world = Net::SSH::Compat.coolio_loop.world
|
90
|
+
ch = world.new_connection MockChannel
|
91
|
+
ch.host = host
|
92
|
+
yield ch
|
93
|
+
end
|
94
|
+
|
95
|
+
def scp
|
96
|
+
@scp ||= Net::MockSCP.new(self)
|
97
|
+
end
|
98
|
+
|
99
|
+
def floop interval
|
100
|
+
Net::SSH::Compat.coolio_loop.world.run_once
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.start *args, &block
|
105
|
+
MockSession.new args
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
module Net
|
110
|
+
class MockSCP
|
111
|
+
def initialize session
|
112
|
+
@session = session
|
113
|
+
@block = nil
|
114
|
+
world = Net::SSH::Compat.coolio_loop.world
|
115
|
+
world.run_queue.push self
|
116
|
+
@dls = []
|
117
|
+
end
|
118
|
+
|
119
|
+
def download remote, local, options, &block
|
120
|
+
ch = Net::SSH::MockChannel.new
|
121
|
+
@dls.push [remote, local, options, ch]
|
122
|
+
ch
|
123
|
+
end
|
124
|
+
|
125
|
+
def run_once
|
126
|
+
return if @dls.empty?
|
127
|
+
remote, local, options, ch = @dls.shift
|
128
|
+
if ch
|
129
|
+
str = File.read remote
|
130
|
+
open(local, 'w') {|f| f.write str}
|
131
|
+
ch.do_eof
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
module Coolio
|
138
|
+
class Loop
|
139
|
+
@@_mock_loop = nil
|
140
|
+
def self.default
|
141
|
+
@@_mock_loop ||= Coolio::MockLoop.new
|
142
|
+
end
|
143
|
+
|
144
|
+
def set_timer(interval, repeating=false, &block)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class MockLoop < Loop
|
149
|
+
attr_accessor :world
|
150
|
+
|
151
|
+
def run_once
|
152
|
+
@world.run_once
|
153
|
+
end
|
154
|
+
|
155
|
+
def stop
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class Listener
|
160
|
+
def initialize listen_socket
|
161
|
+
@listen_socket = listen_socket
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class TCPServer
|
166
|
+
def initialize(host, port = nil, klass = TCPSocket, *args, &block)
|
167
|
+
@host, @port, @server = host, port, args.first
|
168
|
+
super(nil, klass, *args, &block)
|
169
|
+
end
|
170
|
+
|
171
|
+
def attach loop
|
172
|
+
loop.world.bind_socket self, @host, @port
|
173
|
+
#loop.world.bind @server, [@host, @port]
|
174
|
+
self
|
175
|
+
end
|
176
|
+
|
177
|
+
def on_connection socket
|
178
|
+
connection = @klass.new(nil, *@args)
|
179
|
+
connection.__send__(:on_connect)
|
180
|
+
connection
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
module MR
|
186
|
+
class MockTransport
|
187
|
+
include MockWorld::BufferedSendable
|
188
|
+
include MessageReceiver
|
189
|
+
|
190
|
+
attr_accessor :address
|
191
|
+
|
192
|
+
def initialize address=nil
|
193
|
+
@address = address
|
194
|
+
init_buffer
|
195
|
+
end
|
196
|
+
|
197
|
+
def listen server
|
198
|
+
@pac = MessagePack::Unpacker.new
|
199
|
+
@server = server
|
200
|
+
init_buffer
|
201
|
+
@world.bind_socket server, @address
|
202
|
+
end
|
203
|
+
|
204
|
+
def build_transport session, address
|
205
|
+
@session = session
|
206
|
+
@pac = MessagePack::Unpacker.new
|
207
|
+
self
|
208
|
+
end
|
209
|
+
|
210
|
+
def receive_data data
|
211
|
+
@pac.feed_each(data) {|obj| on_message obj}
|
212
|
+
end
|
213
|
+
|
214
|
+
def on_request msgid, method, param
|
215
|
+
@server.on_request self, msgid, method, param
|
216
|
+
end
|
217
|
+
|
218
|
+
def on_response msgid, error, result
|
219
|
+
@session.on_response self, msgid, error, result
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
class TCPServerTransport::ServerSocket
|
224
|
+
include MockWorld::BufferedSendable
|
225
|
+
|
226
|
+
def initialize io, server
|
227
|
+
@server = server
|
228
|
+
init_buffer
|
229
|
+
@pac = MessagePack::Unpacker.new
|
230
|
+
end
|
231
|
+
|
232
|
+
def send_data data
|
233
|
+
buffered_send_data data
|
234
|
+
end
|
235
|
+
|
236
|
+
def receive_data data
|
237
|
+
@pac.feed_each(data) {|obj| on_message obj}
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class MockPipeTransport < PipeTransport
|
242
|
+
include MockWorld::BufferedSendable
|
243
|
+
include MessageReceiver
|
244
|
+
#attr_accessor :world, :mock_receiver
|
245
|
+
|
246
|
+
def initialize
|
247
|
+
end
|
248
|
+
|
249
|
+
def listen server
|
250
|
+
@pac = MessagePack::Unpacker.new
|
251
|
+
@server = server
|
252
|
+
init_buffer
|
253
|
+
end
|
254
|
+
|
255
|
+
def send_data data
|
256
|
+
buffered_send_data data
|
257
|
+
end
|
258
|
+
|
259
|
+
def receive_data data
|
260
|
+
@pac.feed_each(data) {|obj| on_message obj}
|
261
|
+
end
|
262
|
+
|
263
|
+
def on_request msgid, method, param
|
264
|
+
@server.on_request self, msgid, method, param
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
class Server
|
269
|
+
attr_reader :listeners
|
270
|
+
|
271
|
+
def initialize loop=Coolio::Loop.default#Coolio::MockLoop.new
|
272
|
+
@loop = loop
|
273
|
+
@listeners = []
|
274
|
+
end
|
275
|
+
|
276
|
+
# alias :listen0 :listen
|
277
|
+
# def listen arg1, arg2=nil, arg3=nil, arg4=nil
|
278
|
+
# listen0 arg1, arg2, arg3, arg4
|
279
|
+
# end
|
280
|
+
|
281
|
+
def run
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
data/test/mock_pipeio.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'mock_world'
|
2
|
+
require 'mock_mros'
|
3
|
+
|
4
|
+
module Pmux
|
5
|
+
class PipeIO
|
6
|
+
def initialize cmd, mode='r'
|
7
|
+
@io = IO.popen cmd, mode
|
8
|
+
@on_receive = nil
|
9
|
+
@close_flag = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def flush_out_buffer
|
13
|
+
data = @io.read 100
|
14
|
+
if data
|
15
|
+
on_read data
|
16
|
+
else
|
17
|
+
on_close unless @close_flag
|
18
|
+
@close_flag = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def attach loop
|
23
|
+
loop.world.push_connection self
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/test/mock_world.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
class MockError < RuntimeError; end
|
4
|
+
class MockBlockedError < MockError; end
|
5
|
+
|
6
|
+
class MockWorld
|
7
|
+
ROOT_PREFIX = "#{$test_dir}/.t"
|
8
|
+
|
9
|
+
module BufferedSendable
|
10
|
+
attr_accessor :mock_receiver, :world
|
11
|
+
|
12
|
+
def init_buffer
|
13
|
+
@mock_receiver = nil
|
14
|
+
@out_buffer = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def buffered_send_data data
|
18
|
+
@out_buffer << data.dup
|
19
|
+
end
|
20
|
+
|
21
|
+
alias :send_data :buffered_send_data
|
22
|
+
|
23
|
+
def flush_out_buffer
|
24
|
+
if @out_buffer.empty?
|
25
|
+
data = nil
|
26
|
+
else
|
27
|
+
data = @out_buffer.join ''
|
28
|
+
@out_buffer.clear
|
29
|
+
end
|
30
|
+
if @mock_receiver
|
31
|
+
@mock_receiver.receive_data data if data
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def data
|
36
|
+
@out_buffer.join ''
|
37
|
+
end
|
38
|
+
|
39
|
+
def clear
|
40
|
+
@out_buffer.clear
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :connections, :run_queue, :hosts
|
45
|
+
|
46
|
+
def self.cleanup root_prefix=ROOT_PREFIX
|
47
|
+
Dir.glob("#{root_prefix}/{*,.err*}").each {|e| FileUtils.rm_rf e}
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize root_prefix=ROOT_PREFIX
|
51
|
+
@root_prefix = root_prefix
|
52
|
+
@dcount = 1
|
53
|
+
|
54
|
+
@hosts = []
|
55
|
+
@server_sockets = {}
|
56
|
+
@connections = []
|
57
|
+
@run_queue = []
|
58
|
+
MockTCPSocket.world = self
|
59
|
+
# テスト用rootディレクトリ生成
|
60
|
+
FileUtils.mkdir_p root_prefix
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
def bind_socket socket, arg1, arg2=nil
|
65
|
+
if arg2
|
66
|
+
@server_sockets["#{arg1},#{arg2}"] = socket
|
67
|
+
else
|
68
|
+
@server_sockets[arg1] = socket
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
def push_connection connection
|
74
|
+
@connections.push connection
|
75
|
+
end
|
76
|
+
|
77
|
+
# 新しいconnectionを生成して返す。生成したconnectionは記憶される。
|
78
|
+
def new_connection conn_class
|
79
|
+
connection = conn_class.new
|
80
|
+
connection.world = self
|
81
|
+
@connections.push connection
|
82
|
+
connection
|
83
|
+
end
|
84
|
+
|
85
|
+
def delete_connection connection
|
86
|
+
@connections.delete connection
|
87
|
+
end
|
88
|
+
|
89
|
+
# 指定host,portのserverに疑似接続してclient用socketを返す
|
90
|
+
def connect_to_server host, port
|
91
|
+
server = get_server host, port
|
92
|
+
raise MockError, "#{host},#{port}: connection refused" unless server
|
93
|
+
server.get_client_connection
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
def get_server_socket host, port
|
98
|
+
@server_sockets["#{host},#{port}"]
|
99
|
+
end
|
100
|
+
|
101
|
+
def run_once
|
102
|
+
#if @connections.empty? and @run_queue.empty?
|
103
|
+
# raise RuntimeError, "both @connections and @run_queue are empty"
|
104
|
+
#end
|
105
|
+
for c in @connections
|
106
|
+
c.flush_out_buffer
|
107
|
+
end
|
108
|
+
for obj in @run_queue
|
109
|
+
obj.run_once
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# 擬似的な TCPSocket。
|
115
|
+
# 接続相手はConnection経由のserverオブジェクト。
|
116
|
+
class MockTCPSocket
|
117
|
+
attr_reader :received_data
|
118
|
+
attr_accessor :mock_receiver
|
119
|
+
|
120
|
+
class <<self
|
121
|
+
attr_accessor :world
|
122
|
+
alias :open :new
|
123
|
+
end
|
124
|
+
|
125
|
+
def initialize host, port, lhost=nil, lport=nil
|
126
|
+
@host = host
|
127
|
+
@port = port
|
128
|
+
@received_data = ''
|
129
|
+
|
130
|
+
@socket = self.class.world.get_server_socket host, port
|
131
|
+
@mock_receiver = @socket.on_connection nil
|
132
|
+
self.class.world.push_connection @mock_receiver
|
133
|
+
@mock_receiver.mock_receiver = self
|
134
|
+
end
|
135
|
+
|
136
|
+
# 送信。すぐ相手に届く。
|
137
|
+
def write data
|
138
|
+
if @mock_receiver
|
139
|
+
@mock_receiver.receive_data data
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def puts line
|
144
|
+
self.write "#{line}\n"
|
145
|
+
end
|
146
|
+
|
147
|
+
# 受信。一旦、受信バッファに入れる。
|
148
|
+
def receive_data data
|
149
|
+
@received_data << data
|
150
|
+
end
|
151
|
+
|
152
|
+
# 受信バッファから1行分のデータを取り出して返す。
|
153
|
+
# 1行分受信していなかったら、接続先サーバをrun_tickして、それでも
|
154
|
+
# 受信しなかったらBLOCKEDエラー。
|
155
|
+
def gets
|
156
|
+
count = 0
|
157
|
+
while true
|
158
|
+
line, rbuf = @received_data.split(/^/, 2)
|
159
|
+
break if line =~ /\n/
|
160
|
+
#self.class.world.run_tick 1, 1
|
161
|
+
self.class.world.run_once
|
162
|
+
raise MockBlockedError, 'BLOCKED' if (count += 1) > 5
|
163
|
+
end
|
164
|
+
@received_data = (rbuf || '')
|
165
|
+
line
|
166
|
+
end
|
167
|
+
|
168
|
+
def read n=nil
|
169
|
+
count = 0
|
170
|
+
size = 0
|
171
|
+
res = ''
|
172
|
+
while true
|
173
|
+
if n
|
174
|
+
if size + @received_data.size >= n
|
175
|
+
res << @received_data.slice!(0, n-size)
|
176
|
+
break
|
177
|
+
end
|
178
|
+
elsif !@received_data.empty?
|
179
|
+
res << @received_data
|
180
|
+
@received_data.replace ''
|
181
|
+
break
|
182
|
+
end
|
183
|
+
#self.class.world.run_tick 1, 1
|
184
|
+
self.class.world.run_once
|
185
|
+
raise MockBlockedError, 'BLOCKED' if (count += 1) > 5
|
186
|
+
end
|
187
|
+
res
|
188
|
+
end
|
189
|
+
|
190
|
+
def close
|
191
|
+
@mock_receiver.close
|
192
|
+
end
|
193
|
+
end
|
data/test/mock_xattr.rb
ADDED
data/test/runner.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/unittest_helper')
|
2
|
+
|
3
|
+
class TestMain < Test::Unit::TestCase
|
4
|
+
def test_optparse
|
5
|
+
main = Pmux::Application.new
|
6
|
+
options = {}
|
7
|
+
optparser = main.optparse options
|
8
|
+
assert_kind_of OptionParser, optparser
|
9
|
+
optparser.parse!(['--root-dir=/tmp', '--status'])
|
10
|
+
ae options[:root_dir], '/tmp'
|
11
|
+
assert options[:status]
|
12
|
+
end
|
13
|
+
end
|
data/test/test_fixcmd.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/unittest_helper')
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
include Pmux::FixCmdLine
|
6
|
+
|
7
|
+
class TestFixCmdLine < Test::Unit::TestCase
|
8
|
+
def test_fix_cmd_line
|
9
|
+
mapper = 'cat'
|
10
|
+
res = fix_cmd_line 'grep PAT', 'input'
|
11
|
+
ae "grep PAT input 2>/dev/null", res
|
12
|
+
res = fix_cmd_line 'grep PAT|uniq', 'input'
|
13
|
+
ae "grep PAT input 2>/dev/null|uniq", res
|
14
|
+
res = fix_cmd_line 'grep PAT|uniq', 'input', 'output'
|
15
|
+
ae "grep PAT input 2>/dev/null|uniq >>output", res
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/unittest_helper')
|
2
|
+
|
3
|
+
class TestHandler < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@handler = Pmux::Handler.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_get_num_cpu
|
9
|
+
assert(@handler.get_num_cpu > 0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def _test_get_properties
|
13
|
+
p @handler.get_properties
|
14
|
+
end
|
15
|
+
end
|