logstash-integration-jdbc 5.0.0.alpha1

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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +8 -0
  3. data/CONTRIBUTORS +22 -0
  4. data/Gemfile +11 -0
  5. data/LICENSE +13 -0
  6. data/NOTICE.TXT +5 -0
  7. data/README.md +105 -0
  8. data/docs/filter-jdbc_static.asciidoc +606 -0
  9. data/docs/filter-jdbc_streaming.asciidoc +317 -0
  10. data/docs/index.asciidoc +32 -0
  11. data/docs/input-jdbc.asciidoc +573 -0
  12. data/lib/logstash/filters/jdbc/basic_database.rb +125 -0
  13. data/lib/logstash/filters/jdbc/column.rb +39 -0
  14. data/lib/logstash/filters/jdbc/db_object.rb +101 -0
  15. data/lib/logstash/filters/jdbc/loader.rb +119 -0
  16. data/lib/logstash/filters/jdbc/loader_schedule.rb +64 -0
  17. data/lib/logstash/filters/jdbc/lookup.rb +253 -0
  18. data/lib/logstash/filters/jdbc/lookup_processor.rb +100 -0
  19. data/lib/logstash/filters/jdbc/lookup_result.rb +40 -0
  20. data/lib/logstash/filters/jdbc/read_only_database.rb +57 -0
  21. data/lib/logstash/filters/jdbc/read_write_database.rb +108 -0
  22. data/lib/logstash/filters/jdbc/repeating_load_runner.rb +13 -0
  23. data/lib/logstash/filters/jdbc/single_load_runner.rb +46 -0
  24. data/lib/logstash/filters/jdbc/validatable.rb +46 -0
  25. data/lib/logstash/filters/jdbc_static.rb +240 -0
  26. data/lib/logstash/filters/jdbc_streaming.rb +196 -0
  27. data/lib/logstash/inputs/jdbc.rb +341 -0
  28. data/lib/logstash/inputs/tzinfo_jruby_patch.rb +57 -0
  29. data/lib/logstash/plugin_mixins/jdbc/checked_count_logger.rb +43 -0
  30. data/lib/logstash/plugin_mixins/jdbc/jdbc.rb +298 -0
  31. data/lib/logstash/plugin_mixins/jdbc/statement_handler.rb +129 -0
  32. data/lib/logstash/plugin_mixins/jdbc/value_tracking.rb +140 -0
  33. data/lib/logstash/plugin_mixins/jdbc_streaming/cache_payload.rb +28 -0
  34. data/lib/logstash/plugin_mixins/jdbc_streaming/parameter_handler.rb +64 -0
  35. data/lib/logstash/plugin_mixins/jdbc_streaming/statement_handler.rb +143 -0
  36. data/lib/logstash/plugin_mixins/jdbc_streaming.rb +100 -0
  37. data/lib/logstash/plugin_mixins/statement_handler.rb +0 -0
  38. data/lib/logstash-integration-jdbc_jars.rb +5 -0
  39. data/logstash-integration-jdbc.gemspec +44 -0
  40. data/spec/filters/env_helper.rb +10 -0
  41. data/spec/filters/integration/jdbc_static_spec.rb +154 -0
  42. data/spec/filters/integration/jdbcstreaming_spec.rb +173 -0
  43. data/spec/filters/jdbc/column_spec.rb +70 -0
  44. data/spec/filters/jdbc/db_object_spec.rb +81 -0
  45. data/spec/filters/jdbc/loader_spec.rb +77 -0
  46. data/spec/filters/jdbc/lookup_processor_spec.rb +132 -0
  47. data/spec/filters/jdbc/lookup_spec.rb +253 -0
  48. data/spec/filters/jdbc/read_only_database_spec.rb +67 -0
  49. data/spec/filters/jdbc/read_write_database_spec.rb +90 -0
  50. data/spec/filters/jdbc/repeating_load_runner_spec.rb +24 -0
  51. data/spec/filters/jdbc/single_load_runner_spec.rb +16 -0
  52. data/spec/filters/jdbc_static_file_local_spec.rb +83 -0
  53. data/spec/filters/jdbc_static_spec.rb +162 -0
  54. data/spec/filters/jdbc_streaming_spec.rb +350 -0
  55. data/spec/filters/remote_server_helper.rb +24 -0
  56. data/spec/filters/shared_helpers.rb +34 -0
  57. data/spec/helpers/WHY-THIS-JAR.txt +4 -0
  58. data/spec/helpers/derbyrun.jar +0 -0
  59. data/spec/inputs/integration/integ_spec.rb +78 -0
  60. data/spec/inputs/jdbc_spec.rb +1431 -0
  61. data/vendor/jar-dependencies/org/apache/derby/derby/10.14.1.0/derby-10.14.1.0.jar +0 -0
  62. data/vendor/jar-dependencies/org/apache/derby/derbyclient/10.14.1.0/derbyclient-10.14.1.0.jar +0 -0
  63. metadata +319 -0
