From 7132e9625d58ca28f0a93edecd5d5382a6d83d0e Mon Sep 17 00:00:00 2001 From: Tim Mullin Date: Wed, 3 Apr 2024 15:37:56 +0000 Subject: [PATCH] Handle auto-upgrade of out-of-date MySQL installs Case RE-171: Instead of simply having a blocker for old versions of MySQL/MariaDB, give the option to auto-upgrade during the elevate process. We only do this when MySQL/MariaDB is provided by cPanel, not CloudLinux. Changelog: Provide auto-upgrade mechanism for out-of-date MySQL/MariaDB versions (installed via cPanel, not CloudLinux). --- elevate-cpanel | 224 +++++++++++++++++----- lib/Elevate/Blockers/Base.pm | 5 - lib/Elevate/Blockers/Databases.pm | 116 ++++++----- lib/Elevate/Components/DatabaseUpgrade.pm | 40 ++++ lib/Elevate/Database.pm | 77 +++++++- script/elevate-cpanel.PL | 3 + t/Elevate-Database.t | 53 +++++ t/blocker-Databases.t | 189 ++++++++++-------- 8 files changed, 528 insertions(+), 179 deletions(-) create mode 100644 lib/Elevate/Components/DatabaseUpgrade.pm diff --git a/elevate-cpanel b/elevate-cpanel index f6d88a19..ec3bdca2 100755 --- a/elevate-cpanel +++ b/elevate-cpanel @@ -56,6 +56,7 @@ BEGIN { # Suppress load of all of these at earliest point. $INC{'Elevate/Components/WPToolkit.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/Components/SSH.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/Components/AutoSSL.pm'} = 'script/elevate-cpanel.PL.static'; + $INC{'Elevate/Components/DatabaseUpgrade.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/OS.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/OS/CentOS7.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/OS/CloudLinux7.pm'} = 'script/elevate-cpanel.PL.static'; @@ -115,7 +116,6 @@ BEGIN { # Suppress load of all of these at earliest point. use Simple::Accessor qw( blockers - cpconf ); # use Log::Log4perl qw(:easy); @@ -154,10 +154,6 @@ BEGIN { # Suppress load of all of these at earliest point. } } - sub _build_cpconf ($self) { - return Cpanel::Config::LoadCpConf::loadcpconf() // {}; - } - sub is_check_mode ( $self, @args ) { return $self->blockers->is_check_mode(@args); } @@ -564,6 +560,7 @@ BEGIN { # Suppress load of all of these at earliest point. use Cpanel::SafeRun::Simple (); use Cpanel::DB::Map::Collection::Index (); use Cpanel::Exception (); + use Cpanel::MysqlUtils::MyCnf::Basic (); # use Elevate::Blockers::Base(); our @ISA; @@ -578,6 +575,7 @@ BEGIN { # Suppress load of all of these at earliest point. my $ok = 1; $self->_warning_if_postgresql_installed; $ok = 0 unless $self->_blocker_acknowledge_postgresql_datadir; + $ok = 0 unless $self->_blocker_remote_mysql; $ok = 0 unless $self->_blocker_old_mysql; $ok = 0 unless $self->_blocker_mysql_upgrade_in_progress; $self->_warning_mysql_not_enabled(); @@ -650,6 +648,21 @@ BEGIN { # Suppress load of all of these at earliest point. return ( keys %user_hash ); } + sub _blocker_remote_mysql ($self) { + + my $pretty_distro_name = $self->upgrade_to_pretty_name(); + + if ( Cpanel::MysqlUtils::MyCnf::Basic::is_remote_mysql() ) { + return $self->has_blocker( <<~"EOS" ); + The system is currently setup to use a remote database server. + We cannot elevate the system to $pretty_distro_name + unless the system is configured to use the local database server. + EOS + } + + return 0; + } + sub _blocker_old_mysql ($self) { my $mysql_is_provided_by_cloudlinux = Elevate::Database::is_database_provided_by_cloudlinux(0); @@ -681,64 +694,68 @@ BEGIN { # Suppress load of all of these at earliest point. EOS } - sub _blocker_old_cpanel_mysql ( $self, $mysql_version = undef ) { - $mysql_version //= $self->cpconf->{'mysql-version'} // ''; + sub _blocker_old_cpanel_mysql ($self) { - my $pretty_distro_name = $self->upgrade_to_pretty_name(); + my $mysql_version = Elevate::Database::get_local_database_version(); - if ( $mysql_version =~ qr{^\d+(\.\d)?$}a ) { - if ( 5 <= $mysql_version && $mysql_version <= 5.7 ) { - return $self->has_blocker( <<~"EOS"); - You are using MySQL $mysql_version server. - This version is not available for $pretty_distro_name. - You first need to update your MySQL server to 8.0 or later. + if ( Elevate::Database::is_database_version_supported($mysql_version) ) { - You can update to version 8.0 using the following command: + Elevate::StageFile::update_stage_file( { 'mysql-version' => $mysql_version } ); + return 0; + } - /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=8.0 + my $pretty_distro_name = $self->upgrade_to_pretty_name(); + my $database_type_name = Elevate::Database::get_database_type_name_from_version($mysql_version); + my $upgrade_version = Elevate::Database::get_default_upgrade_version(); + my $upgrade_dbtype_name = Elevate::Database::get_database_type_name_from_version($upgrade_version); - Once the MySQL upgrade is finished, you can then retry to elevate to $pretty_distro_name. - EOS - } - elsif ( 10 <= $mysql_version && $mysql_version <= 10.2 ) { + WARN( <<~"EOS" ); + You have $database_type_name $mysql_version installed. + This version is not available for $pretty_distro_name. - my $upgrade_version = $Cpanel::Version::Tiny::major_version <= 108 ? '10.3' : '10.5'; + EOS - return $self->has_blocker( <<~"EOS"); - You are using MariaDB server $mysql_version, this version is not available for $pretty_distro_name. - You first need to update MariaDB server to $upgrade_version or later. + if ( $self->is_check_mode() ) { + INFO( <<~"EOS" ); + You can manually upgrade your installation of $database_type_name using the following command: - You can update to version $upgrade_version using the following command: + /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=$upgrade_version - /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=$upgrade_version + Once the MySQL upgrade is finished, you can then retry to elevate to $pretty_distro_name. - Once the MariaDB upgrade is finished, you can then retry to elevate to $pretty_distro_name. - EOS - } + EOS + return 0; } - my %supported_mysql_versions = ( - map { $_ => 1 } - qw{ - 8.0 - 10.3 - 10.4 - 10.5 - 10.6 - } - ); + WARN( <<~"EOS" ); + Prior to elevating this system to $pretty_distro_name, + we will automatically upgrade your installation of $database_type_name + to $upgrade_dbtype_name $upgrade_version. - if ( !$supported_mysql_versions{$mysql_version} ) { - my $supported_version_str = join( ", ", sort { $a <=> $b } keys %supported_mysql_versions ); - return $self->has_blocker( <<~"EOS"); - We do not know how to upgrade to $pretty_distro_name with MySQL version $mysql_version. - Please upgrade your MySQL server to one of the supported versions before running elevate. + EOS + + if ( !$self->getopt('non-interactive') ) { + if ( + !IO::Prompt::prompt( + '-one_char', + '-yes_no', + '-tty', + -default => 'y', + "Do you consent to upgrading to $upgrade_dbtype_name $upgrade_version [Y/n]: ", + ) + ) { + return $self->has_blocker( <<~"EOS" ); + The system cannot be elevated to $pretty_distro_name until $database_type_name has been upgraded. To upgrade manually: + + /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=$upgrade_version + + To have have this script perform the upgrade, run this script again and consent to allow it to upgrade $upgrade_dbtype_name $upgrade_version. - Supported MySQL server versions are: $supported_version_str EOS + } } - Elevate::StageFile::update_stage_file( { 'mysql-version' => $mysql_version } ); + Elevate::StageFile::update_stage_file( { 'mysql-version' => $upgrade_version } ); return 0; } @@ -4536,6 +4553,41 @@ EOS } # --- END lib/Elevate/Components/AutoSSL.pm +{ # --- BEGIN lib/Elevate/Components/DatabaseUpgrade.pm + + package Elevate::Components::DatabaseUpgrade; + + use cPstrict; + + use Elevate::Database (); + + # use Elevate::Components::Base(); + our @ISA; + BEGIN { push @ISA, qw(Elevate::Components::Base); } + + # use Log::Log4perl qw(:easy); + INIT { Log::Log4perl->import(qw{:easy}); } + + sub pre_leapp ($self) { + + return if Elevate::Database::is_database_provided_by_cloudlinux(); + + return if Elevate::Database::is_database_version_supported( Elevate::Database::get_local_database_version() ); + + Elevate::Database::upgrade_database_server(); + + return; + } + + sub post_leapp ($self) { + + return; + } + + 1; + +} # --- END lib/Elevate/Components/DatabaseUpgrade.pm + { # --- BEGIN lib/Elevate/OS.pm package Elevate::OS; @@ -4842,10 +4894,23 @@ EOS use Elevate::OS (); use Elevate::StageFile (); - use Cpanel::Pkgr (); + use Cpanel::MysqlUtils::Version (); + use Cpanel::MysqlUtils::Versions (); + use Cpanel::Pkgr (); + + # use Log::Log4perl qw(:easy); + INIT { Log::Log4perl->import(qw{:easy}); } use constant MYSQL_BIN => '/usr/sbin/mysqld'; + use constant SUPPORTED_CPANEL_MYSQL_VERSIONS => qw{ + 8.0 + 10.3 + 10.4 + 10.5 + 10.6 + }; + sub is_database_provided_by_cloudlinux ( $use_cache = 1 ) { if ($use_cache) { @@ -4894,6 +4959,68 @@ EOS return ( $db_type, $db_version ); } + sub get_local_database_version () { + + my $version; + + eval { + local $Cpanel::MysqlUtils::Version::USE_LOCAL_MYSQL = 1; + $version = Cpanel::MysqlUtils::Version::uncached_mysqlversion(); + }; + if ( my $exception = $@ ) { + WARN("Error encountered querying the version from the database server: $exception"); + + my $cpconf = Cpanel::Config::LoadCpConf::loadcpconf(); + $version = $cpconf->{'mysql-version'} // ''; + } + + return $version; + } + + sub is_database_version_supported ($version) { + + return scalar grep { $version eq $_ } SUPPORTED_CPANEL_MYSQL_VERSIONS; + } + + sub get_default_upgrade_version () { + + require Whostmgr::Mysql::Upgrade; + + return Whostmgr::Mysql::Upgrade::get_latest_available_version( version => get_local_database_version() ); + } + + sub get_database_type_name_from_version ($version) { + return Cpanel::MariaDB::version_is_mariadb($version) ? 'MariaDB' : 'MySQL'; + } + + sub upgrade_database_server () { + + require Whostmgr::Mysql::Upgrade; + + my $upgrade_version = Elevate::StageFile::read_stage_file( 'mysql-version', '' ); + $upgrade_version ||= Elevate::Database::get_default_upgrade_version(); + + my $upgrade_dbtype_name = Elevate::Database::get_database_type_name_from_version($upgrade_version); + + INFO("Beginning upgrade to $upgrade_dbtype_name $upgrade_version"); + + my $failed_step = Whostmgr::Mysql::Upgrade::unattended_upgrade( + { + upgrade_type => 'unattended_automatic', + selected_version => $upgrade_version, + } + ); + + if ($failed_step) { + FATAL("FAILED to upgrade to $upgrade_dbtype_name $upgrade_version"); + } + else { + INFO("Finished upgrade to $upgrade_dbtype_name $upgrade_version"); + } + + return; + } + 1; } # --- END lib/Elevate/Database.pm @@ -6694,6 +6821,7 @@ use Elevate::Components::RmMod (); use Elevate::Components::WPToolkit (); use Elevate::Components::SSH (); use Elevate::Components::AutoSSL (); +use Elevate::Components::DatabaseUpgrade (); # - fatpack OS use Elevate::OS (); @@ -7434,6 +7562,8 @@ sub run_stage_2 ($self) { $self->ssystem_and_die(qw{/scripts/update-packages}); $self->ssystem_and_die(qw{/usr/bin/yum -y update}); + $self->run_component_once( 'DatabaseUpgrade' => 'pre_leapp' ); + $self->disable_all_cpanel_services(); $self->setup_outdated_services(); diff --git a/lib/Elevate/Blockers/Base.pm b/lib/Elevate/Blockers/Base.pm index 347ff6a5..6391e4e4 100644 --- a/lib/Elevate/Blockers/Base.pm +++ b/lib/Elevate/Blockers/Base.pm @@ -17,7 +17,6 @@ use Cpanel::JSON (); use Simple::Accessor qw( blockers - cpconf ); use Log::Log4perl qw(:easy); @@ -58,10 +57,6 @@ BEGIN { } } -sub _build_cpconf ($self) { - return Cpanel::Config::LoadCpConf::loadcpconf() // {}; -} - =head2 $self->is_check_mode( @args ) Check if the script is called using '--check' diff --git a/lib/Elevate/Blockers/Databases.pm b/lib/Elevate/Blockers/Databases.pm index 590c3e26..a7a2f534 100644 --- a/lib/Elevate/Blockers/Databases.pm +++ b/lib/Elevate/Blockers/Databases.pm @@ -22,6 +22,7 @@ use Cpanel::JSON (); use Cpanel::SafeRun::Simple (); use Cpanel::DB::Map::Collection::Index (); use Cpanel::Exception (); +use Cpanel::MysqlUtils::MyCnf::Basic (); use parent qw{Elevate::Blockers::Base}; @@ -33,6 +34,7 @@ sub check ($self) { my $ok = 1; $self->_warning_if_postgresql_installed; $ok = 0 unless $self->_blocker_acknowledge_postgresql_datadir; + $ok = 0 unless $self->_blocker_remote_mysql; $ok = 0 unless $self->_blocker_old_mysql; $ok = 0 unless $self->_blocker_mysql_upgrade_in_progress; $self->_warning_mysql_not_enabled(); @@ -105,6 +107,23 @@ sub _has_mapped_postgresql_dbs ($self) { return ( keys %user_hash ); } +sub _blocker_remote_mysql ($self) { + + my $pretty_distro_name = $self->upgrade_to_pretty_name(); + + # If we are setup to use remote MySQL, then attempting an upgrade will fail + # TODO: Temporarily disable remote MySQL to allow the database upgrade + if ( Cpanel::MysqlUtils::MyCnf::Basic::is_remote_mysql() ) { + return $self->has_blocker( <<~"EOS" ); + The system is currently setup to use a remote database server. + We cannot elevate the system to $pretty_distro_name + unless the system is configured to use the local database server. + EOS + } + + return 0; +} + sub _blocker_old_mysql ($self) { my $mysql_is_provided_by_cloudlinux = Elevate::Database::is_database_provided_by_cloudlinux(0); @@ -141,67 +160,72 @@ sub _blocker_old_cloudlinux_mysql ($self) { EOS } -sub _blocker_old_cpanel_mysql ( $self, $mysql_version = undef ) { - $mysql_version //= $self->cpconf->{'mysql-version'} // ''; +sub _blocker_old_cpanel_mysql ($self) { - my $pretty_distro_name = $self->upgrade_to_pretty_name(); + my $mysql_version = Elevate::Database::get_local_database_version(); - # checking MySQL version - if ( $mysql_version =~ qr{^\d+(\.\d)?$}a ) { - if ( 5 <= $mysql_version && $mysql_version <= 5.7 ) { - return $self->has_blocker( <<~"EOS"); - You are using MySQL $mysql_version server. - This version is not available for $pretty_distro_name. - You first need to update your MySQL server to 8.0 or later. + # If we are running a local version of MySQL/MariaDB that will be + # supported by the new OS version, we leave it as it is. + if ( Elevate::Database::is_database_version_supported($mysql_version) ) { - You can update to version 8.0 using the following command: + # store the MySQL version we started from + Elevate::StageFile::update_stage_file( { 'mysql-version' => $mysql_version } ); + return 0; + } - /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=8.0 + my $pretty_distro_name = $self->upgrade_to_pretty_name(); + my $database_type_name = Elevate::Database::get_database_type_name_from_version($mysql_version); + my $upgrade_version = Elevate::Database::get_default_upgrade_version(); + my $upgrade_dbtype_name = Elevate::Database::get_database_type_name_from_version($upgrade_version); - Once the MySQL upgrade is finished, you can then retry to elevate to $pretty_distro_name. - EOS - } - elsif ( 10 <= $mysql_version && $mysql_version <= 10.2 ) { + WARN( <<~"EOS" ); + You have $database_type_name $mysql_version installed. + This version is not available for $pretty_distro_name. - # cPanel 110 no longer supports upgrades from something else to 10.3. Suggest 10.5 in that case: - my $upgrade_version = $Cpanel::Version::Tiny::major_version <= 108 ? '10.3' : '10.5'; + EOS - return $self->has_blocker( <<~"EOS"); - You are using MariaDB server $mysql_version, this version is not available for $pretty_distro_name. - You first need to update MariaDB server to $upgrade_version or later. + if ( $self->is_check_mode() ) { + INFO( <<~"EOS" ); + You can manually upgrade your installation of $database_type_name using the following command: - You can update to version $upgrade_version using the following command: + /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=$upgrade_version - /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=$upgrade_version + Once the MySQL upgrade is finished, you can then retry to elevate to $pretty_distro_name. - Once the MariaDB upgrade is finished, you can then retry to elevate to $pretty_distro_name. - EOS - } + EOS + return 0; } - my %supported_mysql_versions = ( - map { $_ => 1 } - qw{ - 8.0 - 10.3 - 10.4 - 10.5 - 10.6 - } - ); - - if ( !$supported_mysql_versions{$mysql_version} ) { - my $supported_version_str = join( ", ", sort { $a <=> $b } keys %supported_mysql_versions ); - return $self->has_blocker( <<~"EOS"); - We do not know how to upgrade to $pretty_distro_name with MySQL version $mysql_version. - Please upgrade your MySQL server to one of the supported versions before running elevate. - - Supported MySQL server versions are: $supported_version_str + WARN( <<~"EOS" ); + Prior to elevating this system to $pretty_distro_name, + we will automatically upgrade your installation of $database_type_name + to $upgrade_dbtype_name $upgrade_version. + + EOS + + if ( !$self->getopt('non-interactive') ) { + if ( + !IO::Prompt::prompt( + '-one_char', + '-yes_no', + '-tty', + -default => 'y', + "Do you consent to upgrading to $upgrade_dbtype_name $upgrade_version [Y/n]: ", + ) + ) { + return $self->has_blocker( <<~"EOS" ); + The system cannot be elevated to $pretty_distro_name until $database_type_name has been upgraded. To upgrade manually: + + /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=$upgrade_version + + To have have this script perform the upgrade, run this script again and consent to allow it to upgrade $upgrade_dbtype_name $upgrade_version. + EOS + } } - # store the MySQL version we started from - Elevate::StageFile::update_stage_file( { 'mysql-version' => $mysql_version } ); + # Change to the version we will uprade to + Elevate::StageFile::update_stage_file( { 'mysql-version' => $upgrade_version } ); return 0; } diff --git a/lib/Elevate/Components/DatabaseUpgrade.pm b/lib/Elevate/Components/DatabaseUpgrade.pm new file mode 100644 index 00000000..bf62a2e6 --- /dev/null +++ b/lib/Elevate/Components/DatabaseUpgrade.pm @@ -0,0 +1,40 @@ +package Elevate::Components::DatabaseUpgrade; + +=encoding utf-8 + +=head1 NAME + +Elevate::Components::DatabaseUpgrade + +Handle auto-upgrades for outdated versions of MySQL/MariaDB + +=cut + +use cPstrict; + +use Elevate::Database (); + +use parent qw{Elevate::Components::Base}; + +use Log::Log4perl qw(:easy); + +sub pre_leapp ($self) { + + # We don't auto-upgrade the database if provided by cloudlinux + return if Elevate::Database::is_database_provided_by_cloudlinux(); + + # If the database version is supported on the new OS version, then no need to upgrade + return if Elevate::Database::is_database_version_supported( Elevate::Database::get_local_database_version() ); + + Elevate::Database::upgrade_database_server(); + + return; +} + +sub post_leapp ($self) { + + # Nothing to do + return; +} + +1; diff --git a/lib/Elevate/Database.pm b/lib/Elevate/Database.pm index 482aacf0..5c1f1749 100644 --- a/lib/Elevate/Database.pm +++ b/lib/Elevate/Database.pm @@ -15,10 +15,22 @@ use cPstrict; use Elevate::OS (); use Elevate::StageFile (); -use Cpanel::Pkgr (); +use Cpanel::MysqlUtils::Version (); +use Cpanel::MysqlUtils::Versions (); +use Cpanel::Pkgr (); + +use Log::Log4perl qw(:easy); use constant MYSQL_BIN => '/usr/sbin/mysqld'; +use constant SUPPORTED_CPANEL_MYSQL_VERSIONS => qw{ + 8.0 + 10.3 + 10.4 + 10.5 + 10.6 +}; + sub is_database_provided_by_cloudlinux ( $use_cache = 1 ) { if ($use_cache) { @@ -75,4 +87,67 @@ sub get_db_info_if_provided_by_cloudlinux ( $use_cache = 1 ) { return ( $db_type, $db_version ); } +sub get_local_database_version () { + + my $version; + + eval { + local $Cpanel::MysqlUtils::Version::USE_LOCAL_MYSQL = 1; + $version = Cpanel::MysqlUtils::Version::uncached_mysqlversion(); + }; + if ( my $exception = $@ ) { + WARN("Error encountered querying the version from the database server: $exception"); + + # Load it from the configuration if we cannot get the version directly + my $cpconf = Cpanel::Config::LoadCpConf::loadcpconf(); + $version = $cpconf->{'mysql-version'} // ''; + } + + return $version; +} + +sub is_database_version_supported ($version) { + + return scalar grep { $version eq $_ } SUPPORTED_CPANEL_MYSQL_VERSIONS; +} + +sub get_default_upgrade_version () { + + require Whostmgr::Mysql::Upgrade; + + return Whostmgr::Mysql::Upgrade::get_latest_available_version( version => get_local_database_version() ); +} + +sub get_database_type_name_from_version ($version) { + return Cpanel::MariaDB::version_is_mariadb($version) ? 'MariaDB' : 'MySQL'; +} + +sub upgrade_database_server () { + + require Whostmgr::Mysql::Upgrade; + + my $upgrade_version = Elevate::StageFile::read_stage_file( 'mysql-version', '' ); + $upgrade_version ||= Elevate::Database::get_default_upgrade_version(); + + my $upgrade_dbtype_name = Elevate::Database::get_database_type_name_from_version($upgrade_version); + + INFO("Beginning upgrade to $upgrade_dbtype_name $upgrade_version"); + + my $failed_step = Whostmgr::Mysql::Upgrade::unattended_upgrade( + { + upgrade_type => 'unattended_automatic', + selected_version => $upgrade_version, + } + ); + + if ($failed_step) { + FATAL("FAILED to upgrade to $upgrade_dbtype_name $upgrade_version"); + } + else { + INFO("Finished upgrade to $upgrade_dbtype_name $upgrade_version"); + } + + return; +} + 1; diff --git a/script/elevate-cpanel.PL b/script/elevate-cpanel.PL index 7da0fba9..e8b7cf77 100755 --- a/script/elevate-cpanel.PL +++ b/script/elevate-cpanel.PL @@ -304,6 +304,7 @@ use Elevate::Components::RmMod (); use Elevate::Components::WPToolkit (); use Elevate::Components::SSH (); use Elevate::Components::AutoSSL (); +use Elevate::Components::DatabaseUpgrade (); # - fatpack OS use Elevate::OS (); @@ -1044,6 +1045,8 @@ sub run_stage_2 ($self) { $self->ssystem_and_die(qw{/scripts/update-packages}); $self->ssystem_and_die(qw{/usr/bin/yum -y update}); + $self->run_component_once( 'DatabaseUpgrade' => 'pre_leapp' ); + $self->disable_all_cpanel_services(); $self->setup_outdated_services(); diff --git a/t/Elevate-Database.t b/t/Elevate-Database.t index b707c438..b1524668 100644 --- a/t/Elevate-Database.t +++ b/t/Elevate-Database.t @@ -6,6 +6,7 @@ use FindBin; use lib "$FindBin::Bin/../lib"; use lib "$FindBin::Bin/lib"; +use Test::Elevate; use Test::Elevate::OS; use Test2::Bundle::Extended; @@ -159,4 +160,56 @@ $mock_stagefile->redefine( ); } +{ + note 'Test get_local_database_version() behavior'; + + my $mysql_version = 42; + my $mysql_version_config = 52; + + my $mock_version = Test::MockModule->new('Cpanel::MysqlUtils::Version'); + $mock_version->redefine( + uncached_mysqlversion => sub { + return $mysql_version if $mysql_version; + die "MYSQL_VERSION_FAIL"; + }, + ); + + my $mock_cpconf = Test::MockModule->new('Cpanel::Config::LoadCpConf'); + $mock_cpconf->redefine( + loadcpconf => sub { return { 'mysql-version' => $mysql_version_config }; }, + ); + + # Happy path test + is( + Elevate::Database::get_local_database_version(), + $mysql_version, + 'Returns result of querying mysql version' + ); + no_messages_seen(); + + # Test regular call failing & getting mysql version from the config + $mysql_version = 0; + is( + Elevate::Database::get_local_database_version(), + $mysql_version_config, + 'Returns result of getting mysql version from the config' + ); + message_seen( 'WARN', qr/MYSQL_VERSION_FAIL/ ); +} + +{ + note 'Test is_database_version_supported() behavior'; + + my @should_pass = qw(8.0 10.3 10.4 10.5 10.6); + my @should_fail = qw(5.0 5.5 5.7 -2 83 3.1415); + + foreach my $pass_version (@should_pass) { + ok( Elevate::Database::is_database_version_supported($pass_version), "$pass_version is supported" ); + } + + foreach my $fail_version (@should_fail) { + ok( !Elevate::Database::is_database_version_supported($fail_version), "$fail_version is NOT supported" ); + } +} + done_testing(); diff --git a/t/blocker-Databases.t b/t/blocker-Databases.t index 0d9568ef..35c513e7 100644 --- a/t/blocker-Databases.t +++ b/t/blocker-Databases.t @@ -44,101 +44,130 @@ my $mock_elevate = Test::MockFile->file('/var/cpanel/elevate'); } { - note 'cPanel MySQL behavior'; - - for my $os ( 'cent', 'cloud' ) { - set_os_to($os); - - my $expected_target_os = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; + note 'Remote MySQL blocker'; - local $Cpanel::Version::Tiny::major_version = 110; - is( - $db->_blocker_old_cpanel_mysql('5.7'), - { - id => q[Elevate::Blockers::Databases::_blocker_old_cpanel_mysql], - msg => <<~"EOS", - You are using MySQL 5.7 server. - This version is not available for $expected_target_os. - You first need to update your MySQL server to 8.0 or later. - - You can update to version 8.0 using the following command: + clear_messages_seen(); - /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=8.0 + my $is_remote_mysql = 0; - Once the MySQL upgrade is finished, you can then retry to elevate to $expected_target_os. - EOS - }, - 'MySQL 5.7 is a blocker.' - ); + my $mock_basic = Test::MockModule->new('Cpanel::MysqlUtils::MyCnf::Basic'); + $mock_basic->redefine( + is_remote_mysql => sub { return $is_remote_mysql; }, + ); - is( - $db->_blocker_old_cpanel_mysql('10.1'), - { - id => q[Elevate::Blockers::Databases::_blocker_old_cpanel_mysql], - msg => <<~"EOS", - You are using MariaDB server 10.1, this version is not available for $expected_target_os. - You first need to update MariaDB server to 10.5 or later. + is( $db->_blocker_remote_mysql(), 0, 'No blocker if remote MySQL disabled' ); + no_messages_seen(); - You can update to version 10.5 using the following command: + # Test blocker on remote mysql + $is_remote_mysql = 1; + like( + $db->_blocker_remote_mysql(), + { + id => q[Elevate::Blockers::Databases::_blocker_remote_mysql], + msg => qr/The system is currently setup to use a remote database server/, + }, + 'Returns blocker if remote MySQL is enabled' + ); + message_seen( 'WARN', qr/remote database server/ ); +} - /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=10.5 +{ + note 'cPanel MySQL behavior'; - Once the MariaDB upgrade is finished, you can then retry to elevate to $expected_target_os. - EOS - }, - 'Maria 10.1 is a blocker.' - ); + clear_messages_seen(); - is( - $db->_blocker_old_cpanel_mysql('10.2'), - { - id => q[Elevate::Blockers::Databases::_blocker_old_cpanel_mysql], - msg => <<~"EOS", - You are using MariaDB server 10.2, this version is not available for $expected_target_os. - You first need to update MariaDB server to 10.5 or later. + my $test_db_version = '13'; + my $is_db_supported = 1; + my $upgrade_version = '42'; + my $stash = undef; + my $is_check_mode = 1; + my $os_pretty_name = 'ShinyOS'; + my $user_consent = 0; + + my $mock_db = Test::MockModule->new('Elevate::Database'); + $mock_db->redefine( + get_local_database_version => sub { return $test_db_version; }, + is_database_version_supported => sub { return $is_db_supported; }, + get_default_upgrade_version => sub { return $upgrade_version; }, + get_database_type_name_from_version => sub { return ( $_[0] eq $test_db_version ) ? 'OldDB' : 'NewDB' }, + ); - You can update to version 10.5 using the following command: + my $mock_stagefile = Test::MockModule->new('Elevate::StageFile'); + $mock_stagefile->redefine( + update_stage_file => sub { $stash = $_[0] }, + ); - /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=10.5 + my $mock_db_blocker = Test::MockModule->new('Elevate::Blockers::Databases'); + $mock_db_blocker->redefine( + is_check_mode => sub { return $is_check_mode; }, + upgrade_to_pretty_name => sub { return $os_pretty_name; }, + ); - Once the MariaDB upgrade is finished, you can then retry to elevate to $expected_target_os. - EOS - }, - 'Maria 10.2 is a blocker.' - ); - - is( - $db->_blocker_old_cpanel_mysql('4.2'), - { - id => q[Elevate::Blockers::Databases::_blocker_old_cpanel_mysql], - msg => <<~"EOS", - We do not know how to upgrade to $expected_target_os with MySQL version 4.2. - Please upgrade your MySQL server to one of the supported versions before running elevate. - - Supported MySQL server versions are: 8.0, 10.3, 10.4, 10.5, 10.6 - EOS - }, - 'MySQL 4.2 is a blocker.' - ); - } + my $mock_io_prompt = Test::MockModule->new('IO::Prompt'); + $mock_io_prompt->redefine( + prompt => sub { return $user_consent; }, + ); - my $stash = undef; + # Test supported DB server (one that doesn't need an upgrade) + is( $db->_blocker_old_cpanel_mysql(), 0, "Supported database returns 0" ); + no_messages_seen(); + is( $stash, { 'mysql-version' => $test_db_version }, 'Stage file updated with original version' ); + + # The rest of the scenarios test where the DB server is NOT supported + $is_db_supported = 0; + $stash = undef; + + # Test warning for check mode + $is_check_mode = 1; + is( $db->_blocker_old_cpanel_mysql(), 0, "Check mode returns 0" ); + is( $stash, undef, 'Stage file not updated due to running a check' ); + message_seen( + 'WARN', + qr/You have OldDB $test_db_version installed.\nThis version is not available for $os_pretty_name/ + ); + message_seen( 'INFO', qr/whmapi1 start_background_mysql_upgrade version=$upgrade_version/ ); - my $mock_stagefile = Test::MockModule->new('Elevate::StageFile'); - $mock_stagefile->redefine( - update_stage_file => sub { $stash = $_[0] }, + # Test for start mode, but the user declines + $is_check_mode = 0; + $user_consent = 0; + like( + $db->_blocker_old_cpanel_mysql(), + { + id => q[Elevate::Blockers::Databases::_blocker_old_cpanel_mysql], + msg => qr/The system cannot be elevated to $os_pretty_name until OldDB has been upgraded./ + }, + 'Returns blocker if user declines the upgrade' + ); + is( $stash, undef, 'Stage file not updated due to blocker' ); + message_seen( + 'WARN', + qr/You have OldDB $test_db_version installed.\nThis version is not available for $os_pretty_name/ + ); + message_seen( + 'WARN', + qr/automatically upgrade .*to NewDB $upgrade_version/s + ); + message_seen( + 'WARN', + qr/The system cannot be elevated to $os_pretty_name until OldDB has been upgraded./ ); - is( $db->_blocker_old_cpanel_mysql('8.0'), 0, "MySQL 8 and we're ok" ); - is $stash, { 'mysql-version' => '8.0' }, " - Stash is updated"; - is( $db->_blocker_old_cpanel_mysql('10.3'), 0, "Maria 10.3 and we're ok" ); - is $stash, { 'mysql-version' => '10.3' }, " - Stash is updated"; - is( $db->_blocker_old_cpanel_mysql('10.4'), 0, "Maria 10.4 and we're ok" ); - is $stash, { 'mysql-version' => '10.4' }, " - Stash is updated"; - is( $db->_blocker_old_cpanel_mysql('10.5'), 0, "Maria 10.5 and we're ok" ); - is $stash, { 'mysql-version' => '10.5' }, " - Stash is updated"; - is( $db->_blocker_old_cpanel_mysql('10.6'), 0, "Maria 10.6 and we're ok" ); - is $stash, { 'mysql-version' => '10.6' }, " - Stash is updated"; + # Test for start mode where the user accepts + $user_consent = 1; + is( + $db->_blocker_old_cpanel_mysql(), + 0, + "Returns 0 when user agrees to upgrade" + ); + is( $stash, { 'mysql-version' => $upgrade_version }, 'Stage file updated with upgrade version' ); + message_seen( + 'WARN', + qr/You have OldDB $test_db_version installed.\nThis version is not available for $os_pretty_name/ + ); + message_seen( + 'WARN', + qr/automatically upgrade .*to NewDB $upgrade_version/s + ); } {