fluent-plugin-dynamodb-alt 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 19e9db9d6797b1ac0b232a1190fe09498ea4502b
4
+ data.tar.gz: 21a3ce6f08bbcb44aa19f35bda31ecac38af0662
5
+ SHA512:
6
+ metadata.gz: de30d92832b0c478a585fb0aa8eff15cc2b640ddb10f74e920fd6788e98415e0ddc9f33888ef4c94b36a16e7cc4de50f06932e4b9300e00dfb8c4c0a74fc20b4
7
+ data.tar.gz: 9cdcde14528e63ac12d402a214d2843d108077a936ae824ecba828805fa7a71920e8ba3237d7c3377f2e4a8627869500c04f7edce5bbabf89461ff5bdafafe28
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ test.rb
16
+ node_modules
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --require spec_helper
2
+ --colour
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+
2
+ language: ruby
3
+ rvm:
4
+ - 2.0.0
5
+ script:
6
+ - bundle install
7
+ - bundle exec rake
8
+ env:
9
+ global:
10
+ - secure: "ESwtSv6459PcZVWzwPbxIFxKVkm7F0BE30JlEgE2N4yWaMaWZfE9i2tsjh6cCB03I89sUuysHjpDtV/Hf7nUQC58s6q5VFPEex0RBBcbAC8ALLL+Ai6go6vPX2tqp0NP8v1OUdNDu7TtPQW2EFyBG75OJ1ga9UT1Q+hGnd2Xdmg="
11
+ - secure: "hHZGXwrDVpY7DtY6nSbkiBcIQlT4uvIlA0Xx/+AHHsEBgeAakSPVO8pVI3JrkpYNRbmYOXoj3TWs0Z0ROjPZdbOwKIaH3xzoFS61zk/oJbHume8wjhvWp8QGNukcbas+/3FuTyBc2CUDcy2zpuEx+p/PEYwccdS3LrTTSp4nYf0="
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-dynamodb-session.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Genki Sugawara
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # fluent-plugin-dynamodb-alt
2
+
3
+ Alternative fluent plugin to output to DynamoDB.
4
+
5
+ ## Features
6
+
7
+ * Use [PutItem](http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) Acrion
8
+ * Sort the records in the timestamp key.
9
+ * Aggregate the records in the primary key.
10
+ * Support [Expected constraint](http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html#DDB-PutItem-request-Expected)
11
+
12
+ [![Gem Version](https://badge.fury.io/rb/fluent-plugin-dynamodb-alt.png)](http://badge.fury.io/rb/fluent-plugin-dynamodb-alt)
13
+ [![Build Status](https://travis-ci.org/winebarrel/fluent-plugin-dynamodb-alt.svg)](https://travis-ci.org/winebarrel/fluent-plugin-dynamodb-alt)
14
+
15
+ ## Installation
16
+
17
+ ```sh
18
+ bundle install
19
+ bundle exec rake install
20
+ ```
21
+
22
+ ## Configuration
23
+
24
+ ```
25
+ <match tag>
26
+ type dynamodb_alt
27
+ aws_key_id AKIAIOSFODNN7EXAMPLE
28
+ aws_sec_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
29
+ region ap-northeast-1
30
+ table_name my_table
31
+ timestamp_key timestamp
32
+ #concurrency 1
33
+
34
+ # see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html#DDB-PutItem-request-Expected
35
+ #expected id NULL,timestamp LT ${timestamp},key EQ "val"
36
+ #conditional_operator OR
37
+
38
+ #include_time_key false
39
+ #include_tag_key false
40
+ </match>
41
+ ```
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+ task :default => :spec
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'fluent-plugin-dynamodb-alt'
7
+ spec.version = '0.1.0'
8
+ spec.authors = ['Genki Sugawara']
9
+ spec.email = ['sgwr_dts@yahoo.co.jp']
10
+ spec.summary = %q{Fluent plugin to output to DynamoDB.}
11
+ spec.description = %q{Fluent plugin to output to DynamoDB.}
12
+ spec.homepage = 'https://github.com/winebarrel/fluent-plugin-dynamodb-alt'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'fluentd'
21
+ spec.add_dependency 'aws-sdk-core', '>= 2.0.0.rc15'
22
+ spec.add_dependency 'parallel'
23
+ spec.add_development_dependency 'bundler'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec', '>= 3.0.0'
26
+ spec.add_development_dependency 'hashie'
27
+ spec.add_development_dependency 'ddbcli'
28
+ end
@@ -0,0 +1,202 @@
1
+ class Fluent::DynamodbAltOutput < Fluent::BufferedOutput
2
+ Fluent::Plugin.register_output('dynamodb_alt', self)
3
+
4
+ include Fluent::SetTimeKeyMixin
5
+ include Fluent::SetTagKeyMixin
6
+
7
+ unless method_defined?(:log)
8
+ define_method('log') { $log }
9
+ end
10
+
11
+ config_param :profile, :string, :default => nil
12
+ config_param :credentials_path, :string, :default => nil
13
+ config_param :aws_key_id, :string, :default => nil
14
+ config_param :aws_sec_key, :string, :default => nil
15
+ config_param :region, :string, :default => nil
16
+ config_param :table_name, :string
17
+ config_param :timestamp_key, :string
18
+ config_param :concurrency, :integer, :default => 1
19
+ config_param :expected, :string, :default => nil
20
+ config_param :conditional_operator, :string, :default => 'AND'
21
+
22
+ config_set_default :include_time_key, true
23
+ config_set_default :include_tag_key, true
24
+
25
+ def initialize
26
+ super
27
+ require 'aws-sdk-core'
28
+ require 'parallel'
29
+ end
30
+
31
+ def configure(conf)
32
+ super
33
+
34
+ aws_opts = {}
35
+
36
+ if @profile
37
+ credentials_opts = {:profile_name => @profile}
38
+ credentials_opts[:path] = @credentials_path if @credentials_path
39
+ credentials = Aws::SharedCredentials.new(credentials_opts)
40
+ aws_opts[:credentials] = credentials
41
+ end
42
+
43
+ aws_opts[:access_key_id] = @aws_key_id if @aws_key_id
44
+ aws_opts[:secret_access_key] = @aws_sec_key if @aws_sec_key
45
+ aws_opts[:region] = @region if @region
46
+
47
+ configure_aws(aws_opts)
48
+
49
+ client = create_client
50
+ table = client.describe_table(:table_name => @table_name)
51
+
52
+ table.table.key_schema.each do |attribute|
53
+ case attribute.key_type
54
+ when 'HASH'
55
+ @hash_key = attribute.attribute_name
56
+ when 'RANGE'
57
+ @range_key = attribute.attribute_name
58
+ else
59
+ raise 'must not happen'
60
+ end
61
+ end
62
+
63
+ if @expected
64
+ @expected = parse_expected(@expected)
65
+ log.info("dynamodb_alt expected: #{@expected.inspect}")
66
+ end
67
+ end
68
+
69
+ def start
70
+ super
71
+
72
+ @client = create_client
73
+ end
74
+
75
+ def format(tag, time, record)
76
+ [tag, time, record].to_msgpack
77
+ end
78
+
79
+ def write(chunk)
80
+ chunk = aggregate_records(chunk)
81
+ block = proc do |tag, time, record|
82
+ put_record(record)
83
+ end
84
+
85
+ if @concurrency > 1
86
+ Parallel.each(chunk, :in_threads => @concurrency, &block)
87
+ else
88
+ chunk.each(&block)
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def configure_aws(options)
95
+ Aws.config.update(options)
96
+ end
97
+
98
+ def create_client
99
+ Aws::DynamoDB::Client.new
100
+ end
101
+
102
+ def put_record(record)
103
+ item = {
104
+ :table_name => @table_name,
105
+ :item => record
106
+ }
107
+
108
+ begin
109
+ if @expected
110
+ expected = create_expected(record)
111
+ return unless expected
112
+ item[:expected] = expected
113
+ item[:conditional_operator] = @conditional_operator if expected.length > 1
114
+ end
115
+
116
+ @client.put_item(item)
117
+ rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException, Aws::DynamoDB::Errors::ValidationException => e
118
+ log.warn("#{e.message}: #{item.inspect}")
119
+ end
120
+ end
121
+
122
+ def validate_record(record)
123
+ if not record[@hash_key]
124
+ log.warn("Hash Key '#{@hash_key}' does not exist in the record: #{record.inspect}")
125
+ return false
126
+ end
127
+
128
+ if @range_key and not record[@range_key]
129
+ log.warn("Range Key '#{@range_key}' does not exist in the record: #{record.inspect}")
130
+ return false
131
+ end
132
+
133
+ if not record[@timestamp_key]
134
+ log.warn("Timestamp Key '#{@timestamp_key}' does not exist in the record: #{record.inspect}")
135
+ return false
136
+ end
137
+
138
+ return true
139
+ end
140
+
141
+ def parse_expected(expected)
142
+ expected.split(',').map do |expr|
143
+ key, op, val = expr.strip.split(/\s+/)
144
+
145
+ if val
146
+ if val =~ /\A\$\{(.+)\}\z/
147
+ record_key = $1.inspect
148
+ val = eval("proc {|record| record[#{record_key}] }")
149
+ else
150
+ begin
151
+ val = JSON.parse("[#{val}]").first if val
152
+ rescue JSON::ParserError => e
153
+ raise "Cannot parse the expected expression (#{expr}): #{e.message}"
154
+ end
155
+ end
156
+ end
157
+
158
+ [key, op, val]
159
+ end
160
+ end
161
+
162
+ def create_expected(record)
163
+ attrs = {}
164
+
165
+ @expected.map do |key, op, val|
166
+ attrs[key] = {:comparison_operator => op}
167
+
168
+ if val
169
+ if val.kind_of?(Proc)
170
+ record_val = val.call(record)
171
+
172
+ unless record_val
173
+ log.warn("Expected value does not exist in the record: #{record.inspect}")
174
+ return nil
175
+ end
176
+
177
+ attrs[key][:attribute_value_list] = [record_val]
178
+ else
179
+ attrs[key][:attribute_value_list] = [val]
180
+ end
181
+ end
182
+ end
183
+
184
+ return attrs
185
+ end
186
+
187
+ def aggregate_records(chunk)
188
+ chunk.enum_for(:msgpack_each).select {|tag, time, record|
189
+ validate_record(record)
190
+ }.chunk {|tag, time, record|
191
+ if @range_key
192
+ record.values_at(@hash_key, @range_key)
193
+ else
194
+ record[@hash_key]
195
+ end
196
+ }.map {|primary_key, records|
197
+ records.sort_by {|tag, time, record|
198
+ record[@timestamp_key]
199
+ }.last
200
+ }
201
+ end
202
+ end
@@ -0,0 +1,430 @@
1
+ describe Fluent::DynamodbAltOutput do
2
+ let(:time) {
3
+ Time.parse('2014-09-01 01:23:45 UTC').to_i
4
+ }
5
+
6
+ context('configure') {
7
+ it do
8
+ driver = create_driver
9
+ expect(driver.instance).to receive(:configure_aws).with(
10
+ :access_key_id => "AKIAIOSFODNN7EXAMPLE",
11
+ :secret_access_key => "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
12
+ :region => "ap-northeast-1")
13
+
14
+ expect(driver.instance).to receive(:create_client) {
15
+ client = double('client')
16
+ allow(client).to receive(:describe_table).with(:table_name => 'my_table') {
17
+ Hashie::Mash.new(:table => {
18
+ :key_schema => [
19
+ {:key_type => 'HASH', :attribute_name => 'hash_key'},
20
+ {:key_type => 'RANGE', :attribute_name => 'range_key'}
21
+ ]})
22
+ }
23
+ client
24
+ }
25
+
26
+ driver.configure(<<-EOS)
27
+ type dynamodb_alt
28
+ aws_key_id AKIAIOSFODNN7EXAMPLE
29
+ aws_sec_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
30
+ region ap-northeast-1
31
+ table_name my_table
32
+ timestamp_key timestamp
33
+ concurrency 2
34
+ conditional_operator OR
35
+ EOS
36
+
37
+ expect(driver.instance.aws_key_id ).to eq 'AKIAIOSFODNN7EXAMPLE'
38
+ expect(driver.instance.aws_sec_key ).to eq 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
39
+ expect(driver.instance.table_name ).to eq 'my_table'
40
+ expect(driver.instance.timestamp_key ).to eq 'timestamp'
41
+ expect(driver.instance.concurrency ).to eq 2
42
+ expect(driver.instance.conditional_operator).to eq 'OR'
43
+ expect(driver.instance.instance_variable_get(:@hash_key) ).to eq 'hash_key'
44
+ expect(driver.instance.instance_variable_get(:@range_key)).to eq 'range_key'
45
+ end
46
+
47
+ it do
48
+ driver = create_driver
49
+ allow(driver.instance).to receive(:configure_aws)
50
+
51
+ allow(driver.instance).to receive(:create_client) {
52
+ client = double('client')
53
+ allow(client).to receive(:describe_table) {
54
+ Hashie::Mash.new(:table => {
55
+ :key_schema => [
56
+ {:key_type => 'HASH', :attribute_name => 'hash_key'},
57
+ ]})
58
+ }
59
+ client
60
+ }
61
+
62
+ driver.configure(<<-EOS)
63
+ type dynamodb_alt
64
+ table_name my_table
65
+ timestamp_key timestamp
66
+ expected timestamp GE 0,key LT 100
67
+ EOS
68
+
69
+ expected = driver.instance.instance_variable_get(:@expected)
70
+ expect(expected).to eq [["timestamp", "GE", 0],["key", "LT", 100]]
71
+ end
72
+
73
+ it do
74
+ driver = create_driver
75
+ allow(driver.instance).to receive(:configure_aws)
76
+
77
+ allow(driver.instance).to receive(:create_client) {
78
+ client = double('client')
79
+ allow(client).to receive(:describe_table) {
80
+ Hashie::Mash.new(:table => {
81
+ :key_schema => [
82
+ {:key_type => 'HASH', :attribute_name => 'hash_key'},
83
+ ]})
84
+ }
85
+ client
86
+ }
87
+
88
+ driver.configure(<<-EOS)
89
+ type dynamodb_alt
90
+ table_name my_table
91
+ timestamp_key timestamp
92
+ expected id NULL,timestamp LT ${ts},key EQ ${k}
93
+ EOS
94
+
95
+ expected = driver.instance.instance_variable_get(:@expected)
96
+
97
+ expect(expected[0]).to eq ["id", "NULL", nil]
98
+
99
+ col1, op1, val1 = expected[1]
100
+ expect(col1).to eq 'timestamp'
101
+ expect(op1 ).to eq 'LT'
102
+ expect(val1.call('ts' => 1)).to eq 1
103
+
104
+ col2, op2, val2 = expected[2]
105
+ expect(col2).to eq 'key'
106
+ expect(op2 ).to eq 'EQ'
107
+ expect(val2.call('k' => '1')).to eq '1'
108
+ end
109
+
110
+ it do
111
+ driver = create_driver
112
+ allow(driver.instance).to receive(:configure_aws)
113
+
114
+ allow(driver.instance).to receive(:create_client) {
115
+ client = double('client')
116
+ allow(client).to receive(:describe_table) {
117
+ Hashie::Mash.new(:table => {
118
+ :key_schema => [
119
+ {:key_type => 'HASH', :attribute_name => 'hash_key'},
120
+ ]})
121
+ }
122
+ client
123
+ }
124
+
125
+ expect {
126
+ driver.configure(<<-EOS)
127
+ type dynamodb_alt
128
+ table_name my_table
129
+ timestamp_key timestamp
130
+ expected key EQ val
131
+ EOS
132
+ }.to raise_error("Cannot parse the expected expression (key EQ val): 399: unexpected token at 'val]'")
133
+ end
134
+
135
+ it do
136
+ driver = create_driver
137
+ allow(driver.instance).to receive(:configure_aws)
138
+
139
+ allow(driver.instance).to receive(:create_client) {
140
+ client = double('client')
141
+ allow(client).to receive(:describe_table) {
142
+ Hashie::Mash.new(:table => {
143
+ :key_schema => [
144
+ {:key_type => 'HASH', :attribute_name => 'hash_key'},
145
+ ]})
146
+ }
147
+ client
148
+ }
149
+
150
+ driver.configure(<<-EOS)
151
+ type dynamodb_alt
152
+ table_name my_table
153
+ timestamp_key timestamp
154
+ expected key1 EQ "str",key2 EQ 1
155
+ EOS
156
+
157
+ expected = driver.instance.instance_variable_get(:@expected)
158
+ expect(expected).to eq [["key1", "EQ", "str"], ["key2", "EQ", 1]]
159
+ end
160
+ }
161
+
162
+ context('emit') {
163
+ before(:all) do
164
+ drop_table
165
+ create_table('(id STRING HASH) READ = 20 WRITE = 20')
166
+ end
167
+
168
+ before(:each) do
169
+ truncate_table
170
+ end
171
+
172
+ after(:all) do
173
+ drop_table
174
+ end
175
+
176
+ context('without condition') {
177
+ it do
178
+ run_driver do |d|
179
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001}, time)
180
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002}, time)
181
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003}, time)
182
+ end
183
+
184
+ expect(select_all).to match_array [
185
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001},
186
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002},
187
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003},
188
+ ]
189
+ end
190
+ }
191
+
192
+ context('concurrency > 1') {
193
+ it do
194
+ run_driver(:concurrency => 2) do |d|
195
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001}, time)
196
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002}, time)
197
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003}, time)
198
+ end
199
+
200
+ expect(select_all).to match_array [
201
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001},
202
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002},
203
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003},
204
+ ]
205
+ end
206
+ }
207
+
208
+ context('with condition (1)') {
209
+ it do
210
+ run_driver(:expected => 'id NULL,timestamp LT ${timestamp}', :conditional_operator => 'OR') do |d|
211
+ expect(d.instance.log).not_to receive(:warn)
212
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001}, time)
213
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002}, time)
214
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003}, time)
215
+ end
216
+
217
+ expect(select_all).to match_array [
218
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001},
219
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002},
220
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003},
221
+ ]
222
+
223
+ run_driver(:expected => 'id NULL,timestamp LT ${timestamp}', :conditional_operator => 'OR') do |d|
224
+ expect(d.instance.log).not_to receive(:warn)
225
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625004, 'key' => 'val'}, time)
226
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625005, 'key' => 'val'}, time)
227
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625006, 'key' => 'val'}, time)
228
+ end
229
+
230
+ expect(select_all).to match_array [
231
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625004, 'key' => 'val'},
232
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625005, 'key' => 'val'},
233
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625006, 'key' => 'val'},
234
+ ]
235
+ end
236
+
237
+ it do
238
+ run_driver(:expected => 'id NULL,timestamp LT ${timestamp}', :conditional_operator => 'OR') do |d|
239
+ expect(d.instance.log).not_to receive(:warn)
240
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001}, time)
241
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002}, time)
242
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003}, time)
243
+ end
244
+
245
+ expect(select_all).to match_array [
246
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001},
247
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002},
248
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003},
249
+ ]
250
+
251
+ run_driver(:expected => 'id NULL,timestamp LT ${timestamp}', :conditional_operator => 'OR') do |d|
252
+ expect(d.instance.log).to receive(:warn)
253
+ .with(%!The conditional request failed: {:table_name=>"#{TEST_TABLE_NAME}", :item=>{"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001, "key"=>"val"}, :expected=>{"id"=>{:comparison_operator=>"NULL"}, "timestamp"=>{:comparison_operator=>"LT", :attribute_value_list=>[1409534625001]}}, :conditional_operator=>"OR"}!)
254
+ expect(d.instance.log).to receive(:warn)
255
+ .with(%!The conditional request failed: {:table_name=>"#{TEST_TABLE_NAME}", :item=>{"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002, "key"=>"val"}, :expected=>{"id"=>{:comparison_operator=>"NULL"}, "timestamp"=>{:comparison_operator=>"LT", :attribute_value_list=>[1409534625002]}}, :conditional_operator=>"OR"}!)
256
+ expect(d.instance.log).to receive(:warn)
257
+ .with(%!The conditional request failed: {:table_name=>"#{TEST_TABLE_NAME}", :item=>{"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003, "key"=>"val"}, :expected=>{"id"=>{:comparison_operator=>"NULL"}, "timestamp"=>{:comparison_operator=>"LT", :attribute_value_list=>[1409534625003]}}, :conditional_operator=>"OR"}!)
258
+
259
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001, 'key' => 'val'}, time)
260
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002, 'key' => 'val'}, time)
261
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003, 'key' => 'val'}, time)
262
+ end
263
+
264
+ expect(select_all).to match_array [
265
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001},
266
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002},
267
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003},
268
+ ]
269
+ end
270
+ }
271
+
272
+ context('with condition (2)') {
273
+ it do
274
+ run_driver do |d|
275
+ expect(d.instance.log).not_to receive(:warn)
276
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001, 'key' => 'val'}, time)
277
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002, 'key' => 'val'}, time)
278
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003, 'key' => 'val'}, time)
279
+ end
280
+
281
+ expect(select_all).to match_array [
282
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001, 'key' => 'val'},
283
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002, 'key' => 'val'},
284
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003, 'key' => 'val'},
285
+ ]
286
+
287
+ run_driver(:expected => 'timestamp LE ${timestamp},key EQ "val"') do |d|
288
+ expect(d.instance.log).not_to receive(:warn)
289
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625004, 'key' => 'val2'}, time)
290
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625005, 'key' => 'val2'}, time)
291
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625006, 'key' => 'val2'}, time)
292
+ end
293
+
294
+ expect(select_all).to match_array [
295
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625004, 'key' => 'val2'},
296
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625005, 'key' => 'val2'},
297
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625006, 'key' => 'val2'},
298
+ ]
299
+ end
300
+
301
+ it do
302
+ run_driver do |d|
303
+ expect(d.instance.log).not_to receive(:warn)
304
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001, 'key' => 'val3'}, time)
305
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002, 'key' => 'val3'}, time)
306
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003, 'key' => 'val3'}, time)
307
+ end
308
+
309
+ expect(select_all).to match_array [
310
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001, 'key' => 'val3'},
311
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002, 'key' => 'val3'},
312
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003, 'key' => 'val3'},
313
+ ]
314
+
315
+ run_driver(:expected => 'timestamp LE ${timestamp},key EQ "val"') do |d|
316
+ expect(d.instance.log).to receive(:warn)
317
+ .with(%!The conditional request failed: {:table_name=>"#{TEST_TABLE_NAME}", :item=>{"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625004, "key"=>"val2"}, :expected=>{"timestamp"=>{:comparison_operator=>"LE", :attribute_value_list=>[1409534625004]}, "key"=>{:comparison_operator=>"EQ", :attribute_value_list=>["val"]}}, :conditional_operator=>"AND"}!)
318
+ expect(d.instance.log).to receive(:warn)
319
+ .with(%!The conditional request failed: {:table_name=>"#{TEST_TABLE_NAME}", :item=>{"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625005, "key"=>"val2"}, :expected=>{"timestamp"=>{:comparison_operator=>"LE", :attribute_value_list=>[1409534625005]}, "key"=>{:comparison_operator=>"EQ", :attribute_value_list=>["val"]}}, :conditional_operator=>"AND"}!)
320
+ expect(d.instance.log).to receive(:warn)
321
+ .with(%!The conditional request failed: {:table_name=>"#{TEST_TABLE_NAME}", :item=>{"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625006, "key"=>"val2"}, :expected=>{"timestamp"=>{:comparison_operator=>"LE", :attribute_value_list=>[1409534625006]}, "key"=>{:comparison_operator=>"EQ", :attribute_value_list=>["val"]}}, :conditional_operator=>"AND"}!)
322
+
323
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625004, 'key' => 'val2'}, time)
324
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625005, 'key' => 'val2'}, time)
325
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625006, 'key' => 'val2'}, time)
326
+ end
327
+
328
+ expect(select_all).to match_array [
329
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001, 'key' => 'val3'},
330
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002, 'key' => 'val3'},
331
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003, 'key' => 'val3'},
332
+ ]
333
+ end
334
+
335
+ it do
336
+ run_driver do |d|
337
+ expect(d.instance.log).not_to receive(:warn)
338
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001, 'key' => 'val3'}, time)
339
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002, 'key' => 'val3'}, time)
340
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003, 'key' => 'val3'}, time)
341
+ end
342
+
343
+ expect(select_all).to match_array [
344
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625001, 'key' => 'val3'},
345
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625002, 'key' => 'val3'},
346
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003, 'key' => 'val3'},
347
+ ]
348
+
349
+ run_driver(:expected => 'timestamp LE ${timestamp},key EQ "val"', :conditional_operator => 'OR') do |d|
350
+ expect(d.instance.log).to_not receive(:warn)
351
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625004, 'key' => 'val2'}, time)
352
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625005, 'key' => 'val2'}, time)
353
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625006, 'key' => 'val2'}, time)
354
+ end
355
+
356
+ expect(select_all).to match_array [
357
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625004, 'key' => 'val2'},
358
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625005, 'key' => 'val2'},
359
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625006, 'key' => 'val2'},
360
+ ]
361
+ end
362
+ }
363
+
364
+ context('key dose not exist') {
365
+ it do
366
+ run_driver do |d|
367
+ expect(d.instance.log).to receive(:warn)
368
+ .with(%!Hash Key 'id' does not exist in the record: {"timestamp"=>1409534625001, "key"=>"val"}!)
369
+ d.emit({'timestamp' => 1409534625001, 'key' => 'val'}, time)
370
+ end
371
+
372
+ expect(select_all).to match_array []
373
+ end
374
+
375
+ it do
376
+ run_driver do |d|
377
+ expect(d.instance.log).to receive(:warn)
378
+ .with(%!Timestamp Key 'timestamp' does not exist in the record: {"id"=>"12345678-1234-1234-1234-123456789001", "key"=>"val"}!)
379
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'key' => 'val'}, time)
380
+ end
381
+
382
+ expect(select_all).to match_array []
383
+ end
384
+ }
385
+
386
+ context('aggregate') {
387
+ it do
388
+ run_driver(:flush_interval => 3) do |d|
389
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625003}, time)
390
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625002}, time)
391
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001}, time)
392
+
393
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625003}, time)
394
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002}, time)
395
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625001}, time)
396
+
397
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003}, time)
398
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625002}, time)
399
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625001}, time)
400
+ end
401
+
402
+ expect(select_all).to match_array [
403
+ {"id"=>"12345678-1234-1234-1234-123456789001", "timestamp"=>1409534625003},
404
+ {"id"=>"12345678-1234-1234-1234-123456789002", "timestamp"=>1409534625003},
405
+ {"id"=>"12345678-1234-1234-1234-123456789003", "timestamp"=>1409534625003},
406
+ ]
407
+ end
408
+
409
+ it do
410
+ run_driver(:flush_interval => 3) do |d|
411
+ expect(d.instance).to receive(:put_record).with('id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625003)
412
+ expect(d.instance).to receive(:put_record).with('id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625003)
413
+ expect(d.instance).to receive(:put_record).with('id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003)
414
+
415
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625003}, time)
416
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625002}, time)
417
+ d.emit({'id' => '12345678-1234-1234-1234-123456789001', 'timestamp' => 1409534625001}, time)
418
+
419
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625003}, time)
420
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625002}, time)
421
+ d.emit({'id' => '12345678-1234-1234-1234-123456789002', 'timestamp' => 1409534625001}, time)
422
+
423
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625003}, time)
424
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625002}, time)
425
+ d.emit({'id' => '12345678-1234-1234-1234-123456789003', 'timestamp' => 1409534625001}, time)
426
+ end
427
+ end
428
+ }
429
+ }
430
+ end
@@ -0,0 +1,99 @@
1
+ require 'fluent/test'
2
+ require 'fluent/plugin/out_dynamodb_alt'
3
+ require 'hashie'
4
+ require 'securerandom'
5
+
6
+ DRIVER_DEFAULT_TAG = 'test.default'
7
+ TEST_TABLE_NAME = 'my_table-' + SecureRandom.uuid
8
+ TEST_AWS_KEY_ID = ENV['OUT_DYNAMODB_ALT_SPEC_AWS_KEY_ID'] || 'scott'
9
+ TEST_AWS_SEC_KEY = ENV['OUT_DYNAMODB_ALT_SPEC_AWS_SEC_KEY'] || 'tiger'
10
+ TEST_REGION = ENV['OUT_DYNAMODB_ALT_SPEC_REGION'] || 'us-west-1'
11
+
12
+ # Disable Test::Unit
13
+ module Test::Unit::RunCount; def run(*); end; end
14
+
15
+ RSpec.configure do |config|
16
+ config.before(:all) do
17
+ Fluent::Test.setup
18
+ end
19
+ end
20
+
21
+ def query(sql)
22
+ out = `ddbcli -k '#{TEST_AWS_KEY_ID}' -s '#{TEST_AWS_SEC_KEY}' -r #{TEST_REGION} -e "#{sql.gsub(/\n/, ' ')}"`
23
+ raise out unless $?.success?
24
+ return out
25
+ end
26
+
27
+ def select_all
28
+ JSON.parse(query("SELECT ALL * FROM #{TEST_TABLE_NAME}"))
29
+ end
30
+
31
+ def create_driver(tag = DRIVER_DEFAULT_TAG)
32
+ Fluent::Test::OutputTestDriver.new(Fluent::DynamodbAltOutput, tag)
33
+ end
34
+
35
+ def run_driver(options = {})
36
+ options = {
37
+ :flush_interval => 0
38
+ }.merge(options.dup)
39
+
40
+ additional_options = options.map {|key, value|
41
+ "#{key} #{value}"
42
+ }.join("\n")
43
+
44
+ fluentd_conf = <<-EOS
45
+ type dynamodb_alt
46
+ aws_key_id #{TEST_AWS_KEY_ID}
47
+ aws_sec_key #{TEST_AWS_SEC_KEY}
48
+ region #{TEST_REGION}
49
+ table_name #{TEST_TABLE_NAME}
50
+ timestamp_key timestamp
51
+ #{additional_options}
52
+ EOS
53
+
54
+ tag = options[:tag] || DRIVER_DEFAULT_TAG
55
+ driver = create_driver(tag)
56
+
57
+ driver.configure(fluentd_conf)
58
+
59
+ driver.run do
60
+ yield(driver)
61
+ end
62
+ end
63
+
64
+ def create_table(attrs)
65
+ query("CREATE TABLE #{TEST_TABLE_NAME} #{attrs}")
66
+
67
+ loop do
68
+ status = query("SHOW TABLE STATUS LIKE '#{TEST_TABLE_NAME}'")
69
+ status = JSON.parse(status)
70
+ break if (not status[TEST_TABLE_NAME] or status[TEST_TABLE_NAME]['TableStatus'] == 'ACTIVE')
71
+ end
72
+ end
73
+
74
+ def drop_table
75
+ table_is_exist = proc do
76
+ tables = query("SHOW TABLES LIKE '#{TEST_TABLE_NAME}'")
77
+ tables = JSON.parse(tables)
78
+ not tables.empty?
79
+ end
80
+
81
+ return unless table_is_exist.call
82
+
83
+ query("DROP TABLE #{TEST_TABLE_NAME}")
84
+
85
+ loop do
86
+ tables = query("SHOW TABLES LIKE '#{TEST_TABLE_NAME}'")
87
+ tables = JSON.parse(tables)
88
+ break unless table_is_exist.call
89
+ end
90
+ end
91
+
92
+ def truncate_table
93
+ query("DELETE ALL FROM #{TEST_TABLE_NAME}")
94
+
95
+ loop do
96
+ count = query("SELECT ALL COUNT(*) FROM #{TEST_TABLE_NAME}").strip
97
+ break if count == '0'
98
+ end
99
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-dynamodb-alt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Genki Sugawara
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fluentd
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-core
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.0.rc15
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 2.0.0.rc15
41
+ - !ruby/object:Gem::Dependency
42
+ name: parallel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: 3.0.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 3.0.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: hashie
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: ddbcli
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Fluent plugin to output to DynamoDB.
126
+ email:
127
+ - sgwr_dts@yahoo.co.jp
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - .gitignore
133
+ - .rspec
134
+ - .travis.yml
135
+ - Gemfile
136
+ - LICENSE.txt
137
+ - README.md
138
+ - Rakefile
139
+ - fluent-plugin-dynamodb-alt.gemspec
140
+ - lib/fluent/plugin/out_dynamodb_alt.rb
141
+ - spec/out_dynamodb_alt_spec.rb
142
+ - spec/spec_helper.rb
143
+ homepage: https://github.com/winebarrel/fluent-plugin-dynamodb-alt
144
+ licenses:
145
+ - MIT
146
+ metadata: {}
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - '>='
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubyforge_project:
163
+ rubygems_version: 2.0.14
164
+ signing_key:
165
+ specification_version: 4
166
+ summary: Fluent plugin to output to DynamoDB.
167
+ test_files:
168
+ - spec/out_dynamodb_alt_spec.rb
169
+ - spec/spec_helper.rb