require 'mysql2' require 'flydata/compatibility_check' require 'flydata-core/mysql/config' require 'flydata-core/mysql/command_generator' require 'flydata-core/mysql/compatibility_checker' require 'flydata-core/errors' module Flydata module SourceMysql class MysqlCompatibilityCheck < CompatibilityCheck include InitialSyncChecks def initialize(dp_hash, de_hash, options={}) super @db_opts = FlydataCore::Mysql::Config.build_mysql_db_opts(de_hash) @dump_dir = options[:dump_dir] || nil @backup_dir = options[:backup_dir] || nil @tables = de_hash['tables'] end def print_errors return if @errors.empty? log_error_stderr "There may be some compatibility issues with your MySQL credentials: " @errors.each do |error| log_error_stderr " * #{error.message}" end raise "Please correct these errors if you wish to run FlyData Sync" end def check_compatibility56_variable FlydataCore::Mysql::MySqlVersion57CompabilityChecker.new(@db_opts).do_check end def check_mysql_user_compat FlydataCore::Mysql::SyncPermissionChecker.new(@db_opts).do_check end def check_mysql_protocol_tcp_compat query = FlydataCore::Mysql::CommandGenerator.generate_mysql_show_grants_cmd(@db_opts) Open3.popen3(query) do |stdin, stdout, stderr| stdin.close while !stderr.eof? lines = [] while line = stderr.gets do lines << line.strip unless line =~ /Warning: Using a password on the command line interface can be insecure/ end unless lines.empty? err_reason = lines.join(" ") log_error("Error occured during access to mysql server.", err: err_reason) 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" end end end end def check_rds_master_status if is_rds? FlydataCore::Mysql::RdsMasterStatusChecker.new(@db_opts).do_check end end def check_mysql_parameters_compat begin FlydataCore::Mysql::OptionalBinlogParameterChecker.new(@db_opts).do_check rescue FlydataCore::MysqlCompatibilityError => e log_warn_stderr(e.to_s) end FlydataCore::Mysql::RequiredBinlogParameterChecker.new(@db_opts).do_check end def check_mysql_binlog_retention if is_rds? run_rds_retention_check else run_mysql_retention_check end end # If table_type='VIEW' or engine='MEMORY', raise error. def check_mysql_table_types return if @tables.empty? option = @db_opts.dup.merge(tables: @tables) FlydataCore::Mysql::TableTypeChecker.new(option).do_check end def run_mysql_retention_check FlydataCore::Mysql::NonRdsRetentionChecker.new(@db_opts).do_check end def run_rds_retention_check FlydataCore::Mysql::RdsRetentionChecker.new(@db_opts).do_check rescue Mysql2::Error => e if e.message =~ /command denied to user/ retention_hours = FlydataCore::Mysql::RdsRetentionChecker::BINLOG_RETENTION_HOURS log_warn_stderr("[WARNING]Cannot verify RDS retention period on current MySQL user account.\n" + "To see retention period, please run this on your RDS:\n" + " $> call mysql.rds_show_configuration;\n" + "Please verify that the hours is not nil and is at least #{retention_hours} hours\n" + "To set binlog retention hours, you can run this on your RDS:\n" + " $> call mysql.rds_set_configuration('binlog retention hours', #{retention_hours});\n" ) else raise e end end def is_rds?(hostname = @db_opts[:host]) hostname.match(/rds.amazonaws.com$/) != nil end end end end