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.
Files changed (59) hide show
  1. data/.gitignore +8 -0
  2. data/README.md +36 -0
  3. data/Rakefile +4 -0
  4. data/bin/pmux +5 -0
  5. data/lib/pmux/application.rb +166 -0
  6. data/lib/pmux/cleaner.rb +28 -0
  7. data/lib/pmux/fiber18.rb +64 -0
  8. data/lib/pmux/fixcmd.rb +25 -0
  9. data/lib/pmux/gatherer.rb +23 -0
  10. data/lib/pmux/handler.rb +262 -0
  11. data/lib/pmux/job.rb +101 -0
  12. data/lib/pmux/joblogger.rb +46 -0
  13. data/lib/pmux/mapper.rb +151 -0
  14. data/lib/pmux/mros.rb +207 -0
  15. data/lib/pmux/multi_session.rb +309 -0
  16. data/lib/pmux/pipeio.rb +19 -0
  17. data/lib/pmux/plugin.rb +23 -0
  18. data/lib/pmux/q.rb +3 -0
  19. data/lib/pmux/reducer.rb +90 -0
  20. data/lib/pmux/storage_adapter.rb +105 -0
  21. data/lib/pmux/task_dispatcher.rb +167 -0
  22. data/lib/pmux/task_queue.rb +11 -0
  23. data/lib/pmux/task_scheduler.rb +166 -0
  24. data/lib/pmux/util_daemon.rb +18 -0
  25. data/lib/pmux/util_logger.rb +137 -0
  26. data/lib/pmux/version.rb +3 -0
  27. data/lib/pmux/worker.rb +91 -0
  28. data/lib/pmux/writer.rb +19 -0
  29. data/lib/pmux.rb +27 -0
  30. data/pmux.gemspec +24 -0
  31. data/test/mock_mros.rb +284 -0
  32. data/test/mock_pipeio.rb +26 -0
  33. data/test/mock_world.rb +193 -0
  34. data/test/mock_xattr.rb +10 -0
  35. data/test/runner.rb +10 -0
  36. data/test/test_application.rb +13 -0
  37. data/test/test_fixcmd.rb +17 -0
  38. data/test/test_handler.rb +15 -0
  39. data/test/test_i_mapreduce.rb +169 -0
  40. data/test/test_i_mros.rb +28 -0
  41. data/test/test_i_msession.rb +27 -0
  42. data/test/test_job.rb +35 -0
  43. data/test/test_joblogger.rb +16 -0
  44. data/test/test_mapper.rb +60 -0
  45. data/test/test_pipeio.rb +24 -0
  46. data/test/test_storage_adapter.rb +63 -0
  47. data/test/test_task_queue.rb +87 -0
  48. data/test/test_task_scheduler.rb +39 -0
  49. data/test/txt/0.log +105 -0
  50. data/test/txt/1.log +105 -0
  51. data/test/txt/2.log +105 -0
  52. data/test/txt/3.log +105 -0
  53. data/test/txt/4.log +105 -0
  54. data/test/txt/5.log +105 -0
  55. data/test/txt/6.log +105 -0
  56. data/test/txt/7.log +105 -0
  57. data/test/txt/8.log +105 -0
  58. data/test/unittest_helper.rb +57 -0
  59. 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
@@ -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
@@ -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
@@ -0,0 +1,10 @@
1
+ # ダミーのXAttr
2
+ class XAttr
3
+ # pathに応じて拡張属性を偽装して返す
4
+ def self.get path, name
5
+ dist = 'DISTRIBUTE:dist-test-dht'
6
+ x = path.hash % 4
7
+ path = path.gsub!(%r{^/}, '')
8
+ res = "(<%s> <POSIX:host%d.example.com:/prefix%s>)" % [dist, x, path]
9
+ end
10
+ end
data/test/runner.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'test/unit'
2
+
3
+ if RUBY_VERSION < '1.9.0'
4
+ force_standalone = (RUBY_VERSION >= '1.8.3')
5
+ exit Test::Unit::AutoRunner.run(force_standalone, '.')
6
+ else
7
+ Test::Unit.setup_argv {|files|
8
+ ['.']
9
+ }
10
+ end
@@ -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
@@ -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