fluent-plugin-mongo 1.2.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ae5027563d8db12e0cc4036dec85ca15dd6d5d19
4
- data.tar.gz: 93fde5d241a4b22bc89a3a13bee779e2ed06faea
2
+ SHA256:
3
+ metadata.gz: baad354dcd89624e9e6ed224718d569de6a37329e7a0339d2b8f41011ba0b970
4
+ data.tar.gz: 15c2c50509c70a325fe6c053a09062e5701e61003e550d316ffa491e9cc99b75
5
5
  SHA512:
6
- metadata.gz: fa962dc7b06fa64e3ca1ee33c20294327f09f21adfc03e89d43231adfe167608711dbe617becedc1e2ea3e353337126b4cbae8663ca560372a82d3b0a3af3d15
7
- data.tar.gz: ba9202858bbfa8259fed50d3d2ecc879d612f18973fd1b5cedc66193e8747c75218a9cfceb225f0164e121f552e9c88727ee7df1c996f16d1dae282d33ff9517
6
+ metadata.gz: 59aeae091430fae5bb089245f24d85e8e204a11f7d886f0b53b7cde6af054c0e5b9c2fc94f17f2b0dfdc7fb79c562be7112adfafae71583e8fcf8b18aa8766ab
7
+ data.tar.gz: 242c5d3d7a58f0be87514909749b066942118d90c24e9990f719eff5956919556a86ae24c427c8d21e55b70b39d03b8796d10c4493d00d7c41a7be635009f738
@@ -1,9 +1,8 @@
1
1
  rvm:
2
- - 2.1
3
- - 2.2.4
4
- - 2.3.1
5
- - 2.4.3
6
- - 2.5.0
2
+ - 2.4.10
3
+ - 2.5
4
+ - 2.6
5
+ - 2.7
7
6
  - ruby-head
8
7
 
9
8
  gemfile:
@@ -12,11 +11,6 @@ gemfile:
12
11
  services:
13
12
  - mongodb
14
13
 
15
- before_install:
16
- - gem update --system
17
- before_script:
18
- - git submodule update -i
19
-
20
14
  script: bundle exec rake test
21
15
 
22
16
  matrix:
data/ChangeLog CHANGED
@@ -1,3 +1,24 @@
1
+ Release 1.4.1 - 2020/08/21
2
+
3
+ * out_mongo: Add expire_after parameter
4
+
5
+ Release 1.4.0 - 2020/04/28
6
+
7
+ * out_mongo: Add date_keys parameter to support MongoDB Date object in record fields.
8
+
9
+ Release 1.3.0 - 2019/04/24
10
+
11
+ * out_mongo: Support auth_mech parameter to allow other authentication
12
+ * in_mongo_tail: Fix replicat set issue
13
+
14
+ Release 1.2.2 - 2019/04/01
15
+
16
+ * out_mongo: Don't handle database placholders when specifying connection_string parameter
17
+
18
+ Release 1.2.1 - 2018/12/18
19
+
20
+ * out_mongo_replset: Fix internal signature mismatch
21
+
1
22
  Release 1.2.0 - 2018/11/29
2
23
 
3
24
  * out_mongo: Support placeholder in database parameter
@@ -53,6 +53,12 @@ Use _mongo_ type in match.
53
53
  capped
54
54
  capped_size 100m
55
55
 
56
+ # Specify date fields in record to use MongoDB's Date object (Optional) default: nil
57
+ # Supported data types are String/Integer/Float/Fuentd EventTime.
58
+ # For Integer type, milliseconds epoch and seconds epoch are supported.
59
+ # eg: updated_at: "2020-02-01T08:22:23.780Z" or updated_at: 1580546457010
60
+ date_keys updated_at
61
+
56
62
  # Other buffer configurations here
57
63
  </match>
58
64
 
@@ -223,7 +229,7 @@ BSON records which include '.' or start with '$' are invalid and they will be st
223
229
  ...
224
230
  # replace '.' in keys with '__dot__'
225
231
  replace_dot_in_key_with __dot__
226
-
232
+
227
233
  # replace '$' in keys with '__dollar__'
228
234
  # Note: This replaces '$' only on first character
