fluent-plugin-td 0.10.17 → 0.10.18

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 04dd7b7e2f098cf317a600be1125138cc6d9a440
4
+ data.tar.gz: 3b211b46e282e9e94189dcc4a388223b45bbc72d
5
+ SHA512:
6
+ metadata.gz: 3c65e15dcaf7ab967da39f5479088d30a22c37de275e2e50a6fb17577e3eb6202167f0bec453eb5729cd7eab745d3c2a926533aef020cb4093ee779ef3e4e77e
7
+ data.tar.gz: dc56feaad2c8a2c554a03fc95a64cf8e9dd84941101efd44f43b5af5258da74f3308af54687ad42ed936359770ac5b8b9ee7a7feab4dedc387621dabfec8fe3b
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+
7
+ gemfile:
8
+ - Gemfile
9
+ - Gemfile.fluentd.lt.0.10.43
10
+
11
+ script: bundle exec rake test
data/AUTHORS CHANGED
@@ -1 +1,2 @@
1
1
  FURUHASHI Sadayuki <frsyuki _at_ gmail.com>
2
+ MASAHIRO Nakagawa <repeatedly _at_ gmail.com>
data/ChangeLog CHANGED
@@ -1,3 +1,12 @@
1
+ Release 0.10.18 - 2014/03/31
2
+
3
+ * use_ssl is true by default
4
+ * Support log_level option
5
+ * Add endpoint option to set treasuredata.com for the future
6
+ * Add experimental out_tditem plugin for item table
7
+ * Update td-client to v0.8.58
8
+
9
+
1
10
  Release 0.10.17 - 2013/12/05
2
11
 
3
12
  * Change table create / check mechanizm
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'fluentd', '= 0.10.42'
4
+ gemspec
data/README.rdoc CHANGED
@@ -1,4 +1,6 @@
1
- = Treasure Data output plugin for Fluent
1
+ = Treasure Data output plugin for Fluentd
2
+
3
+ This plugin is used to upload logs to Treasure Data using Treasure Data REST APIs.
2
4
 
3
5
  == Copyright
4
6
 
data/Rakefile CHANGED
@@ -6,9 +6,8 @@ require 'rake/testtask'
6
6
 
7
7
  Rake::TestTask.new(:test) do |test|
8
8
  test.libs << 'lib' << 'test'
9
- test.test_files = FileList['test/*.rb']
9
+ test.test_files = FileList['test/**/test_*.rb']
10
10
  test.verbose = true
11
11
  end
12
12
 
13
13
  task :default => [:build]
14
-
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.17
1
+ 0.10.18
@@ -17,6 +17,7 @@ Gem::Specification.new do |gem|
17
17
  gem.require_paths = ['lib']
18
18
 
19
19
  gem.add_dependency "fluentd", "~> 0.10.27"
20
- gem.add_dependency "td-client", "~> 0.8.56"
20
+ gem.add_dependency "td-client", "~> 0.8.58"
21
21
  gem.add_development_dependency "rake", ">= 0.9.2"
22
+ gem.add_development_dependency "webmock", "~> 1.16"
22
23
  end
