flydata 0.4.0 → 0.4.1

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.
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: flydata 0.4.0 ruby lib
5
+ # stub: flydata 0.4.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "flydata"
9
- s.version = "0.4.0"
9
+ s.version = "0.4.1"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Koichi Fujikawa", "Masashi Miyazaki", "Matthew Luu", "Mak Inada", "Sriram NS"]
14
- s.date = "2015-06-17"
14
+ s.date = "2015-06-19"
15
15
  s.description = "FlyData Agent"
16
16
  s.email = "sysadmin@flydata.com"
17
17
  s.executables = ["fdmysqldump", "flydata", "serverinfo"]
@@ -47,6 +47,10 @@ Gem::Specification.new do |s|
47
47
  "flydata-core/lib/flydata-core/fluent-plugins/multi_buffer.rb",
48
48
  "flydata-core/lib/flydata-core/fluent/config_helper.rb",
49
49
  "flydata-core/lib/flydata-core/logger.rb",
50
+ "flydata-core/lib/flydata-core/mysql/command_generator.rb",
51
+ "flydata-core/lib/flydata-core/mysql/compatibility_checker.rb",
52
+ "flydata-core/lib/flydata-core/query_job.rb",
53
+ "flydata-core/lib/flydata-core/query_job/redshift.rb",
50
54
  "flydata-core/lib/flydata-core/record/record.rb",
51
55
  "flydata-core/lib/flydata-core/redshift/string.rb",
52
56
  "flydata-core/lib/flydata-core/table_def.rb",
@@ -58,6 +62,9 @@ Gem::Specification.new do |s|
58
62
  "flydata-core/spec/config/user_maintenance_spec.rb",
59
63
  "flydata-core/spec/fluent/config_helper_spec.rb",
60
64
  "flydata-core/spec/logger_spec.rb",
65
+ "flydata-core/spec/mysql/command_generator_spec.rb",
66
+ "flydata-core/spec/mysql/compatibility_checker.rb",
67
+ "flydata-core/spec/query_job/redshift_spec.rb",
61
68
  "flydata-core/spec/redshift/string_spec.rb",
62
69
  "flydata-core/spec/spec_helper.rb",
63
70
  "flydata-core/spec/table_def/autoload_redshift_table_def_spec.rb",
@@ -145,7 +152,6 @@ Gem::Specification.new do |s|
145
152
  "lib/flydata/heroku/instance_methods.rb",
146
153
  "lib/flydata/log_monitor.rb",
147
154
  "lib/flydata/mysql/binlog_position.rb",
148
- "lib/flydata/mysql/mysql_util.rb",
149
155
  "lib/flydata/mysql/table_ddl.rb",
150
156
  "lib/flydata/output/forwarder.rb",
151
157
  "lib/flydata/parser/mysql/dump_parser.rb",
@@ -201,7 +207,6 @@ Gem::Specification.new do |s|
201
207
  "spec/flydata/helper/worker_spec.rb",
202
208
  "spec/flydata/heroku_spec.rb",
203
209
  "spec/flydata/mysql/binlog_position_spec.rb",
204
- "spec/flydata/mysql/mysql_util_spec.rb",
205
210
  "spec/flydata/mysql/table_ddl_spec.rb",
206
211
  "spec/flydata/output/forwarder_spec.rb",
207
212
  "spec/flydata/parser/mysql/alter_table_parser_spec.rb",
@@ -216,7 +221,7 @@ Gem::Specification.new do |s|
216
221
  ]
217
222
  s.homepage = "http://flydata.com/"
218
223
  s.licenses = ["All right reserved."]
219
- s.rubygems_version = "2.4.3"
224
+ s.rubygems_version = "2.4.6"
220
225
  s.summary = "FlyData Agent"
221
226
 
222
227
  if s.respond_to? :specification_version then
@@ -235,7 +240,7 @@ Gem::Specification.new do |s|
235
240
  s.add_runtime_dependency(%q<treetop>, [">= 1.5.3", "~> 1.5"])
236
241
  s.add_runtime_dependency(%q<sys-filesystem>, [">= 1.1.3", "~> 1.1"])
