mogilefs-client 1.0.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.
- data/LICENSE +32 -0
- data/Manifest.txt +18 -0
- data/README +9 -0
- data/Rakefile +56 -0
- data/lib/mogilefs.rb +24 -0
- data/lib/mogilefs/admin.rb +274 -0
- data/lib/mogilefs/backend.rb +219 -0
- data/lib/mogilefs/client.rb +64 -0
- data/lib/mogilefs/httpfile.rb +144 -0
- data/lib/mogilefs/mogilefs.rb +214 -0
- data/lib/mogilefs/nfsfile.rb +81 -0
- data/lib/mogilefs/pool.rb +50 -0
- data/test/setup.rb +55 -0
- data/test/test_admin.rb +37 -0
- data/test/test_backend.rb +208 -0
- data/test/test_client.rb +49 -0
- data/test/test_mogilefs.rb +123 -0
- data/test/test_pool.rb +98 -0
- metadata +63 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'mogilefs'
|
3
|
+
|
4
|
+
class MogileFS::Pool
|
5
|
+
|
6
|
+
class BadObjectError < RuntimeError; end
|
7
|
+
|
8
|
+
def initialize(klass, *args)
|
9
|
+
@args = args
|
10
|
+
@klass = klass
|
11
|
+
@queue = Queue.new
|
12
|
+
@objects = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def get
|
16
|
+
begin
|
17
|
+
object = @queue.pop true
|
18
|
+
rescue ThreadError
|
19
|
+
object = @klass.new(*@args)
|
20
|
+
@objects << object
|
21
|
+
end
|
22
|
+
return object
|
23
|
+
end
|
24
|
+
|
25
|
+
def put(o)
|
26
|
+
raise BadObjectError unless @objects.include? o
|
27
|
+
@queue.push o
|
28
|
+
purge
|
29
|
+
end
|
30
|
+
|
31
|
+
def use
|
32
|
+
object = get
|
33
|
+
yield object
|
34
|
+
ensure
|
35
|
+
put object
|
36
|
+
end
|
37
|
+
|
38
|
+
def purge
|
39
|
+
return if @queue.length < 5
|
40
|
+
begin
|
41
|
+
until @queue.length <= 2 do
|
42
|
+
obj = @queue.pop true
|
43
|
+
@objects.delete obj
|
44
|
+
end
|
45
|
+
rescue ThreadError
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
data/test/setup.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
$TESTING = true
|
4
|
+
|
5
|
+
require 'mogilefs'
|
6
|
+
|
7
|
+
class FakeBackend
|
8
|
+
|
9
|
+
attr_reader :lasterr, :lasterrstr
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@responses = {}
|
13
|
+
@lasterr = nil
|
14
|
+
@lasterrstr = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(sym, *args)
|
18
|
+
sym = sym.to_s
|
19
|
+
if sym =~ /(.*)=$/ then
|
20
|
+
@responses[$1] = args.first
|
21
|
+
else
|
22
|
+
response = @responses[sym]
|
23
|
+
case response
|
24
|
+
when Hash then
|
25
|
+
return response
|
26
|
+
when Array then
|
27
|
+
@lasterr = response.first
|
28
|
+
@lasterrstr = response.last
|
29
|
+
return nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class MogileFS::Client
|
37
|
+
attr_writer :readonly
|
38
|
+
end
|
39
|
+
|
40
|
+
class TestMogileFS < Test::Unit::TestCase
|
41
|
+
|
42
|
+
def setup
|
43
|
+
return if self.class == TestMogileFS
|
44
|
+
@root = '/mogilefs/test'
|
45
|
+
@client = @class.new :hosts => ['kaa:6001'], :domain => 'test',
|
46
|
+
:root => @root
|
47
|
+
@backend = FakeBackend.new
|
48
|
+
@client.instance_variable_set '@backend', @backend
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_nothing
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
data/test/test_admin.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test/setup'
|
2
|
+
|
3
|
+
class TestMogileFS__Admin < TestMogileFS
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@class = MogileFS::Admin
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_clean
|
11
|
+
res = {"host1_remoteroot"=>"/mnt/mogilefs/rur-1",
|
12
|
+
"host1_hostname"=>"rur-1",
|
13
|
+
"host1_hostid"=>"1",
|
14
|
+
"host1_http_get_port"=>"",
|
15
|
+
"host1_altip"=>"",
|
16
|
+
"hosts"=>"1",
|
17
|
+
"host1_hostip"=>"",
|
18
|
+
"host1_http_port"=>"",
|
19
|
+
"host1_status"=>"alive",
|
20
|
+
"host1_altmask"=>""}
|
21
|
+
actual = @client.clean 'hosts', 'host', res
|
22
|
+
|
23
|
+
expected = [{"status"=>"alive",
|
24
|
+
"http_get_port"=>"",
|
25
|
+
"http_port"=>"",
|
26
|
+
"hostid"=>"1",
|
27
|
+
"hostip"=>"",
|
28
|
+
"hostname"=>"rur-1",
|
29
|
+
"remoteroot"=>"/mnt/mogilefs/rur-1",
|
30
|
+
"altip"=>"",
|
31
|
+
"altmask"=>""}]
|
32
|
+
|
33
|
+
assert_equal expected, actual
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
$TESTING = true
|
4
|
+
|
5
|
+
require 'mogilefs/backend'
|
6
|
+
|
7
|
+
class MogileFS::Backend
|
8
|
+
|
9
|
+
attr_accessor :hosts
|
10
|
+
attr_reader :timeout, :dead
|
11
|
+
attr_writer :lasterr, :lasterrstr, :socket
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class FakeSocket
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@closed = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def closed?
|
22
|
+
@closed
|
23
|
+
end
|
24
|
+
|
25
|
+
def close
|
26
|
+
@closed = true
|
27
|
+
return nil
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class TestBackend < Test::Unit::TestCase
|
33
|
+
|
34
|
+
def setup
|
35
|
+
@backend = MogileFS::Backend.new :hosts => ['localhost:6001']
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_initialize
|
39
|
+
assert_raises ArgumentError do MogileFS::Backend.new end
|
40
|
+
assert_raises ArgumentError do MogileFS::Backend.new :hosts => [] end
|
41
|
+
assert_raises ArgumentError do MogileFS::Backend.new :hosts => [''] end
|
42
|
+
|
43
|
+
assert_equal ['localhost:6001'], @backend.hosts
|
44
|
+
assert_equal 3, @backend.timeout
|
45
|
+
assert_equal nil, @backend.lasterr
|
46
|
+
assert_equal nil, @backend.lasterrstr
|
47
|
+
assert_equal({}, @backend.dead)
|
48
|
+
|
49
|
+
@backend = MogileFS::Backend.new :hosts => ['localhost:6001'], :timeout => 1
|
50
|
+
assert_equal 1, @backend.timeout
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_do_request
|
54
|
+
socket_request = ''
|
55
|
+
socket = Object.new
|
56
|
+
def socket.closed?() false end
|
57
|
+
def socket.send(request, flags) return request.length end
|
58
|
+
def @backend.select(*args) return [true] end
|
59
|
+
def socket.gets() return 'OK 1 you=win' end
|
60
|
+
|
61
|
+
@backend.instance_variable_set '@socket', socket
|
62
|
+
|
63
|
+
assert_equal({'you' => 'win'},
|
64
|
+
@backend.do_request('go!', { 'fight' => 'team fight!' }))
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_do_request_send_error
|
68
|
+
socket_request = ''
|
69
|
+
socket = Object.new
|
70
|
+
def socket.closed?() false end
|
71
|
+
def socket.send(request, flags) raise SystemCallError, 'dummy' end
|
72
|
+
|
73
|
+
@backend.instance_variable_set '@socket', socket
|
74
|
+
|
75
|
+
assert_raises RuntimeError do
|
76
|
+
@backend.do_request 'go!', { 'fight' => 'team fight!' }
|
77
|
+
end
|
78
|
+
|
79
|
+
assert_equal nil, @backend.instance_variable_get('@socket')
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_do_request_truncated
|
83
|
+
socket_request = ''
|
84
|
+
socket = Object.new
|
85
|
+
def socket.closed?() false end
|
86
|
+
def socket.send(request, flags) return request.length - 1 end
|
87
|
+
|
88
|
+
@backend.instance_variable_set '@socket', socket
|
89
|
+
|
90
|
+
assert_raises RuntimeError do
|
91
|
+
@backend.do_request 'go!', { 'fight' => 'team fight!' }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_make_request
|
96
|
+
assert_equal "go! fight=team+fight%21\r\n",
|
97
|
+
@backend.make_request('go!', { 'fight' => 'team fight!' })
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_parse_response
|
101
|
+
assert_equal({'foo' => 'bar', 'baz' => 'hoge'},
|
102
|
+
@backend.parse_response('OK 1 foo=bar&baz=hoge'))
|
103
|
+
assert_equal nil, @backend.parse_response('ERR you totally suck')
|
104
|
+
assert_equal 'you', @backend.lasterr
|
105
|
+
assert_equal 'totally suck', @backend.lasterrstr
|
106
|
+
|
107
|
+
assert_raises RuntimeError do
|
108
|
+
@backend.parse_response 'garbage'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_readable_eh_readable
|
113
|
+
socket = Object.new
|
114
|
+
def socket.closed?() false end
|
115
|
+
def @backend.select(*args) return [true] end
|
116
|
+
@backend.instance_variable_set '@socket', socket
|
117
|
+
|
118
|
+
assert_equal true, @backend.readable?
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_readable_eh_not_readable
|
122
|
+
socket = Object.new
|
123
|
+
def socket.closed?() false end
|
124
|
+
def @backend.select(*args) return [] end
|
125
|
+
@backend.instance_variable_set '@socket', socket
|
126
|
+
|
127
|
+
assert_raises MogileFS::UnreadableSocketError do @backend.readable? end
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_socket
|
131
|
+
assert_equal({}, @backend.dead)
|
132
|
+
assert_raises RuntimeError do @backend.socket end
|
133
|
+
assert_equal(['localhost:6001'], @backend.dead.keys)
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_socket_robust
|
137
|
+
@backend.hosts = ['localhost:6001', 'localhost:6002']
|
138
|
+
def @backend.connect_to(host, port)
|
139
|
+
@first = (defined? @first) ? false : true
|
140
|
+
raise Errno::ECONNREFUSED if @first
|
141
|
+
end
|
142
|
+
|
143
|
+
assert_equal({}, @backend.dead)
|
144
|
+
@backend.socket
|
145
|
+
assert_equal false, @backend.dead.keys.empty?
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_shutdown
|
149
|
+
fake_socket = FakeSocket.new
|
150
|
+
@backend.socket = fake_socket
|
151
|
+
assert_equal fake_socket, @backend.socket
|
152
|
+
@backend.shutdown
|
153
|
+
assert_equal nil, @backend.instance_variable_get(:@socket)
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_url_decode
|
157
|
+
assert_equal({"\272z" => "\360opy", "f\000" => "\272r"},
|
158
|
+
@backend.url_decode("%baz=%f0opy&f%00=%bar"))
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_url_encode
|
162
|
+
params = [["f\000", "\272r"], ["\272z", "\360opy"]]
|
163
|
+
assert_equal "f%00=%bar&%baz=%f0opy", @backend.url_encode(params)
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_url_escape # \n for unit_diff
|
167
|
+
actual = (0..255).map { |c| @backend.url_escape c.chr }.join "\n"
|
168
|
+
|
169
|
+
expected = []
|
170
|
+
expected.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
|
171
|
+
expected << '+'
|
172
|
+
expected.push(*(0x21..0x2b).map { |c| "%%%0.2x" % c })
|
173
|
+
expected.push(*%w[, - . /])
|
174
|
+
expected.push(*('0'..'9'))
|
175
|
+
expected.push(*%w[: %3b %3c %3d %3e %3f %40])
|
176
|
+
expected.push(*('A'..'Z'))
|
177
|
+
expected.push(*%w[%5b \\ %5d %5e _ %60])
|
178
|
+
expected.push(*('a'..'z'))
|
179
|
+
expected.push(*(0x7b..0xff).map { |c| "%%%0.2x" % c })
|
180
|
+
|
181
|
+
expected = expected.join "\n"
|
182
|
+
|
183
|
+
assert_equal expected, actual
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_url_unescape
|
187
|
+
input = []
|
188
|
+
input.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
|
189
|
+
input << '+'
|
190
|
+
input.push(*(0x21..0x2b).map { |c| "%%%0.2x" % c })
|
191
|
+
input.push(*%w[, - . /])
|
192
|
+
input.push(*('0'..'9'))
|
193
|
+
input.push(*%w[: %3b %3c %3d %3e %3f %40])
|
194
|
+
input.push(*('A'..'Z'))
|
195
|
+
input.push(*%w[%5b \\ %5d %5e _ %60])
|
196
|
+
input.push(*('a'..'z'))
|
197
|
+
input.push(*(0x7b..0xff).map { |c| "%%%0.2x" % c })
|
198
|
+
|
199
|
+
actual = input.map { |c| @backend.url_unescape c }.join "\n"
|
200
|
+
|
201
|
+
expected = (0..255).map { |c| c.chr }.join "\n"
|
202
|
+
expected.sub! '+', ' '
|
203
|
+
|
204
|
+
assert_equal expected, actual
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
data/test/test_client.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
$TESTING = true
|
4
|
+
|
5
|
+
require 'mogilefs'
|
6
|
+
|
7
|
+
class TestClient < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@client = MogileFS::Client.new :hosts => ['kaa:6001']
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_initialize
|
14
|
+
assert_not_nil @client
|
15
|
+
assert_instance_of MogileFS::Backend, @client.backend
|
16
|
+
assert_equal ['kaa:6001'], @client.hosts
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_err
|
20
|
+
@client.backend.lasterr = 'you'
|
21
|
+
assert_equal 'you', @client.err
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_errstr
|
25
|
+
@client.backend.lasterrstr = 'totally suck'
|
26
|
+
assert_equal 'totally suck', @client.errstr
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_reload
|
30
|
+
orig_backend = @client.backend
|
31
|
+
|
32
|
+
@client.hosts = ['ziz:6001']
|
33
|
+
@client.reload
|
34
|
+
|
35
|
+
assert_not_same @client.backend, orig_backend
|
36
|
+
assert_equal ['ziz:6001'], @client.backend.hosts
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_readonly_eh_readonly
|
40
|
+
client = MogileFS::Client.new :hosts => ['kaa:6001'], :readonly => true
|
41
|
+
assert_equal true, client.readonly?
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_readonly_eh_readwrite
|
45
|
+
assert_equal false, @client.readonly?
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'test/setup'
|
2
|
+
|
3
|
+
class TestMogileFS__MogileFS < TestMogileFS
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@class = MogileFS::MogileFS
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_initialize
|
11
|
+
assert_equal 'test', @client.domain
|
12
|
+
assert_equal '/mogilefs/test', @client.root
|
13
|
+
|
14
|
+
assert_raises ArgumentError do
|
15
|
+
MogileFS::MogileFS.new :hosts => ['kaa:6001'], :root => '/mogilefs/test'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_get_paths
|
20
|
+
path1 = 'rur-1/dev1/0/000/000/0000000062.fid'
|
21
|
+
path2 = 'rur-2/dev2/0/000/000/0000000062.fid'
|
22
|
+
|
23
|
+
@backend.get_paths = { 'paths' => 2, 'path1' => path1, 'path2' => path2 }
|
24
|
+
|
25
|
+
expected = ["#{@root}/#{path1}", "#{@root}/#{path2}"]
|
26
|
+
|
27
|
+
assert_equal expected, @client.get_paths('key').sort
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_get_paths_unknown_key
|
31
|
+
@backend.get_paths = ['unknown_key', '']
|
32
|
+
|
33
|
+
assert_equal nil, @client.get_paths('key')
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_delete_existing
|
37
|
+
@backend.delete = { }
|
38
|
+
assert_nothing_raised do
|
39
|
+
@client.delete 'no_such_key'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_delete_nonexisting
|
44
|
+
@backend.delete = 'unknown_key', ''
|
45
|
+
assert_nothing_raised do
|
46
|
+
assert_equal nil, @client.delete('no_such_key')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_delete_readonly
|
51
|
+
@client.readonly = true
|
52
|
+
assert_raises RuntimeError do
|
53
|
+
@client.delete 'no_such_key'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_list_keys
|
58
|
+
@backend.list_keys = { 'key_count' => 2, 'next_after' => 'new_key_2',
|
59
|
+
'key_1' => 'new_key_1', 'key_2' => 'new_key_2' }
|
60
|
+
|
61
|
+
keys, next_after = @client.list_keys 'new'
|
62
|
+
assert_equal ['new_key_1', 'new_key_2'], keys.sort
|
63
|
+
assert_equal 'new_key_2', next_after
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_new_file_http
|
67
|
+
@client.readonly = true
|
68
|
+
assert_raises RuntimeError do
|
69
|
+
@client.new_file 'new_key', 'test'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_new_file_readonly
|
74
|
+
@client.readonly = true
|
75
|
+
assert_raises RuntimeError do
|
76
|
+
@client.new_file 'new_key', 'test'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_store_content_readonly
|
81
|
+
@client.readonly = true
|
82
|
+
assert_raises RuntimeError do
|
83
|
+
@client.store_content 'new_key', 'test', nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_store_file_readonly
|
88
|
+
@client.readonly = true
|
89
|
+
assert_raises RuntimeError do
|
90
|
+
@client.store_file 'new_key', 'test', nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_rename_existing
|
95
|
+
@backend.rename = {}
|
96
|
+
assert_nothing_raised do
|
97
|
+
assert_equal(nil, @client.rename('from_key', 'to_key'))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_rename_nonexisting
|
102
|
+
@backend.rename = 'unknown_key', ''
|
103
|
+
assert_nothing_raised do
|
104
|
+
assert_equal(nil, @client.rename('from_key', 'to_key'))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_rename_readonly
|
109
|
+
@client.readonly = true
|
110
|
+
assert_raises RuntimeError do
|
111
|
+
@client.rename 'new_key', 'test'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_sleep
|
116
|
+
@backend.sleep = {}
|
117
|
+
assert_nothing_raised do
|
118
|
+
assert_equal({}, @client.sleep(2))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|