@@ -0,0 +1,105 @@
1
+ module Fluent
2
+ class TreasureDataItemOutput < BufferedOutput
3
+ Plugin.register_output('tditem', self)
4
+
5
+ require_relative 'td_plugin_util'
6
+ include TDPluginUtil
7
+
8
+ IMPORT_SIZE_LIMIT = 32 * 1024 * 1024
9
+
10
+ # To support log_level option since Fluentd v0.10.43
11
+ unless method_defined?(:log)
12
+ define_method(:log) { $log }
13
+ end
14
+
15
+ config_param :apikey, :string
16
+ config_param :database, :string
17
+ config_param :table, :string
18
+ config_param :tmpdir, :string, :default => nil
19
+ #config_param :auto_create_table, :bool, :default => true # TODO: implement if user wants this feature
20
+
21
+ config_param :endpoint, :string, :default => nil
22
+ config_param :use_ssl, :bool, :default => true
23
+ config_param :http_proxy, :string, :default => nil
24
+ config_param :connect_timeout, :integer, :default => nil
25
+ config_param :read_timeout, :integer, :default => nil
26
+ config_param :send_timeout, :integer, :default => nil
27
+
28
+ def initialize
29
+ super
30
+
31
+ @auto_create_table = false
32
+ @tmpdir_prefix = 'tditem-'.freeze
33
+ @key_num_limit = 1024 # Item table default limitation
34
+ @record_size_limit = 32 * 1024 * 1024 # TODO
35
+ @empty_gz_data = TreasureData::API.create_empty_gz_data
36
+ @user_agent = "fluent-plugin-td-item: 0.10.18".freeze # TODO: automatic increment version
37
+
38
+ # overwrite default configurations
39
+ @buffer_type = 'file'
40
+ @flush_interval = 300
41
+ end
42
+
43
+ def configure(conf)
44
+ super
45
+
46
+ # overwrite default value of buffer_chunk_limit
47
+ if @buffer.respond_to?(:buffer_chunk_limit=) && !conf.has_key?('buffer_chunk_limit')
48
+ @buffer.buffer_chunk_limit = IMPORT_SIZE_LIMIT
49
+ end
50
+
51
+ validate_database_and_table_name(@database, @table, conf)
52
+ @key = "#{@database}.#{@table}".freeze
53
+ @use_ssl = parse_bool_parameter(@use_ssl) if @use_ssl.instance_of?(String)
54
+ FileUtils.mkdir_p(@tmpdir) unless @tmpdir.nil?
55
+
56
+ if @endpoint.nil?
57
+ $log.warn "tditem plugin will change the API endpoint from api.treasure-data.com to api.treasuredata.com"
58
+ $log.warn "If want to keep api.treasure-data.com, please set 'endpoint api.treasure-data.com' in tditem configuration"
59
+ end
60
+ end
61
+
62
+ def start
63
+ super
64
+
65
+ client_opts = {
66
+ :ssl => @use_ssl, :http_proxy => @http_proxy, :user_agent => @user_agent, :endpoint => @endpoint,
67
+ :connect_timeout => @connect_timeout, :read_timeout => @read_timeout, :send_timeout => @send_timeout
68
+ }
69
+ @client = TreasureData::Client.new(@apikey, client_opts)
70
+
71
+ check_table_existence(@database, @table)
72
+ end
73
+
74
+ def emit(tag, es, chain)
75
+ super(tag, es, chain, @key)
76
+ end
77
+
78
+ def format_stream(tag, es)
79
+ out = ''
80
+ off = out.bytesize
81
+ es.each { |time, record|
82
+ if record.size > @key_num_limit
83
+ log.error "Too many number of keys (#{record.size} keys)" # TODO include summary of the record
84
+ next
85
+ end
86
+
87
+ begin
88
+ record.to_msgpack(out)
89
+ rescue RangeError
90
+ TreasureData::API.normalized_msgpack(record, out)
91
+ end
92
+
93
+ noff = out.bytesize
94
+ sz = noff - off
95
+ if sz > @record_size_limit
96
+ # TODO don't raise error
97
+ #raise "Size of a record too large (#{sz} bytes)" # TODO include summary of the record
98
+ log.warn "Size of a record too large (#{sz} bytes): #{summarize_record(record)}"
99
+ end
100
+ off = noff
101
+ }
102
+ out
103
+ end
104
+ end
105
+ end
@@ -60,6 +60,13 @@ class TreasureDataLogOutput < BufferedOutput
60
60
  end
61
61
  end
62
62
 
63
+ # To support log_level option since Fluentd v0.10.43
64
+ unless method_defined?(:log)
65
+ define_method(:log) { $log }
66
+ end
67
+
68
+ config_param :endpoint, :string, :default => nil
69
+
63
70
  config_param :connect_timeout, :integer, :default => nil
64
71
  config_param :read_timeout, :integer, :default => nil
65
72
  config_param :send_timeout, :integer, :default => nil
@@ -83,7 +90,7 @@ class TreasureDataLogOutput < BufferedOutput
83
90
  @record_size_limit = 32*1024*1024 # TODO
84
91
  @table_list = {}
85
92
  @auto_create_table = true
