logstash-integration-jdbc 5.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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