logstash-input-jdbc 0.1.3 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cec484e2d7cd55a6842131e8f5918c85e3352c35
4
- data.tar.gz: fc85af02081d8e3a94ba080c685935b35f6ff548
3
+ metadata.gz: 9026f853d22bd2451bfe72e0b045318ec605d77b
4
+ data.tar.gz: df99f72e2ba6ddffc1f9d40c193189a1941c2dad
5
5
  SHA512:
6
- metadata.gz: bbd57390af1d2bc22a066c640dbcb19b773a9b5288070f9348f03685042f2c30cf600d74fa234f52956ea4de171c8272e27305b0813447111687cb3e71b21afd
7
- data.tar.gz: aad311585c5b5774b0f5b602528d8a693bc78dd08d0e04701ab91bf262462e684fa5491cc1a0ed1d8a985caf40bcb528bc1cefee2aee88ba451dfe3925ba5456
6
+ metadata.gz: 948d8d265d5b6158daef86e4032958b344f49d0116ca30f6f7d3c697e875b19f69eb12ef63627c6a94c4e8bbcb5c389c9e6f935853ce5552fd2967216fe1cf92
7
+ data.tar.gz: e77a3ee5bac991ef164db17776c5e35713c97e4d4667a3f5703d4b09d2a15f7b67d8e6a5732e42ce450cbaa71a1223a9f5f52282d78e7915eb8c234abfef12d5
data/CHANGELOG.md CHANGED
@@ -0,0 +1,2 @@
1
+ * 1.0.0
2
+ - Initial release
data/NOTICE.TXT ADDED
@@ -0,0 +1,5 @@
1
+ Elasticsearch
2
+ Copyright 2012-2015 Elasticsearch
3
+
4
+ This product includes software developed by The Apache Software
5
+ Foundation (http://www.apache.org/).
data/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # Logstash JDBC Input Plugin
2
2
 
3
- ## WIP: Under Development, NOT FOR PRODUCTION
4
-
5
3
  This is a plugin for [Logstash](https://github.com/elasticsearch/logstash).
6
4
 
7
5
  It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
@@ -15,7 +13,7 @@ Logstash provides infrastructure to automatically generate documentation for thi
15
13
 
16
14
  ## Need Help?
17
15
 
18
- Need help? Try #logstash on freenode IRC or the logstash-users@googlegroups.com mailing list.
16
+ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
19
17
 
20
18
  ## Developing
21
19
 
@@ -86,6 +84,37 @@ bin/plugin install /your/local/plugin/logstash-input-jdbc.gem
86
84
  ```
87
85
  - Start Logstash and proceed to test the plugin
88
86
 
87
+ ## Example configuration
88
+
89
+ Reading data from MySQL:
90
+
91
+ input {
92
+ jdbc {
93
+ jdbc_driver_library => "/path/to/mysql-connector-java-5.1.33-bin.jar"
94
+ jdbc_driver_class => "com.mysql.jdbc.Driver"
95
+ jdbc_connection_string => "jdbc:mysql://host:port/database"
96
+ jdbc_user => "user"
97
+ jdbc_password => "password"
98
+ statement => "SELECT ..."
99
+ jdbc_paging_enabled => "true"
100
+ jdbc_page_size => "50000"
101
+ }
102
+ }
103
+
104
+ filter {
105
+ [some filters here]
106
+ }
107
+
108
+ output {
109
+ stdout {
110
+ codec => rubydebug
111
+ }
112
+ elasticsearch_http {
113
+ host => "host"
114
+ index => "myindex"
115
+ }
116
+ }
117
+
89
118
  ## Contributing
90
119
 
91
120
  All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
@@ -2,41 +2,99 @@
2
2
  require "logstash/inputs/base"
3
3
  require "logstash/namespace"
4
4
  require "logstash/plugin_mixins/jdbc"
5
+ require "yaml" # persistence
5
6
 
6
- # INFORMATION
7
+ # This plugin was created as a way to ingest data in any database
8
+ # with a JDBC interface into Logstash. You can periodically schedule ingestion
9
+ # using a cron syntax (see `schedule` setting) or run the query one time to load
10
+ # data into Logstash. Each row in the resultset becomes a single event.
11
+ # Columns in the resultset are converted into fields in the event.
7
12
  #
8
- # This plugin was created as a way to iteratively ingest any database
9
- # with a JDBC interface into Logstash.
13
+ # ==== Drivers
10
14
  #
11
- # #### JDBC Mixin
15
+ # This plugin does not come packaged with JDBC driver libraries. The desired
16
+ # jdbc driver library must be explicitly passed in to the plugin using the
17
+ # `jdbc_driver_library` configuration option.
18
+ #
19
+ # ==== Scheduling
12
20
  #
13
- # This plugin utilizes a mixin that helps Logstash plugins manage JDBC connections.
14
- # The mixin provides its own set of configurations (some are required) to properly
15
- # set up the connection to the appropriate database.
21
+ # Input from this plugin can be scheduled to run periodically according to a specific
22
+ # schedule. This scheduling syntax is powered by [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler).
23
+ # The syntax is cron-like with some extensions specific to Rufus (e.g. timezone support ).
16
24
  #
17
- # #### Predefined Parameters
18
- #
19
- # Some parameters are built-in and can be used from within your queries.
20
- # Here is the list:
25
+ # Examples:
21
26
  #
22
27
  # |==========================================================
23
- # |sql_last_start |The time the last query executed in plugin
28
+ # | * 5 * 1-3 * | will execute every minute of 5am every day of January through March.
29
+ # | 0 * * * * | will execute on the 0th minute of every hour every day.
30
+ # | 0 6 * * * America/Chicago | will execute at 6:00am (UTC/GMT -5) every day.
24
31
  # |==========================================================
32
+ #
33
+ #
34
+ # Further documentation describing this syntax can be found [here](https://github.com/jmettraux/rufus-scheduler#parsing-cronlines-and-time-strings)
35
+ #
36
+ # ==== State
37
+ #
38
+ # The plugin will persist the `sql_last_start` parameter in the form of a
39
+ # metadata file stored in the configured `last_run_metadata_path`. Upon shutting down,
40
+ # this file will be updated with the current value of `sql_last_start`. Next time
41
+ # the pipeline starts up, this value will be updated by reading from the file. If
42
+ # `clean_run` is set to true, this value will be ignored and `sql_last_start` will be
43
+ # set to Jan 1, 1970, as if no query has ever been executed.
44
+ #
45
+ # ==== Dealing With Large Result-sets
46
+ #
47
+ # Many JDBC drivers use the `fetch_size` parameter to limit how many
48
+ # results are pre-fetched at a time from the cursor into the client's cache
49
+ # before retrieving more results from the result-set. This is configured in
50
+ # this plugin using the `jdbc_fetch_size` configuration option. No fetch size
51
+ # is set by default in this plugin, so the specific driver's default size will
52
+ # be used.
53
+ #
54
+ # ==== Usage:
55
+ #
56
+ # Here is an example of setting up the plugin to fetch data from a MySQL database.
57
+ # First, we place the appropriate JDBC driver library in our current
58
+ # path (this can be placed anywhere on your filesystem). In this example, we connect to
59
+ # the 'mydb' database using the user: 'mysql' and wish to input all rows in the 'songs'
60
+ # table that match a specific artist. The following examples demonstrates a possible
61
+ # Logstash configuration for this. The `schedule` option in this example will
62
+ # instruct the plugin to execute this input statement on the minute, every minute.
25
63
  #
26
- # #### Usage:
27
- # This is an example logstash config
28
64
  # [source,ruby]
65
+ # ----------------------------------
29
66
  # input {
30
67
  # jdbc {
31
- # jdbc_driver_class => "org.apache.derby.jdbc.EmbeddedDriver" (required; from mixin)
32
- # jdbc_connection_string => "jdbc:derby:memory:testdb;create=true" (required; from mixin)
33
- # jdbc_user => "username" (from mixin)
34
- # jdbc_password => "mypass" (from mixin)
35
- # statement => "SELECT * from table where created_at > :sql_last_start and id = :my_id" (required)
36
- # parameters => { "my_id" => "231" }
68
+ # jdbc_driver_library => "mysql-connector-java-5.1.36-bin.jar"
69
+ # jdbc_driver_class => "com.mysql.jdbc.Driver"
70
+ # jdbc_connection_string => "jdbc:mysql://localhost:3306/mydb"
71
+ # jdbc_user => "mysql"
72
+ # parameters => { "favorite_artist" => "Beethoven" }
37
73
  # schedule => "* * * * *"
74
+ # statement => "SELECT * from songs where artist = :favorite_artist"
38
75
  # }
39
76
  # }
77
+ # ----------------------------------
78
+ #
79
+ # ==== Configuring SQL statement
80
+ #
81
+ # A sql statement is required for this input. This can be passed-in via a
82
+ # statement option in the form of a string, or read from a file (`statement_filepath`). File
83
+ # option is typically used when the SQL statement is large or cumbersome to supply in the config.
84
+ # The file option only supports one SQL statement. The plugin will only accept one of the options.
85
+ # It cannot read a statement from a file as well as from the `statement` configuration parameter.
86
+ #
87
+ # ==== Predefined Parameters
88
+ #
89
+ # Some parameters are built-in and can be used from within your queries.
90
+ # Here is the list:
91
+ #
92
+ # |==========================================================
93
+ # |sql_last_start | The last time a statement was executed. This is set to
94
+ # | | Thursday, 1 January 1970 before any query is run, and updated
95
+ # | | accordingly after first query is run.
96
+ # |==========================================================
97
+ #
40
98
  class LogStash::Inputs::Jdbc < LogStash::Inputs::Base
41
99
  include LogStash::PluginMixins::Jdbc
42
100
  config_name "jdbc"
@@ -45,25 +103,59 @@ class LogStash::Inputs::Jdbc < LogStash::Inputs::Base
45
103
  default :codec, "plain"
46
104
 
47
105
  # Statement to execute
106
+ #
48
107
  # To use parameters, use named parameter syntax.
49
108
  # For example:
109
+ #
110
+ # [source, ruby]
111
+ # ----------------------------------
50
112
  # "SELECT * FROM MYTABLE WHERE id = :target_id"
51
- # here ":target_id" is a named parameter
113
+ # ----------------------------------
52
114
  #
53
- config :statement, :validate => :string, :required => true
115
+ # here, ":target_id" is a named parameter. You can configure named parameters
116
+ # with the `parameters` setting.
117
+ config :statement, :validate => :string
118
+
119
+ # Path of file containing statement to execute
120
+ config :statement_filepath, :validate => :path
54
121
 
55
122
  # Hash of query parameter, for example `{ "target_id" => "321" }`
56
123
  config :parameters, :validate => :hash, :default => {}
57
124
 
58
125
  # Schedule of when to periodically run statement, in Cron format
59
126
  # for example: "* * * * *" (execute query every minute, on the minute)
127
+ #
128
+ # There is no schedule by default. If no schedule is given, then the statement is run
129
+ # exactly once.
60
130
  config :schedule, :validate => :string
61
131
 
132
+ # Path to file with last run time
133
+ config :last_run_metadata_path, :validate => :string, :default => "#{ENV['HOME']}/.logstash_jdbc_last_run"
134
+
135
+ # Whether the previous run state should be preserved
136
+ config :clean_run, :validate => :boolean, :default => false
137
+
138
+ # Whether to save state or not in last_run_metadata_path
139
+ config :record_last_run, :validate => :boolean, :default => true
140
+
62
141
  public
63
142
 
64
143
  def register
65
144
  require "rufus/scheduler"
66
- prepare_jdbc_connection()
145
+ prepare_jdbc_connection
146
+
147
+ # load sql_last_start from file if exists
148
+ if @clean_run && File.exist?(@last_run_metadata_path)
149
+ File.delete(@last_run_metadata_path)
150
+ elsif File.exist?(@last_run_metadata_path)
151
+ @sql_last_start = YAML.load(File.read(@last_run_metadata_path))
152
+ end
153
+
154
+ unless @statement.nil? ^ @statement_filepath.nil?
155
+ raise(LogStash::ConfigurationError, "Must set either :statement or :statement_filepath. Only one may be set at a time.")
156
+ end
157
+
158
+ @statement = File.read(@statement_filepath) if @statement_filepath
67
159
  end # def register
68
160
 
69
161
  def run(queue)
@@ -79,13 +171,18 @@ class LogStash::Inputs::Jdbc < LogStash::Inputs::Base
79
171
  end # def run
80
172
 
81
173
  def teardown
82
- if @scheduler
83
- @scheduler.stop
174
+ @scheduler.stop if @scheduler
175
+
176
+ # update state file for next run
177
+ if @record_last_run
178
+ File.write(@last_run_metadata_path, YAML.dump(@sql_last_start))
84
179
  end
85
- close_jdbc_connection()
180
+
181
+ close_jdbc_connection
86
182
  end # def teardown
87
183
 
88
184
  private
185
+
89
186
  def execute_query(queue)
90
187
  # update default parameters
91
188
  @parameters['sql_last_start'] = @sql_last_start
@@ -13,14 +13,14 @@ module LogStash::PluginMixins::Jdbc
13
13
  # Add these methods to the 'base' given.
14
14
  base.extend(self)
15
15
  base.setup_jdbc_config
16
-
17
- @sql_last_start = Time.at(0).utc
18
16
  end
19
17
 
20
18
 
21
19
  public
22
20
  def setup_jdbc_config
23
21
  # JDBC driver library path to third party driver library.
22
+ #
23
+ # If not provided, Plugin will look for the driver class in the Logstash Java classpath.
24
24
  config :jdbc_driver_library, :validate => :path
25
25
 
26
26
  # JDBC driver class to load, for example "oracle.jdbc.OracleDriver" or "org.apache.derby.jdbc.ClientDriver"
@@ -30,18 +30,33 @@ module LogStash::PluginMixins::Jdbc
30
30
  config :jdbc_connection_string, :validate => :string, :required => true
31
31
 
32
32
  # JDBC user
33
- config :jdbc_user, :validate => :string, :default => "tal"
33
+ config :jdbc_user, :validate => :string, :required => true
34
34
 
35
35
  # JDBC password
36
36
  config :jdbc_password, :validate => :password
37
37
 
38
+ # JDBC enable paging
39
+ #
40
+ # This will cause a sql statement to be broken up into multiple queries.
41
+ # Each query will use limits and offsets to collectively retrieve the full
42
+ # result-set. The limit size is set with `jdbc_page_size`.
43
+ #
44
+ # Be aware that ordering is not guaranteed between queries.
45
+ config :jdbc_paging_enabled, :validate => :boolean, :default => false
46
+
47
+ # JDBC page size
48
+ config :jdbc_page_size, :validate => :number, :default => 100000
49
+
50
+ # JDBC fetch size. if not provided, respective driver's default will be used
51
+ config :jdbc_fetch_size, :validate => :number
52
+
38
53
  # Connection pool configuration.
39
54
  # Validate connection before use.
40
55
  config :jdbc_validate_connection, :validate => :boolean, :default => false
41
56
 
42
57
  # Connection pool configuration.
43
58
  # How often to validate a connection (in seconds)
44
- config :jdcb_validation_timeout, :validate => :number, :default => 3600
59
+ config :jdbc_validation_timeout, :validate => :number, :default => 3600
45
60
  end
46
61
 
47
62
  public
@@ -50,18 +65,32 @@ module LogStash::PluginMixins::Jdbc
50
65
  require "sequel"
51
66
  require "sequel/adapters/jdbc"
52
67
  require @jdbc_driver_library if @jdbc_driver_library
53
- Sequel::JDBC.load_driver(@jdbc_driver_class)
68
+ begin
69
+ Sequel::JDBC.load_driver(@jdbc_driver_class)
70
+ rescue Sequel::AdapterNotFound => e
71
+ message = if @jdbc_driver_library.nil?
72
+ ":jdbc_driver_library is not set, are you sure you included
73
+ the proper driver client libraries in your classpath?"
74
+ else
75
+ "Are you sure you've included the correct jdbc driver in :jdbc_driver_library?"
76
+ end
77
+ raise LogStash::ConfigurationError, "#{e}. #{message}"
78
+ end
54
79
  @database = Sequel.connect(@jdbc_connection_string, :user=> @jdbc_user, :password=> @jdbc_password.nil? ? nil : @jdbc_password.value)
80
+ @database.extension(:pagination)
55
81
  if @jdbc_validate_connection
56
82
  @database.extension(:connection_validator)
57
- @database.pool.connection_validation_timeout = @jdcb_validation_timeout
83
+ @database.pool.connection_validation_timeout = @jdbc_validation_timeout
58
84
  end
85
+ @database.fetch_size = @jdbc_fetch_size unless @jdbc_fetch_size.nil?
59
86
  begin
60
87
  @database.test_connection
61
88
  rescue Sequel::DatabaseConnectionError => e
62
89
  #TODO return false and let the plugin raise a LogStash::ConfigurationError
63
90
  raise e
64
91
  end
92
+
93
+ @sql_last_start = Time.at(0).utc
65
94
  end # def prepare_jdbc_connection
66
95
 
67
96
  public
@@ -77,9 +106,19 @@ module LogStash::PluginMixins::Jdbc
77
106
  query = @database[statement, parameters]
78
107
  @logger.debug? and @logger.debug("Executing JDBC query", :statement => statement, :parameters => parameters)
79
108
  @sql_last_start = Time.now.utc
80
- query.all do |row|
81
- #Stringify row keys
82
- yield Hash[row.map { |k, v| [k.to_s, v] }]
109
+
110
+ if @jdbc_paging_enabled
111
+ query.each_page(@jdbc_page_size) do |paged_dataset|
112
+ paged_dataset.each do |row|
113
+ #Stringify row keys
114
+ yield Hash[row.map { |k, v| [k.to_s, v] }]
115
+ end
116
+ end
117
+ else
118
+ query.each do |row|
119
+ #Stringify row keys
120
+ yield Hash[row.map { |k, v| [k.to_s, v] }]
121
+ end
83
122
  end
84
123
  success = true
85
124
  rescue Sequel::DatabaseConnectionError, Sequel::DatabaseError => e
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-input-jdbc'
3
- s.version = '0.1.3'
3
+ s.version = '1.0.0'
4
4
  s.licenses = ['Apache License (2.0)']
5
5
  s.summary = "This example input streams a string at a definable interval."
6
6
  s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
@@ -1,84 +1,304 @@
1
1
  require "logstash/devutils/rspec/spec_helper"
2
2
  require "logstash/inputs/jdbc"
3
3
  require "jdbc/derby"
4
+ require "sequel"
5
+ require "sequel/adapters/jdbc"
4
6
  require "timecop"
7
+ require "stud/temporary"
5
8
 
6
9
 
7
10
  describe "jdbc" do
8
- let(:mixin_settings) { {"jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver", "jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"} }
9
-
10
- before :each do
11
- Jdbc::Derby.load_driver
11
+ let(:mixin_settings) { {"jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver", "jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"} }
12
+ let(:settings) { {} }
13
+ let(:plugin) { LogStash::Inputs::Jdbc.new(mixin_settings.merge(settings)) }
14
+ let(:queue) { Queue.new }
15
+ let (:db) do
16
+ Sequel.connect(mixin_settings['jdbc_connection_string'], :user=> nil, :password=> nil)
12
17
  end
13
18
 
14
- it "should register and tear down" do
15
- settings = {"statement" => "SELECT 1 as col1 FROM SYSIBM.SYSDUMMY1"}
16
- plugin = LogStash::Plugin.lookup("input", "jdbc").new(mixin_settings.merge(settings))
17
- expect { plugin.register }.to_not raise_error
18
- expect { plugin.teardown }.to_not raise_error
19
- end
20
-
21
- it "should retrieve params correctly from Event" do
22
- settings = {"statement" => "SELECT :num_param as num_param FROM SYSIBM.SYSDUMMY1", "parameters" => {"num_param" => 10} }
23
- plugin = LogStash::Inputs::Jdbc.new(mixin_settings.merge(settings))
24
- plugin.register
25
- q = Queue.new
26
- plugin.run(q)
27
- insist { q.size } == 1
28
- insist { q.pop['num_param'] } == settings['parameters']['num_param']
29
- plugin.teardown
30
- end
31
-
32
- it "should properly schedule" do
33
- settings = {"statement" => "SELECT 1 as num_param FROM SYSIBM.SYSDUMMY1", "schedule" => "* * * * *"}
34
- plugin = LogStash::Inputs::Jdbc.new(mixin_settings.merge(settings))
35
- plugin.register
36
- q = Queue.new
37
- Timecop.travel(Time.new(2000))
38
- Timecop.scale(60)
39
- runner = Thread.new do
40
- plugin.run(q)
41
- end
42
- sleep 3
43
- plugin.teardown
44
- runner.kill
45
- runner.join
46
- insist { q.size } == 2
47
- Timecop.return
48
- end
49
-
50
- it "should successfully iterate table with respect to field values" do
51
- require "sequel"
52
- require "sequel/adapters/jdbc"
19
+ before :each do
53
20
  Jdbc::Derby.load_driver
54
- @database = Sequel.connect(mixin_settings['jdbc_connection_string'], :user=> nil, :password=> nil)
55
- @database.create_table :test_table do
21
+ db.create_table :test_table do
56
22
  DateTime :created_at
57
23
  Integer :num
58
24
  end
59
- test_table = @database[:test_table]
60
- settings = {"statement" => "SELECT num, created_at FROM test_table WHERE created_at > :sql_last_start"}
61
- plugin = LogStash::Inputs::Jdbc.new(mixin_settings.merge(settings))
62
- plugin.register
63
- q = Queue.new
25
+ end
26
+
27
+ after :each do
28
+ db.drop_table(:test_table)
29
+ end
30
+
31
+ context "when registering and tearing down" do
32
+ let(:settings) { {"statement" => "SELECT 1 as col1 FROM test_table"} }
33
+
34
+ it "should register without raising exception" do
35
+ expect { plugin.register }.to_not raise_error
36
+ plugin.teardown
37
+ end
38
+
39
+ it "should teardown without raising exception" do
40
+ plugin.register
41
+ expect { plugin.teardown }.to_not raise_error
42
+ end
43
+ end
44
+
45
+ context "when neither statement and statement_filepath arguments are passed" do
46
+ it "should fail to register" do
47
+ expect{ plugin.register }.to raise_error(LogStash::ConfigurationError)
48
+ end
49
+ end
50
+
51
+ context "when both statement and statement_filepath arguments are passed" do
52
+ let(:statement) { "SELECT * from test_table" }
53
+ let(:statement_file_path) { Stud::Temporary.pathname }
54
+ let(:settings) { { "statement_filepath" => statement_file_path, "statement" => statement } }
55
+
56
+ it "should fail to register" do
57
+ expect{ plugin.register }.to raise_error(LogStash::ConfigurationError)
58
+ end
59
+ end
60
+
61
+ context "when statement is passed in from a file" do
62
+ let(:statement) { "SELECT * from test_table" }
63
+ let(:statement_file_path) { Stud::Temporary.pathname }
64
+ let(:settings) { { "statement_filepath" => statement_file_path } }
65
+
66
+ before do
67
+ File.write(statement_file_path, statement)
68
+ plugin.register
69
+ end
70
+
71
+ after do
72
+ plugin.teardown
73
+ end
74
+
75
+ it "should read in statement from file" do
76
+ expect(plugin.statement).to eq(statement)
77
+ end
78
+ end
79
+
80
+ context "when passing parameters" do
81
+ let(:settings) do
82
+ {
83
+ "statement" => "SELECT :num_param as num_param FROM SYSIBM.SYSDUMMY1",
84
+ "parameters" => {"num_param" => 10}
85
+ }
86
+ end
87
+
88
+ before do
89
+ plugin.register
90
+ end
91
+
92
+ after do
93
+ plugin.teardown
94
+ end
95
+
96
+ it "should retrieve params correctly from Event" do
97
+ plugin.run(queue)
98
+ expect(queue.pop['num_param']).to eq(settings['parameters']['num_param'])
99
+ end
100
+ end
101
+
102
+ context "when scheduling" do
103
+ let(:settings) { {"statement" => "SELECT 1 as num_param FROM SYSIBM.SYSDUMMY1", "schedule" => "* * * * * UTC"} }
104
+
105
+ before do
106
+ plugin.register
107
+ end
108
+
109
+ it "should properly schedule" do
110
+ Timecop.travel(Time.new(2000))
111
+ Timecop.scale(60)
112
+ runner = Thread.new do
113
+ plugin.run(queue)
114
+ end
115
+ sleep 3
116
+ plugin.teardown
117
+ runner.kill
118
+ runner.join
119
+ expect(queue.size).to eq(2)
120
+ Timecop.return
121
+ end
122
+
123
+ end
124
+
125
+ context "when iterating result-set via paging" do
126
+
127
+ let(:settings) do
128
+ {
129
+ "statement" => "SELECT * from test_table",
130
+ "jdbc_paging_enabled" => true,
131
+ "jdbc_page_size" => 20
132
+ }
133
+ end
134
+
135
+ let(:num_rows) { 1000 }
136
+
137
+ before do
138
+ plugin.register
139
+ end
140
+
141
+ after do
142
+ plugin.teardown
143
+ end
144
+
145
+ it "should fetch all rows" do
146
+ num_rows.times do
147
+ db[:test_table].insert(:num => 1, :created_at => Time.now.utc)
148
+ end
149
+
150
+ plugin.run(queue)
151
+
152
+ expect(queue.size).to eq(num_rows)
153
+ end
154
+
155
+ end
156
+
157
+ context "when iteratively running plugin#run" do
158
+ let(:settings) do
159
+ {"statement" => "SELECT num, created_at FROM test_table WHERE created_at > :sql_last_start"}
160
+ end
161
+
162
+ let(:nums) { [10, 20, 30, 40, 50] }
64
163
 
65
- nums = [10, 20, 30, 40, 50]
66
- plugin.run(q)
67
- test_table.insert(:num => nums[0], :created_at => Time.now.utc)
68
- test_table.insert(:num => nums[1], :created_at => Time.now.utc)
69
- plugin.run(q)
70
- test_table.insert(:num => nums[2], :created_at => Time.now.utc)
71
- test_table.insert(:num => nums[3], :created_at => Time.now.utc)
72
- test_table.insert(:num => nums[4], :created_at => Time.now.utc)
73
- plugin.run(q)
164
+ before do
165
+ plugin.register
166
+ end
74
167
 
75
- actual_sum = 0
76
- until q.empty? do
77
- actual_sum += q.pop['num']
168
+ after do
169
+ plugin.teardown
78
170
  end
79
171
 
80
- plugin.teardown
172
+ it "should successfully iterate table with respect to field values" do
173
+ test_table = db[:test_table]
174
+
175
+ plugin.run(queue)
176
+ test_table.insert(:num => nums[0], :created_at => Time.now.utc)
177
+ test_table.insert(:num => nums[1], :created_at => Time.now.utc)
178
+ plugin.run(queue)
179
+ test_table.insert(:num => nums[2], :created_at => Time.now.utc)
180
+ test_table.insert(:num => nums[3], :created_at => Time.now.utc)
181
+ test_table.insert(:num => nums[4], :created_at => Time.now.utc)
182
+ plugin.run(queue)
183
+
184
+ actual_sum = 0
185
+ until queue.empty? do
186
+ actual_sum += queue.pop['num']
187
+ end
188
+
189
+ expect(actual_sum).to eq(nums.inject{|sum,x| sum + x })
190
+ end
191
+ end
192
+
193
+ context "when previous runs are to be respected" do
194
+
195
+ let(:settings) do
196
+ { "statement" => "SELECT * FROM test_table",
197
+ "last_run_metadata_path" => Stud::Temporary.pathname }
198
+ end
199
+
200
+ let(:last_run_time) { Time.at(1).utc }
201
+
202
+ before do
203
+ File.write(settings["last_run_metadata_path"], YAML.dump(last_run_time))
204
+ plugin.register
205
+ end
206
+
207
+ after do
208
+ plugin.teardown
209
+ end
210
+
211
+ it "should respect last run metadata" do
212
+ expect(plugin.instance_variable_get("@sql_last_start")).to eq(last_run_time)
213
+ end
214
+ end
215
+
216
+ context "when doing a clean run" do
217
+
218
+ let(:settings) do
219
+ {
220
+ "statement" => "SELECT * FROM test_table",
221
+ "last_run_metadata_path" => Stud::Temporary.pathname,
222
+ "clean_run" => true
223
+ }
224
+ end
225
+
226
+ let(:last_run_time) { Time.at(1).utc }
227
+
228
+ before do
229
+ File.write(settings["last_run_metadata_path"], YAML.dump(last_run_time))
230
+ plugin.register
231
+ end
232
+
233
+ after do
234
+ plugin.teardown
235
+ end
81
236
 
82
- insist { actual_sum } == nums.inject{|sum,x| sum + x }
237
+ it "should ignore last run metadata if :clean_run set to true" do
238
+ expect(plugin.instance_variable_get("@sql_last_start")).to eq(Time.at(0).utc)
239
+ end
240
+ end
241
+
242
+ context "when state is not to be persisted" do
243
+ let(:settings) do
244
+ {
245
+ "statement" => "SELECT * FROM test_table",
246
+ "last_run_metadata_path" => Stud::Temporary.pathname,
247
+ "record_last_run" => false
248
+ }
249
+ end
250
+
251
+ before do
252
+ plugin.register
253
+ end
254
+
255
+ after do
256
+ plugin.teardown
257
+ end
258
+
259
+ it "should not save state if :record_last_run is false" do
260
+ expect(File).not_to exist(settings["last_run_metadata_path"])
261
+ end
262
+ end
263
+
264
+ context "when setting fetch size" do
265
+
266
+ let(:settings) do
267
+ {
268
+ "statement" => "SELECT * from test_table",
269
+ "jdbc_fetch_size" => 1
270
+ }
271
+ end
272
+
273
+ let(:num_rows) { 10 }
274
+
275
+ before do
276
+ num_rows.times do
277
+ db[:test_table].insert(:num => 1, :created_at => Time.now.utc)
278
+ end
279
+
280
+ plugin.register
281
+ end
282
+
283
+ after do
284
+ plugin.teardown
285
+ end
286
+
287
+ it "should fetch all rows" do
288
+ plugin.run(queue)
289
+ expect(queue.size).to eq(num_rows)
290
+ end
291
+ end
292
+
293
+ context "when driver is not found" do
294
+ let(:settings) { { "statement" => "SELECT * FROM test_table" } }
295
+
296
+ before do
297
+ mixin_settings['jdbc_driver_class'] = "org.not.ExistsDriver"
298
+ end
299
+
300
+ it "should fail" do
301
+ expect { plugin.register }.to raise_error(LogStash::ConfigurationError)
302
+ end
83
303
  end
84
304
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-jdbc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-04 00:00:00.000000000 Z
11
+ date: 2015-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logstash-core
@@ -138,6 +138,7 @@ files:
138
138
  - CHANGELOG.md
139
139
  - Gemfile
140
140
  - LICENSE
141
+ - NOTICE.TXT
141
142
  - README.md
142
143
  - Rakefile
143
144
  - lib/logstash/inputs/jdbc.rb