86
- @use_ssl = false
93
+ @use_ssl = true
87
94
  @buffer_type = 'file' # overwrite default buffer_type
88
95
  @flush_interval = 300 # overwrite default flush_interval to 5mins
89
96
  @empty_gz_data = create_empty_gz_data
@@ -159,14 +166,22 @@ class TreasureDataLogOutput < BufferedOutput
159
166
  @anonymizes = nil if @anonymizes.empty?
160
167
 
161
168
  @http_proxy = conf['http_proxy']
162
- @user_agent = "fluent-plugin-td: 0.10.17" # TODO: automatic increment version
169
+ @user_agent = "fluent-plugin-td: 0.10.18" # TODO: automatic increment version
170
+
171
+ if @endpoint.nil?
172
+ $log.warn "tdlog plugin will change the API endpoint from api.treasure-data.com to api.treasuredata.com"
173
+ $log.warn "If want to keep api.treasure-data.com, please set 'endpoint api.treasure-data.com' in tdlog configuration"
174
+ end
163
175
  end
164
176
 
165
177
  def start
166
178
  super
167
179
 
168
- @client = TreasureData::Client.new(@apikey, :ssl => @use_ssl, :http_proxy => @http_proxy, :user_agent => @user_agent,
169
- :connect_timeout => @connect_timeout, :read_timeout => @read_timeout, :send_timeout => @send_timeout)
180
+ client_opts = {
181
+ :ssl => @use_ssl, :http_proxy => @http_proxy, :user_agent => @user_agent, :endpoint => @endpoint,
182
+ :connect_timeout => @connect_timeout, :read_timeout => @read_timeout, :send_timeout => @send_timeout
183
+ }
184
+ @client = TreasureData::Client.new(@apikey, client_opts)
170
185
 
171
186
  if @key
172
187
  if @auto_create_table
@@ -218,8 +233,8 @@ class TreasureDataLogOutput < BufferedOutput
218
233
  # TODO (a) Remove the transaction mechanism of fluentd
219
234
  # or (b) keep transaction boundaries in in/out_forward.
220
235
  # This code disables the transaction mechanism (a).
221
- $log.error "#{$!}: #{summarize_record(record)}"
222
- $log.error_backtrace $!.backtrace
236
+ log.error "#{$!}: #{summarize_record(record)}"
237
+ log.error_backtrace $!.backtrace
223
238
  next
224
239
  end
225
240
 
@@ -234,7 +249,7 @@ class TreasureDataLogOutput < BufferedOutput
234
249
  if sz > @record_size_limit
235
250
  # TODO don't raise error
236
251
  #raise "Size of a record too large (#{sz} bytes)" # TODO include summary of the record
237
- $log.warn "Size of a record too large (#{sz} bytes): #{summarize_record(record)}"
252
+ log.warn "Size of a record too large (#{sz} bytes): #{summarize_record(record)}"
238
253
  end
239
254
  off = noff
240
255
  }
@@ -279,7 +294,7 @@ class TreasureDataLogOutput < BufferedOutput
279
294
 
280
295
  def upload(database, table, io, size, unique_id)
281
296
  unique_str = unique_id.unpack('C*').map {|x| "%02x" % x }.join
282
- $log.trace { "uploading logs to Treasure Data database=#{database} table=#{table} (#{size}bytes)" }
297
+ log.trace { "uploading logs to Treasure Data database=#{database} table=#{table} (#{size}bytes)" }
283
298
 
284
299
  begin
285
300
  begin
@@ -295,7 +310,7 @@ class TreasureDataLogOutput < BufferedOutput
295
310
  end
296
311
  rescue => e
297
312
  elapsed = Time.now - start
298
- ne = RuntimeError.new("Failed to upload to TreasureData: #{$!} (#{size} bytes; #{elapsed} seconds)")
313
+ ne = RuntimeError.new("Failed to upload to Treasure Data '#{database}.#{table}' table: #{$!} (#{size} bytes; #{elapsed} seconds)")
299
314
  ne.set_backtrace(e.backtrace)
300
315
  raise ne
301
316
  end
@@ -304,7 +319,7 @@ class TreasureDataLogOutput < BufferedOutput
304
319
  def check_table_exists(key)
