fluent-plugin-hoop 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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