237
242
  s.add_runtime_dependency(%q<io-console>, [">= 0.4.2", "~> 0.4.2"])
238
- s.add_runtime_dependency(%q<kodama>, [">= 0.1.5", "~> 0.1.2"])
243
+ s.add_runtime_dependency(%q<kodama>, [">= 0.1.6", "~> 0.1.2"])
239
244
  s.add_runtime_dependency(%q<serverengine>, ["~> 1.5"])
240
245
  s.add_development_dependency(%q<jeweler>, [">= 1.8.8", "~> 1.8"])
241
246
  s.add_development_dependency(%q<rspec>, ["~> 3.0"])
@@ -259,7 +264,7 @@ Gem::Specification.new do |s|
259
264
  s.add_dependency(%q<treetop>, [">= 1.5.3", "~> 1.5"])
260
265
  s.add_dependency(%q<sys-filesystem>, [">= 1.1.3", "~> 1.1"])
261
266
  s.add_dependency(%q<io-console>, [">= 0.4.2", "~> 0.4.2"])
262
- s.add_dependency(%q<kodama>, [">= 0.1.5", "~> 0.1.2"])
267
+ s.add_dependency(%q<kodama>, [">= 0.1.6", "~> 0.1.2"])
263
268
  s.add_dependency(%q<serverengine>, ["~> 1.5"])
264
269
  s.add_dependency(%q<jeweler>, [">= 1.8.8", "~> 1.8"])
265
270
  s.add_dependency(%q<rspec>, ["~> 3.0"])
@@ -284,7 +289,7 @@ Gem::Specification.new do |s|
284
289
  s.add_dependency(%q<treetop>, [">= 1.5.3", "~> 1.5"])
285
290
  s.add_dependency(%q<sys-filesystem>, [">= 1.1.3", "~> 1.1"])
286
291
  s.add_dependency(%q<io-console>, [">= 0.4.2", "~> 0.4.2"])
287
- s.add_dependency(%q<kodama>, [">= 0.1.5", "~> 0.1.2"])
292
+ s.add_dependency(%q<kodama>, [">= 0.1.6", "~> 0.1.2"])
288
293
  s.add_dependency(%q<serverengine>, ["~> 1.5"])
289
294
  s.add_dependency(%q<jeweler>, [">= 1.8.8", "~> 1.8"])
290
295
  s.add_dependency(%q<rspec>, ["~> 3.0"])
@@ -1,3 +1,4 @@
1
+ require 'json'
1
2
  require 'flydata/api/base'
2
3
 
3
4
  module Flydata
@@ -17,6 +18,14 @@ module Flydata
17
18
  def cleanup_sync(data_entry_id, tables)
18
19
  @client.post("/#{@model_name.pluralize}/#{data_entry_id}/cleanup_sync", nil, {tables: tables.join(',')})
19
20
  end
21
+
22
+ # Update validity of tables
23
+ # table_validity_hash: { "bad_table": "error reason", "good_table": nil }
24
+ # table "bad_table" will be marked invalid with reason "error reason"
25
+ # table "good table" will be marked valid, that is, clear its error reason if it's set.
26
+ def update_table_validity(data_entry_id, table_validity_hash)
27
+ @client.post("/#{@model_name.pluralize}/#{data_entry_id}/update_table_validity", {:headers => {:content_type => :json}}, table_validity_hash.to_json)
28
+ end
20
29
  end
21
30
  end
22
31
  end
@@ -1,5 +1,6 @@
1
1
  require 'open3'
2
2
  require 'flydata/command/sync'
3
+ require 'flydata-core/mysql/command_generator'
3
4
 
4
5
  module Flydata
5
6
  module Command
@@ -8,7 +9,7 @@ module Flydata
8
9
  def run
9
10
  de = retrieve_sync_data_entry
10
11
  de['mysql_data_entry_preference'].delete('tables')
11
- cmd = Flydata::Mysql::MysqlUtil::generate_mysql_cmd(de['mysql_data_entry_preference'])
12
+ cmd = FlydataCore::Mysql::CommandGenerator::generate_mysql_cmd(de['mysql_data_entry_preference'])
12
13
  if $stdin.tty?