305
320
  unless @table_list.has_key?(key)
306
321
  database, table = key.split('.',2)
307
- $log.debug "checking whether table '#{database}.#{table}' exists on Treasure Data"
322
+ log.debug "checking whether table '#{database}.#{table}' exists on Treasure Data"
308
323
  io = StringIO.new(@empty_gz_data)
309
324
  begin
310
325
  @client.import(database, table, "msgpack.gz", io, io.size)
@@ -312,8 +327,8 @@ class TreasureDataLogOutput < BufferedOutput
312
327
  rescue TreasureData::NotFoundError
313
328
  raise "Table #{key.inspect} does not exist on Treasure Data. Use 'td table:create #{database} #{table}' to create it."
314
329
  rescue
315
- $log.warn "failed to check table existence on Treasure Data", :error=>$!.to_s
316
- $log.debug_backtrace $!
330
+ log.warn "failed to check existence of '#{database}.#{table}' table on Treasure Data", :error=>$!.to_s
331
+ log.debug_backtrace $!
317
332
  end
318
333
  end
319
334
  end
@@ -332,7 +347,7 @@ class TreasureDataLogOutput < BufferedOutput
332
347
  end
333
348
 
334
349
  def ensure_database_and_table(database, table)
335
- $log.info "Creating table #{database}.#{table} on TreasureData"
350
+ log.info "Creating table #{database}.#{table} on TreasureData"
336
351
  begin
337
352
  @client.create_log_table(database, table)
338
353
  rescue TreasureData::NotFoundError