229
235
  replace_dollar_in_key_with __dollar__
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0
1
+ 1.4.1
@@ -10,14 +10,13 @@ Gem::Specification.new do |gem|
10
10
  gem.version = File.read("VERSION").strip
11
11
  gem.authors = ["Masahiro Nakagawa"]
12
12
  gem.email = "repeatedly@gmail.com"
13
- gem.has_rdoc = false
14
13
  #gem.platform = Gem::Platform::RUBY
15
14
  gem.files = `git ls-files`.split("\n")
16
15
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
16
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
17
  gem.require_paths = ['lib']
19
18
 
20
- gem.add_dependency "fluentd", [">= 0.14.12", "< 2"]
19
+ gem.add_dependency "fluentd", [">= 0.14.22", "< 2"]
21
20
  gem.add_runtime_dependency "mongo", "~> 2.6.0"
22
21
  gem.add_development_dependency "rake", ">= 0.9.2"
23
22
  gem.add_development_dependency "simplecov", ">= 0.5.4"
@@ -118,10 +118,10 @@ module Fluent::Plugin
118
118
  private
119
119
 
120
120
  def client
121
- @client_options[:database] = @database
121
+ @client_options[:database] = @database if @database
122
122
  @client_options[:user] = @user if @user
123
123
  @client_options[:password] = @password if @password
124
- Mongo::Client.new(["#{node_string}"], @client_options)
124
+ Mongo::Client.new(node_string, @client_options)
125
125
  end
126
126
 
127
127
  def get_collection
@@ -133,7 +133,7 @@ module Fluent::Plugin
133
133
  def node_string
134
134
  case
135
135
  when @database
136
- "#{@host}:#{@port}"
136
+ ["#{@host}:#{@port}"]
137
137
  when @url
138
138
  @url
139
139
  end
@@ -8,23 +8,25 @@ module Fluent
8
8
  config_param :password, :string, default: nil, secret: true
9
9
  desc "MongoDB authentication database"
10
10
  config_param :auth_source, :string, default: nil
11
+ desc "MongoDB authentication mechanism"
12
+ config_param :auth_mech, :string, default: nil
11
13
  }
12
14
  end
13
15
  end
14
16
 
15
17
  module MongoAuth
16
18
  def authenticate(client)
17
- unless @user.nil? || @password.nil?
18
- begin
19
- if @auth_source.nil?
20
- client = client.with(user: @user, password: @password)
21
- else
22
- client = client.with(user: @user, password: @password, auth_source: @auth_source)
23
- end
24
- rescue Mongo::Auth::Unauthorized => e
25
- log.fatal e
26
- exit!
19
+ begin
20
+ if [@user, @password, @auth_source].all?
21
+ client = client.with(user: @user, password: @password, auth_source: @auth_source)
22
+ elsif [@user, @password].all?
23
+ client = client.with(user: @user, password: @password)
24
+ elsif [@user, @auth_source, @auth_mech].all?
25
+ client = client.with(user: @user, auth_source: @auth_source, auth_mech: @auth_mech.to_sym)
27
26
  end
27
+ rescue Mongo::Auth::Unauthorized => e
28
+ log.fatal e
29
+ exit!
28
30
  end
29
31
  client
30
32
  end
@@ -38,6 +38,10 @@ module Fluent::Plugin
38
38
  desc "Replace dollar with specified string"
39
39
  config_param :replace_dollar_in_key_with, :string, default: nil
40
40
 
41
+ # Additional date field to be used to Date object
42
+ desc "Specify keys to use MongoDB's Date. Supported value types are Integer/Float/EventTime/String"
43
+ config_param :date_keys, :array, default: nil
44
+
41
45
  # tag mapping mode
42
46
  desc "Use tag_mapped mode"
43
47
  config_param :tag_mapped, :bool, default: false,
@@ -45,6 +49,9 @@ module Fluent::Plugin
45
49
  desc "Remove tag prefix"
46
50
  config_param :remove_tag_prefix, :string, default: nil,
47
51
  deprecated: "use @label instead for event routing."
52
+ # expire indexes
53
+ desc "Specify expire after seconds"
54
+ config_param :expire_after, :time, default: 0
48
55
 
49
56
  # SSL connection
50
57
  config_param :ssl, :bool, default: false
