logstash-input-rethinker 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5026e50d97fea101dc31c6023a563c84456b492a
4
+ data.tar.gz: 3924c222de9b343df2405c7b3c9ec0b35c2a79fc
5
+ SHA512:
6
+ metadata.gz: 71158b58a616f62f6aa7487977159ce5c67407876a6e2f3d422b4a1ee36d8b9050f9c849e12b437f6d01d05c5a73bb9031894b235c11867d2f0ca0684d2c5955
7
+ data.tar.gz: 4e540e573ef48cd56876fc510be71ad332a77759e13442b472594aa33f85f17d59ab0594592fda1b89e62713f05a1cbc5506dc663864223f611eb2f60219c55a
@@ -0,0 +1,2 @@
1
+ ## 0.1.0
2
+ - Plugin created with the logstash plugin generator
@@ -0,0 +1,10 @@
1
+ The following is a list of people who have contributed ideas, code, bug
2
+ reports, or in general have helped logstash along its way.
3
+
4
+ Contributors:
5
+ * Wayann - wayann@nrg.re
6
+
7
+ Note: If you've sent us patches, bug reports, or otherwise contributed to
8
+ Logstash, and you aren't on the list above and want to be, please let us know
9
+ and we'll make sure you're here. Contributions from folks like you are what make
10
+ open source awesome.
@@ -0,0 +1,2 @@
1
+ # logstash-input-rethinker
2
+ Example input plugin. This should help bootstrap your effort to write your own input plugin!
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ gem "eventmachine"
3
+ gem "rethinkdb"
4
+ gemspec
5
+
data/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Licensed under the Apache License, Version 2.0 (the "License");
2
+ you may not use this file except in compliance with the License.
3
+ You may obtain a copy of the License at
4
+
5
+ http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
@@ -0,0 +1,50 @@
1
+ # Rethinker Logstash input plugin
2
+
3
+ This is a plugin for [Logstash](https://github.com/elasticsearch/logstash).
4
+
5
+ **Currently this is BETA software. It contains some known limitations (see below)**
6
+
7
+ ## Using the plugin
8
+
9
+ You'll need to use this with the Logstash 1.5 or higher, which you can [download here](https://www.elastic.co/downloads/logstash).
10
+
11
+ - Install the plugin from the Logstash home directory (you will need jruby to bundle install)
12
+ ```sh
13
+ $ git clone https://github.com/wayann/logstash-input-rethinker
14
+ $ cd logstash-input-rethinker
15
+ $ bundle install
16
+ $ gem build logstash-input-rethinker.gemspec
17
+ $ bin/plugin install --no-verify logstash-input-rethinker-0.1.0.gem
18
+ ```
19
+
20
+ - Now you can test the plugin using the stdout output:
21
+
22
+ ```sh
23
+ $ bin/logstash -f rethinker.conf
24
+ ```
25
+
26
+ This will immediately watch the tables `test.foo` and `db2.baz`, and it will also watch the databases `db1` and `db2` for new or dropped tables and watch or unwatch those tables appropriately. Since `backfill` is `true`, it will automatically send events for the documents that already exist in those tables during initialization.
27
+
28
+ ### Format of the events:
29
+
30
+ The events are encoded with the "json_lines" codec, which puts compressed json documents one event per line
31
+
32
+ Fields:
33
+
34
+ - **db**: the database that emitted the event
35
+ - **table**: the table that emitted the event
36
+ - **old_val**: the old value of the document (see [changefeeds](http://rethinkdb.com/docs/changefeeds/ruby/))
37
+ - **new_val**: the new value of the document
38
+ - **@timestamp**: timestamp added by logstash (so may not correspond to the rethinkdb server time the change was emitted)
39
+ - **@version**: version number added by logstash (always 1)
40
+
41
+ ## Known limitations
42
+
43
+ There are two limitations that should be known by anyone using this in production systems:
44
+
45
+ 1. Until RethinkDB supports [resuming changefeeds](https://github.com/rethinkdb/rethinkdb/issues/3471), this plugin cannot guarantee that no changes are missed if a connection to the database is dropped. Again, once that functionality is implemented, this plugin will be modified to provide reliable "at least once" semantics for changes (meaning once it reconnects, it can catch back up and send any changes that it missed).
46
+ 2. Documents that are deleted in RethinkDB while the LogStash plugin is disconnected will not be synchronized. This is true even if `backfill` is enabled. This limitation is a consequence of LogStash operating on a document-by-document basis.
47
+
48
+ ## License
49
+
50
+ Apache 2.0
@@ -0,0 +1,279 @@
1
+ require "logstash/inputs/base"
2
+ require "logstash/namespace"
3
+ require "eventmachine"
4
+ require "rethinkdb"
5
+ require "stud/interval"
6
+ require "socket"
7
+
8
+ class LogStash::Inputs::Rethinker < LogStash::Inputs::Base
9
+ config_name "rethinker"
10
+
11
+ # If undefined, Logstash will complain, even if codec is unused.
12
+ default :codec, "json_lines"
13
+
14
+ include RethinkDB::Shortcuts
15
+
16
+ # Hostname of RethinkDB server
17
+ config :host, :validate => :string, :default => "localhost"
18
+ # Driver connection port of RethinkDB server
19
+ config :port, :validate => :number, :default => 28015
20
+ # Auth key of RethinkDB server (don't provide if nil)
21
+ config :auth_key, :validate => :string, :default => ""
22
+ # Time period to squash changefeeds on. Defaults to no squashing.
23
+ config :squash, :default => true
24
+ # Which tables to watch for changes
25
+ config :watch_tables, :validate => :array, :default => ["test.foo", "test.bar"]
26
+ # Which databases to watch for changes. Tables added or removed from
27
+ # these databases will be watched or unwatched accordingly
28
+ config :watch_dbs, :validate => :array, :default => ["test"]
29
+ # Whether to backfill documents from the dbs and tables when
30
+ # (re)connecting to RethinkDB. This ensures all documents in the
31
+ # RethinkDB tables will be sent over logstash, but it may cause a
32
+ # lot of traffic with very large tables and/or unstable connections.
33
+ config :backfill, :default => true
34
+ #ssl support
35
+ config :ca_certs, :default => nil
36
+ # Credentials as of RethinkDB v2.3.x
37
+ config :user, :validate => :string, :default => "admin"
38
+ config :password, :validate => :string, :default => ""
39
+
40
+ public
41
+ def register
42
+ # {db => {table => QueryHandle}}
43
+ @table_feeds = Hash.new { |hsh, key| hsh[key] = {} }
44
+ # {db => QueryHandle}
45
+ @db_feeds = {}
46
+ @queue = nil
47
+ @backfill = @backfill && @backfill != 'false'
48
+ @squash = @squash && @squash != 'false'
49
+ end # def register
50
+
51
+ def run(queue)
52
+ @queue = queue
53
+ if @ca_certs
54
+ ssl = { :ca_certs => @ca_certs }
55
+ else
56
+ ssl = nil
57
+ end
58
+
59
+ unless @auth_key == ""
60
+ @conn = r.connect(
61
+ :host => @host,
62
+ :port => @port,
63
+ :auth_key => @auth_key,
64
+ :ssl => ssl
65
+ )
66
+ else
67
+ @conn = r.connect(
68
+ :host => @host,
69
+ :port => @port,
70
+ :user => @user,
71
+ :password => @password,
72
+ :ssl => ssl
73
+ )
74
+ end
75
+
76
+ EM.run do
77
+ @logger.info "Eventmachine loop started"
78
+ @watch_dbs.uniq.each do |db|
79
+ create_db_feed(db, DBHandler.new(db, self))
80
+ end
81
+ @watch_tables.uniq.each do |db_table|
82
+ db, table = db_table.split '.'
83
+ db, table = "test", db if table.nil?
84
+ update_db_tables(nil, {'db' => db, 'name' => table})
85
+ end
86
+ end
87
+ end # def run
88
+
89
+ def send(db, table, old_val, new_val)
90
+ event = LogStash::Event.new(
91
+ "db" => db,
92
+ "table" => table,
93
+ "old_val" => old_val,
94
+ "new_val" => new_val
95
+ )
96
+ decorate(event)
97
+ @queue << event
98
+ end
99
+
100
+ def update_db_tables(old_val, new_val)
101
+ unless new_val.nil?
102
+ handler = TableHandler.new(new_val['db'], new_val['name'], self)
103
+ create_table_feed(new_val['db'], new_val['name'], handler)
104
+ end
105
+ unless old_val.nil?
106
+ unregister_table(old_val['db'], old_val['name'], nil)
107
+ end
108
+ end
109
+
110
+ def register_table(db, table, qhandle)
111
+ # Add a table feed to the registry
112
+ unless @table_feeds.has_key?(db) &&
113
+ @table_feeds[db].has_key?(table)
114
+ @logger.info("Watching table #{db}.#{table}")
115
+ @table_feeds[db][table] = qhandle
116
+ else
117
+ qhandle.close
118
+ end
119
+ end
120
+
121
+ def unregister_table(db, table, qhandle)
122
+ # Remove a table from the registry.
123
+ if @table_feeds.has_key?(db) &&
124
+ @table_feeds[db].has_key?(table) &&
125
+ # If a duplicate feed comes in for the same table and needs to
126
+ # be unregistered, we need to check if the handle is the same
127
+ (qhandle.nil? || @table_feeds[db][table].equal?(qhandle))
128
+ @logger.info("Unregistering table #{db}.#{table}")
129
+ @table_feeds[db].delete(table).close
130
+ end
131
+ end
132
+
133
+ def register_db(db, qhandle)
134
+ # Add a db to the registry to watch it for updates to which tables
135
+ # are listed in it.
136
+ unless @db_feeds.has_key? db
137
+ @db_feeds[db] = qhandle
138
+ @logger.info "Feed for db '#{db}' registered"
139
+ end
140
+ end
141
+
142
+ def unregister_db(db)
143
+ # Remove a db from the registry, close all of its feeds, and
144
+ # remove its entry in @db_admin_tables
145
+ if @table_feeds.has_key?(db)
146
+ @logger.info("Unregistering feed for db '#{db}'")
147
+ @table_feeds[db].keys.each do |table|
148
+ unregister_table(db, table, nil)
149
+ end
150
+ @table_feeds.delete(db)
151
+ @db_feeds.delete(db).close
152
+ end
153
+ end
154
+
155
+ def create_db_feed(db, handler)
156
+ r.db('rethinkdb').
157
+ table('table_status').
158
+ changes(:include_initial => @backfill,
159
+ :squash => @squash,
160
+ :include_states => true).
161
+ # The filter and pluck are after .changes due to bug #5241. When
162
+ # that's solved they can be moved before .changes and can be
163
+ # simplified since they won't have to operate over both new_val
164
+ # and old_val
165
+ filter{|row| r([row['old_val']['db'], row['new_val']['db']]).contains(db).
166
+ and(row['status']['all_replicas_ready'])}.
167
+ pluck({:new_val => ['db', 'name'], :old_val => ['db', 'name']}).
168
+ em_run(@conn, handler)
169
+ end
170
+
171
+ def create_table_feed(db, table, handler)
172
+ options = {
173
+ :time_format => 'raw',
174
+ :binary_format => 'raw',
175
+ }
176
+ r.db(db).
177
+ table(table).
178
+ changes(:include_initial => @backfill,
179
+ :squash => @squash,
180
+ :include_states => true).
181
+ em_run(@conn, options, handler)
182
+ end
183
+
184
+ def teardown
185
+ # Goes through all existing handles and closes them, then clears
186
+ # out the registry and closes the connection to RethinkDB
187
+ @table_feeds.values.each do |tables|
188
+ tables.values.each { |qhandle| qhandle.close }
189
+ end
190
+ @db_feeds.values.each { |qhandle| qhandle.close }
191
+ @table_feeds.clear
192
+ @db_feeds.clear
193
+ @conn.close
194
+ EM.stop
195
+ @queue = nil
196
+ end
197
+
198
+ def stop
199
+ # nothing to do in this case so it is not necessary to define stop
200
+ # examples of common "stop" tasks:
201
+ # * close sockets (unblocking blocking reads/accepts)
202
+ # * cleanup temporary files
203
+ # * terminate spawned threads
204
+ end
205
+ end # class LogStash::Inputs::Rethinker
206
+
207
+ # This handles feeds listening for changes to documents in a table
208
+ class TableHandler < RethinkDB::Handler
209
+ attr_accessor :db
210
+ attr_accessor :table
211
+ def initialize(db, table, plugin)
212
+ super()
213
+ @db = db
214
+ @table = table
215
+ @plugin = plugin
216
+ end
217
+
218
+ def on_initial_val(val)
219
+ @plugin.send(@db, @table, nil, val)
220
+ end
221
+
222
+ def on_change(old_val, new_val)
223
+ @plugin.send(@db, @table, old_val, new_val)
224
+ end
225
+
226
+ def on_open(qhandle)
227
+ @plugin.register_table(@db, @table, qhandle)
228
+ end
229
+
230
+ def on_close(qhandle)
231
+ @plugin.unregister_table(@db, @table, qhandle)
232
+ end
233
+
234
+ def on_error(err, qhandle)
235
+ @plugin.logger.error(err.to_s)
236
+ @plugin.unregister_table(@db, @table, qhandle)
237
+ end
238
+
239
+ def on_change_error(err_str)
240
+ @plugin.logger.warn(err.to_s)
241
+ end
242
+ end
243
+
244
+ # Handler for changes to the tables in a database
245
+ class DBHandler < RethinkDB::Handler
246
+
247
+ attr_accessor :db
248
+
249
+ def initialize(db, plugin)
250
+ super()
251
+ @db = db
252
+ @plugin = plugin
253
+ end
254
+
255
+ def on_open(qhandle)
256
+ @plugin.register_db(@db, qhandle)
257
+ end
258
+
259
+ def on_close(qhandle)
260
+ @plugin.unregister_db(@db)
261
+ end
262
+
263
+ def on_error(err, qhandle)
264
+ @plugin.logger.error(err.to_s)
265
+ @plugin.unregister_db(@db)
266
+ end
267
+
268
+ def on_change_error(err_str)
269
+ @plugin.logger.warn(err_str)
270
+ end
271
+
272
+ def on_initial_val(val)
273
+ @plugin.update_db_tables(nil, val)
274
+ end
275
+
276
+ def on_change(old_val, new_val)
277
+ @plugin.update_db_tables(old_val, new_val)
278
+ end
279
+ end
@@ -0,0 +1,27 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-input-rethinker'
3
+ s.version = '0.1.0'
4
+ s.licenses = ['Apache License (2.0)']
5
+ s.summary = 'RethinkDB updated logstash plugin'
6
+ s.description = 'Copy paste of old RethinkDB plugin to new logstash format'
7
+ s.homepage = 'https://github.com/wayann/logstash-input-rethinker'
8
+ s.authors = ['Wayann']
9
+ s.email = 'wayann@nrg.re'
10
+ s.require_paths = ['lib']
11
+
12
+ # Files
13
+ s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
14
+ # Tests
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+
17
+ # Special flag to let us know this is actually a logstash plugin
18
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
19
+
20
+ # Gem dependencies
21
+ s.add_runtime_dependency 'logstash-core-plugin-api', '~> 2.0'
22
+ s.add_runtime_dependency 'logstash-codec-plain', '~> 3.0', '>= 3.0.2'
23
+ s.add_runtime_dependency 'stud', '~> 0.0.22'
24
+ s.add_runtime_dependency 'rethinkdb', '~> 2.0', '>= 2.3.0'
25
+ s.add_runtime_dependency 'eventmachine', '~> 1.0', '>= 1.0.9.1'
26
+ s.add_development_dependency 'logstash-devutils', '~> 1.3', '>= 1.3.1'
27
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ # require "logstash/inputs/rethinker"
4
+
5
+ # describe LogStash::Inputs::Rethinker do
6
+
7
+ # it_behaves_like "an interruptible input plugin" do
8
+ # let(:config) { { "interval" => 100 } }
9
+ # end
10
+
11
+ # end
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-input-rethinker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Wayann
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ name: logstash-core-plugin-api
20
+ prerelease: false
21
+ type: :runtime
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 3.0.2
36
+ name: logstash-codec-plain
37
+ prerelease: false
38
+ type: :runtime
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '3.0'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.0.2
47
+ - !ruby/object:Gem::Dependency
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: 0.0.22
53
+ name: stud
54
+ prerelease: false
55
+ type: :runtime
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 0.0.22
61
+ - !ruby/object:Gem::Dependency
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '2.0'
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 2.3.0
70
+ name: rethinkdb
71
+ prerelease: false
72
+ type: :runtime
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '2.0'
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 2.3.0
81
+ - !ruby/object:Gem::Dependency
82
+ requirement: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - "~>"
85
+ - !ruby/object:Gem::Version
86
+ version: '1.0'
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 1.0.9.1
90
+ name: eventmachine
91
+ prerelease: false
92
+ type: :runtime
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.0'
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 1.0.9.1
101
+ - !ruby/object:Gem::Dependency
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: '1.3'
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 1.3.1
110
+ name: logstash-devutils
111
+ prerelease: false
112
+ type: :development
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.3'
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 1.3.1
121
+ description: Copy paste of old RethinkDB plugin to new logstash format
122
+ email: wayann@nrg.re
123
+ executables: []
124
+ extensions: []
125
+ extra_rdoc_files: []
126
+ files:
127
+ - CHANGELOG.md
128
+ - CONTRIBUTORS
129
+ - DEVELOPER.md
130
+ - Gemfile
131
+ - LICENSE
132
+ - README.md
133
+ - lib/logstash/inputs/rethinker.rb
134
+ - logstash-input-rethinker.gemspec
135
+ - spec/inputs/rethinker_spec.rb
136
+ homepage: https://github.com/wayann/logstash-input-rethinker
137
+ licenses:
138
+ - Apache License (2.0)
139
+ metadata:
140
+ logstash_plugin: 'true'
141
+ logstash_group: input
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 2.6.8
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: RethinkDB updated logstash plugin
162
+ test_files:
163
+ - spec/inputs/rethinker_spec.rb