@@ -0,0 +1,107 @@
1
+ module Fluent
2
+ module TDPluginUtil
3
+ require 'fileutils'
4
+ require 'stringio'
5
+ require 'tempfile'
6
+ require 'zlib'
7
+ require 'td-client'
8
+
9
+ def validate_database_and_table_name(database, table, conf)
10
+ begin
11
+ TreasureData::API.validate_database_name(database)
12
+ rescue => e
13
+ raise ConfigError, "Invalid database name #{database.inspect}: #{e}: #{conf}"
14
+ end
15
+ begin
16
+ TreasureData::API.validate_table_name(table)
17
+ rescue => e
18
+ raise ConfigError, "Invalid table name #{table.inspect}: #{e}: #{conf}"
19
+ end
20
+ end
21
+
22
+ def parse_bool_parameter(param)
23
+ if param.empty?
24
+ true
25
+ else
26
+ param = Config.bool_value(param)
27
+ raise ConfigError, "'true' or 'false' is required for #{key} option on tdlog output" if param.nil?
28
+ param
29
+ end
30
+ end
31
+
32
+ def summarize_record(record)
33
+ json = record.to_json
34
+ if json.size > 100
35
+ json[0..97] + "..."
36
+ else
37
+ json
38
+ end
39
+ end
40
+
41
+ def check_table_existence(database, table)
42
+ @table_list ||= {}
43
+ key = "#{database}.#{table}"
44
+ unless @table_list.has_key?(key)
45
+ log.debug "checking whether table '#{key}' exists on Treasure Data"
46
+ io = StringIO.new(@empty_gz_data)
47
+ begin
48
+ # here doesn't check whether target table is item table or not because import-only user can't read the table status.
49
+ # So I use empty import request to check table existence.
50
+ @client.import(database, table, "msgpack.gz", io, io.size)
51
+ @table_list[key] = true
52
+ rescue TreasureData::NotFoundError
53
+ args = self.class == TreasureDataItemOutput ? ' -t item' : ''
54
+ raise "Table #{key.inspect} does not exist on Treasure Data. Use 'td table:create #{database} #{table}#{args}' to create it."
55
+ rescue => e
56
+ log.warn "failed to check table existence on Treasure Data", :error => e.to_s
57
+ log.debug_backtrace e
58
+ end
59
+ end
60
+ end
61
+
62
+ def write(chunk)
63
+ unique_id = chunk.unique_id
64
+ database, table = chunk.key.split('.', 2)
65
+
66
+ FileUtils.mkdir_p(@tmpdir) unless @tmpdir.nil?
67
+ f = Tempfile.new(@tmpdir_prefix, @tmpdir)
68
+ w = Zlib::GzipWriter.new(f)
69
+
70
+ chunk.write_to(w)
71
+ w.finish
72
+ w = nil
73
+
74
+ size = f.pos
75
+ f.pos = 0
76
+ upload(database, table, f, size, unique_id)
77
+ ensure
78
+ w.close if w
79
+ f.close if f
80
+ end
81
+
82
+ # assume @client and @auto_create_table variable exist
83
+ def upload(database, table, io, size, unique_id)
84
+ unique_str = unique_id.unpack('C*').map {|x| "%02x" % x }.join
85
+ log.trace { "uploading logs to Treasure Data database=#{database} table=#{table} (#{size}bytes)" }
86
+
87
+ begin
88
+ begin
89
+ start = Time.now
90
+ @client.import(database, table, "msgpack.gz", io, size, unique_str)
91
+ rescue TreasureData::NotFoundError => e
92
+ unless @auto_create_table
93
+ raise e
94
+ end
95
+ ensure_database_and_table(database, table)
96
+ io.pos = 0
97
+ retry
98
+ end
99
+ rescue => e
100
+ elapsed = Time.now - start
101
+ ne = RuntimeError.new("Failed to upload to TreasureData: #{e} (#{size} bytes; #{elapsed} seconds)")
102
+ ne.set_backtrace(e.backtrace)
103
+ raise ne
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,84 @@
1
+ require 'test_helper'
2
+ require 'fluent/plugin/out_tditem'
3
+
4
+ class TreasureDataItemOutputTest < Test::Unit::TestCase
5
+ def setup
6
+ Fluent::Test.setup
7
+ end
8
+
9
+ DEFAULT_CONFIG = %[
10
+ database test
11
+ table table
12
+ ]
13
+
14
+ def create_driver(conf = DEFAULT_CONFIG)
15
+ config = %[
16
+ apikey testkey
17
+ buffer_type memory
18
+ ] + conf
19
+
20
+ Fluent::Test::BufferedOutputTestDriver.new(Fluent::TreasureDataItemOutput) do
21
+ def write(chunk)
22
+ # TestDriver doesn't call acutual Output#emit so set key to get database and table in this place.
23
+ chunk.instance_variable_set(:@key, @key)
24
+ super(chunk)
25
+ end
26
+ end.configure(config)
27
+ end
28
+
29
+ def test_configure
30
+ d = create_driver
31
+
32
+ assert_equal(d.instance.apikey, 'testkey')
33
+ assert_equal(d.instance.database, 'test')
34
+ assert_equal(d.instance.table, 'table')
35
+ assert_equal(d.instance.use_ssl, true)
36
+ end
37
+
38
+ def test_configure_with_invalid_database
39
+ assert_raise(Fluent::ConfigError) {
40
+ create_driver(%[
41
+ database a
42
+ table table
43
+ ])
44
+ }
45
+ end
46
+
47
+ def test_configure_with_invalid_table
48
+ assert_raise(Fluent::ConfigError) {
49
+ create_driver(%[
50
+ database test
51
+ table 1
52
+ ])
53
+ }
54
+ end
55
+
56
+ def test_emit
57
+ d = create_driver
58
+
59
+ time, records = stub_seed_values
60
+ stub_td_import_request(stub_request_body(records), d.instance.database, d.instance.table)
61
+
62
+ records.each { |record|
63
+ d.emit(record, time)
64
+ }
65
+ d.run
66
+
67
+ assert_equal(@auth_header, 'TD1 testkey')
68
+ end
69
+
70
+ def test_emit
71
+ d = create_driver(DEFAULT_CONFIG + "endpoint foo.bar.baz")
72
+ opts = {:endpoint => 'foo.bar.baz'}
73
+
74
+ time, records = stub_seed_values
75
+ stub_td_import_request(stub_request_body(records), d.instance.database, d.instance.table, opts)
76
+
77
+ records.each { |record|
78
+ d.emit(record, time)
79
+ }
80
+ d.run
81
+
82
+ assert_equal(@auth_header, 'TD1 testkey')
83
+ end
84
+ end
@@ -0,0 +1,106 @@
1
+ require 'fluent/test'
2
+ require 'fluent/plugin/out_tdlog'
3
+
4
+ class TreasureDataLogOutputTest < Test::Unit::TestCase
5
+ # BufferedOutputTestDriver uses module_eval, not inheritance.
6
+ # This DummyOutput is for testing actual write method with webmock
7
+ class TreasureDataLogDummyOutput < Fluent::TreasureDataLogOutput
8
+ end
9
+
10
+ def setup
11
+ Fluent::Test.setup
12
+ end
13
+
14
+ TMP_DIR = File.dirname(__FILE__) + "/tmp"
15
+
16
+ DEFAULT_CONFIG = %[
17
+ database test
18
+ table table
19
+ ]
20
+
21
+ def create_driver(conf = DEFAULT_CONFIG)
22
+ config = %[
23
+ apikey testkey
24
+ buffer_path #{TMP_DIR}/buffer
25
+ ] + conf
26
+
27
+ Fluent::Test::BufferedOutputTestDriver.new(TreasureDataLogDummyOutput) do
28
+ def write(chunk)
29
+ chunk.instance_variable_set(:@key, @key)
30
+ super(chunk)
31
+ end
32
+ end.configure(config)
33
+ end
34
+
35
+ def test_configure
36
+ d = create_driver
37
+
38
+ {:@apikey => 'testkey', :@use_ssl => true, :@auto_create_table => true,
39
+ :@buffer_type => 'file', :@flush_interval => 300}.each { |k, v|
40
+ assert_equal(d.instance.instance_variable_get(k), v)
41
+ }
42
+ end
43
+
44
+ def test_emit
45
+ d = create_driver
46
+
47
+ time, records = stub_seed_values
48
+ database, table = d.instance.instance_variable_get(:@key).split(".", 2)
49
+ stub_td_table_create_request(database, table)
50
+ stub_td_import_request(stub_request_body(records, time), database, table)
51
+
52
+ records.each { |record|
53
+ d.emit(record, time)
54
+ }
55
+ d.run
56
+
57
+ assert_equal(@auth_header, 'TD1 testkey')
58
+ end
59
+
60
+ def test_emit_with_endpoint
61
+ d = create_driver(DEFAULT_CONFIG + "endpoint foo.bar.baz")
62
+ opts = {:endpoint => 'foo.bar.baz'}
63
+ time, records = stub_seed_values
64
+ database, table = d.instance.instance_variable_get(:@key).split(".", 2)
65
+ stub_td_table_create_request(database, table, opts)
66
+ stub_td_import_request(stub_request_body(records, time), database, table, opts)
67
+
68
+ records.each { |record|
69
+ d.emit(record, time)
70
+ }
71
+ d.run
72
+ end
73
+
74
+ # TODO: add normalized_msgpack / key_num_limit / tag split test
75
+
76
+ ## TODO invalid names are normalized
77
+ # def test_invalid_name
78
+ # d = create_driver
79
+ # d.instance.start
80
+ #
81
+ # es = Fluent::OneEventStream.new(Time.now.to_i, {})
82
+ # chain = Fluent::NullOutputChain.instance
83
+ # assert_raise(RuntimeError) do
84
+ # d.instance.emit("test.invalid-name", es, chain)
85
+ # end
86
+ # assert_raise(RuntimeError) do
87
+ # d.instance.emit("empty", es, chain)
88
+ # end
89
+ # assert_raise(RuntimeError) do
90
+ # d.instance.emit("", es, chain)
91
+ # end
92
+ # end
93
+
94
+ ## TODO invalid data is ignored
95
+ # def test_invalid_data
96
+ # d = create_driver
97
+ # d.instance.start
98
+ #
99
+ # es = Fluent::OneEventStream.new(Time.now.to_i, "invalid")
100
+ # chain = Fluent::NullOutputChain.instance
101
+ # assert_nothing_raised do
102
+ # d.instance.emit("test.name", es, chain)
103
+ # end
104
+ # end
105
+ end
106
+
@@ -0,0 +1,71 @@
1
+ require 'json'
2
+ require 'msgpack'
3
+ require 'fluent/test'
4
+ require 'webmock/test_unit'
5
+ require 'stringio'
6
+ require 'td-client'
7
+ require 'zlib'
8
+
9
+ def e(s)
10
+ require 'cgi'
11
+ CGI.escape(s.to_s)
12
+ end
13
+
14
+ class Test::Unit::TestCase
15
+ def stub_seed_values
16
+ time = Time.parse("2014-01-01 00:00:00 UTC").to_i
17
+ records = [{"a" => 1}, {"a" => 2}]
18
+ return time, records
19
+ end
20
+
21
+ def stub_request_body(records, time = nil)
22
+ out = ''
23
+ records.each { |record|
24
+ r = record.dup
25
+ if time
26
+ r['time'] = time
27
+ end
28
+ r.to_msgpack(out)
29
+ }
30
+
31
+ io = StringIO.new
32
+ gz = Zlib::GzipWriter.new(io)
33
+ FileUtils.copy_stream(StringIO.new(out), gz)
34
+ gz.finish
35
+ io.string
36
+ end
37
+
38
+ def stub_gzip_unwrap(body)
39
+ io = StringIO.new(body)
40
+ gz = Zlib::GzipReader.new(io)
41
+ gz.read
42
+ end
43
+
44
+ def stub_td_table_create_request(database, table, opts = {})
45
+ opts[:use_ssl] = true unless opts.has_key?(:use_ssl)
46
+ schema = opts[:use_ssl] ? 'https' : 'http'
47
+ response = {"database" => database, "table" => table}.to_json
48
+ endpoint = opts[:endpoint] ? opts[:endpoint] : TreasureData::API::DEFAULT_ENDPOINT
49
+
50
+ url = "#{schema}://#{endpoint}/v3/table/create/#{e(database)}/#{e(table)}/log"
51
+ stub_request(:post, url).to_return(:status => 200, :body => response)
52
+ end
53
+
54
+ def stub_td_import_request(body, db, table, opts = {})
55
+ opts[:use_ssl] = true unless opts.has_key?(:use_ssl)
56
+ format = opts[:format] || 'msgpack.gz'
57
+ schema = opts[:use_ssl] ? 'https' : 'http'
58
+ response = {"database" => db, "table" => table, "elapsed_time" => 0}.to_json
59
+ endpoint = opts[:endpoint] ? opts[:endpoint] : TreasureData::API::DEFAULT_IMPORT_ENDPOINT
60
+
61
+ # for check_table_existence
62
+ url_with_empty = "#{schema}://#{endpoint}//v3/table/import/#{e(db)}/#{e(table)}/#{format}"
63
+ stub_request(:put, url_with_empty).to_return(:status => 200, :body => response)
64
+
65
+ url_with_unique = Regexp.compile("#{schema}://#{endpoint}//v3/table/import_with_id/#{e(db)}/#{e(table)}/.*/#{format}")
66
+ stub_request(:put, url_with_unique).with(:headers => {'Content-Type' => 'application/octet-stream'}) { |req|
67
+ @auth_header = req.headers["Authorization"]
68
+ stub_gzip_unwrap(req.body) == stub_gzip_unwrap(body)
69
+ }.to_return(:status => 200, :body => response)
70
+ end
71
+ end
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-td
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.17
5
- prerelease:
4
+ version: 0.10.18
6
5
  platform: ruby