@@ -54,6 +61,7 @@ module Fluent::Plugin
54
61
  config_param :ssl_verify, :bool, default: false
55
62
  config_param :ssl_ca_cert, :string, default: nil
56
63
 
64
+
57
65
  config_section :buffer do
58
66
  config_set_default :@type, DEFAULT_BUFFER_TYPE
59
67
  config_set_default :chunk_keys, ['tag']
@@ -108,12 +116,12 @@ module Fluent::Plugin
108
116
 
109
117
  super
110
118
 
111
- if @connection_string.nil? && @database.nil?
112
- raise Fluent::ConfigError, "connection_string or database parameter is required"
119
+ if @auth_mech && !Mongo::Auth::SOURCES.has_key?(@auth_mech.to_sym)
120
+ raise Fluent::ConfigError, Mongo::Auth::InvalidMechanism.new(@auth_mech.to_sym)
113
121
  end
114
122
 
115
- unless @ignore_invalid_record
116
- log.warn "Since v0.8, invalid record detection will be removed because mongo driver v2.x and API spec don't provide it. You may lose invalid records, so you should not send such records to mongo plugin"
123
+ if @connection_string.nil? && @database.nil?
124
+ raise Fluent::ConfigError, "connection_string or database parameter is required"
117
125
  end
118
126
 
119
127
  if conf.has_key?('tag_mapped')
@@ -172,8 +180,9 @@ module Fluent::Plugin
172
180
  end
173
181
 
174
182
  def write(chunk)
175
- collection_name = extract_placeholders(@collection, chunk.metadata)
176
- database_name = extract_placeholders(@database, chunk.metadata)
183
+ collection_name = extract_placeholders(@collection, chunk)
184
+ # In connection_string case, we shouldn't handle extract_placeholers for @database.
185
+ database_name = extract_placeholders(@database, chunk) unless @connection_string
177
186
  operate(database_name, format_collection_name(collection_name), collect_records(chunk))
178
187
  end
179
188
 
@@ -193,11 +202,40 @@ module Fluent::Plugin
193
202
  def collect_records(chunk)
194
203
  records = []
195
204
  time_key = @inject_config.time_key if @inject_config
205
+ date_keys = @date_keys
206
+
196
207
  tag = chunk.metadata.tag
197
208
  chunk.msgpack_each {|time, record|
198
209
  record = inject_values_to_record(tag, time, record)
199
210
  # MongoDB uses BSON's Date for time.
200
211
  record[time_key] = Time.at(time || record[time_key]) if time_key
212
+
213
+ if date_keys
214
+ date_keys.each { |date_key|
215
+ begin
216
+ date_value = record[date_key]
217
+ case date_value
218
+ when Fluent::EventTime
219
+ record[date_key] = date_value.to_time
220
+ when Integer
221
+ record[date_key] = if date_value > 9999999999
222
+ # epoch with milliseconds: e.g. javascript
223
+ Time.at(date_value / 1000.0)
224
+ else
225
+ # epoch with seconds: e.g. ruby
226
+ Time.at(date_value)
227
+ end
228
+ when Float
229
+ record[date_key] = Time.at(date_value)
230
+ else
231
+ record[date_key] = Time.parse(date_value)
232
+ end
233
+ rescue ArgumentError
234
+ log.warn "Failed to parse '#{date_key}' field. Expected date types are Integer/Float/String/EventTime: #{record[date_key]}"
235
+ record[date_key] = nil
236
+ end
237
+ }
238
+ end
201
239
  records << record
202
240
  }
203
241
  records
@@ -235,6 +273,13 @@ module Fluent::Plugin
235
273
  unless collection_exists?(name)
236
274
  log.trace "Create collection #{name} with options #{options}"
237
275
  @client[name, options].create
276
+ if @expire_after > 0 && @inject_config
277
+ log.trace "Create expiring index with key: \"#{@inject_config.time_key}\" and seconds: \"#{@expire_after}\""
278
+ @client[name].indexes.create_one(
279
+ {"#{@inject_config.time_key}": 1},
280
+ expire_after: @expire_after
281
+ )
282
+ end
238
283
  end
239
284
  @collections[name] = true
240
285
  @client[name]
@@ -35,9 +35,9 @@ module Fluent::Plugin
35
35
 