13
14
  # interactive shell
14
15
  system cmd
@@ -13,9 +13,9 @@ require 'flydata/output/forwarder'
13
13
  require 'flydata/parser/mysql/dump_parser'
14
14
  require 'flydata/preference/data_entry_preference'
15
15
  require 'flydata/sync_file_manager'
16
- require 'flydata/mysql/mysql_util'
17
16
  require 'flydata-core/table_def'
18
17
  require 'flydata/mysql/table_ddl'
18
+ require 'flydata-core/mysql/command_generator'
19
19
  #require 'ruby-prof'
20
20
 
21
21
  module Flydata
@@ -74,11 +74,8 @@ module Flydata
74
74
  exit 1
75
75
  end
76
76
 
77
- # Setup instance variables
78
- set_current_tables(tables)
79
-
80
77
  # Start initial sync with check
81
- handle_mysql_sync
78
+ handle_mysql_sync(tables)
82
79
 
83
80
  # Start continuous sync by starting fluentd process
84
81
  unless opts.no_flydata_start?
@@ -97,9 +94,6 @@ module Flydata
97
94
  # Public method
98
95
  # - Called from Sender#start/restart
99
96
  def try_mysql_sync
100
- # Setup instance variables
101
- set_current_tables
102
-
103
97
  # Start initial sync
104
98
  handle_mysql_sync
105
99
  rescue SyncDataEntryError
@@ -233,7 +227,7 @@ EOS
233
227
  Flydata::MysqlCompatibilityCheck.new(dp, de['mysql_data_entry_preference']).check
234
228
 
235
229
  # Set instance variables
236
- set_current_tables(tables)
230
+ set_current_tables(tables, include_invalid_tables: true)
237
231
 
238
232
  do_generate_table_ddl(de)
239
233
  end
@@ -290,10 +284,18 @@ EOS
290
284
 
291
285
  # Initial sync
292
286
 
293
- def handle_mysql_sync
287
+ def handle_mysql_sync(tables = nil)
294
288
  de = data_entry
295
289
 
296
- unless @new_tables.empty?
290
+ # Setup instance variables
291
+ sync_resumed = set_current_tables(tables)
292
+
293
+ if sync_resumed
294
+ # skip confirmation prompts and resume sync right away. #initial_sync knows
295
+ # where to resume from.
296
+ log_info_stdout("Resuming the initial sync...")
297
+ initial_sync(de, sync_resumed: true)
298
+ elsif !@new_tables.empty?
297
299
  show_purpose_name
298
300
  unsynced_table_message = "We've noticed that these tables have not been synced yet: #{@new_tables.join(", ")}\n"
299
301
  unless @ddl_tables.empty?
@@ -305,7 +307,7 @@ EOS
305
307
  end
306
308
  log_info_stdout(unsynced_table_message)
307
309
  if ask_yes_no("Do you want to run initial sync on all of these tables now?")
308
- initial_sync(de)
310
+ initial_sync(de, sync_resumed: false)
309
311
  else
310
312
  #If generate_table_ddl has not been run for these tables, warn user
311
313
  unless @ddl_tables.empty?
@@ -318,13 +320,16 @@ EOS
318
320
  end
319
321
  end
320
322
 
321
- def initial_sync(de)
323
+ def initial_sync(de, opt)
322
324
  # Load sync information from file
323
- log_info_stdout("Checking FlyData Servers for existing buffer data...")
324
- load_sync_info(de)
325
325
  validate_initial_sync_status
326
326
  begin
327
- flush_buffer_and_stop(target_tables_for_api) unless @full_initial_sync
327
+ unless @full_initial_sync || opt[:sync_resumed]
328
+ # flush is unnecessary for full initial sync or sync resume because in either
329
+ # case it's guaranteed that agent is stopped with no leftover buffer.
330
+ log_info_stdout("Sending the existing buffer data...")
331
+ flush_buffer_and_stop(target_tables_for_api)
332
+ end
328
333
  sync_mysql_to_redshift(de)
329
334
  rescue ServerDataProcessingTimeout => e