7
6
  authors:
8
7
  - Treasure Data, Inc.
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-12-05 00:00:00.000000000 Z
11
+ date: 2014-03-31 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: fluentd
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,35 +27,45 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: td-client
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
37
- version: 0.8.56
33
+ version: 0.8.58
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
45
- version: 0.8.56
40
+ version: 0.8.58
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rake
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: 0.9.2
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: 0.9.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.16'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.16'
62
69
  description: Treasure Data Cloud Data Service plugin for Fluentd
63
70
  email: support@treasure-data.com
64
71
  executables: []
@@ -66,45 +73,46 @@ extensions: []
66
73
  extra_rdoc_files: []
67
74
  files:
68
75
  - .gitignore
76
+ - .travis.yml
69
77
  - AUTHORS
70
78
  - ChangeLog
71
79
  - Gemfile
80
+ - Gemfile.fluentd.lt.0.10.43
72
81
  - README.rdoc
73
82
  - Rakefile
74
83
  - VERSION
75
84
  - example.conf
76
85
  - fluent-plugin-td.gemspec
86
+ - lib/fluent/plugin/out_tditem.rb
77
87
  - lib/fluent/plugin/out_tdlog.rb
78
- - test/out_tdlog.rb
88
+ - lib/fluent/plugin/td_plugin_util.rb
89
+ - test/plugin/test_out_tditem.rb
90
+ - test/plugin/test_out_tdlog.rb
91
+ - test/test_helper.rb
79
92
  homepage: http://www.treasuredata.com/