@@ -0,0 +1,90 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash-integration-jdbc_jars"
4
+ require "logstash/util/password"
5
+ require "logstash/filters/jdbc/db_object"
6
+ require "logstash/filters/jdbc/read_write_database"
7
+ require "stud/temporary"
8
+
9
+ module LogStash module Filters module Jdbc
10
+ describe ReadWriteDatabase do
11
+ let(:db) { Sequel.connect('mock://mydb') }
12
+ let(:connection_string_regex) { /jdbc:derby:memory:\w+;create=true/ }
13
+ let(:temp_import_path_plugin) { Stud::Temporary.pathname }
14
+ subject(:read_write_db) { described_class.create }
15
+
16
+ describe "basic operations" do
17
+ context "connecting to a db" do
18
+ it "connects with defaults" do
19
+ expect(::Sequel::JDBC).to receive(:load_driver).once.with("org.apache.derby.jdbc.EmbeddedDriver")
20
+ # two calls to connect because ReadWriteDatabase does verify_connection and connect
21
+ expect(::Sequel).to receive(:connect).once.with(connection_string_regex, {:test => true}).and_return(db)
22
+ expect(::Sequel).to receive(:connect).once.with(connection_string_regex, {}).and_return(db)
23
+ expect(read_write_db.empty_record_set).to eq([])
24
+ end
25
+
26
+ it "connects with fully specified arguments" do
27
+ connection_str = "a connection string"
28
+ user = "a user"
29
+ password = Util::Password.new("secret")
30
+ expect(::Sequel::JDBC).to receive(:load_driver).once.with("a driver class")
31
+ expect(::Sequel).to receive(:connect).once.with(connection_str, {:user => user, :password => password.value, :test => true}).and_return(db)
32
+ expect(::Sequel).to receive(:connect).once.with(connection_str, {:user => user, :password => password.value}).and_return(db)
33
+ described_class.create(connection_str, "a driver class", nil, user, password)
34
+ end
35
+ end
36
+
37
+ describe "methods" do
38
+ let(:dataset) { double("Sequel::Dataset") }
39
+ let(:loaders) { [] }
40
+ let(:loader) { double("Loader") }
41
+ let(:table_name) { "users" }
42
+ let(:random_table_name) { "foobarbaz" }
43
+ let(:multi_insert_sql) { "INSERT VALUES (1, 2, 3)" }
44
+
45
+ before(:each) do
46
+ FileUtils.mkdir_p(temp_import_path_plugin)
47
+ allow(::Sequel::JDBC).to receive(:load_driver)
48
+ # allow(::Sequel).to receive(:connect).twice.and_return(db)
49
+ allow(::Sequel).to receive(:connect).twice.and_return(db)
50
+ allow(loader).to receive(:fetch).and_return([{:a => 1, :b => 2, :c => 3}])
51
+ allow(loader).to receive(:table).and_return(table_name)
52
+ allow(loader).to receive(:id).and_return("some_id")
53
+ allow(loader).to receive(:staging_directory).and_return(temp_import_path_plugin)
54
+ allow(described_class).to receive(:random_name).and_return(random_table_name)
55
+ loaders.push(loader)
56
+ end
57
+
58
+ it "the populate_all method fills a local_db from the dataset" do
59
+ expect(db).to receive(:[]).with(loader.table).once.and_return(dataset)
60
+ expect(dataset).to receive(:literal).and_return(1.to_s, 2.to_s, 3.to_s)
61
+ import_file_path = ::File.join(temp_import_path_plugin, loader.table)
62
+ expect(db).to receive(:execute_ddl).once.with("CALL SYSCS_UTIL.SYSCS_IMPORT_TABLE (null,'#{loader.table.upcase}','#{import_file_path}',null,'''',null,1)")
63
+ read_write_db.populate_all(loaders)
64
+ end
65
+
66
+ it "the repopulate_all method fills a local_db from the dataset" do
67
+ expect(db).to receive(:[]).with(loader.table).once.and_return(dataset)
68
+ expect(dataset).to receive(:literal).and_return(1.to_s, 2.to_s, 3.to_s)
69
+ import_file_path = ::File.join(temp_import_path_plugin, loader.table)
70
+ expect(db).to receive(:execute_ddl).once.with("CALL SYSCS_UTIL.SYSCS_IMPORT_TABLE (null,'#{loader.table.upcase}','#{import_file_path}',null,'''',null,1)")
71
+ read_write_db.repopulate_all(loaders)
72
+ end
73
+
74
+ it "the fetch method executes a parameterised SQL statement on the local db" do
75
+ statement = "select 1 from dual"
76
+ parameters = 42
77
+ expect(db).to receive(:[]).with(statement, parameters).once.and_return(dataset)
78
+ expect(dataset).to receive(:all).once.and_return([1,2,3])
79
+ read_write_db.fetch(statement, parameters)
80
+ end
81
+
82
+ it "lends the local db to a DbObject build instance method" do
83
+ db_object = DbObject.new("type" => "index", "name" => "servers_idx", "table" => "servers", "columns" => ["ip"])
84
+ expect(db_object).to receive(:build).once.with(db)
85
+ read_write_db.build_db_object(db_object)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end end end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require_relative "../shared_helpers"
4
+ require "logstash/filters/jdbc/repeating_load_runner"
5
+
6
+ module LogStash module Filters module Jdbc
7
+ describe RepeatingLoadRunner do
8
+ let(:local_db) { double("local_db") }
9
+ let(:loaders) { Object.new }
10
+ let(:local_db_objects) { [] }
11
+ subject(:runner) { described_class.new(local_db, loaders, local_db_objects) }
12
+
13
+ it_behaves_like "a single load runner"
14
+
15
+ context "when repeating" do
16
+ it "repopulates the local db" do
17
+ expect(local_db).to receive(:populate_all).once.with(loaders)
18
+ expect(local_db).to receive(:repopulate_all).once.with(loaders)
19
+ runner.initial_load
20
+ subject.call
21
+ end
22
+ end
23
+ end
24
+ end end end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require_relative "../shared_helpers"
4
+
5
+ require "logstash/filters/jdbc/single_load_runner"
6
+
7
+ module LogStash module Filters module Jdbc
8
+ describe SingleLoadRunner do
9
+ let(:local_db) { double("local_db") }
10
+ let(:loaders) { Object.new }
11
+ let(:local_db_objects) { [] }
12
+ subject(:runner) { described_class.new(local_db, loaders, local_db_objects) }
13
+
14
+ it_behaves_like "a single load runner"
15
+ end
16
+ end end end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ require_relative "env_helper"
3
+ require_relative "remote_server_helper"
4
+
5
+ require "logstash/devutils/rspec/spec_helper"
6
+ require "logstash/filters/jdbc_static"
7
+ require "sequel"
8
+ require "sequel/adapters/jdbc"
9
+
10
+ module LogStash module Filters
11
+ describe JdbcStatic, :skip => "temporary disable lookup_jdbc_* settings" do
12
+ let(:db1) { ::Sequel.connect("jdbc:derby:memory:testdb;create=true", :user=> nil, :password=> nil) }
13
+ let(:test_loader) { "SELECT * FROM reference_table" }
14
+ let(:test_records) { db1[test_loader].all }
15
+ let(:lookup_db) { "lookupdb" }
16
+
17
+ let(:local_db_objects) do
18
+ [
19
+ {"name" => "servers", "preserve_existing" => true, "index_columns" => ["ip"], "columns" => [["ip", "varchar(64)"], ["name", "varchar(64)"], ["location", "varchar(64)"]]},
20
+ ]
21
+ end
22
+
23
+ let(:settings) do
24
+ {
25
+ "loaders" => [
26
+ {
27
+ "id" =>"servers",
28
+ "query" => "select ip, name, location from reference_table",
29
+ "local_table" => "servers"
30
+ }
31
+ ],
32
+ "local_db_objects" => local_db_objects,
33
+ "local_lookups" => [
34
+ {
35
+ "query" => "select * from servers WHERE ip LIKE :ip",
36
+ "parameters" => {"ip" => "%%{[ip]}"},
37
+ "target" => "server"
38
+ }
39
+ ]
40
+ }
41
+ end
42
+
43
+ let(:client_jar_path) { ::File.join(BASE_DERBY_DIR, "derbyclient.jar") }
44
+
45
+ let(:mixin_settings) do
46
+ { "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
47
+ "jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true",
48
+ "lookup_jdbc_driver_class" => "org.apache.derby.jdbc.ClientDriver",
49
+ "lookup_jdbc_driver_library" => nil,
50
+ "lookup_jdbc_connection_string" => "jdbc:derby://localhost:1527/#{lookup_db};create=true" }
51
+ end
52
+ let(:plugin) { JdbcStatic.new(mixin_settings.merge(settings)) }
53
+
54
+ after do
55
+ plugin.stop
56
+ ServerProcessHelpers.jdbc_static_stop_derby_server(lookup_db)
57
+ end
58
+
59
+ before do
60
+ ServerProcessHelpers.jdbc_static_start_derby_server
61
+ db1.drop_table(:reference_table) rescue nil
62
+ db1.create_table :reference_table do
63
+ String :ip
64
+ String :name
65
+ String :location
66
+ end
67
+ db1[:reference_table].insert(:ip => "10.1.1.1", :name => "ldn-server-1", :location => "LDN-2-3-4")
68
+ db1[:reference_table].insert(:ip => "10.2.1.1", :name => "nyc-server-1", :location => "NYC-5-2-8")
69
+ db1[:reference_table].insert(:ip => "10.3.1.1", :name => "mv-server-1", :location => "MV-9-6-4")
70
+
71
+ plugin.register
72
+ end
73
+
74
+ let(:event) { ::LogStash::Event.new("message" => "some text", "ip" => ipaddr) }
75
+
76
+ let(:ipaddr) { ".3.1.1" }
77
+
78
+ it "enhances an event" do
79
+ plugin.filter(event)
80
+ expect(event.get("server")).to eq([{"ip"=>"10.3.1.1", "name"=>"mv-server-1", "location"=>"MV-9-6-4"}])
81
+ end
82
+ end
83
+ end end
@@ -0,0 +1,162 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/filters/jdbc_static"
4
+ require "sequel"
5
+ require "sequel/adapters/jdbc"
6
+ require "stud/temporary"
7
+ require "timecop"
8
+
9
+ # LogStash::Logging::Logger::configure_logging("WARN")
10
+
11
+ module LogStash module Filters
12
+ describe JdbcStatic do
13
+
14
+ before(:all) do
15
+ @thread_abort = Thread.abort_on_exception
16
+ Thread.abort_on_exception = true
17
+ end
18
+
19
+ let(:jdbc_connection_string) { "jdbc:derby:memory:jdbc_static_testdb;create=true" }
20
+ let(:db1) { ::Sequel.connect(jdbc_connection_string, :user=> nil, :password=> nil) }
21
+ let(:loader_statement) { "SELECT ip, name, location FROM reference_table" }
22
+ let(:lookup_statement) { "SELECT * FROM servers WHERE ip LIKE :ip" }
23
+ let(:parameters_rhs) { "%%{[ip]}" }
24
+ let(:temp_import_path_plugin) { Stud::Temporary.pathname }
25
+ let(:temp_import_path_rspec) { Stud::Temporary.pathname }
26
+
27
+ let(:local_db_objects) do
28
+ [
29
+ {"name" => "servers", "index_columns" => ["ip"], "columns" => [["ip", "varchar(64)"], ["name", "varchar(64)"], ["location", "varchar(64)"]]},
30
+ ]
31
+ end
32
+
33
+ let(:settings) do
34
+ {
35
+ "loaders" => [
36
+ {
37
+ "id" =>"servers",
38
+ "query" => loader_statement,
39
+ "local_table" => "servers"
40
+ }
41
+ ],
42
+ "local_db_objects" => local_db_objects,
43
+ "local_lookups" => [
44
+ {
45
+ "query" => lookup_statement,
46
+ "parameters" => {"ip" => parameters_rhs},
47
+ "target" => "server"
48
+ }
49
+ ]
50
+ }
51
+ end
52
+
53
+ let(:mixin_settings) do
54
+ { "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
55
+ "jdbc_connection_string" => "#{jdbc_connection_string}",
56
+ "staging_directory" => temp_import_path_plugin
57
+ }
58
+ end
59
+
60
+ let(:add_records) do
61
+ lambda do |fd|
62
+ fd.puts "'10.1.1.1', 'ldn-server-1', 'LDN-2-3-4'"
63
+ fd.puts "'10.2.1.1', 'nyc-server-1', 'NYC-5-2-8'"
64
+ fd.puts "'10.3.1.1', 'mv-serv''r-1', 'MV-9-6-4'"
65
+ end
66
+ end
67
+
68
+ let(:plugin) { JdbcStatic.new(mixin_settings.merge(settings)) }
69
+
70
+ before do
71
+ db1.drop_table(:reference_table) rescue nil
72
+ db1.create_table(:reference_table) do
73
+ String :ip
74
+ String :name
75
+ String :location
76
+ end
77
+ ::File.open(temp_import_path_rspec, "w") do |fd|
78
+ add_records.call(fd)
79
+ end
80
+ import_cmd = "CALL SYSCS_UTIL.SYSCS_IMPORT_TABLE (null,'REFERENCE_TABLE','#{temp_import_path_rspec}',null,'''',null,1)"
81
+ db1.execute_ddl(import_cmd)
82
+ end
83
+
84
+ let(:event) { ::LogStash::Event.new("message" => "some text", "ip" => ipaddr) }
85
+
86
+ let(:ipaddr) { ".3.1.1" }
87
+
88
+ describe "non scheduled operation" do
89
+ after { plugin.close }
90
+
91
+ context "under normal conditions" do
92
+ it "enhances an event" do
93
+ plugin.register
94
+ plugin.filter(event)
95
+ expect(event.get("server")).to eq([{"ip"=>"10.3.1.1", "name"=>"mv-serv'r-1", "location"=>"MV-9-6-4"}])
96
+ end
97
+ end
98
+
99
+ context "when the loader query returns no results" do
100
+ let(:loader_statement) { "SELECT ip, name, location FROM reference_table WHERE ip LIKE '20%'" }
101
+ it "add an empty array to the target field" do
102
+ plugin.register
103
+ plugin.filter(event)
104
+ expect(event.get("server")).to eq([])
105
+ end
106
+ end
107
+
108
+ context "when the loader query returns a large recordset, local db is filled in chunks" do
109
+ let(:add_records) do
110
+ lambda do |fd|
111
+ 256.times do |octet3|
112
+ 256.times do |octet4|
113
+ fd.puts "'10.4.#{octet3}.#{octet4}', 'server-#{octet3}-#{octet4}', 'MV-10-#{octet3}-#{octet4}'"
114
+ end
115
+ end
116
+ end
117
+ end
118
+ let(:ipaddr) { "10.4.254.255" }
119
+ let(:lookup_statement) { "SELECT * FROM servers WHERE ip = :ip" }
120
+ let(:parameters_rhs) { "ip" }
121
+ it "enhances an event" do
122
+ plugin.register
123
+ plugin.filter(event)
124
+ expect(event.get("server")).to eq([{"ip"=>ipaddr, "name"=>"server-254-255", "location"=>"MV-10-254-255"}])
125
+ end
126
+ end
127
+
128
+ context "under normal conditions when index_columns is not specified" do
129
+ let(:local_db_objects) do
130
+ [
131
+ {"name" => "servers", "columns" => [["ip", "varchar(64)"], ["name", "varchar(64)"], ["location", "varchar(64)"]]},
132
+ ]
133
+ end
134
+ it "enhances an event" do
135
+ plugin.register
136
+ plugin.filter(event)
137
+ expect(event.get("server")).to eq([{"ip"=>"10.3.1.1", "name"=>"mv-serv'r-1", "location"=>"MV-9-6-4"}])
138
+ end
139
+ end
140
+ end
141
+
142
+ describe "scheduled operation" do
143
+ context "given a loader_schedule" do
144
+ it "should properly schedule" do
145
+ settings["loader_schedule"] = "*/10 * * * * * UTC"
146
+ Timecop.travel(Time.now.utc - 3600)
147
+ Timecop.scale(60)
148
+ static_filter = JdbcStatic.new(mixin_settings.merge(settings))
149
+ runner = Thread.new(static_filter) do |filter|
150
+ filter.register
151
+ end
152
+ sleep 3
153
+ static_filter.filter(event)
154
+ expect(static_filter.loader_runner.reload_count).to be > 1
155
+ static_filter.close
156
+ Timecop.return
157
+ expect(event.get("server")).to eq([{"ip"=>"10.3.1.1", "name"=>"mv-serv'r-1", "location"=>"MV-9-6-4"}])
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end end