36
36
  private
37
37
 
38
- def operate(client, records)
38
+ def operate(database, client, records)
39
39
  rescue_connection_failure do
40
- super(client, records)
40
+ super(database, client, records)
41
41
  end
42
42
  end
43
43
 
@@ -100,7 +100,7 @@ class MongoTailInputTest < Test::Unit::TestCase
100
100
  end
101
101
  events = d.events
102
102
  assert_equal "input.mongo", events[0][0]
103
- assert_equal event_time(@time.to_s), events[0][1]
103
+ assert events[0][1].is_a?(Fluent::EventTime)
104
104
  assert_equal "test", events[0][2]["message"]
105
105
  end
106
106
 
@@ -114,7 +114,7 @@ class MongoTailInputTest < Test::Unit::TestCase
114
114
  time_key time
115
115
  ])
116
116
  d.run(expect_records: 1, timeout: 5) do
117
- @client[collection_name].insert_one({message: "test", tag: "user.defined", time: Fluent::Engine.now})
117
+ @client[collection_name].insert_one({message: "test", tag: "user.defined", time: Time.at(Fluent::Engine.now)})
118
118
  end
119
119
  events = d.events
120
120
  assert_equal "user.defined", events[0][0]
@@ -138,7 +138,7 @@ class MongoTailInputTest < Test::Unit::TestCase
138
138
  events = d.events
139
139
  assert_equal 1, events.size
140
140
  assert_equal "input.mongo.last_id", events[0][0]
141
- assert_equal event_time(@time.to_s), events[0][1]
141
+ assert events[0][1].is_a?(Fluent::EventTime)
142
142
  assert_equal "can obtain", events[0][2]["message"]
143
143
  end
144
144
  end
@@ -92,6 +92,22 @@ class MongoOutputTest < ::Test::Unit::TestCase
92
92
  end
93
93
  end
94
94
 
95
+ def test_configure_auth_mechanism
96
+ Mongo::Auth::SOURCES.each do |key, value|
97
+ conf = default_config + %[
98
+ auth_mech #{key}
99
+ ]
100
+ d = create_driver(conf)
101
+ assert_equal(key.to_s, d.instance.auth_mech)
102
+ end
103
+ assert_raise Fluent::ConfigError do
104
+ conf = default_config + %[
105
+ auth_mech invalid
106
+ ]
107
+ d = create_driver(conf)
108
+ end
109
+ end
110
+
95
111
  def test_configure_with_ssl
96
112
  conf = default_config + %[
97
113
  ssl true
@@ -164,6 +180,10 @@ class MongoOutputTest < ::Test::Unit::TestCase
164
180
  @client[collection].find.to_a.map {|e| e.delete('_id'); e}
165
181
  end
166
182
 
183
+ def get_indexes(collection = collection_name)
184
+ @client[collection].indexes
185
+ end
186
+
167
187
  def emit_documents(d)
168
188
  time = event_time("2011-01-02 13:14:15 UTC")
169
189
  d.feed(time, {'a' => 1})
@@ -183,6 +203,52 @@ class MongoOutputTest < ::Test::Unit::TestCase
183
203
  assert_equal(expected, actual_documents)
184
204
  end
185
205
 
206
+ def test_write_with_connection_string
207
+ d = create_driver(%[
208
+ @type mongo
209
+ connection_string mongodb://localhost:#{port}/#{database_name}
210
+ collection #{collection_name}
211
+ capped
212
+ capped_size 100
213
+ ])
214
+ assert_equal("mongodb://localhost:#{port}/#{database_name}", d.instance.connection_string)
215
+ assert_nil d.instance.database
216
+
217
+ d.run(default_tag: 'test') do
218
+ emit_documents(d)
219
+ end
220
+ actual_documents = get_documents
221
+ time = event_time("2011-01-02 13:14:15 UTC")
222
+ expected = [{'a' => 1, d.instance.inject_config.time_key => Time.at(time).localtime},
223
+ {'a' => 2, d.instance.inject_config.time_key => Time.at(time).localtime}]
224
+ assert_equal(expected, actual_documents)
225
+ end
226
+
227
+ def test_write_with_expire_index
228
+ d = create_driver(%[
229
+ @type mongo
230
+ connection_string mongodb://localhost:#{port}/#{database_name}
231
+ collection #{collection_name}
232
+ capped
233
+ capped_size 100
234
+ expire_after 120
235
+ ])
236
+ assert_equal("mongodb://localhost:#{port}/#{database_name}", d.instance.connection_string)
237
+ assert_nil d.instance.database
238
+ d.run(default_tag: 'test') do
239
+ emit_documents(d)
240
+ end
241
+ actual_documents = get_documents
242
+ time = event_time("2011-01-02 13:14:15 UTC")
243
+ expected = [{'a' => 1, d.instance.inject_config.time_key => Time.at(time).localtime},
244
+ {'a' => 2, d.instance.inject_config.time_key => Time.at(time).localtime}]
245
+ assert_equal(expected, actual_documents)
246
+
247
+ indexes = get_indexes()
248
+ expire_after_hash = indexes.map {|e| e.select{|k, v| k == "expireAfterSeconds"} }.reject{|e| e.empty?}.first
249
+ assert_equal({"expireAfterSeconds"=>120.0}, expire_after_hash)
250
+ end
251
+
186
252
  class WriteWithCollectionPlaceholder < self
187
253
  def setup
188
254
  @tag = 'custom'
@@ -278,7 +344,7 @@ class MongoOutputTest < ::Test::Unit::TestCase
278
344
  "$foo$bar" => "baz"
279
345
  }
