fluent-plugin-hoop 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.
@@ -0,0 +1,145 @@
1
+ require 'helper'
2
+
3
+ class HoopOutputRealServerTest < Test::Unit::TestCase
4
+ # setup/teardown and tests of dummy hoop server defined at the end of this class...
5
+
6
+ @@start_dummy_server = (not ENV['HOOP_SERVER'])
7
+
8
+ def create_driver()
9
+ server = ENV['HOOP_SERVER'] || 'localhost:14000'
10
+ path_prefix = ENV['HOOP_PATH_PREFIX'] || '/test/realhoop'
11
+ path = path_prefix + '/%Y%m%d/test-%H.log'
12
+ username = ENV['HOOP_USERNAME'] || 'hoopuser'
13
+ Fluent::Test::TimeSlicedOutputTestDriver.new(Fluent::HoopOutput).configure(<<"EOC")
14
+ hoop_server #{server}
15
+ path #{path}
16
+ username #{username}
17
+ EOC
18
+ end
19
+
20
+ def test_output
21
+ d = create_driver
22
+
23
+ time = Time.now.to_i
24
+ d.emit({"data"=>'test_out_hoop_realserver: test_output: 1'}, time)
25
+ d.emit({"data"=>'test_out_hoop_realserver: test_output: 2'}, time)
26
+ paths = d.run
27
+ assert true
28
+ end
29
+
30
+ VALID_COOKIE_STRING = 'alfredo.auth="u=hoopuser&p=hoopuser&t=simple&e=1322203001386&s=SErpv88rOAVEItSOIoCtIV/DSpE="'
31
+ RES_COOKIE_AUTH_FAILURE = WEBrick::Cookie.parse_set_cookie('alfredo.auth=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/')
32
+ RES_COOKIE_AUTH_SUCCESS = WEBrick::Cookie.parse_set_cookie(VALID_COOKIE_STRING + '; Version=1; Path=/')
33
+ RES_BODY_STATUS_ROOT = '{"path":"http:\/\/localhost:14000\/","isDir":true,"len":0,"owner":"hoopuser","group":"supergroup","permission":"-rwxr-xr-x","accessTime":0,"modificationTime":1320055230010,"blockSize":0,"replication":0}'
34
+ RES_FORMAT_ALREADY_EXISTS = "{\"statusCode\":500,\"reason\":\"Internal Server Error\",\"message\":\"java.io.IOException: failed to create file %s on client 127.0.0.1 either because the filename is invalid or the file exists\",\"exception\":\"org.apache.hadoop.ipc.RemoteException\"}"
35
+ RES_FORMAT_NOT_FOUND = "{\"statusCode\":404,\"reason\":\"Not Found\",\"message\":\"java.io.FileNotFoundException: failed to append to non-existent file %s on client 127.0.0.1\",\"exception\":\"java.io.FileNotFoundException\"}"
36
+ RES_FORMAT_NOT_FOUND_GET = "{\"statusCode\":404,\"reason\":\"Not Found\",\"message\":\"File does not exist: %s\",\"exception\":\"java.io.FileNotFoundException\"}"
37
+
38
+ CONTENT_TYPE_JSON = 'application/json'
39
+
40
+ def setup
41
+ Fluent::Test.setup
42
+
43
+ return unless @@start_dummy_server
44
+
45
+ @dummy_server_thread = Thread.new do
46
+ srv = if ENV['FLUENT_TEST_DEBUG']
47
+ logger = WEBrick::Log.new('/dev/null', WEBrick::BasicLog::DEBUG)
48
+ WEBrick::HTTPServer.new({:BindAddress => '127.0.0.1', :Port => 14000, :Logger => logger, :AccessLog => []})
49
+ else
50
+ WEBrick::HTTPServer.new({:BindAddress => '127.0.0.1', :Port => 14000})
51
+ end
52
+ @fsdata = {}
53
+ begin
54
+ srv.mount_proc('/'){|req,res|
55
+ # status only...
56
+ if req.query['user.name'] or req.cookies.index{|item| item.name == 'alfredo.auth' and item.value}
57
+ res.status = 200
58
+ res.content_type = CONTENT_TYPE_JSON
59
+ res.cookies << RES_COOKIE_AUTH_SUCCESS
60
+ res.body = RES_BODY_STATUS_ROOT
61
+ else
62
+ res.cookies << RES_COOKIE_AUTH_FAILURE
63
+ res.status = 401
64
+ end
65
+ }
66
+ srv.mount_proc('/logs/from/fluentd') {|req, res|
67
+ if req.request_method == 'POST' or req.request_method == 'PUT' or req.request_method == 'DELETE'
68
+ # WEBrick's default handler ignores query parameter of URI without method GET
69
+ req.query.update(Hash[*(req.request_line.split(' ')[1].split('?')[1].split('&').map{|kv|kv.split('=')}.flatten)])
70
+ end
71
+ case
72
+ when (not req.query['user.name'] and req.cookies.index{|item| item.name == 'alfredo.auth' and item.value} < 0)
73
+ res.cookies << RES_COOKIE_AUTH_FAILURE
74
+ res.status = 401
75
+ when (req.query['op'] == 'create' and @fsdata[req.path] and req.query['overwrite'] and req.query['overwrite'] == 'false')
76
+ res.status = 500
77
+ res.content_type = CONTENT_TYPE_JSON
78
+ res.cookies << RES_COOKIE_AUTH_SUCCESS
79
+ res.body = sprintf RES_FORMAT_ALREADY_EXISTS, req.path
80
+ when req.query['op'] == 'create'
81
+ @fsdata[req.path] = req.body
82
+ res.status = 201
83
+ res['Location'] = 'http://localhost:14000' + req.path
84
+ res.content_type = CONTENT_TYPE_JSON
85
+ res.cookies << RES_COOKIE_AUTH_SUCCESS
86
+ when (req.query['op'] == 'append' and @fsdata[req.path])
87
+ @fsdata[req.path] += req.body
88
+ res.status = 200
89
+ res['Location'] = 'http://localhost:14000' + req.path
90
+ res.content_type = CONTENT_TYPE_JSON
91
+ res.cookies << RES_COOKIE_AUTH_SUCCESS
92
+ when req.query['op'] == 'append'
93
+ res.status = 404
94
+ res.content_type = CONTENT_TYPE_JSON
95
+ res.cookies << RES_COOKIE_AUTH_SUCCESS
96
+ res.body = sprintf RES_FORMAT_NOT_FOUND, req.path
97
+ when (req.request_method == 'GET' and @fsdata[req.path]) # maybe GET
98
+ res.status = 200
99
+ res.content_type = 'application/octet-stream'
100
+ res.cookies << RES_COOKIE_AUTH_SUCCESS
101
+ res.body = @fsdata[req.path]
102
+ else
103
+ res.status = 404
104
+ res.content_type = CONTENT_TYPE_JSON
105
+ res.cookies << RES_COOKIE_AUTH_SUCCESS
106
+ res.body = sprintf RES_FORMAT_NOT_FOUND_GET, req.path
107
+ end
108
+ }
109
+ srv.start
110
+ ensure
111
+ srv.shutdown
112
+ end
113
+ end
114
+
115
+ # to wait completion of dummy server.start()
116
+ require 'thread'
117
+ cv = ConditionVariable.new
118
+ watcher = Thread.new {
119
+ connected = false
120
+ while not connected
121
+ begin
122
+ get_content('localhost', 14000, '/', {'Cookie' => VALID_COOKIE_STRING})
123
+ connected = true
124
+ rescue Errno::ECONNREFUSED
125
+ sleep 0.1
126
+ rescue StandardError => e
127
+ p e
128
+ sleep 0.1
129
+ end
130
+ end
131
+ cv.signal
132
+ }
133
+ mutex = Mutex.new
134
+ mutex.synchronize {
135
+ cv.wait(mutex)
136
+ }
137
+ end
138
+
139
+ def teardown
140
+ return unless @@start_dummy_server
141
+
142
+ @dummy_server_thread.kill
143
+ @dummy_server_thread.join
144
+ end
145
+ end
@@ -0,0 +1,185 @@
1
+ require 'helper'
2
+
3
+ class HoopOutputReconnectTest < Test::Unit::TestCase
4
+ # setup/teardown and tests of dummy hoop server defined at the end of this class...
5
+
6
+ CONFIG = %[
7
+ hoop_server localhost:14000
8
+ path /logs/from/fluentd/foo-%Y%m%d
9
+ username hoopuser
10
+ ]
11
+
12
+ def create_driver(conf = CONFIG)
13
+ Fluent::Test::TimeSlicedOutputTestDriver.new(Fluent::HoopOutput).configure(conf)
14
+ end
15
+
16
+ def test_write
17
+ d = create_driver CONFIG
18
+
19
+ assert_equal '404', get_code('localhost', 14000, '/logs/from/fluentd/foo-20111124', {'Cookie' => VALID_COOKIE_STRING})
20
+
21
+ time = Time.parse("2011-10-01 00:14:15 UTC").to_i
22
+ d.emit({"a"=>1}, time)
23
+ d.emit({"a"=>2}, time)
24
+ paths = d.run
25
+ assert_equal ['/logs/from/fluentd/foo-20111001'], paths
26
+ assert_equal %[2011-10-01T00:14:15Z\ttest\t{"a":1}\n2011-10-01T00:14:15Z\ttest\t{"a":2}\n], get_content('localhost', 14000, paths.first, {'Cookie' => VALID_COOKIE_STRING})
27
+
28
+ # d = create_driver CONFIG
29
+ restart_dummy_server
30
+
31
+ time = Time.parse("2011-10-02 00:14:15 UTC").to_i
32
+ d.emit({"a"=>3}, time)
33
+ d.emit({"a"=>4}, time)
34
+ paths = d.run
35
+
36
+ assert_equal ['/logs/from/fluentd/foo-20111001', '/logs/from/fluentd/foo-20111002'], paths.sort
37
+ assert_equal %[2011-10-02T00:14:15Z\ttest\t{"a":3}\n2011-10-02T00:14:15Z\ttest\t{"a":4}\n], get_content('localhost', 14000, paths.sort.last, {'Cookie' => VALID_COOKIE_STRING})
38
+
39
+ end
40
+
41
+ VALID_COOKIE_STRING = 'alfredo.auth="u=hoopuser&p=hoopuser&t=simple&e=1322203001386&s=SErpv88rOAVEItSOIoCtIV/DSpE="'
42
+ RES_COOKIE_AUTH_FAILURE = WEBrick::Cookie.parse_set_cookie('alfredo.auth=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/')
43
+ RES_COOKIE_AUTH_SUCCESS = WEBrick::Cookie.parse_set_cookie(VALID_COOKIE_STRING + '; Version=1; Path=/')
44
+ RES_BODY_STATUS_ROOT = '{"path":"http:\/\/localhost:14000\/","isDir":true,"len":0,"owner":"hoopuser","group":"supergroup","permission":"-rwxr-xr-x","accessTime":0,"modificationTime":1320055230010,"blockSize":0,"replication":0}'
45
+ RES_FORMAT_ALREADY_EXISTS = "{\"statusCode\":500,\"reason\":\"Internal Server Error\",\"message\":\"java.io.IOException: failed to create file %s on client 127.0.0.1 either because the filename is invalid or the file exists\",\"exception\":\"org.apache.hadoop.ipc.RemoteException\"}"
46
+ RES_FORMAT_NOT_FOUND = "{\"statusCode\":404,\"reason\":\"Not Found\",\"message\":\"java.io.FileNotFoundException: failed to append to non-existent file %s on client 127.0.0.1\",\"exception\":\"java.io.FileNotFoundException\"}"
47
+ RES_FORMAT_NOT_FOUND_GET = "{\"statusCode\":404,\"reason\":\"Not Found\",\"message\":\"File does not exist: %s\",\"exception\":\"java.io.FileNotFoundException\"}"
48
+
49
+ CONTENT_TYPE_JSON = 'application/json'
50
+
51
+ def start_server
52
+ @dummy_server_thread = Thread.new do
53
+ srv = if ENV['FLUENT_TEST_DEBUG']
54
+ logger = WEBrick::Log.new('/dev/null', WEBrick::BasicLog::DEBUG)
55
+ WEBrick::HTTPServer.new({:BindAddress => '127.0.0.1', :Port => 14000, :Logger => logger, :AccessLog => []})
56
+ else
57
+ WEBrick::HTTPServer.new({:BindAddress => '127.0.0.1', :Port => 14000})
58
+ end
59
+ @fsdata = {}
60
+ begin
61
+ srv.mount_proc('/'){|req,res|
62
+ # status only...
63
+ if req.query['user.name'] or req.cookies.index{|item| item.name == 'alfredo.auth' and item.value}
64
+ res.status = 200
65
+ res.content_type = CONTENT_TYPE_JSON
66
+ res.cookies << RES_COOKIE_AUTH_SUCCESS
67
+ res.body = RES_BODY_STATUS_ROOT
68
+ else
69
+ res.cookies << RES_COOKIE_AUTH_FAILURE
70
+ res.status = 401
71
+ end
72
+ }
73
+ srv.mount_proc('/logs/from/fluentd') {|req, res|
74
+ if req.request_method == 'POST' or req.request_method == 'PUT' or req.request_method == 'DELETE'
75
+ # WEBrick's default handler ignores query parameter of URI without method GET
76
+ req.query.update(Hash[*(req.request_line.split(' ')[1].split('?')[1].split('&').map{|kv|kv.split('=')}.flatten)])
77
+ end
78
+ case
79
+ when (not req.query['user.name'] and req.cookies.index{|item| item.name == 'alfredo.auth' and item.value} < 0)
80
+ res.cookies << RES_COOKIE_AUTH_FAILURE
81
+ res.status = 401
82
+ when (req.query['op'] == 'create' and @fsdata[req.path] and req.query['overwrite'] and req.query['overwrite'] == 'false')
83
+ res.status = 500
84
+ res.content_type = CONTENT_TYPE_JSON
85
+ res.body = sprintf RES_FORMAT_ALREADY_EXISTS, req.path
86
+ when req.query['op'] == 'create'
87
+ @fsdata[req.path] = req.body
88
+ res.status = 201
89
+ res['Location'] = 'http://localhost:14000' + req.path
90
+ res.content_type = CONTENT_TYPE_JSON
91
+ when (req.query['op'] == 'append' and @fsdata[req.path])
92
+ @fsdata[req.path] += req.body
93
+ res.status = 200
94
+ res['Location'] = 'http://localhost:14000' + req.path
95
+ res.content_type = CONTENT_TYPE_JSON
96
+ when req.query['op'] == 'append'
97
+ res.status = 404
98
+ res.content_type = CONTENT_TYPE_JSON
99
+ res.body = sprintf RES_FORMAT_NOT_FOUND, req.path
100
+ when (req.request_method == 'GET' and @fsdata[req.path]) # maybe GET
101
+ res.status = 200
102
+ res.content_type = 'application/octet-stream'
103
+ res.body = @fsdata[req.path]
104
+ else
105
+ res.status = 404
106
+ res.content_type = CONTENT_TYPE_JSON
107
+ res.body = sprintf RES_FORMAT_NOT_FOUND_GET, req.path
108
+ end
109
+ }
110
+ srv.start
111
+ ensure
112
+ srv.shutdown
113
+ end
114
+ end
115
+
116
+ # to wait completion of dummy server.start()
117
+ require 'thread'
118
+ cv = ConditionVariable.new
119
+ watcher = Thread.new {
120
+ connected = false
121
+ while not connected
122
+ begin
123
+ get_content('localhost', 14000, '/', {'Cookie' => VALID_COOKIE_STRING})
124
+ connected = true
125
+ rescue Errno::ECONNREFUSED
126
+ sleep 0.1
127
+ rescue StandardError => e
128
+ p e
129
+ sleep 0.1
130
+ end
131
+ end
132
+ cv.signal
133
+ }
134
+ mutex = Mutex.new
135
+ mutex.synchronize {
136
+ cv.wait(mutex)
137
+ }
138
+ end
139
+
140
+ def setup
141
+ Fluent::Test.setup
142
+ start_server
143
+ end
144
+
145
+ def restart_dummy_server
146
+ @dummy_server_thread.kill
147
+ @dummy_server_thread.join
148
+ start_server
149
+ end
150
+
151
+ def test_dummy_server
152
+ d = create_driver
153
+ authheader = {'Cookie' => VALID_COOKIE_STRING}
154
+ client = Net::HTTP.start(d.instance.hoop_server.split(':')[0], d.instance.hoop_server.split(':')[1])
155
+ assert_equal '401', client.request_get('/').code
156
+ assert_equal '200', client.request_get('/?user.name=hoopuser').code
157
+ assert_equal '200', client.request_get('/', authheader).code
158
+
159
+ # /logs/from/fluentd
160
+ path1 = '/logs/from/fluentd/hoge001/moge-access-log'
161
+ path1_line1 = "1111111111111111111111111111111\n"
162
+ path1_line2 = "2222222222222222222222222222222222222222222222222\n"
163
+ assert_equal '404', client.request_put(path1 + '?op=append', path1_line1, authheader).code
164
+ assert_equal '201', client.request_post(path1 + '?op=create&overwrite=false', path1_line1, authheader).code
165
+ assert_equal path1_line1, client.request_get(path1, authheader).body
166
+ assert_equal '200', client.request_put(path1 + '?op=append', path1_line2, authheader).code
167
+ assert_equal path1_line1 + path1_line2, client.request_get(path1, authheader).body
168
+
169
+ path2 = '/logs/from/fluentd/hoge002/moge-access-log'
170
+ path2_line1 = "XXXXX___1111111111111111111111111111111\n"
171
+ path2_line2 = "YYYYY___2222222222222222222222222222222222222222222222222\n"
172
+ assert_equal '404', client.request_put(path2 + '?op=append', path2_line1, authheader).code
173
+ assert_equal '201', client.request_post(path2 + '?op=create&overwrite=false', path2_line1, authheader).code
174
+ assert_equal '500', client.request_post(path2 + '?op=create&overwrite=false', path2_line1, authheader).code
175
+ assert_equal path2_line1, client.request_get(path2, authheader).body
176
+ assert_equal '200', client.request_put(path2 + '?op=append', path2_line2, authheader).code
177
+ assert_equal path2_line1 + path2_line2, client.request_get(path2, authheader).body
178
+ assert_equal path2_line1 + path2_line2, get_content('localhost', 14000, path2, authheader)
179
+ end
180
+
181
+ def teardown
182
+ @dummy_server_thread.kill
183
+ @dummy_server_thread.join
184
+ end
185
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-hoop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - TAGOMORI Satoshi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-26 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rdoc
16
+ requirement: &2157501280 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2157501280
25
+ - !ruby/object:Gem::Dependency
26
+ name: shoulda
27
+ requirement: &2157500800 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2157500800
36
+ - !ruby/object:Gem::Dependency
37
+ name: bundler
38
+ requirement: &2157500320 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2157500320
47
+ - !ruby/object:Gem::Dependency
48
+ name: jeweler
49
+ requirement: &2157499840 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.4
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2157499840
58
+ - !ruby/object:Gem::Dependency
59
+ name: rcov
60
+ requirement: &2157499360 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2157499360
69
+ - !ruby/object:Gem::Dependency
70
+ name: fluentd
71
+ requirement: &2157498880 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 0.10.8
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *2157498880
80
+ - !ruby/object:Gem::Dependency
81
+ name: rake
82
+ requirement: &2157498400 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: 0.9.2
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *2157498400
91
+ - !ruby/object:Gem::Dependency
92
+ name: simplecov
93
+ requirement: &2157497920 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: 0.5.4
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *2157497920
102
+ description: Hoop (HDFS http-fs) plugin for Fluent event collector
103
+ email: tagomoris@gmail.com
104
+ executables: []
105
+ extensions: []
106
+ extra_rdoc_files:
107
+ - LICENSE.txt
108
+ - README.rdoc
109
+ files:
110
+ - .document
111
+ - .gitignore
112
+ - .gitmodules
113
+ - AUTHORS
114
+ - Gemfile
115
+ - LICENSE.txt
116
+ - README.rdoc
117
+ - Rakefile
118
+ - VERSION
119
+ - fluent-plugin-hoop.gemspec
120
+ - lib/fluent/plugin/out_hoop.rb
121
+ - test/helper.rb
122
+ - test/plugin/test_out_hoop.rb
123
+ - test/plugin/test_out_hoop_realserver.rb
124
+ - test/plugin/test_out_hoop_reconnect.rb
125
+ homepage: http://github.com/tagomoris/fluent-plugin-hoop
126
+ licenses: []
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ segments:
138
+ - 0
139
+ hash: -3094368316846613574
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubyforge_project:
148
+ rubygems_version: 1.8.6
149
+ signing_key:
150
+ specification_version: 3
151
+ summary: Hoop (HDFS http-fs) plugin for Fluent event collector
152
+ test_files:
153
+ - test/helper.rb
154
+ - test/plugin/test_out_hoop.rb
155
+ - test/plugin/test_out_hoop_realserver.rb
156
+ - test/plugin/test_out_hoop_reconnect.rb