fluent-plugin-norikra 0.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.
@@ -0,0 +1,388 @@
1
+ module Fluent
2
+ class NorikraOutput < Fluent::BufferedOutput
3
+ Fluent::Plugin.register_output('norikra', self)
4
+
5
+ config_set_default :flush_interval, 1 # 1sec
6
+
7
+ config_param :norikra, :string, :default => 'localhost:26571'
8
+
9
+ config_param :connect_timeout, :integer, :default => nil
10
+ config_param :send_timeout, :integer, :default => nil
11
+ config_param :receive_timeout, :integer, :default => nil
12
+
13
+ #<server>
14
+ attr_reader :execute_server, :execute_server_path
15
+
16
+ config_param :remove_tag_prefix, :string, :default => nil
17
+
18
+ config_param :target_map_tag, :bool, :default => false
19
+ config_param :target_map_key, :string, :default => nil
20
+ config_param :target_string, :string, :default => nil
21
+
22
+ # <default>
23
+ # <target TARGET>
24
+
25
+ # <events>
26
+ attr_reader :event_method, :event_tag_generator, :event_sweep_interval
27
+
28
+ def initialize
29
+ super
30
+ require_relative 'norikra_target'
31
+ require 'norikra/client'
32
+ end
33
+
34
+ def configure(conf)
35
+ super
36
+
37
+ @host,@port = @norikra.split(':', 2)
38
+ @port = @port.to_i
39
+
40
+ if !@target_map_tag && @target_map_key.nil? && @target_string.nil?
41
+ raise Fluent::ConfigError, 'target naming not specified (target_map_tag/target_map_key/target_string)'
42
+ end
43
+ @target_generator = case
44
+ when @target_string
45
+ lambda {|tag,record| @target_string}
46
+ when @target_map_key
47
+ lambda {|tag,record| record[@target_map_key]}
48
+ when @target_map_tag
49
+ lambda {|tag,record| tag.gsub(/^#{@remove_tag_prefix}(\.)?/, '')}
50
+ else
51
+ raise Fluent::ConfigError, "no one way specified to decide target"
52
+ end
53
+
54
+ # target map already prepared (opened, and related queries registered)
55
+ @target_map = {} # 'target' => instance of Fluent::NorikraOutput::Target
56
+
57
+ # for conversion from query_name to tag
58
+ @query_map = {} # 'query_name' => instance of Fluent::NorikraOutput::Query
59
+
60
+ @default_target = ConfigSection.new(Fluent::Config::Element.new('default', nil, {}, []))
61
+ @config_targets = {}
62
+
63
+ @execute_server = false
64
+
65
+ event_section = nil
66
+ conf.elements.each do |element|
67
+ case element.name
68
+ when 'default'
69
+ @default_target = ConfigSection.new(element)
70
+ when 'target'
71
+ c = ConfigSection.new(element)
72
+ @config_targets[c.target] = c
73
+ when 'server'
74
+ @execute_server = Fluent::Config.bool_value(element['execute'])
75
+ @execute_server_path = element['path']
76
+ when 'event'
77
+ event_section = element
78
+ else
79
+ raise Fluent::ConfigError, "unknown configuration section name for this plugin: #{element.name}"
80
+ end
81
+ end
82
+
83
+ @event_method = @event_tag_generator = @event_sweep_interval = nil
84
+ if event_section
85
+ @event_method = case event_section['method']
86
+ when 'sweep' then :sweep
87
+ when 'listen'
88
+ raise Fluent::ConfigError, "not implemeneted now"
89
+ else
90
+ raise Fluent::ConfigError, "unknown method #{event_section['method']}"
91
+ end
92
+ unless event_section['tag']
93
+ raise Fluent::ConfigError, "<event> section needs 'tag' configuration"
94
+ end
95
+ tag_prefix = if event_section.has_key?('tag_prefix')
96
+ event_section['tag_prefix'] + (event_section['tag_prefix'] =~ /\.$/ ? '' : '.')
97
+ else
98
+ ''
99
+ end
100
+ tag_by, tag_arg = event_section['tag'].split(/ +/, 2)
101
+ @event_tag_generator = case tag_by
102
+ when 'query_name' then lambda{|query_name,record| tag_prefix + query_name}
103
+ when 'field' then lambda{|query_name,record| tag_prefix + record[tag_arg]}
104
+ when 'string' then lambda{|query_name,record| tag_prefix + tag_arg}
105
+ else
106
+ raise Fluent::ConfigError, "unknown tag configuration specified:#{event_section['tag']}"
107
+ end
108
+ @event_sweep_interval = Fluent::Config.time_value(event_section['sweep_interval'] || '10s')
109
+ end
110
+
111
+ @mutex = Mutex.new
112
+ end
113
+
114
+ def client(opts={})
115
+ Norikra::Client.new(@host, @port, {
116
+ :connect_timeout => opts[:connect_timeout] || @connect_timeout,
117
+ :send_timeout => opts[:send_timeout] || @send_timeout,
118
+ :receive_timeout => opts[:receive_timeout] || @receive_timeout,
119
+ })
120
+ end
121
+
122
+ def start
123
+ super
124
+
125
+ @norikra_started = false
126
+
127
+ if @execute_server
128
+ @norikra_pid = nil
129
+ @norikra_thread = Thread.new(&method(:server_starter))
130
+ # @norikra_started will be set in server_starter
131
+ else
132
+ @norikra_pid = nil
133
+ @norikra_thread = nil
134
+ @norikra_started = true
135
+ end
136
+
137
+ # register worker thread
138
+ @register_queue = []
139
+ @register_thread = Thread.new(&method(:register_worker))
140
+
141
+ # fetch worker thread
142
+ @fetch_queue = []
143
+ @fetch_thread = Thread.new(&method(:fetch_worker))
144
+
145
+ # for sweep
146
+ if @event_method
147
+ @fetch_queue.push(FetchRequest.new(nil, @event_sweep_interval))
148
+ end
149
+ end
150
+
151
+ def shutdown
152
+ @register_thread.kill
153
+ @fetch_thread.kill
154
+ Process.kill(:TERM, @norikra_pid) if @execute_server
155
+
156
+ @register_thread.join
157
+ @fetch_thread.join
158
+ begin
159
+ counter = 0
160
+ while !Process.waitpid(@norikra_pid, Process::WNOHANG)
161
+ sleep 1
162
+ break if counter > 3
163
+ end
164
+ rescue Errno::ECHILD
165
+ # norikra server process exited.
166
+ end
167
+ end
168
+
169
+ def server_starter
170
+ $log.info "starting Norikra server process #{@host}:#{@port}"
171
+ @norikra_pid = fork do
172
+ ENV.keys.select{|k| k =~ /^(RUBY|GEM|BUNDLE|RBENV|RVM|rvm)/}.each {|k| ENV.delete(k)}
173
+ exec [@execute_server_path, 'norikra(fluentd)'], 'start', '-H', @host, '-P', @port.to_s
174
+ end
175
+ connecting = true
176
+ $log.info "trying to confirm norikra server status..."
177
+ while connecting
178
+ begin
179
+ $log.debug "start to connect norikra server #{@host}:#{@port}"
180
+ client(:connect_timeout => 1, :send_timeout => 1, :receive_timeout => 1).targets
181
+ # discard result: no exceptions is success
182
+ connecting = false
183
+ next
184
+ rescue HTTPClient::TimeoutError
185
+ $log.debug "Norikra server test connection timeout. retrying..."
186
+ rescue Errno::ECONNREFUSED
187
+ $log.debug "Norikra server test connection refused. retrying..."
188
+ rescue => e
189
+ $log.error "unknown error in confirming norikra server, #{e.class}:#{e.message}"
190
+ end
191
+ sleep 3
192
+ end
193
+ $log.info "confirmed that norikra server #{@host}:#{@port} started."
194
+ @norikra_started = true
195
+ end
196
+
197
+ def register_worker
198
+ while sleep(0.25)
199
+ next unless @norikra_started
200
+
201
+ c = client()
202
+
203
+ targets = @register_queue.shift(10)
204
+ targets.each do |t|
205
+ next if @target_map[t.name]
206
+
207
+ $log.debug "Preparing norikra target #{t.name} on #{@host}:#{@port}"
208
+ if prepare_target(c, t)
209
+ $log.debug "success to prepare target #{t.name} on #{@host}:#{@port}"
210
+ # success
211
+ t.queries.each do |query|
212
+ @query_map[query.name] = query
213
+ insert_fetch_queue(FetchRequest.new(query)) unless query.tag.empty? || @event_method
214
+ end
215
+ @target_map[t.name] = t
216
+ else
217
+ $log.error "Failed to prepare norikra data for target:#{t.name}"
218
+ @norikra_started.push(t)
219
+ end
220
+ end
221
+ end
222
+ end
223
+
224
+ def fetch_worker
225
+ while sleep(1)
226
+ next unless @norikra_started
227
+ next if @fetch_queue.first.nil? || @fetch_queue.first.time > Time.now
228
+
229
+ now = Time.now
230
+ while @fetch_queue.first.time <= now
231
+ req = @fetch_queue.shift
232
+ if req.query.nil?
233
+ sweep()
234
+ else
235
+ fetch(req.query)
236
+ end
237
+ insert_fetch_queue(req)
238
+ end
239
+ end
240
+ end
241
+
242
+ def format_stream(tag, es)
243
+ tobe_registered_target_names = []
244
+
245
+ out = ''
246
+
247
+ es.each do |time,record|
248
+ target = @target_generator.call(tag, record)
249
+
250
+ t = @target_map[target]
251
+ unless t || tobe_registered_target_names.include?(target)
252
+ t = Target.new(target, @default_target + @config_targets[target])
253
+ @register_queue.push(t)
254
+ tobe_registered_target_names.push(target)
255
+ end
256
+
257
+ event = t.filter(record)
258
+
259
+ out << [target,event].to_msgpack
260
+ end
261
+
262
+ out
263
+ end
264
+
265
+ def prepared?(target_names)
266
+ @norikra_started && target_names.reduce(true){|r,t| r && @target_map[t]}
267
+ end
268
+
269
+ def write(chunk)
270
+ events_map = {} # target => [event]
271
+ chunk.msgpack_each do |target, event|
272
+ events_map[target] ||= []
273
+ events_map[target].push(event)
274
+ end
275
+
276
+ unless prepared?(events_map.keys)
277
+ raise RuntimeError, "norikra server is not ready for this targets: #{events_map.keys.join(',')}"
278
+ end
279
+
280
+ c = client()
281
+
282
+ events_map.each do |target, events|
283
+ c.send(target, events)
284
+ end
285
+ end
286
+
287
+ def prepare_target(client, target)
288
+ # target open and reserve fields
289
+ $log.debug "Going to prepare about target"
290
+ begin
291
+ unless client.targets.include?(target.name)
292
+ $log.debug "opening target #{target.name}"
293
+ client.open(target.name, target.reserve_fields)
294
+ $log.debug "opening target #{target.name}, done."
295
+ end
296
+
297
+ reserving = target.reserve_fields
298
+ reserved = []
299
+ client.fields(target.name).each do |field|
300
+ if reserving[field['name']]
301
+ reserved.push(field['name'])
302
+ if reserving[field['name']] != field['type']
303
+ $log.warn "field type mismatch, reserving:#{reserving[field['name']]} but reserved:#{field['type']}"
304
+ end
305
+ end
306
+ end
307
+
308
+ reserving.each do |fieldname,type|
309
+ client.reserve(target, fieldname, type) unless reserved.include?(fieldname)
310
+ end
311
+ rescue => e
312
+ $log.error "failed to prepare target:#{target.name}", :norikra => "#{@host}:#{@port}", :error => e.class, :message => e.message
313
+ return false
314
+ end
315
+
316
+ # query registration
317
+ begin
318
+ registered = Hash[client.queries.map{|q| [q['name'], q['expression']]}]
319
+ target.queries.each do |query|
320
+ if registered.has_key?(query.name) # query already registered
321
+ if registered[query.name] != query.expression
322
+ $log.warn "query name and expression mismatch, check norikra server status. target query name:#{query.name}"
323
+ end
324
+ next
325
+ end
326
+ client.register(query.name, query.expression)
327
+ end
328
+ rescue => e
329
+ $log.warn "failed to register query", :norikra => "#{@host}:#{@port}", :error => e.class, :message => e.message
330
+ end
331
+ end
332
+
333
+ class FetchRequest
334
+ attr_accessor :time, :query
335
+ def initialize(query, interval=nil)
336
+ @query = query
337
+ @interval = interval || query.interval
338
+ @time = Time.now + @interval
339
+ end
340
+ def <=>(other)
341
+ self.time <=> other.time
342
+ end
343
+ def next!
344
+ @time = Time.now + @interval
345
+ end
346
+ end
347
+
348
+ def insert_fetch_queue(request)
349
+ @mutex.synchronize do
350
+ request.next!
351
+ # if @fetch_queue.size > 0
352
+ # next_pos = @fetch_queue.bsearch{|req| req.time > request.time}
353
+ # @fetch_queue.insert(next_pos, request)
354
+ # else
355
+ # @fetch_queue.push(request)
356
+ # end
357
+ @fetch_queue.push(request)
358
+ @fetch_queue.sort!
359
+ end
360
+ rescue => e
361
+ $log.error "unknown log encountered", :error_class => e.class, :message => e.message
362
+ end
363
+
364
+ def sweep
365
+ begin
366
+ client().sweep.each do |query_name, event_array|
367
+ query = @query_map[query_name]
368
+ event_array.each do |time,event|
369
+ tag = (query && !query.tag.empty?) ? query.tag : @event_tag_generator.call(query_name, event)
370
+ Fluent::Engine.emit(tag, time, event)
371
+ end
372
+ end
373
+ rescue => e
374
+ $log.error "failed to sweep", :norikra => "#{@host}:#{@port}", :error => e.class, :message => e.message
375
+ end
376
+ end
377
+
378
+ def fetch(query)
379
+ begin
380
+ client().event(query.name).each do |time,event| # [[time(int from epoch), event], ...]
381
+ Fluent::Engine.emit(query.tag, time, event)
382
+ end
383
+ rescue => e
384
+ $log.error "failed to fetch for query:#{query.name}", :norikra => "#{@host}:#{@port}", :error => e.class, :message => e.message
385
+ end
386
+ end
387
+ end
388
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+
15
+ require 'fluent/test'
16
+ unless ENV.has_key?('VERBOSE')
17
+ nulllogger = Object.new
18
+ nulllogger.instance_eval {|obj|
19
+ def method_missing(method, *args)
20
+ # pass
21
+ end
22
+ }
23
+ $log = nulllogger
24
+ end
25
+
26
+ require 'fluent/plugin/out_norikra'
27
+
28
+ class Test::Unit::TestCase
29
+ end
@@ -0,0 +1,179 @@
1
+ require 'helper'
2
+ require 'fluent/plugin/norikra_target'
3
+
4
+ class ConfigSectionTest < Test::Unit::TestCase
5
+ def setup
6
+ @this = Fluent::NorikraOutput::ConfigSection
7
+ end
8
+
9
+ def test_init_default
10
+ q1 = Fluent::Config::Element.new('query', nil, {
11
+ 'name' => 'q1_${target}',
12
+ 'expression' => 'SELECT * FROM ${target}.win:time_batch(10 min) WHERE q1',
13
+ 'tag' => 'q1.${target}'
14
+ }, [])
15
+ q2 = Fluent::Config::Element.new('query', nil, {
16
+ 'name' => 'q2_${target}',
17
+ 'expression' => 'SELECT * FROM ${target}.win:time_batch(50 min) WHERE q2.length() > 0',
18
+ 'tag' => 'q2.${target}'
19
+ }, [])
20
+ c1 = Fluent::Config::Element.new('default', nil, {
21
+ 'include' => '*',
22
+ 'exclude' => 'flag',
23
+ 'exclude_regexp' => 'f_.*',
24
+ 'field_string' => 's1,s2,s3',
25
+ 'field_boolean' => 'bool1,bool2',
26
+ 'field_int' => 'i1,i2,i3,i4',
27
+ 'field_long' => 'num1,num2',
28
+ 'field_float' => 'f1,f2',
29
+ 'field_double' => 'd'
30
+ }, [q1,q2])
31
+ s1 = @this.new(c1)
32
+
33
+ assert_nil s1.target
34
+ assert_equal({:include => '*', :include_regexp => nil, :exclude => 'flag', :exclude_regexp => 'f_.*'}, s1.filter_params)
35
+ assert_equal({
36
+ :string => %w(s1 s2 s3), :boolean => %w(bool1 bool2), :int => %w(i1 i2 i3 i4), :long => %w(num1 num2),
37
+ :float => %w(f1 f2), :double => %w(d)
38
+ }, s1.field_definitions)
39
+ assert_equal 2, s1.query_generators.size
40
+ assert_equal (10 * 60 / 5), s1.query_generators.map(&:fetch_interval).sort.first
41
+ end
42
+
43
+ def test_init_target
44
+ q3 = Fluent::Config::Element.new('query', nil, {
45
+ 'name' => 'q3_test2',
46
+ 'expression' => 'SELECT * FROM ${target}.win:time_batch(30 min) WHERE q3="/"',
47
+ 'tag' => 'q3.test2'
48
+ }, [])
49
+ c2 = Fluent::Config::Element.new('target', 'test2', {
50
+ 'exclude_regexp' => '(f|g)_.*',
51
+ 'field_double' => 'd1,d2,d3,d4'
52
+ }, [q3])
53
+ s2 = @this.new(c2)
54
+
55
+ assert_equal 'test2', s2.target
56
+ assert_equal({:include => nil, :include_regexp => nil, :exclude => nil, :exclude_regexp => '(f|g)_.*'}, s2.filter_params)
57
+ assert_equal({:string => [], :boolean => [], :int => [], :long => [], :float => [], :double => %w(d1 d2 d3 d4)}, s2.field_definitions)
58
+ assert_equal 1, s2.query_generators.size
59
+ assert_equal (30 * 60 / 5), s2.query_generators.map(&:fetch_interval).sort.first
60
+ end
61
+
62
+ def test_init_target_query_only
63
+ q4 = Fluent::Config::Element.new('query', nil, {
64
+ 'name' => 'q4_test3',
65
+ 'expression' => 'SELECT * FROM ${target}.win:time_batch(30 min) WHERE q4 > 10',
66
+ 'tag' => 'q4.test3',
67
+ 'fetch_interval' => '1s'
68
+ }, [])
69
+ c3 = Fluent::Config::Element.new('target', 'test3', {}, [q4])
70
+ s3 = @this.new(c3)
71
+
72
+ assert_equal 'test3', s3.target
73
+ assert_equal({:include => nil, :include_regexp => nil, :exclude => nil, :exclude_regexp => nil}, s3.filter_params)
74
+ assert_equal({:string => [], :boolean => [], :int => [], :long => [], :float => [], :double => []}, s3.field_definitions)
75
+ assert_equal 1, s3.query_generators.size
76
+ end
77
+
78
+ def test_init_target_without_query
79
+ c4 = Fluent::Config::Element.new('target', 'test4', {
80
+ 'field_int' => 'status'
81
+ }, [])
82
+ s4 = @this.new(c4)
83
+
84
+ assert_equal 'test4', s4.target
85
+ assert_equal({:include => nil, :include_regexp => nil, :exclude => nil, :exclude_regexp => nil}, s4.filter_params)
86
+ assert_equal({:string => [], :boolean => [], :int => ['status'], :long => [], :float => [], :double => []}, s4.field_definitions)
87
+ assert_equal 0, s4.query_generators.size
88
+ end
89
+
90
+ def test_init_target_blank
91
+ c5 = Fluent::Config::Element.new('target', 'test5', {}, [])
92
+ s5 = @this.new(c5)
93
+
94
+ assert_equal 'test5', s5.target
95
+ assert_equal({:include => nil, :include_regexp => nil, :exclude => nil, :exclude_regexp => nil}, s5.filter_params)
96
+ assert_equal({:string => [], :boolean => [], :int => [], :long => [], :float => [], :double => []}, s5.field_definitions)
97
+ assert_equal 0, s5.query_generators.size
98
+ end
99
+
100
+ def test_join
101
+ q1 = Fluent::Config::Element.new('query', nil, {
102
+ 'name' => 'q1_${target}',
103
+ 'expression' => 'SELECT * FROM ${target}.win:time_batch(10 min) WHERE q1',
104
+ 'tag' => 'q1.${target}'
105
+ }, [])
106
+ q2 = Fluent::Config::Element.new('query', nil, {
107
+ 'name' => 'q2_${target}',
108
+ 'expression' => 'SELECT * FROM ${target}.win:time_batch(50 min) WHERE q2.length() > 0',
109
+ 'tag' => 'q2.${target}'
110
+ }, [])
111
+ c1 = Fluent::Config::Element.new('default', nil, {
112
+ 'include' => '*',
113
+ 'exclude' => 'flag',
114
+ 'exclude_regexp' => 'f_.*',
115
+ 'field_string' => 's1,s2,s3',
116
+ 'field_boolean' => 'bool1,bool2',
117
+ 'field_int' => 'i1,i2,i3,i4',
118
+ 'field_long' => 'num1,num2',
119
+ 'field_float' => 'f1,f2',
120
+ 'field_double' => 'd'
121
+ }, [q1,q2])
122
+ s1 = @this.new(c1)
123
+
124
+ q3 = Fluent::Config::Element.new('query', nil, {
125
+ 'name' => 'q3_test',
126
+ 'expression' => 'SELECT * FROM ${target}.win:time_batch(30 min) WHERE q3="/"',
127
+ 'tag' => 'q3.test'
128
+ }, [])
129
+ c2 = Fluent::Config::Element.new('target', 'test', {
130
+ 'exclude_regexp' => '(f|g)_.*',
131
+ 'field_double' => 'd1,d2,d3,d4'
132
+ }, [q3])
133
+ s2 = @this.new(c2)
134
+
135
+ s = s1 + s2
136
+
137
+ assert_equal 'test', s.target
138
+ assert_equal({:include => '*', :include_regexp => nil, :exclude => 'flag', :exclude_regexp => '(f|g)_.*'}, s.filter_params)
139
+ assert_equal({
140
+ :string => %w(s1 s2 s3), :boolean => %w(bool1 bool2), :int => %w(i1 i2 i3 i4), :long => %w(num1 num2),
141
+ :float => %w(f1 f2), :double => %w(d d1 d2 d3 d4)
142
+ }, s.field_definitions)
143
+ assert_equal 3, s.query_generators.size
144
+ assert_equal (10 * 60 / 5), s.query_generators.map(&:fetch_interval).sort.first
145
+ end
146
+
147
+ def test_join_with_nil
148
+ q1 = Fluent::Config::Element.new('query', nil, {
149
+ 'name' => 'q1_${target}',
150
+ 'expression' => 'SELECT * FROM ${target}.win:time_batch(10 min) WHERE q1',
151
+ 'tag' => 'q1.${target}'
152
+ }, [])
153
+ q2 = Fluent::Config::Element.new('query', nil, {
154
+ 'name' => 'q2_${target}',
155
+ 'expression' => 'SELECT * FROM ${target}.win:time_batch(50 min) WHERE q2.length() > 0',
156
+ 'tag' => 'q2.${target}'
157
+ }, [])
158
+ c1 = Fluent::Config::Element.new('default', nil, {
159
+ 'include' => '*',
160
+ 'exclude' => 'flag',
161
+ 'exclude_regexp' => 'f_.*',
162
+ 'field_string' => 's1,s2,s3',
163
+ 'field_boolean' => 'bool1,bool2',
164
+ 'field_int' => 'i1,i2,i3,i4',
165
+ 'field_long' => 'num1,num2',
166
+ 'field_float' => 'f1,f2',
167
+ 'field_double' => 'd'
168
+ }, [q1,q2])
169
+ s1 = @this.new(c1)
170
+
171
+ s = s1 + nil
172
+
173
+ assert_equal 'dummy', s.target
174
+ assert_equal s1.filter_params, s.filter_params
175
+ assert_equal s1.field_definitions, s.field_definitions
176
+ assert_equal s1.query_generators.size, s.query_generators.size
177
+ assert_equal s1.query_generators.map(&:fetch_interval).sort.first, s.query_generators.map(&:fetch_interval).sort.first
178
+ end
179
+ end
@@ -0,0 +1,12 @@
1
+ require 'helper'
2
+ require 'fluent/plugin/norikra_target'
3
+
4
+ class QueryTest < Test::Unit::TestCase
5
+ def test_init
6
+ q = Fluent::NorikraOutput::Query.new('name', 'expression', 'tag', 10)
7
+ assert_equal 'name', q.name
8
+ assert_equal 'expression', q.expression
9
+ assert_equal 'tag', q.tag
10
+ assert_equal 10, q.interval
11
+ end
12
+ end
@@ -0,0 +1,49 @@
1
+ require 'helper'
2
+ require 'fluent/plugin/norikra_target'
3
+
4
+ class QueryGeneratorTest < Test::Unit::TestCase
5
+ def setup
6
+ @this = Fluent::NorikraOutput::QueryGenerator
7
+ end
8
+
9
+ def test_replace_target
10
+ expected = 'SELECT * FROM replaced.win:time_batch(10 hours) WHERE x=1'
11
+ assert_equal expected, @this.replace_target('replaced', 'SELECT * FROM ${target}.win:time_batch(10 hours) WHERE x=1')
12
+ end
13
+
14
+ def test_parse_time_period
15
+ assert_equal [0,0,0,0,0,0,0,0], @this.parse_time_period('')
16
+
17
+ assert_equal [10,0,0,0,0,0,0,0], @this.parse_time_period(' 10 year')
18
+ assert_equal [0,11,0,0,0,0,0,0], @this.parse_time_period('11 month')
19
+ assert_equal [0,233,0,0,0,0,0,0], @this.parse_time_period('233 months')
20
+ assert_equal [0,0,10,0,0,0,0,0], @this.parse_time_period('10 weeks')
21
+ assert_equal [0,0,0,1,0,0,0,0], @this.parse_time_period('1 day')
22
+ assert_equal [0,0,0,201,0,0,0,0], @this.parse_time_period('201 days')
23
+ assert_equal [0,0,0,0,1,0,0,0], @this.parse_time_period('1 hour')
24
+ assert_equal [0,0,0,0,11,0,0,0], @this.parse_time_period('11 hours')
25
+ assert_equal [0,0,0,0,0,2,0,0], @this.parse_time_period('2 minutes')
26
+ assert_equal [0,0,0,0,0,1,0,0], @this.parse_time_period('1 min')
27
+ assert_equal [0,0,0,0,0,133,0,0], @this.parse_time_period('133 minute')
28
+ assert_equal [0,0,0,0,0,0,12,0], @this.parse_time_period('12 sec')
29
+ assert_equal [0,0,0,0,0,0,1,0], @this.parse_time_period('1 second')
30
+ assert_equal [0,0,0,0,0,0,256,0], @this.parse_time_period(' 256 seconds')
31
+ assert_equal [0,0,0,0,0,0,0,1], @this.parse_time_period('1 msec')
32
+ assert_equal [0,0,0,0,0,0,0,111], @this.parse_time_period('111 milliseconds')
33
+
34
+ assert_equal [1,12,4,365,23,59,60,0], @this.parse_time_period('1 year 12 months 4 weeks 365 days 23 hours 59 min 60 seconds')
35
+ end
36
+
37
+ def test_generate
38
+ g = @this.new('query_${target}', 'SELECT * FROM ${target}.win:time_batch( 10 min ) WHERE x=1', 'tag.${target}')
39
+ q = g.generate('test')
40
+ assert_equal 'query_test', q.name
41
+ assert_equal 'SELECT * FROM test.win:time_batch( 10 min ) WHERE x=1', q.expression
42
+ assert_equal 'tag.test', q.tag
43
+ end
44
+
45
+ def test_fetch_interval
46
+ g = @this.new('query_${target}', 'SELECT * FROM ${target}.win:time_batch( 12 min ) WHERE x=1', 'tag.${target}')
47
+ assert_equal (12*60/5), g.fetch_interval
48
+ end
49
+ end