280
346
  ],
281
- })
347
+ })
282
348
  end
283
349
 
284
350
  documents = get_documents
@@ -351,4 +417,56 @@ class MongoOutputTest < ::Test::Unit::TestCase
351
417
  assert authenticate(@client)
352
418
  end
353
419
  end
420
+
421
+ sub_test_case 'date_keys' do
422
+ setup do
423
+ @updated_at_str = "2020-02-01T08:22:23.780Z"
424
+ @updated_at_t = Time.parse(@updated_at_str)
425
+ end
426
+
427
+ def emit_date_documents(d)
428
+ time = event_time("2011-01-02 13:14:15 UTC")
429
+ d.feed(time, {'a' => 1, updated_at: @updated_at_str})
430
+ d.feed(time, {'a' => 2, updated_at: @updated_at_t.to_f})
431
+ d.feed(time, {'a' => 3, updated_at: @updated_at_t.to_i})
432
+ time
433
+ end
434
+
435
+ def emit_invalid_date_documents(d)
436
+ time = event_time("2011-01-02 13:14:15 UTC")
437
+ d.feed(time, {'a' => 1, updated_at: "Invalid Date String"})
438
+ time
439
+ end
440
+
441
+ def test_write_with_date_keys
442
+ d = create_driver(default_config + %[
443
+ date_keys updated_at
444
+ time_key created_at
445
+ ])
446
+
447
+ d.run(default_tag: 'test') do
448
+ emit_date_documents(d)
449
+ end
450
+
451
+ actual_documents = get_documents
452
+ date_key = d.instance.date_keys.first
453
+ actual_documents.each_with_index { |doc, i|
454
+ assert_equal(i + 1, doc['a'])
455
+ assert doc[date_key].is_a?(Time)
456
+ }
457
+ end
458
+
459
+ def test_write_with_parsed_date_key_invalid_string
460
+ d = create_driver(default_config + %[
461
+ date_keys updated_at
462
+ time_key created_at
463
+ ])
464
+
465
+ d.run(default_tag: 'test') do
466
+ emit_invalid_date_documents(d)
467
+ end
468
+ actual_documents = get_documents
469
+ assert_nil actual_documents.first['updated_at']
470
+ end
471
+ end
354
472
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-mongo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahiro Nakagawa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-29 00:00:00.000000000 Z
11
+ date: 2020-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.14.12
19
+ version: 0.14.22
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '2'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 0.14.12
29
+ version: 0.14.22
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '2'
@@ -159,8 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
159
  - !ruby/object:Gem::Version
160
160
  version: '0'
161
161
  requirements: []
162
- rubyforge_project:
163
- rubygems_version: 2.6.14.1
162
+ rubygems_version: 3.0.3
164
163
  signing_key:
165
164
  specification_version: 4
166
165
  summary: MongoDB plugin for Fluentd