flydata 0.4.0 → 0.4.1

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