fluent-plugin-sqlquery-ssh 0.1.10

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 783745761f7b0874235b8e93946c8f09845794ad
4
+ data.tar.gz: 736147289cd85f28d7e8eda154c229d2b2d244ab
5
+ SHA512:
6
+ metadata.gz: 82b1ce653d51a8e33138fa5d0336b69366a749b5e9c8a730f35dbb5be57ff4ce01aab4d28f38a5e80b030957960415e78e189aa3f6cfae994c1ab7134a7041f2
7
+ data.tar.gz: e3ceb2f54ffab7efa67d100e8a1e533bbb83faf04b3d663b9d31adabbafc0faaec47c063c29e065381cb8f264092e3a1732db41f03c44ca1e33955016966bfff
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-munin.gemspec
4
+ gemspec
@@ -0,0 +1,14 @@
1
+ Copyright (c) 2012- Kentaro Yoshida
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
@@ -0,0 +1,43 @@
1
+
2
+ ===========================
3
+
4
+ Fluentd Input plugin to execute mysql query and fetch rows across multiple dbs based on an initial shard query returning array of host/db to hit
5
+
6
+ ## Installation
7
+
8
+ install with gem or fluent-gem command as:
9
+
10
+ ```
11
+ # for fluentd
12
+ $ fluentd-gem install fluent-plugin-sqlquery-ssh
13
+
14
+ # for td-agent
15
+ $ sudo /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-sqlquery-ssh
16
+ ```
17
+
18
+ ## Configuration
19
+
20
+ ### Config Sample
21
+ `````
22
+ <source>
23
+ type sqlquery_ssh
24
+ shard_map_query SELECT database_host, group_concat(database_name) AS database_name FROM cust_customer WHERE status = 'customer' AND collect_stats=1 GROUP BY database_host order by database_host DESC;
25
+ query SELECT database() as customer from invoice
26
+ host system-reporting.db.prd.us-east-silo.ls #only use for reporting slaves
27
+ ssh_gateway mgmt.us-east-silo.lightspeedapp.com
28
+ skip_shards [""] #currently broken. use shard query to reduce set.
29
+ ssh_username #user name with jumphost access
30
+ db_username #reporting db username (do not use this on prod other than reporting)
31
+ db_password #the db password for the user above.
32
+ nest_result no #whether to nest the result set
33
+ nest_key data #key to nest
34
+ row_count no #add row count to the return
35
+ row_count_key row_count
36
+ tag debug #the matching tag
37
+ </source>
38
+
39
+ <match debug*>
40
+ type stdout
41
+ </match>
42
+ `````
43
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ Rake::TestTask.new(:test) do |test|
4
+ test.libs << 'lib' << 'test'
5
+ test.pattern = 'test/**/test_*.rb'
6
+ test.verbose = true
7
+ end
8
+
9
+ task :default => :test
10
+
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "fluent-plugin-sqlquery-ssh"
6
+ s.version = "0.1.10"
7
+ s.license = "Apache-2.0"
8
+ s.authors = "Niall Brown"
9
+ s.email = "niall.brown@lightspeedretail.com"
10
+ # s.homepage = "https://github.com/y-ken/fluent-plugin-mysql-query-ssh"
11
+ s.summary = %q{Fluentd Input plugin to execute mysql query and fetch rows. It is useful for stationary interval metrics measurement.}
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_development_dependency "rake", ['~> 10.0', '>= 10.0.4']
19
+ s.add_development_dependency "test-unit", ['~> 3.1', '>= 3.1.0']
20
+ s.add_runtime_dependency "fluentd", [">= 0.10.30", "< 2"]
21
+ s.add_runtime_dependency "mysql2", "~> 0.3.11"
22
+ s.add_runtime_dependency "net-ssh-gateway", ['~> 1.2', '>= 1.2.0']
23
+ end
@@ -0,0 +1,259 @@
1
+ require 'fluent/plugin/input'
2
+ require 'fluent/config/error'
3
+
4
+ module Fluent::Plugin
5
+ class SQLQueryInput < Input
6
+ Fluent::Plugin.register_input('sqlquery_ssh', self)
7
+
8
+ helpers :thread, :storage, :compat_parameters
9
+ unless method_defined?(:log)
10
+ define_method(:log) { $log }
11
+ end
12
+
13
+ def initialize
14
+ super
15
+ require 'mysql2'
16
+ require 'net/ssh/gateway'
17
+ require 'bigdecimal'
18
+ end
19
+
20
+ config_param :host, :string, :default => '127.0.0.1'
21
+ config_param :shard_db_name, :string, :default => 'customer'
22
+ config_param :skip_shards, :array, :default => nil
23
+ config_param :shard_map_query, :string, :default => nil
24
+ config_param :ssh_key, :integer, :default => 'id_rsa'
25
+ config_param :ssh_local_port, :integer, :default => 33009
26
+ config_param :ssh_gateway, :string, :default => nil
27
+ config_param :ssh_username, :string, :default => nil, :secret => true
28
+ config_param :ssh_passphrase, :string, :default => nil, :secret => true
29
+ config_param :db_username, :string, :default => 'root', :secret => true
30
+ config_param :db_password, :string, :default => 'atqM2rQZxp9GMY', :secret => true
31
+ #config_param :database, :string, :default => nil
32
+ #config_param :databases, :array, :default => nil #databases if passed in.
33
+ config_param :db_port, :integer, :default => 33006
34
+ config_param :encoding, :string, :default => 'utf8'
35
+ config_param :interval, :time, :default => '1d'
36
+ config_param :tag, :string
37
+ config_param :cast, :hash, :default => nil #cast a hash of key (column) to cast as (type)
38
+ config_param :query, :string
39
+ config_param :nest_result, :bool, :default => false
40
+ config_param :nest_key, :string, :default => 'data'
41
+ config_param :row_count, :bool, :default => true
42
+ config_param :row_count_key, :string, :default => 'row_count'
43
+ config_param :record_hostname, :bool, :default => true
44
+
45
+ config_param :connect_timeout, :integer, :default => '30'
46
+ config_param :read_timeout, :integer, :default => '600'
47
+ config_param :init_sql, :string, :default => nil
48
+
49
+
50
+ def configure(conf)
51
+ super
52
+ end
53
+
54
+ def start
55
+ super
56
+ #thread_create(:, &method(:run))
57
+ @thread = Thread.new(&method(:run))
58
+ @ssh_port = nil
59
+ end
60
+
61
+ def shutdown
62
+ @stop = "ending run"
63
+ @ssh_gate.close(@ssh_local_port)
64
+ @mysql = nil
65
+ if @thread
66
+ @thread.join
67
+ @thread = nil
68
+ end
69
+ super
70
+ end
71
+
72
+ def run
73
+ begin
74
+ $log.info "Begin run."
75
+ loop do
76
+ break if @stop
77
+ @shard_map ||= get_shards
78
+ #loop thru shards
79
+ begin
80
+ @shard_map.each do |shard|
81
+ break if @stop
82
+ shard.each do |remotehost,db_names|
83
+ break if @stop
84
+ @mysql = nil #close connection
85
+ @ssh_gate.close(@ssh_local_port)
86
+ hostname = remotehost.gsub(/mylocaldb([0-9]*)/, "customers\\1-reporting.db.prd.us-east-silo.ls")
87
+ @host = hostname
88
+ $log.info "start shard: #{hostname}\n"
89
+ #handle the dbs
90
+ process_shard_dbs(remotehost, db_names)
91
+ $log.info "finished shard: #{hostname}\n"
92
+ end
93
+ end
94
+ @mysql = nil #close connection
95
+ @ssh_gate.close(@ssh_local_port)
96
+ #process_shards if @shard_map
97
+ $log.info "completed run all shards. sleeping for #{@interval}\n"
98
+ #end don't loop
99
+ rescue Exception => e
100
+ $log.error "error on shard: #{@current_shard}\n"
101
+ $log.error "#{e.message}\n#{e.backtrace.join("\n")}\n"
102
+ end
103
+ sleep @interval
104
+ end
105
+ rescue
106
+ # ignore
107
+ end
108
+ end
109
+ #get the shards. put the info somewhere?
110
+ def get_shards
111
+ begin
112
+ @conn ||= get_connection
113
+ @conn.select_db(@shard_db_name) #switch the customerdb
114
+ $log.info "Getting Shard Mapping: [#{@shard_map_query}]"
115
+ @conn.query("SET SESSION group_concat_max_len= 1844674407370")
116
+ shard_mapping = Array.new
117
+ shardlist = @conn.query(@shard_map_query, :cast => false, :cache_rows => false)
118
+ shardlist.each do |row|
119
+ customer_dbs = row['database_name'].split(',')
120
+ cust_host = row['database_host']
121
+ shard = Hash.new(cust_host)
122
+ shard[cust_host] = customer_dbs
123
+ shard_mapping.push(shard)
124
+ $log.info "adding #{customer_dbs}\n"
125
+ end
126
+ @conn.close
127
+ return shard_mapping
128
+ rescue Exception => e
129
+ $log.error "Can't get shard info\n"
130
+ $log.error "#{e.message}\n#{e.backtrace.join("\n")}\n"
131
+ exit!
132
+ end
133
+ end
134
+
135
+ def process_shard_dbs(remotehost, db_names)
136
+ db_names.each do |db|
137
+ $log.info "customer: #{db} shard: #{remotehost}"
138
+ return if @stop
139
+ @mysql ||= get_connection #open conn to shard.
140
+ @current_shard = remotehost
141
+ @mysql.select_db(db) #switch the customerdb
142
+ #$log.info "check customer: #{db}\n"
143
+ tag = "#{@tag}_#{db}_#{remotehost}"
144
+ record = Hash.new
145
+ result = get_exec_result
146
+ #record.store(@row_count_key, result.size) if @row_count
147
+ result.each_with_index do |data, index|
148
+ router.emit(tag, Fluent::Engine.now, record.merge(data))
149
+ end
150
+ $log.info "completed #{db} rows: #{result.size}\n"
151
+ end
152
+ end
153
+
154
+ #get the query results
155
+ def get_exec_result
156
+ result = Array.new
157
+ stmt = query(@query)
158
+ stmt.each do |row|
159
+ #to be replaced by the cast array
160
+ row['avg'] = row['avg'].to_f.round(2) if row['avg']
161
+ row['daily_total'] = row['daily_total'].to_f if row['daily_total']
162
+ row['total_transactions'] = row['total_transactions'].to_i if row['total_transactions']
163
+ row['cust_of_customer'] = row['cust_of_customer'].to_i if row['cust_of_customer']
164
+ row['shop_count'] = row['shop_count'].to_i if row['shop_count']
165
+ row['items'] = row['items'].to_i if row['items']
166
+ row['register_count'] = row['register_count'].to_i if row['register_count']
167
+ row['customer_shard'] = @current_shard if @current_shard
168
+ result.push(row)
169
+ end
170
+ return result
171
+ end
172
+
173
+ def query(query)
174
+ begin
175
+ return if @mysql.nil?
176
+ return @mysql.query(query, :cast => false, :cache_rows => false)
177
+ rescue Exception => e
178
+ $log.error "fluent-plugin-sqlquery-ssh: Query ERROR!\n"
179
+ $log.error "#{e.message}\n#{e.backtrace.join("\n")}\n"
180
+ end
181
+ end
182
+
183
+ # Returns +true+ if the column is either of type string or text.
184
+ def text?
185
+ type == :string || type == :text
186
+ end
187
+
188
+ # Returns +true+ if the column is either of type integer, float or decimal.
189
+ def number?
190
+ type == :integer || type == :float || type == :decimal
191
+ end
192
+
193
+ # convert something to a boolean
194
+ def value_to_boolean(value)
195
+ if value.is_a?(String) && value.empty?
196
+ nil
197
+ else
198
+ TRUE_VALUES.include?(value)
199
+ end
200
+ end
201
+
202
+ # Used to convert values to integer.
203
+ # handle the case when an integer column is used to store boolean values
204
+ def value_to_integer(value)
205
+ case value
206
+ when TrueClass, FalseClass
207
+ value ? 1 : 0
208
+ else
209
+ value.to_i rescue nil
210
+ end
211
+ end
212
+
213
+ # convert something to a BigDecimal
214
+ def value_to_decimal(value)
215
+ # Using .class is faster than .is_a? and
216
+ # subclasses of BigDecimal will be handled
217
+ # in the else clause
218
+ if value.class == BigDecimal
219
+ value
220
+ elsif value.respond_to?(:to_d)
221
+ value.to_d
222
+ else
223
+ value.to_s.to_d
224
+ end
225
+ end
226
+
227
+ def get_connection
228
+ begin
229
+ $log.info "Opening ssh tunnel to #{@ssh_gateway}\n"
230
+ @ssh_gate ||= Net::SSH::Gateway.new(@ssh_gateway, @ssh_username, :verbose => :debug ,:keys => [@ssh_key], :forward_agent => true)
231
+ @ssh_port = @ssh_gate.open(@host, 3306, @ssh_local_port) || @db_port
232
+ $log.info "connecting to #{@host}\n"
233
+ return Mysql2::Client.new({
234
+ :host => '127.0.0.1',
235
+ :port => @ssh_port,
236
+ :username => @db_username,
237
+ :password => @db_password,
238
+ :encoding => @encoding,
239
+ :reconnect => true,
240
+ :read_timeout => 600,
241
+ :connect_timeout => 30
242
+ })
243
+ rescue Exception => e
244
+ $log.error "fluent-plugin-sqlquery-ssh: Main Connect ERROR!\n"
245
+ $log.error "MSG: #{e.message}\n TRACE:#{e.backtrace.join("\n")} PORT: #{@ssh_port}\n"
246
+ #sleep @interval
247
+ #retry
248
+ end
249
+ return nil
250
+ end
251
+
252
+
253
+ def get_mysql_hostname
254
+ query("SHOW VARIABLES LIKE 'hostname'").each do |row|
255
+ return row.fetch('Value')
256
+ end
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'fluent/test'
15
+ unless ENV.has_key?('VERBOSE')
16
+ nulllogger = Object.new
17
+ nulllogger.instance_eval {|obj|
18
+ def method_missing(method, *args)
19
+ # pass
20
+ end
21
+ }
22
+ $log = nulllogger
23
+ end
24
+
25
+ require 'fluent/plugin/in_mysql_query'
26
+
27
+ class Test::Unit::TestCase
28
+ end
@@ -0,0 +1,40 @@
1
+ require 'helper'
2
+
3
+ class SQLQueryInputTest < Test::Unit::TestCase
4
+ def setup
5
+ Fluent::Test.setup
6
+ end
7
+
8
+ CONFIG = %[
9
+ host localhost
10
+ port 3306
11
+ interval 30
12
+ tag input.mysql
13
+ query SHOW VARIABLES LIKE 'Thread_%'
14
+ record_hostname yes
15
+ ]
16
+
17
+ def create_driver(conf=CONFIG,tag='test')
18
+ Fluent::Test::OutputTestDriver.new(Fluent::MysqlQueryInput, tag).configure(conf)
19
+ end
20
+
21
+ def test_configure
22
+ assert_raise(Fluent::ConfigError) {
23
+ d = create_driver('')
24
+ }
25
+ d = create_driver %[
26
+ host localhost
27
+ port 3306
28
+ interval 30
29
+ tag input.mysql
30
+ query SHOW VARIABLES LIKE 'Thread_%'
31
+ record_hostname yes
32
+ ]
33
+ assert_equal 'localhost', d.instance.host
34
+ assert_equal 3306, d.instance.port
35
+ assert_equal 30, d.instance.interval
36
+ assert_equal 'input.mysql', d.instance.tag
37
+ assert_equal true, d.instance.record_hostname
38
+ assert_equal false, d.instance.nest_result
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-sqlquery-ssh
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.10
5
+ platform: ruby
6
+ authors:
7
+ - Niall Brown
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '10.0'
20
+ - - '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 10.0.4
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '10.0'
30
+ - - '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 10.0.4
33
+ - !ruby/object:Gem::Dependency
34
+ name: test-unit
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ~>
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: 3.1.0
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ~>
48
+ - !ruby/object:Gem::Version
49
+ version: '3.1'
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: 3.1.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: fluentd
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - '>='
58
+ - !ruby/object:Gem::Version
59
+ version: 0.10.30
60
+ - - <
61
+ - !ruby/object:Gem::Version
62
+ version: '2'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.10.30
70
+ - - <
71
+ - !ruby/object:Gem::Version
72
+ version: '2'
73
+ - !ruby/object:Gem::Dependency
74
+ name: mysql2
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ version: 0.3.11
80
+ type: :runtime
81
+ prerelease: false
82
+ version_requirements: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
86
+ version: 0.3.11
87
+ - !ruby/object:Gem::Dependency
88
+ name: net-ssh-gateway
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.2'
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 1.2.0
97
+ type: :runtime
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '1.2'
104
+ - - '>='
105
+ - !ruby/object:Gem::Version
106
+ version: 1.2.0
107
+ description:
108
+ email: niall.brown@lightspeedretail.com
109
+ executables: []
110
+ extensions: []
111
+ extra_rdoc_files: []
112
+ files:
113
+ - .gitignore
114
+ - Gemfile
115
+ - LICENSE.txt
116
+ - README.md
117
+ - Rakefile
118
+ - fluent-plugin-sqlquery-ssh.gemspec
119
+ - lib/fluent/plugin/in_sqlquery_ssh.rb
120
+ - test/helper.rb
121
+ - test/plugin/test_in_sqlquery_ssh.rb
122
+ homepage:
123
+ licenses:
124
+ - Apache-2.0
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.0.14.1
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Fluentd Input plugin to execute mysql query and fetch rows. It is useful
146
+ for stationary interval metrics measurement.
147
+ test_files:
148
+ - test/helper.rb
149
+ - test/plugin/test_in_sqlquery_ssh.rb