330
335
  ee = ServerDataProcessingTimeout.new("Delayed Data Processing")
@@ -748,6 +753,7 @@ EOM
748
753
  mp = de['mysql_data_entry_preference']
749
754
 
750
755
  tables = opts.all_tables? ? @full_tables : (@input_tables.empty? ? @new_tables : @input_tables)
756
+
751
757
  raise "There are no valid unsynced tables, if you want to just get ddl for all tables, please run \`flydata sync:generate_table_ddl --all-tables\`" if tables.empty?
752
758
 
753
759
  %w(host username database).each do |conf_name|
@@ -759,7 +765,7 @@ EOM
759
765
 
760
766
  create_flydata_ctl_table = true
761
767
  option = {skip_parimay_key_check: opts.skip_primary_key_check?}.merge(mp)
762
- Mysql::MysqlUtil.each_mysql_tabledef(tables, option) do |mysql_tabledef, error|
768
+ missing_tables = FlydataCore::Mysql::CommandGenerator.each_mysql_tabledef(tables, option) do |mysql_tabledef, error|
763
769
  if error
764
770
  error_list << error.err_hash
765
771
  next
@@ -768,18 +774,37 @@ EOM
768
774
  puts FlydataCore::TableDef::SyncRedshiftTableDef.from_flydata_tabledef(flydata_tabledef, flydata_ctl_table: create_flydata_ctl_table, schema_name: schema_name, ctl_only: opts.ctl_only?)
769
775
  create_flydata_ctl_table = false
770
776
  end
777
+ if missing_tables
778
+ missing_tables.each {|missing_table| error_list << { error: 'table does not exist in the MySQL database', table: missing_table } }
779
+ end
780
+ table_validity_hash = {}
781
+ tables_without_error = tables
771
782
  unless error_list.empty?
772
- log_error_stderr("We have noticed the following error(s):")
783
+ log_error_stderr("\n\nERROR: FlyData Sync will not sync the following table(s) due to an error.")
773
784
  group_error = error_list.group_by {|d| d[:error]}
774
- group_error.each_key do |a|
775
- log_error_stderr("The following table(s) have #{a}:")
776
- group_error[a].each do |hash|
777
- log_error_stderr(" - #{hash[:table]}") if hash[:table]
785
+ group_error.each_key do |error|
786
+ group_error[error].each do |hash|
787
+ if table = hash[:table]
788
+ log_error_stderr(" - #{table} (#{error})")
789
+ table_validity_hash[table] = error
790
+ end
778
791
  end
779
792
  end
780
- log_error_stderr("Please fix the above error(s) to try to sync those table(s) or contact us for further help.")
793
+ log_error_stderr(<<EOS)
794
+ To sync these table(s), please fix the error(s) and run "flydata sync:generate_table_ddl" again.
795
+ EOS
796
+ tables_without_error = tables - error_list.inject([]){|arr, err| arr << err[:table] if err[:table]}
797
+ unless tables_without_error.empty?
798
+ log_error_stderr(<<EOS)
799
+
800
+ The other tables are ready to sync. To start sync, run the generated script on the Redshift cluster and run "flydata start".
801
+ EOS
802
+ end
781
803
  end
782
- tables_without_error = tables - error_list.inject([]){|arr, err| arr << err[:table] if err[:table]}
804
+
805
+ fixed_tables = @invalid_tables & tables_without_error
806
+ fixed_tables.each {|table| table_validity_hash[table] = nil }
807
+ flydata.data_entry.update_table_validity(de['id'], {updated_tables: table_validity_hash}) unless table_validity_hash.empty?
783
808
 
784
809
  sync_fm = create_sync_file_manager(de)
785
810
  sync_fm.save_generated_ddl(tables_without_error, Mysql::TableDdl::VERSION)
@@ -819,20 +844,25 @@ Thank you for using FlyData!
819
844
 
820
845
  # Utility methods
821
846
 
822
- def set_current_tables(input_tables = nil)
847
+ def set_current_tables(input_tables = nil, options = {})
823
848
  de = data_entry
824
849
  sync_fm = create_sync_file_manager(de)