80
93
  licenses: []
94
+ metadata: {}
81
95
  post_install_message:
82
96
  rdoc_options: []
83
97
  require_paths:
84
98
  - lib
85
99
  required_ruby_version: !ruby/object:Gem::Requirement
86
- none: false
87
100
  requirements:
88
- - - ! '>='
101
+ - - '>='
89
102
  - !ruby/object:Gem::Version
90
103
  version: '0'
91
- segments:
92
- - 0
93
- hash: 79461157441894892
94
104
  required_rubygems_version: !ruby/object:Gem::Requirement
95
- none: false
96
105
  requirements:
97
- - - ! '>='
106
+ - - '>='
98
107
  - !ruby/object:Gem::Version
99
108
  version: '0'
100
- segments:
101
- - 0
102
- hash: 79461157441894892
103
109
  requirements: []
104
110
  rubyforge_project:
105
- rubygems_version: 1.8.23
111
+ rubygems_version: 2.0.2
106
112
  signing_key:
107
- specification_version: 3
113
+ specification_version: 4
108
114
  summary: Treasure Data Cloud Data Service plugin for Fluentd
109
115
  test_files:
110
- - test/out_tdlog.rb
116
+ - test/plugin/test_out_tditem.rb
117
+ - test/plugin/test_out_tdlog.rb
118
+ - test/test_helper.rb
data/test/out_tdlog.rb DELETED
@@ -1,67 +0,0 @@
1
- require 'fluent/test'
2
- require 'fluent/plugin/out_tdlog'
3
-
4
- class TreasureDataLogOutputTest < Test::Unit::TestCase
5
- def setup
6
- Fluent::Test.setup
7
- end
8
-
9
- TMP_DIR = File.dirname(__FILE__) + "/tmp"
10
-
11
- CONFIG = %[
12
- apikey testkey
13
- buffer_path #{TMP_DIR}/buffer
14
- ]
15
-
16
- def create_driver(conf = CONFIG)
17
- Fluent::Test::BufferedOutputTestDriver.new(Fluent::TreasureDataLogOutput) do
18
- def start
19
- super
20
- end
21
-
22
- def write(chunk)
23
- chunk.read
24
- end
25
- end.configure(conf)
26
- end
27
-
28
- def test_emit
29
- d = create_driver
30
-
31
- time = Time.parse("2011-01-02 13:14:15 UTC").to_i
32
- d.emit({"a"=>1}, time)
33
- d.emit({"a"=>2}, time)
34
- d.run
35
- end
36
-
37
- ## TODO invalid names are normalized
38
- # def test_invalid_name
39
- # d = create_driver
40
- # d.instance.start
41
- #
42
- # es = Fluent::OneEventStream.new(Time.now.to_i, {})
43
- # chain = Fluent::NullOutputChain.instance
44
- # assert_raise(RuntimeError) do
45
- # d.instance.emit("test.invalid-name", es, chain)
46
- # end
47
- # assert_raise(RuntimeError) do
48
- # d.instance.emit("empty", es, chain)
49
- # end
50
- # assert_raise(RuntimeError) do
51
- # d.instance.emit("", es, chain)
52
- # end
53
- # end
54
-
55
- ## TODO invalid data is ignored
56
- # def test_invalid_data
57
- # d = create_driver
58
- # d.instance.start
59
- #
60
- # es = Fluent::OneEventStream.new(Time.now.to_i, "invalid")
61
- # chain = Fluent::NullOutputChain.instance
62
- # assert_nothing_raised do
63
- # d.instance.emit("test.name", es, chain)
64
- # end
65
- # end
66
- end
67
-