825
850
  @input_tables = input_tables || []
826
- @full_tables = de['mysql_data_entry_preference']['tables']
851
+ @invalid_tables = de['mysql_data_entry_preference']['invalid_tables'] # tables marked as invalid as a result of previous check
852
+ @full_tables = options[:include_invalid_tables] ? de['mysql_data_entry_preference']['tables'] + @invalid_tables : de['mysql_data_entry_preference']['tables']
827
853
 
828
854
  @new_tables = sync_fm.get_new_table_list(@full_tables, "pos")
829
855
  @ddl_tables = sync_fm.get_new_table_list(@full_tables, "generated_ddl")
830
856
 
831
857
  @full_initial_sync = (@new_tables == @full_tables)
832
858
 
859
+ sync_resumed = load_sync_info(sync_fm)
860
+
833
861
  sync_fm.close
834
862
 
835
863
  verify_input_tables(@input_tables, @full_tables)
864
+
865
+ sync_resumed
836
866
  end
837
867
 
838
868
  def validate_initial_sync_status
@@ -847,17 +877,17 @@ Thank you for using FlyData!
847
877
  end
848
878
  end
849
879
 
850
- def load_sync_info(de)
880
+ def load_sync_info(sync_fm)
881
+ sync_resumed = false
851
882
  # for debug
852
883
  raise "!AssertionError. set_current_tables needs to be called in advance" if @full_tables.nil?
853
884
 
854
- sync_fm = create_sync_file_manager(de)
855
885
  if (rs = sync_fm.load_sync_info)
856
886
  @full_initial_sync = rs[:initial_sync]
857
887
  @input_tables = rs[:tables]
888
+ sync_resumed = true
858
889
  end
859
- sync_fm.close
860
- de
890
+ sync_resumed
861
891
  end
862
892
 
863
893
  def target_tables
@@ -886,7 +916,7 @@ Thank you for using FlyData!
886
916
  end
887
917
 
888
918
  def retrieve_sync_data_entry
889
- de = retrieve_data_entries.first unless de
919
+ de = retrieve_data_entries.first
890
920
  raise "There are no data entries." unless de
891
921
  case de['type']
892
922
  when 'RedshiftMysqlDataEntry'
@@ -897,6 +927,7 @@ Thank you for using FlyData!
897
927
  else
898
928
  mp['tables'] = mp['tables'].split(",").uniq
899
929
  end
930
+ mp['invalid_tables'] = mp['invalid_tables'].kind_of?(String) ? mp['invalid_tables'].split(",").uniq : []
900
931
 
901
932
  unless mp['ssl_ca_content'].to_s.strip.empty?
902
933
  sync_fm = create_sync_file_manager(de)
@@ -1,15 +1,14 @@
1
1
  require 'mysql2'
2
2
  require 'flydata/command_loggable'
3
- require 'flydata/mysql/mysql_util'
3
+ require 'flydata-core/mysql/command_generator'
4
+ require 'flydata-core/mysql/compatibility_checker'
5
+ require 'flydata-core/errors'
4
6
 
5
7
  module Flydata
6
8
 
7
9
  class CompatibilityCheck
8
10
  include CommandLoggable
9
11
 
10
- class CompatibilityError < StandardError
11
- end
12
-
13
12
  def initialize(dp_hash, de_hash=nil, options={})
14
13
  @dp = dp_hash
15
14
  @errors=[]
@@ -21,7 +20,7 @@ module Flydata
21
20
  send(m)
22
21
  rescue ArgumentError => e
23
22
  # ignore
24
- rescue CompatibilityError => e
23
+ rescue FlydataCore::CompatibilityError => e
25
24
  @errors << e
26
25
  end
27
26
  end
@@ -40,8 +39,6 @@ module Flydata
40
39
  end
41
40
 
42
41
  class AgentCompatibilityCheck < CompatibilityCheck
43
- class AgentCompatibilityError < StandardError
44
- end
45
42
 
46
43
  TCP_PORT=45326
47
44
  SSL_PORT=45327
@@ -66,21 +63,13 @@ module Flydata
66
63
  errors.each do |port, e|
67
64
  message += " Port #{port}, Error #{e.class.name}: #{e.to_s}"
68
65
  end
69
- raise AgentCompatibilityError, message
66
+ raise FlydataCore::AgentCompatibilityError, message
70
67
  end
71
68
  end
72
69
  end
73
70
 
74
71
  class MysqlCompatibilityCheck < CompatibilityCheck
75
72
 
76
- class MysqlCompatibilityError < StandardError
77
- end
78
-
79
- SELECT_QUERY_TMPLT = "SELECT %s"
80
- BINLOG_RETENTION_HOURS = 24
81
- SELECT_TABLE_INFO_TMPLT = "SELECT table_name, table_type, engine FROM information_schema.tables WHERE table_schema = '%s' and table_name in(%s)"
82
-
83
- #def initialize(de_hash, dump_dir=nil)
84
73
  def initialize(dp_hash, de_hash, options={})
85
74
  super
86
75
  @db_opts = [:host, :port, :username, :password, :database, :ssl_ca].inject({}) {|h, sym| h[sym] = de_hash[sym.to_s]; h}
@@ -100,47 +89,11 @@ module Flydata
100
89
  end
101
90
 
102
91
  def check_mysql_user_compat
103
- databases = ['mysql', @db_opts[:database]]
104
- get_grant_regex = /GRANT (?<privs>.*) ON (`)?(?<db_name>[^`]*)(`)?\.\* TO '#{@db_opts[:username]}/
105
- necessary_permission_fields = ["SELECT","RELOAD","LOCK TABLES","REPLICATION SLAVE","REPLICATION CLIENT"]
106
- all_privileges_field = ["ALL PRIVILEGES"]
107
-
108
- # Do not catch MySQL connection problem because check should stop if no MySQL connection can be made.
109
- grants_sql = "SHOW GRANTS"
110
- client = Mysql2::Client.new(@db_opts)
111
- result = client.query(grants_sql)
112
- client.close
113
-
114
- found_priv = Hash[databases.map {|d| [d,[]]}]
115
- missing_priv = {}
116
-
117
- result.each do |res|
118
- # SHOW GRANTS should only return one column
119
- res_value = res.values.first
120
- matched_values = res_value.match(get_grant_regex)
121
- next unless matched_values
122
- line_priv = matched_values["privs"].split(", ")
123
- if matched_values["db_name"] == "*"
124
- return true if (all_privileges_field - line_priv).empty?
125
- databases.each {|d| found_priv[d] << line_priv }
126
- elsif databases.include? matched_values["db_name"]
127
- if (all_privileges_field - line_priv).empty?
128
- found_priv[matched_values["db_name"]] = necessary_permission_fields
129
- else
130
- found_priv[matched_values["db_name"]] << line_priv
131
- end
132
- end
133
- missing_priv = get_missing_privileges(found_priv, necessary_permission_fields)
134
- return true if missing_priv.empty?
135
- end
136
- error_text = "The user '#{@db_opts[:username]}' does not have the correct permissions to run FlyData Sync\n"
137
- error_text << " * These privileges are missing...\n"
138
- missing_priv.each_key {|db| error_text << " for the database '#{db}': #{missing_priv[db].join(", ")}\n"}
139
- raise MysqlCompatibilityError, error_text
92
+ FlydataCore::Mysql::SyncPermissionChecker.new(@db_opts).do_check
140
93
  end
141
94
 
142
95
  def check_mysql_protocol_tcp_compat
143
- query = Mysql::MysqlUtil.generate_mysql_show_grants_cmd(@db_opts)
96
+ query = FlydataCore::Mysql::CommandGenerator.generate_mysql_show_grants_cmd(@db_opts)
144
97
 
145
98
  Open3.popen3(query) do |stdin, stdout, stderr|
146
99
  stdin.close
@@ -151,64 +104,27 @@ module Flydata
151
104
  log_error("Error occured during access to mysql server.", {err: err_reason})
152
105
 
153
106
  unless /Warning: Using a password on the command line interface can be insecure/ === err_reason
154
- raise MysqlCompatibilityError, "Cannot connect to MySQL database. Please make sure you can connect with this command:\n $ mysql -u #{@db_opts[:username]} -h #{@db_opts[:host]} -P #{@db_opts[:port]} #{@db_opts[:database]} --protocol=tcp -p"
107
+ raise FlydataCore::MysqlCompatibilityError, "Cannot connect to MySQL database. Please make sure you can connect with this command:\n $ mysql -u #{@db_opts[:username]} -h #{@db_opts[:host]} -P #{@db_opts[:port]} #{@db_opts[:database]} --protocol=tcp -p"
155
108
  end
156
109
  end
157
110
  end
158
111
  end
159
112
 
160
113
  def check_mysql_parameters_compat
161
- sys_var_to_check = {'@@binlog_format'=>'ROW', '@@binlog_checksum'=>'NONE', '@@log_bin_use_v1_row_events'=>1, '@@log_slave_updates'=>1}
162
- errors={}
163
-
164
- client = Mysql2::Client.new(@db_opts)
165
-
166
- begin
167
- sys_var_to_check.each_key do |sys_var|
168
- sel_query = SELECT_QUERY_TMPLT % sys_var
169
- begin
170
- result = client.query(sel_query)
171
- unless result.first[sys_var] == sys_var_to_check[sys_var]
172
- errors[sys_var]=result.first[sys_var]
173
- end
174
- rescue Mysql2::Error => e
175
- if e.message =~ /Unknown system variable/
176
- unless e.message =~ /(binlog_checksum|log_bin_use_v1_row_events)/
177
- errors[sys_var] = false
178
- end
179
- else
180
- raise e
181
- end
182
- end
183
- end
184
- ensure
185
- client.close
186
- end
187
- unless errors.empty?
188
- error_explanation = ""
189
- errors.each_key do |err_key|
190
- error_explanation << "\n * #{err_key} is #{errors[err_key]} but should be #{sys_var_to_check[err_key]}"
191
- end
192
- raise MysqlCompatibilityError, "These system variable(s) are not the correct value: #{error_explanation}\n Please change these system variables for FlyData Sync to run correctly"
193
- end
114
+ FlydataCore::Mysql::BinlogParameterChecker.new(@db_opts).do_check
194
115
  end
195
116
 
196
117
  def check_mysql_binlog_retention
197
- client = Mysql2::Client.new(@db_opts)
198
- begin
199
- if is_rds?(@db_opts[:host])
200
- run_rds_retention_check(client)
201
- else
202
- run_mysql_retention_check(client)
203
- end
204
- ensure
205
- client.close
118
+ if is_rds?(@db_opts[:host])
119
+ run_rds_retention_check
120
+ else
121
+ run_mysql_retention_check
206
122
  end
207
123
  end
208
124
 
209
125
  def check_writing_permissions
210
126
  write_errors = []
211
- paths_to_check = ["~/.flydata"]
127
+ paths_to_check = [FLYDATA_HOME]
212
128
  paths_to_check << @dump_dir unless @dump_dir.to_s.empty?
213
129
  paths_to_check << @backup_dir unless @backup_dir.to_s.empty?
214
130
  paths_to_check.each do |path|
@@ -218,71 +134,36 @@ module Flydata
218
134
  end
219
135
  unless write_errors.empty?
220
136
  error_dir = write_errors.join(", ")
221
- raise MysqlCompatibilityError, "We cannot access the directories: #{error_dir}"
137
+ raise FlydataCore::MysqlCompatibilityError, "We cannot access the directories: #{error_dir}"
222
138
  end
223
139
  end
224
140
 
225
141
  # If table_type='VIEW' or engine='MEMORY', raise error.
226
142
  def check_mysql_table_types
227
143
  return if @tables.empty?
228
- client = Mysql2::Client.new(@db_opts)
229
- sel_query = SELECT_TABLE_INFO_TMPLT % [client.escape(@db_opts[:database]), @tables.collect{|t| "'#{client.escape(t)}'"}.join(", ")]
230
- begin
231
- invalid_tables = []
232
- client.query(sel_query).each do |r|
233
- invalid_tables.push(r['table_name']) if r['table_type'] == 'VIEW' || r['engine'] == 'MEMORY'
234
- end
235
- raise MysqlCompatibilityError, "FlyData does not support VIEW and MEMORY ENGINE table. Remove following tables from data entry: #{invalid_tables.join(", ")}" unless invalid_tables.empty?
236
- ensure
237
- client.close
238
- end
239
- end
240
-
241
- def get_missing_privileges(found_priv, all_priv)
242
- return_hash = {}
243
- found_priv.each_key do |key|
244
- missing_priv = all_priv - found_priv[key].flatten.uniq
245
- return_hash[key] = missing_priv unless missing_priv.empty?
246
- end
247
- return_hash
144
+ option = @db_opts.dup.merge(tables: @tables)
145
+ FlydataCore::Mysql::TableTypeChecker.new(option).do_check
248
146
  end
249
147
 
250
- def run_mysql_retention_check(mysql_client)
251
- expire_logs_days_limit = BINLOG_RETENTION_HOURS / 24
252
- sel_query = SELECT_QUERY_TMPLT % '@@expire_logs_days'
253
- result = mysql_client.query(sel_query)
254
- if result.first["@@expire_logs_days"]!=0 and result.first["@@expire_logs_days"] <= expire_logs_days_limit
255
- raise MysqlCompatibilityError, "Binary log retention is too short\n " +
256
- " We recommend the system variable '@@expire_logs_days' to be either set to 0 or at least #{expire_logs_days_limit} days"
257
- end
148
+ def run_mysql_retention_check
149
+ FlydataCore::Mysql::NonRdsRetentionChecker.new(@db_opts).do_check
258
150
  end
259
151
 
260
- def run_rds_retention_check(mysql_client)
261
- sql_query = "call mysql.rds_show_configuration;"
262
-
263
- begin
264
- result = mysql_client.query(sql_query)
265
- if result.first["name"]=="binlog retention hours"
266
- if result.first["value"].nil? or result.first["value"].to_i <= BINLOG_RETENTION_HOURS
267
- raise MysqlCompatibilityError, "Binary log retention is too short\n" +
268
- " We recommend setting RDS binlog retention to be at least #{BINLOG_RETENTION_HOURS} hours. To do this, run this on your RDS MySQL database:\n" +
269
- " $> call mysql.rds_set_configuration('binlog retention hours', 94);"
270
- end
271
- end
272
- rescue Mysql2::Error => e
273
- if e.message =~ /command denied to user/
274
- log_warn_stderr("[WARNING]Cannot verify RDS retention period on current MySQL user account.\n" +
275
- "To see retention period, please run this on your RDS:\n" +
276
- " $> call mysql.rds_show_configuration;\n" +
277
- "Please verify that the hours is not nil and is at least #{BINLOG_RETENTION_HOURS} hours\n" +
278
- "To set binlog retention hours, you can run this on your RDS:\n" +
279
- " $> call mysql.rds_set_configuration('binlog retention hours', #{BINLOG_RETENTION_HOURS});\n"
280
- )
281
- else
282
- raise e
283
- end
152
+ def run_rds_retention_check
153
+ FlydataCore::Mysql::RdsRetentionChecker.new(@db_opts).do_check
154
+ rescue Mysql2::Error => e
155
+ if e.message =~ /command denied to user/
156
+ retention_hours = FlydataCore::Mysql::RdsRetentionChecker::BINLOG_RETENTION_HOURS
157
+ log_warn_stderr("[WARNING]Cannot verify RDS retention period on current MySQL user account.\n" +
158
+ "To see retention period, please run this on your RDS:\n" +
159
+ " $> call mysql.rds_show_configuration;\n" +
160
+ "Please verify that the hours is not nil and is at least #{retention_hours} hours\n" +
161
+ "To set binlog retention hours, you can run this on your RDS:\n" +
162
+ " $> call mysql.rds_set_configuration('binlog retention hours', #{retention_hours});\n"
163
+ )
164
+ else
165
+ raise e
284
166
  end
285
-
286
167
  end
287
168
 
288
169
  def is_rds?(hostname)