diff --git a/elevate-cpanel b/elevate-cpanel index ce72f3ad..42d33a6f 100755 --- a/elevate-cpanel +++ b/elevate-cpanel @@ -51,7 +51,12 @@ BEGIN { # Suppress load of all of these at earliest point. $INC{'Elevate/Components/RmMod.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/Components/WPToolkit.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/Components/SSH.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'; + $INC{'Elevate/OS/RHEL.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/Fetch.pm'} = 'script/elevate-cpanel.PL.static'; + $INC{'Elevate/Leapp.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/Logger.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/Motd.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/Notify.pm'} = 'script/elevate-cpanel.PL.static'; @@ -61,6 +66,8 @@ BEGIN { # Suppress load of all of these at earliest point. $INC{'Elevate/Service.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/SystemctlService.pm'} = 'script/elevate-cpanel.PL.static'; $INC{'Elevate/Usage.pm'} = 'script/elevate-cpanel.PL.static'; + $INC{'Elevate/DNF.pm'} = 'script/elevate-cpanel.PL.static'; + $INC{'Elevate/YUM.pm'} = 'script/elevate-cpanel.PL.static'; } { # --- BEGIN lib/Elevate/Constants.pm @@ -471,6 +478,7 @@ BEGIN { # Suppress load of all of these at earliest point. use cPstrict; use Cpanel::OS (); + use Cpanel::Pkgr (); use Cpanel::Version::Tiny (); use Cpanel::JSON (); use Cpanel::SafeRun::Simple (); @@ -490,6 +498,7 @@ BEGIN { # Suppress load of all of these at earliest point. $ok = 0 unless $self->_blocker_acknowledge_postgresql_datadir; $ok = 0 unless $self->_blocker_old_mysql; $ok = 0 unless $self->_blocker_mysql_upgrade_in_progress; + $ok = 0 unless $self->_blocker_mysql_governor; $self->_warning_mysql_not_enabled(); return $ok; } @@ -640,6 +649,21 @@ BEGIN { # Suppress load of all of these at earliest point. return 0; } + sub _blocker_mysql_governor ($self) { + + if ( Cpanel::Pkgr::is_installed('governor-mysql') ) { + return $self->has_blocker( <<~'EOS' ); +You have MySQL Governor installed. Upgrades with this software in place are not currently supported. +For more information regarding MySQL Governor, please review the documentation: + + https://docs.cloudlinux.com/shared/cloudlinux_os_components/#mysql-governor + +EOS + } + + return 0; + } + sub _warning_mysql_not_enabled ($self) { require Cpanel::Services::Enabled; my $enabled = Cpanel::Services::Enabled::is_enabled('mysql'); @@ -760,13 +784,15 @@ EOS our @ISA; BEGIN { push @ISA, qw(Elevate::Blockers::Base); } + use Elevate::OS (); + # use Log::Log4perl qw(:easy); INIT { Log::Log4perl->import(qw{:easy}); } sub check ($self) { my @checks = qw{ - _blocker_is_non_centos7 + _blocker_os_is_not_supported _blocker_is_old_centos7 _blocker_is_experimental_os }; @@ -779,16 +805,13 @@ EOS return 0; } - sub _blocker_is_non_centos7 ($self) { - unless ( Cpanel::OS::major() == 7 && Cpanel::OS::distro() eq 'centos' ) { - my $pretty_distro_name = $self->upgrade_to_pretty_name(); - return $self->has_blocker(qq[This script is only designed to upgrade CentOS 7 to $pretty_distro_name.]); - } - + sub _blocker_os_is_not_supported ($self) { + Elevate::OS::is_supported(); # dies return 0; } sub _blocker_is_old_centos7 ($self) { + if ( Cpanel::OS::minor() < MINIMUM_CENTOS_7_SUPPORTED ) { my $pretty_distro_name = $self->upgrade_to_pretty_name(); return $self->has_blocker( @@ -811,12 +834,7 @@ EOS } sub bail_out_on_inappropriate_distro () { - - if ( !( eval { Cpanel::OS::can_be_elevated() } // ( Cpanel::OS::distro() eq 'centos' && Cpanel::OS::major() == 7 ) ) ) { - FATAL(qq[This script is designed to only run on CentOS 7 servers.\n]); - exit 1; - } - + Elevate::OS::is_supported(); # dies return; } @@ -1509,9 +1527,13 @@ EOS our @ISA; BEGIN { push @ISA, qw(Elevate::Blockers::Base); } + use Elevate::OS (); + use Cpanel::Pkgr (); sub check ($self) { + return if Elevate::OS::leapp_can_handle_python36(); + my $pkg = Cpanel::Pkgr::what_provides('python36'); return unless $pkg && Cpanel::Pkgr::is_installed($pkg); return $self->has_blocker( <<~"END" ); @@ -1536,6 +1558,7 @@ EOS use Cpanel::JSON (); use Elevate::Constants (); + use Elevate::OS (); # use Elevate::Blockers::Base(); our @ISA; @@ -1544,92 +1567,6 @@ EOS # use Log::Log4perl qw(:easy); INIT { Log::Log4perl->import(qw{:easy}); } - use constant DISABLE_MYSQL_YUM_REPOS => qw{ - Mysql57.repo - Mysql80.repo - - MariaDB102.repo - MariaDB103.repo - MariaDB105.repo - MariaDB106.repo - - mysql-community.repo - }; - - use constant VETTED_MYSQL_YUM_REPO_IDS => qw{ - mysql-cluster-7.5-community - mysql-cluster-7.5-community-source - mysql-cluster-7.5-community-source - mysql-cluster-7.6-community - mysql-cluster-7.6-community-source - mysql-cluster-7.6-community-source - mysql-cluster-8.0-community - mysql-cluster-8.0-community-debuginfo - mysql-cluster-8.0-community-source - mysql-connectors-community - mysql-connectors-community-debuginfo - mysql-connectors-community-source - mysql-connectors-community-source - mysql-tools-community - mysql-tools-community-debuginfo - mysql-tools-community-source - mysql-tools-preview - mysql-tools-preview-source - mysql55-community - mysql55-community-source - mysql56-community - mysql56-community-source - mysql57-community - mysql57-community-source - mysql80-community - mysql80-community-debuginfo - mysql80-community-source - MariaDB102 - MariaDB103 - MariaDB105 - MariaDB106 - }; - - use constant VETTED_YUM_REPO => qw{ - base - c7-media - centos-kernel - centos-kernel-experimental - centosplus - cp-dev-tools - cpanel-addons-production-feed - cpanel-plugins - cr - ct-preset - digitalocean-agent - droplet-agent - EA4 - EA4-c$releasever - elasticsearch - elasticsearch-7.x - elevate - elevate-source - epel - epel-testing - extras - fasttrack - imunify360 - imunify360-ea-php-hardened - imunify360-rollout-1 - imunify360-rollout-2 - imunify360-rollout-3 - imunify360-rollout-4 - imunify360-rollout-5 - imunify360-rollout-6 - imunify360-rollout-7 - imunify360-rollout-8 - influxdb - kernelcare - updates - wp-toolkit-cpanel - wp-toolkit-thirdparties - }, VETTED_MYSQL_YUM_REPO_IDS; - sub check ($self) { my $ok = 1; $ok = 0 unless $self->_blocker_system_update; @@ -1728,7 +1665,7 @@ EOS $self->{_yum_repos_to_disable} = []; $self->{_yum_repos_unsupported_with_packages} = []; - my %vetted = map { $_ => 1 } VETTED_YUM_REPO; + my @vetted_repos = Elevate::OS::vetted_yum_repo(); my $repo_dir = Elevate::Constants::YUM_REPOS_D; @@ -1755,7 +1692,7 @@ EOS return unless length $current_repo_name; return unless $current_repo_enabled; - my $is_vetted = $vetted{$current_repo_name} || $vetted{ lc $current_repo_name }; + my $is_vetted = grep { $current_repo_name =~ m/$_/ } @vetted_repos; if ( !$is_vetted ) { $status{'UNVETTED'} = 1; @@ -2121,6 +2058,8 @@ EOS use Simple::Accessor qw( cpev rpm + yum + dnf ); # use Log::Log4perl qw(:easy); @@ -2154,6 +2093,14 @@ EOS return Elevate::RPM->new( cpev => $self ); } + sub _build_yum ($self) { + return Elevate::YUM->new( cpev => $self ); + } + + sub _build_dnf ($self) { + return Elevate::DNF->new( cpev => $self ); + } + sub run_once ( $self, $subname ) { my $cpev = $self->cpev; @@ -2291,7 +2238,9 @@ EOS use cPstrict; use Elevate::Constants (); + use Elevate::OS (); use Elevate::RPM (); + use Elevate::YUM (); use Cwd (); @@ -2338,7 +2287,7 @@ EOS sub _cleanup_rpm_db ($self) { - $self->ssystem(q{/usr/bin/yum -y erase ea-*}); + $self->yum->remove('ea-*'); return; } @@ -2397,7 +2346,7 @@ EOS sub _get_ea4_profile ($self) { - my $ea_alias = $self->upgrade_to_rocky() ? 'CentOS_8' : 'AlmaLinux_8'; + my $ea_alias = Elevate::OS::ea_alias(); my @cmd = ( '/usr/local/bin/ea_current_to_profile', "--target-os=$ea_alias" ); @@ -2687,6 +2636,7 @@ EOS use Elevate::Constants (); use Elevate::Fetch (); use Elevate::Notify (); + use Elevate::OS (); use Cwd (); @@ -2709,6 +2659,8 @@ EOS sub pre_leapp ($self) { + return if Elevate::OS::leapp_can_handle_imunify(); + return unless $self->is_installed; $self->run_once("_capture_imunify_features"); @@ -2720,6 +2672,8 @@ EOS sub post_leapp ($self) { + return if Elevate::OS::leapp_can_handle_imunify(); + $self->run_once('_reinstall_imunify_360'); $self->run_once('_restore_imunify_features'); $self->run_once("_restore_imunify_packages"); @@ -3122,6 +3076,7 @@ EOS use cPstrict; use Elevate::Constants (); + use Elevate::OS (); use Cwd (); use File::Copy (); @@ -3136,6 +3091,8 @@ EOS sub pre_leapp ($self) { + return if Elevate::OS::leapp_can_handle_kernelcare(); + $self->run_once("_remove_kernelcare_if_needed"); return; @@ -3143,6 +3100,8 @@ EOS sub post_leapp ($self) { + return if Elevate::OS::leapp_can_handle_kernelcare(); + $self->run_once('_restore_kernelcare'); return; @@ -3883,8 +3842,9 @@ EOS use cPstrict; - use Elevate::Constants (); - use Elevate::Blockers::Repositories (); + use Elevate::Constants (); + use Elevate::OS (); + use Elevate::RPM (); use Cpanel::SafeRun::Simple (); use Cwd (); @@ -3899,6 +3859,8 @@ EOS sub pre_leapp ($self) { + $self->run_once("_disable_epel"); + $self->run_once("_disable_yum_plugin_fastestmirror"); $self->run_once("_disable_known_yum_repositories"); return; @@ -3906,8 +3868,7 @@ EOS sub _disable_known_yum_repositories { - my @repo_files = map { Elevate::Constants::YUM_REPOS_D . '/' . $_ } # - Elevate::Blockers::Repositories::DISABLE_MYSQL_YUM_REPOS; + my @repo_files = map { Elevate::Constants::YUM_REPOS_D . '/' . $_ } Elevate::OS::disable_mysql_yum_repos(); foreach my $f (@repo_files) { next unless -e $f; @@ -3924,6 +3885,27 @@ EOS return; } + sub _disable_yum_plugin_fastestmirror ($self) { + my $pkg = 'yum-plugin-fastestmirror'; + $self->_erase_package($pkg); + return; + } + + sub _disable_epel ($self) { + + return if Elevate::OS::leapp_can_handle_epel(); + + my $pkg = 'epel-release'; + $self->_erase_package($pkg); + return; + } + + sub _erase_package ( $self, $pkg ) { + return unless Cpanel::Pkgr::is_installed($pkg); + $self->rpm->remove_no_dependencies($pkg); + return; + } + 1; } # --- END lib/Elevate/Components/Repositories.pm @@ -3935,6 +3917,7 @@ EOS use cPstrict; use Elevate::Constants (); + use Elevate::DNF (); use Cwd (); use File::Copy (); @@ -3942,7 +3925,8 @@ EOS # use Log::Log4perl qw(:easy); INIT { Log::Log4perl->import(qw{:easy}); } - use Cpanel::Pkgr (); + use Cpanel::Pkgr (); + use Cpanel::Yum::Vars (); # use Elevate::Components::Base(); our @ISA; @@ -3957,17 +3941,39 @@ EOS sub _cleanup_rpms ($self) { - $self->ssystem(q{/usr/bin/rpm -e --justdb --nodeps `/usr/bin/rpm -qa | /usr/bin/egrep '^cpanel-.*\.x86_64'`}); - - foreach my $rpm (qw/ yum-plugin-fastestmirror epel-release/) { - next unless Cpanel::Pkgr::is_installed($rpm); - $self->ssystem( '/usr/bin/rpm', '-e', '--nodeps', $rpm ); - } + $self->rpm->remove_cpanel_arch_rpms(); return; } sub post_leapp ($self) { + + $self->run_once("_sysup"); + + return; + } + + sub _sysup ($self) { + Cpanel::Yum::Vars::install(); + $self->dnf->clean_all(); + + my $epel_url = 'https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm'; + + unless ( Cpanel::Pkgr::is_installed('epel-release') ) { + $self->dnf->install_rpm_via_url($epel_url); + } + + $self->dnf->config_manager_enable('powertools'); + $self->dnf->config_manager_enable('epel'); + + $self->ssystem(qw{/usr/bin/rm -f /usr/local/cpanel/3rdparty/perl/536/cpanel-lib/X/Tiny.pm}); + { + local $ENV{'CPANEL_BASE_INSTALL'} = 1; # Don't fix more than perl itself. + $self->ssystem(qw{/usr/local/cpanel/scripts/fix-cpanel-perl}); + } + $self->dnf->update_allow_erasing( '--disablerepo', 'cpanel-plugins' ); + $self->ssystem_and_die(qw{/usr/local/cpanel/scripts/sysup}); + return; } @@ -4132,6 +4138,278 @@ EOS } # --- END lib/Elevate/Components/SSH.pm +{ # --- BEGIN lib/Elevate/OS.pm + + package Elevate::OS; + + use cPstrict; + + use Carp (); + + # use Log::Log4perl qw(:easy); + INIT { Log::Log4perl->import(qw{:easy}); } + + use constant SUPPORTED_DISTROS => ( + 'CentOS 7', + 'CloudLinux 7', + ); + + our $OS; + + sub factory { + my $distro_with_version = cpev::read_stage_file( 'upgrade_from', '' ); + + my $distro; + my $major; + if ( !$distro_with_version ) { + $distro = Cpanel::OS::distro(); + $distro = 'CentOS' if $distro eq 'centos'; + $distro = 'CloudLinux' if $distro eq 'cloudlinux'; + $major = Cpanel::OS::major(); + $distro_with_version = $distro . $major; + } + + my $class = "Elevate::OS::" . $distro_with_version; + my $class_path = "Elevate/OS/$distro_with_version.pm"; + + require $class_path unless $INC{$class_path}; + + my $self = bless {}, $class; + return $self; + } + + sub instance { + return $OS if $OS; + + $OS = eval { factory(); }; + + if ( !$OS ) { + my $supported_distros = join( "\n", SUPPORTED_DISTROS() ); + die "This script is only designed to upgrade the following OSs:\n\n$supported_distros\n"; + } + + return $OS; + } + + my %methods; + + BEGIN { + + %methods = map { $_ => 0 } ( + 'available_upgrade_paths', # This returns a list of possible upgrade paths for the OS + 'default_upgrade_to', # This is the default OS that the current OS should upgrade to (i.e. CL7->CL8, C7->A8) + 'disable_mysql_yum_repos', # This is a list of mysql repo files to disable + 'ea_alias', # This is the value for the --target-os flag used when backing up an EA4 profile + 'elevate_rpm_url', # This is the URL used to install the leapp RPM/repo + 'is_experimental', # This is used to determine if the OS is experimental or not + 'is_supported', # This is used to determine if the OS is supported or not + 'leapp_can_handle_epel', # This is used to determine if we can skip removing the EPEL repo pre_leapp or not + 'leapp_can_handle_imunify', # This is used to determine if we can skip the Imunify component or not + 'leapp_can_handle_kernelcare', # This is used to determine if we can skip the kernelcare component or not + 'leapp_can_handle_python36', # This is used to determine if we can skip the python36 blocker or not + 'leapp_data_pkg', # This is used to determine which leapp data package to install + 'leapp_flag', # This is used to determine if we need to pass any flags to the leapp script or not + 'name', # This is the name of the OS we are upgrading from (i.e. CentOS7, or CloudLinux7) + 'pretty_name', # This is the pretty name of the OS we are upgrading from (i.e. 'CentOS 7') + 'vetted_mysql_yum_repo_ids', # This is a list of known mysql yum repo ids + 'vetted_yum_repo', # This is a list of known yum repos that we do not block on + ); + } + + sub supported_methods { + return sort keys %methods; + } + + our $AUTOLOAD; + + sub AUTOLOAD { + my $sub = $AUTOLOAD; + $sub =~ s/.*:://; + + exists $methods{$sub} or Carp::Croak("$sub is not a supported data variable for Elevate::OS"); + + my $i = instance(); + my $can = $i->can($sub) or Carp::Croak( ref($i) . " does not implement $sub" ); + return $can->( $i, @_ ); + } + + sub DESTROY { } # This is a must for autoload modules + + sub can_upgrade_to ($flavor) { + return grep { $_ eq $flavor } Elevate::OS::available_upgrade_paths(); + } + + sub upgrade_to () { + my $default = Elevate::OS::default_upgrade_to(); + return cpev::read_stage_file( 'upgrade_to', $default ); + } + + 1; + +} # --- END lib/Elevate/OS.pm + +{ # --- BEGIN lib/Elevate/OS/CentOS7.pm + + package Elevate::OS::CentOS7; + + use cPstrict; + + # use Log::Log4perl qw(:easy); + INIT { Log::Log4perl->import(qw{:easy}); } + + # use Elevate::OS::RHEL(); + our @ISA; + BEGIN { push @ISA, qw(Elevate::OS::RHEL); } + + use constant available_upgrade_paths => ( + 'alma', + 'almalinux', + 'rocky', + 'rockylinux', + ); + + use constant default_upgrade_to => 'AlmaLinux'; + use constant ea_alias => 'CentOS_8'; + use constant elevate_rpm_url => 'https://repo.almalinux.org/elevate/elevate-release-latest-el7.noarch.rpm'; + use constant name => 'CentOS7'; + use constant pretty_name => 'CentOS 7'; + + sub leapp_data_pkg ($self) { + my $upgrade_to = Elevate::OS::upgrade_to(); + return $upgrade_to =~ m/^rocky/ai ? 'leapp-data-rocky' : 'leapp-data-almalinux'; + } + + 1; + +} # --- END lib/Elevate/OS/CentOS7.pm + +{ # --- BEGIN lib/Elevate/OS/CloudLinux7.pm + + package Elevate::OS::CloudLinux7; + + use cPstrict; + + # use Log::Log4perl qw(:easy); + INIT { Log::Log4perl->import(qw{:easy}); } + + # use Elevate::OS::RHEL(); + our @ISA; + BEGIN { push @ISA, qw(Elevate::OS::RHEL); } + + use constant available_upgrade_paths => ( + 'cloud', + 'cloudlinux', + ); + + use constant default_upgrade_to => 'CloudLinux'; + use constant ea_alias => 'CloudLinux_8'; + use constant elevate_rpm_url => 'https://repo.cloudlinux.com/elevate/elevate-release-latest-el7.noarch.rpm'; + use constant is_experimental => 1; + use constant leapp_can_handle_epel => 1; + use constant leapp_can_handle_imunify => 1; + use constant leapp_can_handle_kernelcare => 1; + use constant leapp_can_handle_python36 => 1; + use constant leapp_data_pkg => 'leapp-data-cloudlinux'; + use constant leapp_flag => '--nowarn'; + use constant name => 'CloudLinux7'; + use constant pretty_name => 'CloudLinux 7'; + + sub vetted_yum_repo ($self) { + my @vetted_cloudlinux_yum_repo = ( + qr/^cloudlinux(?:-(?:base|updates|extras|compat|imunify360|elevate))?$/, + qr/^cloudlinux-rollout(?:-[0-9]+)?$/, + qr/^cloudlinux-ea4(?:-[0-9]+)?$/, + qr/^cloudlinux-ea4-rollout(?:-[0-9]+)?$/, + 'cl-ea4', + qr/^cl-mysql(?:-meta)?/, + ); + + my @repos = $self->SUPER::vetted_yum_repo(); + push @repos, @vetted_cloudlinux_yum_repo; + return @repos; + } + + 1; + +} # --- END lib/Elevate/OS/CloudLinux7.pm + +{ # --- BEGIN lib/Elevate/OS/RHEL.pm + + package Elevate::OS::RHEL; + + use cPstrict; + + # use Log::Log4perl qw(:easy); + INIT { Log::Log4perl->import(qw{:easy}); } + + use constant disable_mysql_yum_repos => qw{ + Mysql57.repo + Mysql80.repo + + MariaDB102.repo + MariaDB103.repo + MariaDB105.repo + MariaDB106.repo + + mysql-community.repo + }; + + use constant vetted_mysql_yum_repo_ids => ( + qr/^mysql-cluster-[0-9.]{3}-community(?:-(?:source|debuginfo))?$/, + qr/^mysql-connectors-community(?:-(?:source|debuginfo))?$/, + qr/^mysql-tools-community(?:-(?:source|debuginfo))?$/, + qr/^mysql-tools-preview(?:-source)?$/, + qr/^mysql[0-9]{2}-community(?:-(?:source|debuginfo))?$/, + qr/^MariaDB[0-9]{3}$/, + ); + + use constant vetted_yum_repo => ( + 'base', + 'c7-media', + qr/^centos-kernel(?:-experimental)?$/, + 'centosplus', + 'cp-dev-tools', + 'cpanel-addons-production-feed', + 'cpanel-plugins', + 'cr', + 'ct-preset', + 'digitalocean-agent', + 'droplet-agent', + qr/^EA4(?:-c\$releasever)?$/, + qr/^elasticsearch(?:7\.x)?$/, + qr/^elevate(?:-source)?$/, + qr/^epel(?:-testing)?$/, + 'extras', + 'fasttrack', + 'imunify360', + 'imunify360-ea-php-hardened', + qr/^imunify360-rollout-[0-9]+$/, + 'influxdb', + 'kernelcare', + 'updates', + qr/^wp-toolkit-(?:cpanel|thirdparties)$/, + ), + vetted_mysql_yum_repo_ids; + + use constant available_upgrade_paths => undef; + use constant default_upgrade_to => undef; + use constant ea_alias => undef; + use constant elevate_rpm_url => undef; + use constant is_experimental => 0; + use constant is_supported => 1; + use constant leapp_can_handle_epel => 0; + use constant leapp_can_handle_imunify => 0; + use constant leapp_can_handle_kernelcare => 0; + use constant leapp_can_handle_python36 => 0; + use constant leapp_data_package => undef; + use constant leapp_flag => undef; + use constant name => 'RHEL'; + use constant pretty_name => 'RHEL'; + + 1; + +} # --- END lib/Elevate/OS/RHEL.pm + { # --- BEGIN lib/Elevate/Fetch.pm package Elevate::Fetch; @@ -4177,6 +4455,176 @@ EOS } # --- END lib/Elevate/Fetch.pm +{ # --- BEGIN lib/Elevate/Leapp.pm + + package Elevate::Leapp; + + use cPstrict; + + use Cpanel::JSON (); + use Cpanel::Pkgr (); + + use Elevate::OS (); + use Elevate::YUM (); + + use Config::Tiny (); + + # use Log::Log4perl qw(:easy); + INIT { Log::Log4perl->import(qw{:easy}); } + + use constant LEAPP_REPORT_JSON => q[/var/log/leapp/leapp-report.json]; + use constant LEAPP_REPORT_TXT => q[/var/log/leapp/leapp-report.txt]; + + use Simple::Accessor qw{ + cpev + yum + }; + + sub _build_cpev { + die q[Missing cpev]; + } + + sub _build_yum ($self) { + return Elevate::YUM->new( cpev => $self->cpev() ); + } + + sub install ($self) { + + unless ( Cpanel::Pkgr::is_installed('elevate-release') ) { + my $elevate_rpm_url = Elevate::OS::elevate_rpm_url(); + $self->yum->install_rpm_via_url($elevate_rpm_url); + } + + my $leapp_data_pkg = Elevate::OS::leapp_data_pkg(); + + unless ( Cpanel::Pkgr::is_installed('leapp-upgrade') && Cpanel::Pkgr::is_installed($leapp_data_pkg) ) { + $self->yum->install( 'leapp-upgrade', $leapp_data_pkg ); + } + + if ( Cpanel::Pkgr::is_installed('kernel-devel') ) { + $self->yum->remove('kernel-devel'); + } + + return; + } + + sub upgrade ($self) { + + return unless $self->cpev->should_run_leapp(); + + $self->cpev->run_once( + setup_answer_file => sub { + $self->setup_answer_file(); + }, + ); + + my $leapp_flag = Elevate::OS::leapp_flag(); + my $leapp_bin = '/usr/bin/leapp'; + my @leapp_args = ('upgrade'); + push( @leapp_args, $leapp_flag ) if $leapp_flag; + + INFO("Running leapp upgrade"); + + my $ok = eval { + local $ENV{LEAPP_OVL_SIZE} = cpev::read_stage_file('env')->{'LEAPP_OVL_SIZE'} || 3000; + $self->cpev->ssystem_and_die( { keep_env => 1 }, $leapp_bin, @leapp_args ); + 1; + }; + + return 1 if $ok; + + $self->_report_leapp_failure_and_die(); + return; + } + + sub _report_leapp_failure_and_die ($self) { + + my $msg = <<'EOS'; +The 'leapp upgrade' process failed. + +Please investigate, resolve then re-run the following command to continue the update: + + /scripts/elevate-cpanel --continue + +EOS + + my $leapp_json_report = LEAPP_REPORT_JSON; + if ( -e $leapp_json_report ) { + my $report = eval { Cpanel::JSON::LoadFile($leapp_json_report) } // {}; + + my $entries = $report->{entries}; + if ( ref $entries eq 'ARRAY' ) { + foreach my $e (@$entries) { + next unless ref $e && $e->{title} =~ qr{Missing.*answer}i; + + $msg .= $e->{summary} if $e->{summary}; + + if ( ref $e->{detail} ) { + my $d = $e->{detail}; + + if ( ref $d->{remediations} ) { + foreach my $remed ( $d->{remediations}->@* ) { + next unless $remed->{type} && $remed->{type} eq 'command'; + next unless ref $remed->{context}; + my @hint = $remed->{context}->@*; + next unless scalar @hint; + $hint[0] = q[/usr/bin/leapp] if $hint[0] && $hint[0] eq 'leapp'; + my $cmd = join( ' ', @hint ); + + $msg .= "\n\n"; + $msg .= <<"EOS"; +Consider running this command: + + $cmd +EOS + } + } + + } + + } + } + } + + if ( -e LEAPP_REPORT_TXT ) { + $msg .= qq[\nYou can read the full leapp report at: ] . LEAPP_REPORT_TXT; + } + + die qq[$msg\n]; + return; + } + + sub setup_answer_file ($self) { + my $leapp_dir = '/var/log/leapp'; + mkdir $leapp_dir unless -d $leapp_dir; + + my $answerfile_path = $leapp_dir . '/answerfile'; + system touch => $answerfile_path unless -e $answerfile_path; + + my $do_write; # no point in overwriting the file if nothing needs to change + + my $ini_obj = Config::Tiny->read( $answerfile_path, 'utf8' ); + LOGDIE( 'Failed to read leapp answerfile: ' . Config::Tiny->errstr ) unless $ini_obj; + + my $SECTION = 'remove_pam_pkcs11_module_check'; + + if ( not defined $ini_obj->{$SECTION}->{'confirm'} or $ini_obj->{$SECTION}->{'confirm'} ne 'True' ) { + $do_write = 1; + $ini_obj->{$SECTION}->{'confirm'} = 'True'; + } + + if ($do_write) { + $ini_obj->write( $answerfile_path, 'utf8' ) # + or LOGDIE( 'Failed to write leapp answerfile: ' . $ini_obj->errstr ); + } + + return; + } + + 1; + +} # --- END lib/Elevate/Leapp.pm + { # --- BEGIN lib/Elevate/Logger.pm package Elevate::Logger; @@ -4519,6 +4967,8 @@ EOS cpev }; + our $rpm = '/usr/bin/rpm'; + sub _build_cpev { die q[Missing cpev]; } @@ -4536,7 +4986,7 @@ EOS my %config_files; foreach my $pkg (@$pkgs) { - my $out = $self->cpev->ssystem_capture_output( '/usr/bin/rpm', '-qc', $pkg ) || {}; + my $out = $self->cpev->ssystem_capture_output( $rpm, '-qc', $pkg ) || {}; if ( $out->{status} != 0 ) { @@ -4575,6 +5025,37 @@ EOS return; } + sub remove_no_dependencies ( $self, $pkg ) { + $self->cpev->ssystem( $rpm, '-e', '--nodeps', $pkg ); + return; + } + + sub remove_no_dependencies_and_justdb ( $self, $pkg ) { + $self->cpev->ssystem( $rpm, '-e', '--nodeps', '--justdb', $pkg ); + return; + } + + sub get_installed_rpms ($self) { + my $out = $self->cpev->ssystem_capture_output( $rpm, '-qa' ); + return @{ $out->{stdout} }; + } + + sub get_cpanel_arch_rpms ($self) { + my @installed_rpms = $self->get_installed_rpms(); + my @cpanel_arch_rpms = grep { $_ =~ m/^cpanel-.*\.x86_64$/ } @installed_rpms; + return @cpanel_arch_rpms; + } + + sub remove_cpanel_arch_rpms ($self) { + my @rpms_to_remove = $self->get_cpanel_arch_rpms(); + + foreach my $rpm (@rpms_to_remove) { + $self->remove_no_dependencies_and_justdb($rpm); + } + + return; + } + 1; } # --- END lib/Elevate/RPM.pm @@ -4989,6 +5470,107 @@ EOS } # --- END lib/Elevate/Usage.pm +{ # --- BEGIN lib/Elevate/DNF.pm + + package Elevate::DNF; + + use cPstrict; + + # use Log::Log4perl qw(:easy); + INIT { Log::Log4perl->import(qw{:easy}); } + + # use Elevate::YUM(); + our @ISA; + BEGIN { push @ISA, qw(Elevate::YUM); } + + sub _build_pkgmgr { + return '/usr/bin/dnf'; + } + + sub config_manager_enable ( $self, $repo ) { + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem( $pkgmgr, 'config-manager', '--enable', $repo ); + + return; + } + + sub update_allow_erasing ( $self, @args ) { + my $pkgmgr = $self->pkgmgr; + + my @additional_args = scalar @args ? @args : ''; + + $self->cpev->ssystem( $pkgmgr, '-y', '--allowerasing', @additional_args, 'update' ); + + return; + } + + 1; + +} # --- END lib/Elevate/DNF.pm + +{ # --- BEGIN lib/Elevate/YUM.pm + + package Elevate::YUM; + + use cPstrict; + + # use Log::Log4perl qw(:easy); + INIT { Log::Log4perl->import(qw{:easy}); } + + use Simple::Accessor qw{ + cpev + pkgmgr + }; + + sub _build_cpev { + die q[Missing cpev]; + } + + sub _build_pkgmgr { + return '/usr/bin/yum'; + } + + sub remove ( $self, @pkgs ) { + return unless scalar @pkgs; + + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem_and_die( $pkgmgr, '-y', 'remove', @pkgs ); + + return; + } + + sub clean_all ($self) { + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem( $pkgmgr, 'clean', 'all' ); + + return; + } + + sub install_rpm_via_url ( $self, $rpm_url ) { + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem_and_die( $pkgmgr, '-y', 'install', $rpm_url ); + + return; + } + + sub install ( $self, @pkgs ) { + return unless scalar @pkgs; + + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem_and_die( $pkgmgr, '-y', 'install', @pkgs ); + + return; + } + + 1; + +} # --- END lib/Elevate/YUM.pm + package main; # Copyright 2023 cPanel L.L.C. @@ -5026,38 +5608,47 @@ package cpev; =head1 DESCRIPTION -Helps to upgrade CentOS 7 cPanel servers to AlmaLinux 8 or Rocky Linux 8. +Wrapper around the ELevate project which is designed to enable migrations +between major version of RHEL® derivatives. + +Currently supported upgrade paths: + +CentOS 7 => AlmaLinux 8 +CentOS 7 => Rocky Linux 8 +CloudLinux 7 => CloudLinux 8 =head1 SYNOPSIS /scripts/elevate-cpanel [OPTIONS] Optional: - --start Start the conversion process - --continue Continue the conversion: retry the last step - --check[=BLOCKER_FILE] Check if your system has any known blockers to upgrade. - --log Show the current elevation log - --status Check the current elevation status - --clean Cleanup scripts and files created by elevate-cpanel - --upgrade-to=[rocky|almalinux] Update to AlmaLinux 8 or Rocky Linux 8 - [default to 'almalinux'] - --non-interactive Skip the yes/no prompt before proceeding with the upgrade. + --start Start the conversion process + --continue Continue the conversion: retry the last step + --check[=BLOCKER_FILE] Check if your system has any known blockers to upgrade. + --log Show the current elevation log + --status Check the current elevation status + --clean Cleanup scripts and files created by elevate-cpanel + --upgrade-to=[rocky|almalinux|cloudlinux] Update to AlmaLinux 8 or Rocky Linux 8 + [Servers running CentOS 7 will default to 'almalinux'] + [Servers running CloudLinux 7 will default to 'cloudlinux'] + + NOTE: servers running CentOS 7 can only upgrade to almalinux or rocky + servers running CloudLinux 7 can only upgrade to cloudlinux - --update Instruct the script to replace itself on disk with a downloaded copy of the latest version. - --version Print the version number and exit. + --non-interactive Skip the yes/no prompt before proceeding with the upgrade. - --skip-elevate-version-check Skip the check for whether this script is up to date. - --skip-cpanel-version-check Skip the check for whether cPanel is up to date. - This option is intended only for testing! + --update Instruct the script to replace itself on disk with a downloaded copy of the latest version. + --version Print the version number and exit. - --no-leapp Do not try to run leapp, and pause instead. - Once leapp has been run you should remove the file - /waiting_for_distro_upgrade + --skip-elevate-version-check Skip the check for whether this script is up to date. + --skip-cpanel-version-check Skip the check for whether cPanel is up to date. + This option is intended only for testing! - --manual-reboots The script will stop and require the user to reboot manually rather than - automatically rebooting when reboots are needed + --no-leapp Do not try to run leapp, and pause instead. + Once leapp has been run you should remove the file + /waiting_for_distro_upgrade - --help Display this documentation. + --help Display this documentation. =head1 COMMON USAGE @@ -5067,13 +5658,17 @@ Helps to upgrade CentOS 7 cPanel servers to AlmaLinux 8 or Rocky Linux 8. You can start an elevation update by running: - # AlmaLinux 8 update + # AlmaLinux 8 update from CentOS 7 /scripts/elevate-cpanel --start /scripts/elevate-cpanel --start --upgrade-to=almalinux - # Rocky Linux 8 update + # Rocky Linux 8 update from CentOS 7 /scripts/elevate-cpanel --start --upgrade-to=rocky + # CloudLinux 8 update from CloudLinux 7 + /scripts/elevate-cpanel --start + /scripts/elevate-cpanel --start --upgrade-to=cloudlinux + =item Start an installation and skip the confirmation prompt By default, you will be asked if you wish to proceed with the upgrade. @@ -5087,13 +5682,17 @@ If you wish to skip this prompt (by assuming yes): You can check if your server is compatible with the upgrade process without starting an upgrade process. - # Check AlmaLinux 8 update + # Check AlmaLinux 8 update from CentOS 7 /scripts/elevate-cpanel --check /scripts/elevate-cpanel --check --upgrade-to=almalinux - # Check Rocky Linux 8 update + # Check Rocky Linux 8 update from CentOS 7 /scripts/elevate-cpanel --check --upgrade-to=rocky + # Check CloudLinux 8 update from CloudLinux 7 + /scripts/elevate-cpanel --check + /scripts/elevate-cpanel --check --upgrade-to=cloudlinux + This will also save a JSON representation of the blockers to a file, C by default, but passing an optional argument will use the file given instead: @@ -5133,7 +5732,7 @@ the update process by running: =head3 Using an alternative tool to upgrade your distro -By default, the elevate script runs the [leapp process](https://almalinux.org/elevate/) +By default, the elevate script runs the L to upgrade you from 7 to 8. `Leapp` may not be compatible with your system. Using the `--no-leapp` option gives you a way to do the actual distro upgrade in your own way. @@ -5165,8 +5764,7 @@ NOTE: `--no-leapp` is not required for helper commands like `--continue` or `--s =head1 WARNINGS -The elevation process from CentOS 7 to AlmaLinux 8 or Rocky Linux 8 distribution -is not a risk free update. +The elevation process to perform a major OS upgrade is not a risk free update. Depending on the state of your current distribution multiple errors can occur. We recommend updating your system to the last upstream state before starting. @@ -5199,7 +5797,6 @@ BEGIN { use Log::Log4perl qw(:easy); use Config; -use Config::Tiny (); use Carp (); use Errno (); use File::Basename (); @@ -5275,7 +5872,14 @@ use Elevate::Components::RmMod (); use Elevate::Components::WPToolkit (); use Elevate::Components::SSH (); +# - fatpack OS +use Elevate::OS (); +use Elevate::OS::CentOS7 (); +use Elevate::OS::CloudLinux7 (); +use Elevate::OS::RHEL (); + use Elevate::Fetch (); +use Elevate::Leapp (); use Elevate::Logger (); use Elevate::Motd (); use Elevate::Notify (); @@ -5285,6 +5889,8 @@ use Elevate::Script (); use Elevate::Service (); use Elevate::SystemctlService (); use Elevate::Usage (); +use Elevate::DNF (); +use Elevate::YUM (); #< 24; @@ -5298,9 +5904,6 @@ use constant VALID_STAGES => 5; use constant IGNORE_OUTDATED_SERVICES_FILE => q[/etc/cpanel/local/ignore_outdated_services]; -use constant LEAPP_REPORT_JSON => q[/var/log/leapp/leapp-report.json]; -use constant LEAPP_REPORT_TXT => q[/var/log/leapp/leapp-report.txt]; - use constant NOC_RECOMMENDATIONS_TOUCH_FILE => q[/var/cpanel/elevate-noc-recommendations]; # XXX TODO verify that imunify reponames are in fact correct. @@ -5310,8 +5913,9 @@ use constant ACTION_REBOOT_NEEDED => 4242; # just one unique id use constant ACTION_PAUSE_REQUESTED => 4243; # prefer string over integers so we can read the configuration file (no need for bitmask) -use constant UPGRADE_TO_ALMALINUX => q[AlmaLinux]; -use constant UPGRADE_TO_ROCKY => q[Rocky]; +use constant UPGRADE_TO_ALMALINUX => q[AlmaLinux]; +use constant UPGRADE_TO_ROCKY => q[Rocky]; +use constant UPGRADE_TO_CLOUDLINUX => q[CloudLinux]; use constant PAUSE_ELEVATE_TOUCHFILE => q[/waiting_for_distro_upgrade]; @@ -5319,6 +5923,7 @@ use Simple::Accessor qw{ service script blockers + leapp }; # after Simple::Accessor @@ -5341,6 +5946,12 @@ sub _build_blockers ($self) { return Elevate::Blockers->new( cpev => $self ); } +sub _build_leapp ($self) { + + # FIXME weaken + return Elevate::Leapp->new( cpev => $self ); +} + sub _build_script ($self) { return Elevate::Script->new; } @@ -5370,8 +5981,6 @@ sub run ( $pkg, @args ) { return $self->do_update(); } - $self->_parse_opt_upgrade_to(); - if ( $self->getopt('start') ) { die qq[Unsupported option with --start\n] if $self->getopt('continue') || $self->getopt('service'); return 1 if $self->start(); @@ -5431,17 +6040,37 @@ sub get_blocker ( $self, $name ) { # helper for tests sub _parse_opt_upgrade_to ($self) { - return unless my $flavor = $self->getopt('upgrade-to'); + my $flavor = $self->getopt('upgrade-to'); if ( !defined $self->getopt('start') && !defined $self->getopt('check') ) { die qq[--upgrade-to option is only supported with --start or --check\n]; } + + Elevate::Blockers::Distros::bail_out_on_inappropriate_distro(); + + $flavor ||= Elevate::OS::default_upgrade_to(); + $flavor = lc $flavor; - if ( $flavor !~ m{^(?:almalinux|alma|rocky)$} ) { - die qq[Invalid --upgrade_to value '$flavor'. Only 'almalinux' or 'rocky' are supported.\n]; + if ( !Elevate::OS::can_upgrade_to($flavor) ) { + my @available_flavors = Elevate::OS::available_upgrade_paths(); + my $af = join( "\n", @available_flavors ); + die qq[The current OS can only upgrade to the following flavors:\n\n$af\n]; } - $self->{upgrade_to} = $flavor eq 'rocky' ? UPGRADE_TO_ROCKY : UPGRADE_TO_ALMALINUX; + $self->_set_upgrade_to($flavor); + + return; +} + +sub _set_upgrade_to ( $self, $flavor ) { + my $upgrade_to = UPGRADE_TO_ROCKY if $flavor =~ m/^rocky/a; + + $upgrade_to = UPGRADE_TO_ALMALINUX if $flavor =~ m/^alma/a; + $upgrade_to = UPGRADE_TO_CLOUDLINUX if $flavor =~ m/^cloud/a; + + die qq['$flavor' is not a valid path to upgrade to\n] unless $upgrade_to; + + $self->{upgrade_to} = $upgrade_to; return; } @@ -5477,16 +6106,20 @@ sub do_update ($self) { } sub upgrade_to ($self) { # main helper to know the upgrade_to distro - return $self->{upgrade_to} if $self->{upgrade_to}; - return read_stage_file( 'upgrade_to', UPGRADE_TO_ALMALINUX ); # default to AlmaLinux + return $self->{upgrade_to} ? $self->{upgrade_to} : Elevate::OS::upgrade_to(); } sub upgrade_to_rocky ($self) { return $self->upgrade_to() eq UPGRADE_TO_ROCKY; } +sub upgrade_to_cloudlinux ($self) { + return $self->upgrade_to() eq UPGRADE_TO_CLOUDLINUX; +} + sub upgrade_to_pretty_name ($self) { # used by output messages return q[Rocky Linux 8] if $self->upgrade_to_rocky; + return q[CloudLinux 8] if $self->upgrade_to_cloudlinux; return q[AlmaLinux 8]; } @@ -5514,7 +6147,7 @@ sub monitor_upgrade ($self) { } sub start ($self) { - Elevate::Blockers::Distros::bail_out_on_inappropriate_distro(); + $self->_parse_opt_upgrade_to(); my $stage = get_stage(); if ( $stage != 0 ) { my $header; @@ -5611,6 +6244,13 @@ It is *highly* recommended that you have a full backup or snapshot of your server before proceeding. EOS + if ( Elevate::OS::is_experimental() ) { + say <getopt('non-interactive') ) { if ( !IO::Prompt::prompt( @@ -5781,10 +6421,11 @@ sub check_status ($self) { sub _notify_success ($self) { + my $upgrade_from_name = Elevate::OS::pretty_name(); my $pretty_distro_name = $self->upgrade_to_pretty_name(); my $msg = <<"EOS"; -The cPanel & WHM server has completed the elevation process from CentOS 7 to $pretty_distro_name. +The cPanel & WHM server has completed the elevation process from $upgrade_from_name to $pretty_distro_name. EOS if ( my $warnings = read_stage_file( 'final_notifications', [] ) ) { @@ -5945,6 +6586,11 @@ sub run_stage_1 ($self) { # store 'upgrade_to' early so later stages can access it update_stage_file( { upgrade_to => $self->upgrade_to } ); + # store 'upgrade_from' early so that Elevate::OS can access it + # Elevate::OS is based on the OS we are upgrading from since the information + # that it provides is mostly based on what leapp can do when upgrading from an OS + update_stage_file( { upgrade_from => Elevate::OS::name() } ); + return $self->service->install(); } @@ -5983,15 +6629,16 @@ sub run_stage_2 ($self) { =head2 run_stage_3 -Setup the AlmaLinux elevate-release-latest-el7 repo and install leapp packages. -Prepare the cPanel packages for the update. +Setup the elevate-release-latest-el7 repo from the appropriate fork and install +leapp Packages. Prepare the cPnael packages for the update. Remove some known conflicting packages. (Reinstall later). Provide answers to a few leapp questions. Attempt to perform the leapp upgrade itself. -In case of failure you probably want to reply to a few extra questions or remove some conflicting packages. +In case of failure you probably want to reply to a few extra questions or remove +some conflicting packages. =cut @@ -6003,8 +6650,13 @@ sub run_stage_3 ($self) { return $self->_request_to_upgrade_distro_manually(); } - $self->run_once('_install_leapp'); - $self->_do_leapp_upgrade(); + $self->run_once( + install_leapp => sub { + $self->leapp->install(); + }, + ); + + $self->leapp->upgrade(); WARN(<<~'EOS'); Rebooting for distro upgrade. This will take over 10 minutes to run. @@ -6016,27 +6668,6 @@ sub run_stage_3 ($self) { # This takes a while because on reboot it's installing 800 packages } -sub _install_leapp ($self) { - - unless ( Cpanel::Pkgr::is_installed('elevate-release') ) { - - # provides leapp-data-almalinux & leapp-data-rocky - $self->ssystem_and_die(qw{/usr/bin/yum install -y http://repo.almalinux.org/elevate/elevate-release-latest-el7.noarch.rpm}); - } - - my $leap_data_pkg = $self->upgrade_to_rocky() ? 'leapp-data-rocky' : 'leapp-data-almalinux'; - - unless ( Cpanel::Pkgr::is_installed('leapp-upgrade') && Cpanel::Pkgr::is_installed($leap_data_pkg) ) { - $self->ssystem_and_die( qw{/usr/bin/yum install -y leapp-upgrade }, $leap_data_pkg ); - } - - if ( Cpanel::Pkgr::is_installed('kernel-devel') ) { - $self->ssystem_and_die(qw{/usr/bin/yum -y remove kernel-devel}); - } - - return; -} - sub _request_to_upgrade_distro_manually ($self) { my $pause_file = PAUSE_ELEVATE_TOUCHFILE; @@ -6058,7 +6689,7 @@ EOS =head2 run_stage_4 -At this stage we should now run Alamalinux 8. +At this stage, we should now be run the upgraded OS (i.e. AlmaLinux 8). Update cPanel product for the new distro. Restore removed packages during the previous stage. @@ -6097,29 +6728,7 @@ sub run_stage_4 ($self) { $stash->{stage4} //= {}; # run once each blocks - $self->run_once( - sysup => sub { - Cpanel::Yum::Vars::install(); - $self->ssystem_and_die(qw{/usr/bin/dnf clean all}); - - # no failures once already installed: no need to check for the epel-release version - $self->ssystem_and_die(qw{/usr/bin/dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm}); - - $self->ssystem(qw{/usr/bin/dnf config-manager --enable powertools}); - $self->ssystem(qw{/usr/bin/dnf config-manager --enable epel}); - - # Break cpanel-perl (NOTE: This only works on perl 5.36) - $self->ssystem(qw{/usr/bin/rm -f /usr/local/cpanel/3rdparty/perl/536/cpanel-lib/X/Tiny.pm}); - { - local $ENV{'CPANEL_BASE_INSTALL'} = 1; # Don't fix more than perl itself. - $self->ssystem(qw{/usr/local/cpanel/scripts/fix-cpanel-perl}); - } - $self->ssystem(qw{/usr/bin/dnf -y --allowerasing --disablerepo cpanel-plugins update}); - $self->ssystem_and_die(qw{/usr/local/cpanel/scripts/sysup}); - - return; - } - ); + $self->run_component_once( 'RpmDB', => 'post_leapp' ); $self->run_once( restore_cpanel_services => sub { @@ -6153,7 +6762,7 @@ sub run_stage_4 ($self) { $self->post_leapp_update_restore(); my @known_modules_that_dont_convert = qw{libtermkey msgpack btrfs-progs elevate-release - leapp leapp-data-almalinux leapp-data-rocky leapp-upgrade-el7toel8 + leapp leapp-data-almalinux leapp-data-rocky leapp-data-cloudlinux leapp-upgrade-el7toel8 python2-leapp alt-pcre802 alt-pcre802-devel}; my @to_remove = grep { Cpanel::Pkgr::is_installed($_) } @known_modules_that_dont_convert; @@ -6178,7 +6787,7 @@ The elevate-cpanel service is now removed. =cut sub run_stage_5 ($self) { - return 1 if $self->post_upgrade_check(); + return 0 if $self->post_upgrade_check(); # we cannot stop the service ( ourself ) $self->service->disable; @@ -6242,14 +6851,6 @@ sub elevation_success_marker ($self) { return; } -=pod - -Example: - - $self->run_component_once( Imunify => 'backup' ); - -=cut - sub run_component_once ( $self, $name, $function ) { my $component = $self->component($name); @@ -6431,76 +7032,6 @@ sub disable_all_cpanel_services ($self) { return; } -sub _do_leapp_upgrade ($self) { - - return unless $self->should_run_leapp(); - - $self->run_once('setup_answer_file'); - - INFO("Running leapp upgrade"); - - my $ok = eval { - local $ENV{LEAPP_OVL_SIZE} = read_stage_file('env')->{'LEAPP_OVL_SIZE'} || 3000; - $self->ssystem_and_die( { keep_env => 1 }, qw{/usr/bin/leapp upgrade} ); - 1; - }; - - return 1 if $ok; - - my $msg = <<'EOS'; -The 'leapp upgrade' process failed. - -Please investigate, resolve then re-run the following command to continue the update: - - /scripts/elevate-cpanel --continue - -EOS - - my $leapp_json_report = LEAPP_REPORT_JSON; - if ( -e $leapp_json_report ) { - my $report = eval { Cpanel::JSON::LoadFile($leapp_json_report) } // {}; - - my $entries = $report->{entries}; - if ( ref $entries eq 'ARRAY' ) { - foreach my $e (@$entries) { - next unless ref $e && $e->{title} =~ qr{Missing.*answer}i; - - $msg .= $e->{summary} if $e->{summary}; - - if ( ref $e->{detail} ) { - my $d = $e->{detail}; - - if ( ref $d->{remediations} ) { - foreach my $remed ( $d->{remediations}->@* ) { - next unless $remed->{type} && $remed->{type} eq 'command'; - next unless ref $remed->{context}; - my @hint = $remed->{context}->@*; - next unless scalar @hint; - $hint[0] = q[/usr/bin/leapp] if $hint[0] && $hint[0] eq 'leapp'; - my $cmd = join( ' ', @hint ); - - $msg .= "\n\n"; - $msg .= <<"EOS"; -Consider running this command: - - $cmd -EOS - } - } - - } - - } - } - } - - if ( -e LEAPP_REPORT_TXT ) { - $msg .= qq[\nYou can read the full leapp report at: ] . LEAPP_REPORT_TXT; - } - - die qq[$msg\n]; -} - # remove and store sub run_final_components_pre_leapp ($self) { @@ -6655,12 +7186,12 @@ sub remove_rpms_from_repos ( $self, @repo_list ) { sub post_upgrade_check ($self) { - my $expect_distro = $self->upgrade_to_rocky() ? 'rocky' : 'almalinux'; + my $expect_distro = Elevate::OS::upgrade_to(); + $expect_distro = lc $expect_distro; unless ( Cpanel::OS::major() == 8 && Cpanel::OS::distro() eq $expect_distro ) { my $pretty_distro_name = $self->upgrade_to_pretty_name(); - FATAL("Your distro does not looks like $pretty_distro_name."); - return 1; + die "Your distro does not look like $pretty_distro_name.\n"; } # call a cpanel binary @@ -6669,40 +7200,6 @@ sub post_upgrade_check ($self) { return 0; } -=pod - - > cat /var/log/leapp/answerfile - [remove_pam_pkcs11_module_check] - confirm=True -=cut - -sub setup_answer_file { - my $leapp_dir = '/var/log/leapp'; - mkdir $leapp_dir unless -d $leapp_dir; - - my $answerfile_path = $leapp_dir . '/answerfile'; - system touch => $answerfile_path unless -e $answerfile_path; - - my $do_write; # no point in overwriting the file if nothing needs to change - - my $ini_obj = Config::Tiny->read( $answerfile_path, 'utf8' ); - LOGDIE( 'Failed to read leapp answerfile: ' . Config::Tiny->errstr ) unless $ini_obj; - - my $SECTION = 'remove_pam_pkcs11_module_check'; - - if ( not defined $ini_obj->{$SECTION}->{'confirm'} or $ini_obj->{$SECTION}->{'confirm'} ne 'True' ) { - $do_write = 1; - $ini_obj->{$SECTION}->{'confirm'} = 'True'; - } - - if ($do_write) { - $ini_obj->write( $answerfile_path, 'utf8' ) # - or LOGDIE( 'Failed to write leapp answerfile: ' . $ini_obj->errstr ); - } - - return; -} - # TODO: We're going to need to store reboot time so we know if the last reboot has happened when we re-run the script. sub should_run_leapp ($self) { diff --git a/lib/Elevate/Blockers/Databases.pm b/lib/Elevate/Blockers/Databases.pm index 35635b41..1c2dd8b0 100644 --- a/lib/Elevate/Blockers/Databases.pm +++ b/lib/Elevate/Blockers/Databases.pm @@ -13,6 +13,7 @@ Blockers for datbase: MySQL, PostgreSQL... use cPstrict; use Cpanel::OS (); +use Cpanel::Pkgr (); use Cpanel::Version::Tiny (); use Cpanel::JSON (); use Cpanel::SafeRun::Simple (); @@ -29,6 +30,7 @@ sub check ($self) { $ok = 0 unless $self->_blocker_acknowledge_postgresql_datadir; $ok = 0 unless $self->_blocker_old_mysql; $ok = 0 unless $self->_blocker_mysql_upgrade_in_progress; + $ok = 0 unless $self->_blocker_mysql_governor; $self->_warning_mysql_not_enabled(); return $ok; } @@ -185,6 +187,21 @@ sub _blocker_mysql_upgrade_in_progress ($self) { return 0; } +sub _blocker_mysql_governor ($self) { + + if ( Cpanel::Pkgr::is_installed('governor-mysql') ) { + return $self->has_blocker( <<~'EOS' ); +You have MySQL Governor installed. Upgrades with this software in place are not currently supported. +For more information regarding MySQL Governor, please review the documentation: + + https://docs.cloudlinux.com/shared/cloudlinux_os_components/#mysql-governor + +EOS + } + + return 0; +} + sub _warning_mysql_not_enabled ($self) { require Cpanel::Services::Enabled; my $enabled = Cpanel::Services::Enabled::is_enabled('mysql'); diff --git a/lib/Elevate/Blockers/Distros.pm b/lib/Elevate/Blockers/Distros.pm index 867b263a..701a5bda 100644 --- a/lib/Elevate/Blockers/Distros.pm +++ b/lib/Elevate/Blockers/Distros.pm @@ -18,12 +18,14 @@ use constant MINIMUM_CENTOS_7_SUPPORTED => 9; use parent qw{Elevate::Blockers::Base}; +use Elevate::OS (); + use Log::Log4perl qw(:easy); sub check ($self) { my @checks = qw{ - _blocker_is_non_centos7 + _blocker_os_is_not_supported _blocker_is_old_centos7 _blocker_is_experimental_os }; @@ -36,16 +38,13 @@ sub check ($self) { return 0; } -sub _blocker_is_non_centos7 ($self) { - unless ( Cpanel::OS::major() == 7 && Cpanel::OS::distro() eq 'centos' ) { - my $pretty_distro_name = $self->upgrade_to_pretty_name(); - return $self->has_blocker(qq[This script is only designed to upgrade CentOS 7 to $pretty_distro_name.]); - } - +sub _blocker_os_is_not_supported ($self) { + Elevate::OS::is_supported(); # dies return 0; } sub _blocker_is_old_centos7 ($self) { + if ( Cpanel::OS::minor() < MINIMUM_CENTOS_7_SUPPORTED ) { my $pretty_distro_name = $self->upgrade_to_pretty_name(); return $self->has_blocker( @@ -69,12 +68,7 @@ sub _blocker_is_experimental_os ($self) { # We are OK if can_be_elevated or if sub bail_out_on_inappropriate_distro () { - - if ( !( eval { Cpanel::OS::can_be_elevated() } // ( Cpanel::OS::distro() eq 'centos' && Cpanel::OS::major() == 7 ) ) ) { - FATAL(qq[This script is designed to only run on CentOS 7 servers.\n]); - exit 1; - } - + Elevate::OS::is_supported(); # dies return; } diff --git a/lib/Elevate/Blockers/Python.pm b/lib/Elevate/Blockers/Python.pm index fb68f51d..48a6cd59 100644 --- a/lib/Elevate/Blockers/Python.pm +++ b/lib/Elevate/Blockers/Python.pm @@ -14,9 +14,13 @@ use cPstrict; use parent qw{Elevate::Blockers::Base}; +use Elevate::OS (); + use Cpanel::Pkgr (); sub check ($self) { + return if Elevate::OS::leapp_can_handle_python36(); + my $pkg = Cpanel::Pkgr::what_provides('python36'); return unless $pkg && Cpanel::Pkgr::is_installed($pkg); return $self->has_blocker( <<~"END" ); diff --git a/lib/Elevate/Blockers/Repositories.pm b/lib/Elevate/Blockers/Repositories.pm index 4117e965..a5d04e7f 100644 --- a/lib/Elevate/Blockers/Repositories.pm +++ b/lib/Elevate/Blockers/Repositories.pm @@ -16,99 +16,12 @@ use Cpanel::OS (); use Cpanel::JSON (); use Elevate::Constants (); +use Elevate::OS (); use parent qw{Elevate::Blockers::Base}; use Log::Log4perl qw(:easy); -# still used by disable_known_yum_repositories function -use constant DISABLE_MYSQL_YUM_REPOS => qw{ - Mysql57.repo - Mysql80.repo - - MariaDB102.repo - MariaDB103.repo - MariaDB105.repo - MariaDB106.repo - - mysql-community.repo -}; - -# FIXME use some RegExp... -use constant VETTED_MYSQL_YUM_REPO_IDS => qw{ - mysql-cluster-7.5-community - mysql-cluster-7.5-community-source - mysql-cluster-7.5-community-source - mysql-cluster-7.6-community - mysql-cluster-7.6-community-source - mysql-cluster-7.6-community-source - mysql-cluster-8.0-community - mysql-cluster-8.0-community-debuginfo - mysql-cluster-8.0-community-source - mysql-connectors-community - mysql-connectors-community-debuginfo - mysql-connectors-community-source - mysql-connectors-community-source - mysql-tools-community - mysql-tools-community-debuginfo - mysql-tools-community-source - mysql-tools-preview - mysql-tools-preview-source - mysql55-community - mysql55-community-source - mysql56-community - mysql56-community-source - mysql57-community - mysql57-community-source - mysql80-community - mysql80-community-debuginfo - mysql80-community-source - MariaDB102 - MariaDB103 - MariaDB105 - MariaDB106 -}; - -use constant VETTED_YUM_REPO => qw{ - base - c7-media - centos-kernel - centos-kernel-experimental - centosplus - cp-dev-tools - cpanel-addons-production-feed - cpanel-plugins - cr - ct-preset - digitalocean-agent - droplet-agent - EA4 - EA4-c$releasever - elasticsearch - elasticsearch-7.x - elevate - elevate-source - epel - epel-testing - extras - fasttrack - imunify360 - imunify360-ea-php-hardened - imunify360-rollout-1 - imunify360-rollout-2 - imunify360-rollout-3 - imunify360-rollout-4 - imunify360-rollout-5 - imunify360-rollout-6 - imunify360-rollout-7 - imunify360-rollout-8 - influxdb - kernelcare - updates - wp-toolkit-cpanel - wp-toolkit-thirdparties -}, VETTED_MYSQL_YUM_REPO_IDS; - sub check ($self) { my $ok = 1; $ok = 0 unless $self->_blocker_system_update; @@ -217,7 +130,7 @@ sub _check_yum_repos ($self) { $self->{_yum_repos_to_disable} = []; $self->{_yum_repos_unsupported_with_packages} = []; - my %vetted = map { $_ => 1 } VETTED_YUM_REPO; + my @vetted_repos = Elevate::OS::vetted_yum_repo(); my $repo_dir = Elevate::Constants::YUM_REPOS_D; @@ -244,7 +157,7 @@ sub _check_yum_repos ($self) { return unless length $current_repo_name; return unless $current_repo_enabled; - my $is_vetted = $vetted{$current_repo_name} || $vetted{ lc $current_repo_name }; + my $is_vetted = grep { $current_repo_name =~ m/$_/ } @vetted_repos; if ( !$is_vetted ) { $status{'UNVETTED'} = 1; diff --git a/lib/Elevate/Components/Base.pm b/lib/Elevate/Components/Base.pm index f1a996fd..8089327f 100644 --- a/lib/Elevate/Components/Base.pm +++ b/lib/Elevate/Components/Base.pm @@ -19,6 +19,8 @@ use Carp (); use Simple::Accessor qw( cpev rpm + yum + dnf ); use Log::Log4perl qw(:easy); @@ -52,6 +54,14 @@ sub _build_rpm ($self) { return Elevate::RPM->new( cpev => $self ); } +sub _build_yum ($self) { + return Elevate::YUM->new( cpev => $self ); +} + +sub _build_dnf ($self) { + return Elevate::DNF->new( cpev => $self ); +} + sub run_once ( $self, $subname ) { my $cpev = $self->cpev; diff --git a/lib/Elevate/Components/EA4.pm b/lib/Elevate/Components/EA4.pm index 8acf1234..3e377a80 100644 --- a/lib/Elevate/Components/EA4.pm +++ b/lib/Elevate/Components/EA4.pm @@ -13,7 +13,9 @@ Perform am EA4 backup pre-elevate then restore it after the elevation process. use cPstrict; use Elevate::Constants (); +use Elevate::OS (); use Elevate::RPM (); +use Elevate::YUM (); use Cwd (); use Log::Log4perl qw(:easy); @@ -73,7 +75,7 @@ sub post_leapp ($self) { sub _cleanup_rpm_db ($self) { # remove all ea- packages - $self->ssystem(q{/usr/bin/yum -y erase ea-*}); + $self->yum->remove('ea-*'); return; } @@ -134,8 +136,7 @@ sub _backup_ea4_profile ($self) { ## _backup_ea4_profile sub _get_ea4_profile ($self) { - # obs_project_aliases from /etc/cpanel/ea4/ea4-metainfo.json - my $ea_alias = $self->upgrade_to_rocky() ? 'CentOS_8' : 'AlmaLinux_8'; + my $ea_alias = Elevate::OS::ea_alias(); my @cmd = ( '/usr/local/bin/ea_current_to_profile', "--target-os=$ea_alias" ); diff --git a/lib/Elevate/Components/Grub2.pm b/lib/Elevate/Components/Grub2.pm index 524d6e85..6b4aabf4 100644 --- a/lib/Elevate/Components/Grub2.pm +++ b/lib/Elevate/Components/Grub2.pm @@ -28,10 +28,6 @@ use Elevate::Blockers (); use constant GRUB_EDITENV => '/usr/bin/grub2-editenv'; use constant GRUB_ENV_FILE => '/boot/grub2/grubenv'; -## -## Call early so we can use a blocker based on existing ea4 profile -## - sub pre_leapp ($self) { $self->run_once('_update_grub2_workaround_if_needed'); # required part diff --git a/lib/Elevate/Components/Imunify.pm b/lib/Elevate/Components/Imunify.pm index 2fefa95e..b4348e4d 100644 --- a/lib/Elevate/Components/Imunify.pm +++ b/lib/Elevate/Components/Imunify.pm @@ -15,6 +15,7 @@ use cPstrict; use Elevate::Constants (); use Elevate::Fetch (); use Elevate::Notify (); +use Elevate::OS (); use Cwd (); use Log::Log4perl qw(:easy); @@ -33,6 +34,8 @@ use constant IMUNIFY_LICENSE_BACKUP => Elevate::Constants::ELEVATE_BACKUP_DIR . sub pre_leapp ($self) { + return if Elevate::OS::leapp_can_handle_imunify(); + return unless $self->is_installed; $self->run_once("_capture_imunify_features"); @@ -44,6 +47,8 @@ sub pre_leapp ($self) { sub post_leapp ($self) { + return if Elevate::OS::leapp_can_handle_imunify(); + # order matters $self->run_once('_reinstall_imunify_360'); $self->run_once('_restore_imunify_features'); diff --git a/lib/Elevate/Components/KernelCare.pm b/lib/Elevate/Components/KernelCare.pm index 1103ad08..de8af141 100644 --- a/lib/Elevate/Components/KernelCare.pm +++ b/lib/Elevate/Components/KernelCare.pm @@ -13,6 +13,7 @@ Capture and reinstall KernelCare. use cPstrict; use Elevate::Constants (); +use Elevate::OS (); use Cwd (); use File::Copy (); @@ -23,6 +24,8 @@ use parent qw{Elevate::Components::Base}; sub pre_leapp ($self) { + return if Elevate::OS::leapp_can_handle_kernelcare(); + $self->run_once("_remove_kernelcare_if_needed"); return; @@ -30,6 +33,8 @@ sub pre_leapp ($self) { sub post_leapp ($self) { + return if Elevate::OS::leapp_can_handle_kernelcare(); + $self->run_once('_restore_kernelcare'); return; diff --git a/lib/Elevate/Components/Repositories.pm b/lib/Elevate/Components/Repositories.pm index 82bae165..31716954 100644 --- a/lib/Elevate/Components/Repositories.pm +++ b/lib/Elevate/Components/Repositories.pm @@ -12,8 +12,9 @@ Disable some repostiories. use cPstrict; -use Elevate::Constants (); -use Elevate::Blockers::Repositories (); +use Elevate::Constants (); +use Elevate::OS (); +use Elevate::RPM (); use Cpanel::SafeRun::Simple (); use Cwd (); @@ -24,6 +25,8 @@ use parent qw{Elevate::Components::Base}; sub pre_leapp ($self) { + $self->run_once("_disable_epel"); + $self->run_once("_disable_yum_plugin_fastestmirror"); $self->run_once("_disable_known_yum_repositories"); return; @@ -32,8 +35,7 @@ sub pre_leapp ($self) { sub _disable_known_yum_repositories { # remove all MySQL repos - my @repo_files = map { Elevate::Constants::YUM_REPOS_D . '/' . $_ } # - Elevate::Blockers::Repositories::DISABLE_MYSQL_YUM_REPOS; + my @repo_files = map { Elevate::Constants::YUM_REPOS_D . '/' . $_ } Elevate::OS::disable_mysql_yum_repos(); foreach my $f (@repo_files) { next unless -e $f; @@ -50,4 +52,25 @@ sub _disable_known_yum_repositories { return; } +sub _disable_yum_plugin_fastestmirror ($self) { + my $pkg = 'yum-plugin-fastestmirror'; + $self->_erase_package($pkg); + return; +} + +sub _disable_epel ($self) { + + return if Elevate::OS::leapp_can_handle_epel(); + + my $pkg = 'epel-release'; + $self->_erase_package($pkg); + return; +} + +sub _erase_package ( $self, $pkg ) { + return unless Cpanel::Pkgr::is_installed($pkg); + $self->rpm->remove_no_dependencies($pkg); + return; +} + 1; diff --git a/lib/Elevate/Components/RpmDB.pm b/lib/Elevate/Components/RpmDB.pm index 9fc4a7d4..49416aa1 100644 --- a/lib/Elevate/Components/RpmDB.pm +++ b/lib/Elevate/Components/RpmDB.pm @@ -13,12 +13,14 @@ Perform some maintenance on the RPM database. use cPstrict; use Elevate::Constants (); +use Elevate::DNF (); use Cwd (); use File::Copy (); use Log::Log4perl qw(:easy); -use Cpanel::Pkgr (); +use Cpanel::Pkgr (); +use Cpanel::Yum::Vars (); use parent qw{Elevate::Components::Base}; @@ -31,19 +33,42 @@ sub pre_leapp ($self) { sub _cleanup_rpms ($self) { - # remove all arch cpanel packages - # This also potentially removes - $self->ssystem(q{/usr/bin/rpm -e --justdb --nodeps `/usr/bin/rpm -qa | /usr/bin/egrep '^cpanel-.*\.x86_64'`}); - - foreach my $rpm (qw/ yum-plugin-fastestmirror epel-release/) { - next unless Cpanel::Pkgr::is_installed($rpm); - $self->ssystem( '/usr/bin/rpm', '-e', '--nodeps', $rpm ); - } + # potential to remove other things, but the goal here to remove cpanel packages provided by rpm.versions + $self->rpm->remove_cpanel_arch_rpms(); return; } sub post_leapp ($self) { + + $self->run_once("_sysup"); + + return; +} + +sub _sysup ($self) { + Cpanel::Yum::Vars::install(); + $self->dnf->clean_all(); + + my $epel_url = 'https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm'; + + # no failures once already installed: no need to check for the epel-release version + unless ( Cpanel::Pkgr::is_installed('epel-release') ) { + $self->dnf->install_rpm_via_url($epel_url); + } + + $self->dnf->config_manager_enable('powertools'); + $self->dnf->config_manager_enable('epel'); + + # Break cpanel-perl (NOTE: This only works on perl 5.36) + $self->ssystem(qw{/usr/bin/rm -f /usr/local/cpanel/3rdparty/perl/536/cpanel-lib/X/Tiny.pm}); + { + local $ENV{'CPANEL_BASE_INSTALL'} = 1; # Don't fix more than perl itself. + $self->ssystem(qw{/usr/local/cpanel/scripts/fix-cpanel-perl}); + } + $self->dnf->update_allow_erasing( '--disablerepo', 'cpanel-plugins' ); + $self->ssystem_and_die(qw{/usr/local/cpanel/scripts/sysup}); + return; } diff --git a/lib/Elevate/DNF.pm b/lib/Elevate/DNF.pm new file mode 100644 index 00000000..77bce640 --- /dev/null +++ b/lib/Elevate/DNF.pm @@ -0,0 +1,41 @@ +package Elevate::DNF; + +=encoding utf-8 + +=head1 NAME + +Elevate::DNF + +Logic wrapping the 'dnf' system binary + +=cut + +use cPstrict; + +use Log::Log4perl qw(:easy); + +use parent qw{Elevate::YUM}; + +sub _build_pkgmgr { + return '/usr/bin/dnf'; +} + +sub config_manager_enable ( $self, $repo ) { + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem( $pkgmgr, 'config-manager', '--enable', $repo ); + + return; +} + +sub update_allow_erasing ( $self, @args ) { + my $pkgmgr = $self->pkgmgr; + + my @additional_args = scalar @args ? @args : ''; + + $self->cpev->ssystem( $pkgmgr, '-y', '--allowerasing', @additional_args, 'update' ); + + return; +} + +1; diff --git a/lib/Elevate/Leapp.pm b/lib/Elevate/Leapp.pm new file mode 100644 index 00000000..ed3f5fc1 --- /dev/null +++ b/lib/Elevate/Leapp.pm @@ -0,0 +1,174 @@ +package Elevate::Leapp; + +=encoding utf-8 + +=head1 NAME + +Elevate::Leapp + +Object to install and execute the leapp script + +=cut + +use cPstrict; + +use Cpanel::JSON (); +use Cpanel::Pkgr (); + +use Elevate::OS (); +use Elevate::YUM (); + +use Config::Tiny (); + +use Log::Log4perl qw(:easy); + +use constant LEAPP_REPORT_JSON => q[/var/log/leapp/leapp-report.json]; +use constant LEAPP_REPORT_TXT => q[/var/log/leapp/leapp-report.txt]; + +use Simple::Accessor qw{ + cpev + yum +}; + +sub _build_cpev { + die q[Missing cpev]; +} + +sub _build_yum ($self) { + return Elevate::YUM->new( cpev => $self->cpev() ); +} + +sub install ($self) { + + unless ( Cpanel::Pkgr::is_installed('elevate-release') ) { + my $elevate_rpm_url = Elevate::OS::elevate_rpm_url(); + $self->yum->install_rpm_via_url($elevate_rpm_url); + } + + my $leapp_data_pkg = Elevate::OS::leapp_data_pkg(); + + unless ( Cpanel::Pkgr::is_installed('leapp-upgrade') && Cpanel::Pkgr::is_installed($leapp_data_pkg) ) { + $self->yum->install( 'leapp-upgrade', $leapp_data_pkg ); + } + + if ( Cpanel::Pkgr::is_installed('kernel-devel') ) { + $self->yum->remove('kernel-devel'); + } + + return; +} + +sub upgrade ($self) { + + return unless $self->cpev->should_run_leapp(); + + $self->cpev->run_once( + setup_answer_file => sub { + $self->setup_answer_file(); + }, + ); + + my $leapp_flag = Elevate::OS::leapp_flag(); + my $leapp_bin = '/usr/bin/leapp'; + my @leapp_args = ('upgrade'); + push( @leapp_args, $leapp_flag ) if $leapp_flag; + + INFO("Running leapp upgrade"); + + my $ok = eval { + local $ENV{LEAPP_OVL_SIZE} = cpev::read_stage_file('env')->{'LEAPP_OVL_SIZE'} || 3000; + $self->cpev->ssystem_and_die( { keep_env => 1 }, $leapp_bin, @leapp_args ); + 1; + }; + + return 1 if $ok; + + $self->_report_leapp_failure_and_die(); + return; +} + +sub _report_leapp_failure_and_die ($self) { + + my $msg = <<'EOS'; +The 'leapp upgrade' process failed. + +Please investigate, resolve then re-run the following command to continue the update: + + /scripts/elevate-cpanel --continue + +EOS + + my $leapp_json_report = LEAPP_REPORT_JSON; + if ( -e $leapp_json_report ) { + my $report = eval { Cpanel::JSON::LoadFile($leapp_json_report) } // {}; + + my $entries = $report->{entries}; + if ( ref $entries eq 'ARRAY' ) { + foreach my $e (@$entries) { + next unless ref $e && $e->{title} =~ qr{Missing.*answer}i; + + $msg .= $e->{summary} if $e->{summary}; + + if ( ref $e->{detail} ) { + my $d = $e->{detail}; + + if ( ref $d->{remediations} ) { + foreach my $remed ( $d->{remediations}->@* ) { + next unless $remed->{type} && $remed->{type} eq 'command'; + next unless ref $remed->{context}; + my @hint = $remed->{context}->@*; + next unless scalar @hint; + $hint[0] = q[/usr/bin/leapp] if $hint[0] && $hint[0] eq 'leapp'; + my $cmd = join( ' ', @hint ); + + $msg .= "\n\n"; + $msg .= <<"EOS"; +Consider running this command: + + $cmd +EOS + } + } + + } + + } + } + } + + if ( -e LEAPP_REPORT_TXT ) { + $msg .= qq[\nYou can read the full leapp report at: ] . LEAPP_REPORT_TXT; + } + + die qq[$msg\n]; + return; +} + +sub setup_answer_file ($self) { + my $leapp_dir = '/var/log/leapp'; + mkdir $leapp_dir unless -d $leapp_dir; + + my $answerfile_path = $leapp_dir . '/answerfile'; + system touch => $answerfile_path unless -e $answerfile_path; + + my $do_write; # no point in overwriting the file if nothing needs to change + + my $ini_obj = Config::Tiny->read( $answerfile_path, 'utf8' ); + LOGDIE( 'Failed to read leapp answerfile: ' . Config::Tiny->errstr ) unless $ini_obj; + + my $SECTION = 'remove_pam_pkcs11_module_check'; + + if ( not defined $ini_obj->{$SECTION}->{'confirm'} or $ini_obj->{$SECTION}->{'confirm'} ne 'True' ) { + $do_write = 1; + $ini_obj->{$SECTION}->{'confirm'} = 'True'; + } + + if ($do_write) { + $ini_obj->write( $answerfile_path, 'utf8' ) # + or LOGDIE( 'Failed to write leapp answerfile: ' . $ini_obj->errstr ); + } + + return; +} + +1; diff --git a/lib/Elevate/OS.pm b/lib/Elevate/OS.pm new file mode 100644 index 00000000..96a555ac --- /dev/null +++ b/lib/Elevate/OS.pm @@ -0,0 +1,134 @@ +package Elevate::OS; + +=encoding utf-8 + +=head1 NAME + +Elevate::OS + +Abstract interface to the OS to obviate the need for if-this-os-do-this-elsif-elsif-else tech debt + +=cut + +use cPstrict; + +use Carp (); + +use Log::Log4perl qw(:easy); + +use constant SUPPORTED_DISTROS => ( + 'CentOS 7', + 'CloudLinux 7', +); + +our $OS; + +sub factory { + my $distro_with_version = cpev::read_stage_file( 'upgrade_from', '' ); + + my $distro; + my $major; + if ( !$distro_with_version ) { + $distro = Cpanel::OS::distro(); + $distro = 'CentOS' if $distro eq 'centos'; + $distro = 'CloudLinux' if $distro eq 'cloudlinux'; + $major = Cpanel::OS::major(); + $distro_with_version = $distro . $major; + } + + my $class = "Elevate::OS::" . $distro_with_version; + my $class_path = "Elevate/OS/$distro_with_version.pm"; + + # Ok if it dies since instance() should be the only thing calling this + # Since this is a fat packed script, we only want to require the class in tests + require $class_path unless $INC{$class_path}; + + my $self = bless {}, $class; + return $self; +} + +sub instance { + return $OS if $OS; + + $OS = eval { factory(); }; + + if ( !$OS ) { + my $supported_distros = join( "\n", SUPPORTED_DISTROS() ); + die "This script is only designed to upgrade the following OSs:\n\n$supported_distros\n"; + } + + return $OS; +} + +## NOTE: private methods (beginning with _) are NOT allowed in this list! +my %methods; + +BEGIN { + + # The key specifies what the method that all platforms we support. + # The value specifies how many args the method is designed to take. + %methods = map { $_ => 0 } ( + ### General distro specific methods. + 'available_upgrade_paths', # This returns a list of possible upgrade paths for the OS + 'default_upgrade_to', # This is the default OS that the current OS should upgrade to (i.e. CL7->CL8, C7->A8) + 'disable_mysql_yum_repos', # This is a list of mysql repo files to disable + 'ea_alias', # This is the value for the --target-os flag used when backing up an EA4 profile + 'elevate_rpm_url', # This is the URL used to install the leapp RPM/repo + 'is_experimental', # This is used to determine if the OS is experimental or not + 'is_supported', # This is used to determine if the OS is supported or not + 'leapp_can_handle_epel', # This is used to determine if we can skip removing the EPEL repo pre_leapp or not + 'leapp_can_handle_imunify', # This is used to determine if we can skip the Imunify component or not + 'leapp_can_handle_kernelcare', # This is used to determine if we can skip the kernelcare component or not + 'leapp_can_handle_python36', # This is used to determine if we can skip the python36 blocker or not + 'leapp_data_pkg', # This is used to determine which leapp data package to install + 'leapp_flag', # This is used to determine if we need to pass any flags to the leapp script or not + 'name', # This is the name of the OS we are upgrading from (i.e. CentOS7, or CloudLinux7) + 'pretty_name', # This is the pretty name of the OS we are upgrading from (i.e. 'CentOS 7') + 'vetted_mysql_yum_repo_ids', # This is a list of known mysql yum repo ids + 'vetted_yum_repo', # This is a list of known yum repos that we do not block on + ); +} + +sub supported_methods { + return sort keys %methods; +} + +our $AUTOLOAD; + +sub AUTOLOAD { + my $sub = $AUTOLOAD; + $sub =~ s/.*:://; + + exists $methods{$sub} or Carp::Croak("$sub is not a supported data variable for Elevate::OS"); + + my $i = instance(); + my $can = $i->can($sub) or Carp::Croak( ref($i) . " does not implement $sub" ); + return $can->( $i, @_ ); +} + +sub DESTROY { } # This is a must for autoload modules + +=head1 can_upgrade_to + +This returns true or false depending on whether the current OS +is able to upgrade to the requested OS or not. + +=cut + +sub can_upgrade_to ($flavor) { + return grep { $_ eq $flavor } Elevate::OS::available_upgrade_paths(); +} + +=head1 upgrade_to + +This is the name of the OS we are upgrading to. Data is stored +within the stages file. + +=cut + +sub upgrade_to () { + my $default = Elevate::OS::default_upgrade_to(); + return cpev::read_stage_file( 'upgrade_to', $default ); +} + +1; diff --git a/lib/Elevate/OS/CentOS7.pm b/lib/Elevate/OS/CentOS7.pm new file mode 100644 index 00000000..6c4f53eb --- /dev/null +++ b/lib/Elevate/OS/CentOS7.pm @@ -0,0 +1,35 @@ +package Elevate::OS::CentOS7; + +=encoding utf-8 + +=head1 NAME + +Elevate::OS::CentOS7 - CentOS7 custom values + +=cut + +use cPstrict; + +use Log::Log4perl qw(:easy); + +use parent 'Elevate::OS::RHEL'; + +use constant available_upgrade_paths => ( + 'alma', + 'almalinux', + 'rocky', + 'rockylinux', +); + +use constant default_upgrade_to => 'AlmaLinux'; +use constant ea_alias => 'CentOS_8'; +use constant elevate_rpm_url => 'https://repo.almalinux.org/elevate/elevate-release-latest-el7.noarch.rpm'; +use constant name => 'CentOS7'; +use constant pretty_name => 'CentOS 7'; + +sub leapp_data_pkg ($self) { + my $upgrade_to = Elevate::OS::upgrade_to(); + return $upgrade_to =~ m/^rocky/ai ? 'leapp-data-rocky' : 'leapp-data-almalinux'; +} + +1; diff --git a/lib/Elevate/OS/CloudLinux7.pm b/lib/Elevate/OS/CloudLinux7.pm new file mode 100644 index 00000000..19e41ad7 --- /dev/null +++ b/lib/Elevate/OS/CloudLinux7.pm @@ -0,0 +1,50 @@ +package Elevate::OS::CloudLinux7; + +=encoding utf-8 + +=head1 NAME + +Elevate::OS::CloudLinux7 - CloudLinux7 custom values + +=cut + +use cPstrict; + +use Log::Log4perl qw(:easy); + +use parent 'Elevate::OS::RHEL'; + +use constant available_upgrade_paths => ( + 'cloud', + 'cloudlinux', +); + +use constant default_upgrade_to => 'CloudLinux'; +use constant ea_alias => 'CloudLinux_8'; +use constant elevate_rpm_url => 'https://repo.cloudlinux.com/elevate/elevate-release-latest-el7.noarch.rpm'; +use constant is_experimental => 1; +use constant leapp_can_handle_epel => 1; +use constant leapp_can_handle_imunify => 1; +use constant leapp_can_handle_kernelcare => 1; +use constant leapp_can_handle_python36 => 1; +use constant leapp_data_pkg => 'leapp-data-cloudlinux'; +use constant leapp_flag => '--nowarn'; +use constant name => 'CloudLinux7'; +use constant pretty_name => 'CloudLinux 7'; + +sub vetted_yum_repo ($self) { + my @vetted_cloudlinux_yum_repo = ( + qr/^cloudlinux(?:-(?:base|updates|extras|compat|imunify360|elevate))?$/, + qr/^cloudlinux-rollout(?:-[0-9]+)?$/, + qr/^cloudlinux-ea4(?:-[0-9]+)?$/, + qr/^cloudlinux-ea4-rollout(?:-[0-9]+)?$/, + 'cl-ea4', + qr/^cl-mysql(?:-meta)?/, + ); + + my @repos = $self->SUPER::vetted_yum_repo(); + push @repos, @vetted_cloudlinux_yum_repo; + return @repos; +} + +1; diff --git a/lib/Elevate/OS/RHEL.pm b/lib/Elevate/OS/RHEL.pm new file mode 100644 index 00000000..d0d30b80 --- /dev/null +++ b/lib/Elevate/OS/RHEL.pm @@ -0,0 +1,81 @@ +package Elevate::OS::RHEL; + +=encoding utf-8 + +=head1 NAME + +Elevate::OS::RHEL + +Rhel base class + +=cut + +use cPstrict; + +use Log::Log4perl qw(:easy); + +use constant disable_mysql_yum_repos => qw{ + Mysql57.repo + Mysql80.repo + + MariaDB102.repo + MariaDB103.repo + MariaDB105.repo + MariaDB106.repo + + mysql-community.repo +}; + +use constant vetted_mysql_yum_repo_ids => ( + qr/^mysql-cluster-[0-9.]{3}-community(?:-(?:source|debuginfo))?$/, + qr/^mysql-connectors-community(?:-(?:source|debuginfo))?$/, + qr/^mysql-tools-community(?:-(?:source|debuginfo))?$/, + qr/^mysql-tools-preview(?:-source)?$/, + qr/^mysql[0-9]{2}-community(?:-(?:source|debuginfo))?$/, + qr/^MariaDB[0-9]{3}$/, +); + +use constant vetted_yum_repo => ( + 'base', + 'c7-media', + qr/^centos-kernel(?:-experimental)?$/, + 'centosplus', + 'cp-dev-tools', + 'cpanel-addons-production-feed', + 'cpanel-plugins', + 'cr', + 'ct-preset', + 'digitalocean-agent', + 'droplet-agent', + qr/^EA4(?:-c\$releasever)?$/, + qr/^elasticsearch(?:7\.x)?$/, + qr/^elevate(?:-source)?$/, + qr/^epel(?:-testing)?$/, + 'extras', + 'fasttrack', + 'imunify360', + 'imunify360-ea-php-hardened', + qr/^imunify360-rollout-[0-9]+$/, + 'influxdb', + 'kernelcare', + 'updates', + qr/^wp-toolkit-(?:cpanel|thirdparties)$/, + ), + vetted_mysql_yum_repo_ids; + +use constant available_upgrade_paths => undef; +use constant default_upgrade_to => undef; +use constant ea_alias => undef; +use constant elevate_rpm_url => undef; +use constant is_experimental => 0; +use constant is_supported => 1; +use constant leapp_can_handle_epel => 0; +use constant leapp_can_handle_imunify => 0; +use constant leapp_can_handle_kernelcare => 0; +use constant leapp_can_handle_python36 => 0; +use constant leapp_data_package => undef; +use constant leapp_flag => undef; +use constant name => 'RHEL'; +use constant pretty_name => 'RHEL'; + +1; diff --git a/lib/Elevate/RPM.pm b/lib/Elevate/RPM.pm index 2105c1fb..e8eae60a 100644 --- a/lib/Elevate/RPM.pm +++ b/lib/Elevate/RPM.pm @@ -18,6 +18,8 @@ use Simple::Accessor qw{ cpev }; +our $rpm = '/usr/bin/rpm'; + sub _build_cpev { die q[Missing cpev]; } @@ -35,7 +37,7 @@ sub _get_config_files ( $self, $pkgs ) { my %config_files; foreach my $pkg (@$pkgs) { - my $out = $self->cpev->ssystem_capture_output( '/usr/bin/rpm', '-qc', $pkg ) || {}; + my $out = $self->cpev->ssystem_capture_output( $rpm, '-qc', $pkg ) || {}; if ( $out->{status} != 0 ) { @@ -80,4 +82,35 @@ sub restore_config_files ( $self, @files ) { return; } +sub remove_no_dependencies ( $self, $pkg ) { + $self->cpev->ssystem( $rpm, '-e', '--nodeps', $pkg ); + return; +} + +sub remove_no_dependencies_and_justdb ( $self, $pkg ) { + $self->cpev->ssystem( $rpm, '-e', '--nodeps', '--justdb', $pkg ); + return; +} + +sub get_installed_rpms ($self) { + my $out = $self->cpev->ssystem_capture_output( $rpm, '-qa' ); + return @{ $out->{stdout} }; +} + +sub get_cpanel_arch_rpms ($self) { + my @installed_rpms = $self->get_installed_rpms(); + my @cpanel_arch_rpms = grep { $_ =~ m/^cpanel-.*\.x86_64$/ } @installed_rpms; + return @cpanel_arch_rpms; +} + +sub remove_cpanel_arch_rpms ($self) { + my @rpms_to_remove = $self->get_cpanel_arch_rpms(); + + foreach my $rpm (@rpms_to_remove) { + $self->remove_no_dependencies_and_justdb($rpm); + } + + return; +} + 1; diff --git a/lib/Elevate/YUM.pm b/lib/Elevate/YUM.pm new file mode 100644 index 00000000..ddd5b26c --- /dev/null +++ b/lib/Elevate/YUM.pm @@ -0,0 +1,66 @@ +package Elevate::YUM; + +=encoding utf-8 + +=head1 NAME + +Elevate::YUM + +Logic wrapping the 'yum' system binary + +=cut + +use cPstrict; + +use Log::Log4perl qw(:easy); + +use Simple::Accessor qw{ + cpev + pkgmgr +}; + +sub _build_cpev { + die q[Missing cpev]; +} + +sub _build_pkgmgr { + return '/usr/bin/yum'; +} + +sub remove ( $self, @pkgs ) { + return unless scalar @pkgs; + + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem_and_die( $pkgmgr, '-y', 'remove', @pkgs ); + + return; +} + +sub clean_all ($self) { + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem( $pkgmgr, 'clean', 'all' ); + + return; +} + +sub install_rpm_via_url ( $self, $rpm_url ) { + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem_and_die( $pkgmgr, '-y', 'install', $rpm_url ); + + return; +} + +sub install ( $self, @pkgs ) { + return unless scalar @pkgs; + + my $pkgmgr = $self->pkgmgr; + + $self->cpev->ssystem_and_die( $pkgmgr, '-y', 'install', @pkgs ); + + return; +} + +1; diff --git a/script/elevate-cpanel.PL b/script/elevate-cpanel.PL index f9ed2f0a..adadb1ad 100755 --- a/script/elevate-cpanel.PL +++ b/script/elevate-cpanel.PL @@ -35,38 +35,47 @@ package cpev; =head1 DESCRIPTION -Helps to upgrade CentOS 7 cPanel servers to AlmaLinux 8 or Rocky Linux 8. +Wrapper around the ELevate project which is designed to enable migrations +between major version of RHEL® derivatives. + +Currently supported upgrade paths: + +CentOS 7 => AlmaLinux 8 +CentOS 7 => Rocky Linux 8 +CloudLinux 7 => CloudLinux 8 =head1 SYNOPSIS /scripts/elevate-cpanel [OPTIONS] Optional: - --start Start the conversion process - --continue Continue the conversion: retry the last step - --check[=BLOCKER_FILE] Check if your system has any known blockers to upgrade. - --log Show the current elevation log - --status Check the current elevation status - --clean Cleanup scripts and files created by elevate-cpanel - --upgrade-to=[rocky|almalinux] Update to AlmaLinux 8 or Rocky Linux 8 - [default to 'almalinux'] - --non-interactive Skip the yes/no prompt before proceeding with the upgrade. + --start Start the conversion process + --continue Continue the conversion: retry the last step + --check[=BLOCKER_FILE] Check if your system has any known blockers to upgrade. + --log Show the current elevation log + --status Check the current elevation status + --clean Cleanup scripts and files created by elevate-cpanel + --upgrade-to=[rocky|almalinux|cloudlinux] Update to AlmaLinux 8 or Rocky Linux 8 + [Servers running CentOS 7 will default to 'almalinux'] + [Servers running CloudLinux 7 will default to 'cloudlinux'] - --update Instruct the script to replace itself on disk with a downloaded copy of the latest version. - --version Print the version number and exit. + NOTE: servers running CentOS 7 can only upgrade to almalinux or rocky + servers running CloudLinux 7 can only upgrade to cloudlinux - --skip-elevate-version-check Skip the check for whether this script is up to date. - --skip-cpanel-version-check Skip the check for whether cPanel is up to date. - This option is intended only for testing! + --non-interactive Skip the yes/no prompt before proceeding with the upgrade. - --no-leapp Do not try to run leapp, and pause instead. - Once leapp has been run you should remove the file - /waiting_for_distro_upgrade + --update Instruct the script to replace itself on disk with a downloaded copy of the latest version. + --version Print the version number and exit. - --manual-reboots The script will stop and require the user to reboot manually rather than - automatically rebooting when reboots are needed + --skip-elevate-version-check Skip the check for whether this script is up to date. + --skip-cpanel-version-check Skip the check for whether cPanel is up to date. + This option is intended only for testing! - --help Display this documentation. + --no-leapp Do not try to run leapp, and pause instead. + Once leapp has been run you should remove the file + /waiting_for_distro_upgrade + + --help Display this documentation. =head1 COMMON USAGE @@ -76,13 +85,17 @@ Helps to upgrade CentOS 7 cPanel servers to AlmaLinux 8 or Rocky Linux 8. You can start an elevation update by running: - # AlmaLinux 8 update + # AlmaLinux 8 update from CentOS 7 /scripts/elevate-cpanel --start /scripts/elevate-cpanel --start --upgrade-to=almalinux - # Rocky Linux 8 update + # Rocky Linux 8 update from CentOS 7 /scripts/elevate-cpanel --start --upgrade-to=rocky + # CloudLinux 8 update from CloudLinux 7 + /scripts/elevate-cpanel --start + /scripts/elevate-cpanel --start --upgrade-to=cloudlinux + =item Start an installation and skip the confirmation prompt By default, you will be asked if you wish to proceed with the upgrade. @@ -96,13 +109,17 @@ If you wish to skip this prompt (by assuming yes): You can check if your server is compatible with the upgrade process without starting an upgrade process. - # Check AlmaLinux 8 update + # Check AlmaLinux 8 update from CentOS 7 /scripts/elevate-cpanel --check /scripts/elevate-cpanel --check --upgrade-to=almalinux - # Check Rocky Linux 8 update + # Check Rocky Linux 8 update from CentOS 7 /scripts/elevate-cpanel --check --upgrade-to=rocky + # Check CloudLinux 8 update from CloudLinux 7 + /scripts/elevate-cpanel --check + /scripts/elevate-cpanel --check --upgrade-to=cloudlinux + This will also save a JSON representation of the blockers to a file, C by default, but passing an optional argument will use the file given instead: @@ -142,7 +159,7 @@ the update process by running: =head3 Using an alternative tool to upgrade your distro -By default, the elevate script runs the [leapp process](https://almalinux.org/elevate/) +By default, the elevate script runs the L to upgrade you from 7 to 8. `Leapp` may not be compatible with your system. Using the `--no-leapp` option gives you a way to do the actual distro upgrade in your own way. @@ -174,8 +191,7 @@ NOTE: `--no-leapp` is not required for helper commands like `--continue` or `--s =head1 WARNINGS -The elevation process from CentOS 7 to AlmaLinux 8 or Rocky Linux 8 distribution -is not a risk free update. +The elevation process to perform a major OS upgrade is not a risk free update. Depending on the state of your current distribution multiple errors can occur. We recommend updating your system to the last upstream state before starting. @@ -208,7 +224,6 @@ BEGIN { use Log::Log4perl qw(:easy); use Config; -use Config::Tiny (); use Carp (); use Errno (); use File::Basename (); @@ -284,7 +299,14 @@ use Elevate::Components::RmMod (); use Elevate::Components::WPToolkit (); use Elevate::Components::SSH (); +# - fatpack OS +use Elevate::OS (); +use Elevate::OS::CentOS7 (); +use Elevate::OS::CloudLinux7 (); +use Elevate::OS::RHEL (); + use Elevate::Fetch (); +use Elevate::Leapp (); use Elevate::Logger (); use Elevate::Motd (); use Elevate::Notify (); @@ -294,6 +316,8 @@ use Elevate::Script (); use Elevate::Service (); use Elevate::SystemctlService (); use Elevate::Usage (); +use Elevate::DNF (); +use Elevate::YUM (); #< 1; @@ -307,9 +331,6 @@ use constant VALID_STAGES => 5; use constant IGNORE_OUTDATED_SERVICES_FILE => q[/etc/cpanel/local/ignore_outdated_services]; -use constant LEAPP_REPORT_JSON => q[/var/log/leapp/leapp-report.json]; -use constant LEAPP_REPORT_TXT => q[/var/log/leapp/leapp-report.txt]; - use constant NOC_RECOMMENDATIONS_TOUCH_FILE => q[/var/cpanel/elevate-noc-recommendations]; # XXX TODO verify that imunify reponames are in fact correct. @@ -319,8 +340,9 @@ use constant ACTION_REBOOT_NEEDED => 4242; # just one unique id use constant ACTION_PAUSE_REQUESTED => 4243; # prefer string over integers so we can read the configuration file (no need for bitmask) -use constant UPGRADE_TO_ALMALINUX => q[AlmaLinux]; -use constant UPGRADE_TO_ROCKY => q[Rocky]; +use constant UPGRADE_TO_ALMALINUX => q[AlmaLinux]; +use constant UPGRADE_TO_ROCKY => q[Rocky]; +use constant UPGRADE_TO_CLOUDLINUX => q[CloudLinux]; use constant PAUSE_ELEVATE_TOUCHFILE => q[/waiting_for_distro_upgrade]; @@ -328,6 +350,7 @@ use Simple::Accessor qw{ service script blockers + leapp }; # after Simple::Accessor @@ -350,6 +373,12 @@ sub _build_blockers ($self) { return Elevate::Blockers->new( cpev => $self ); } +sub _build_leapp ($self) { + + # FIXME weaken + return Elevate::Leapp->new( cpev => $self ); +} + sub _build_script ($self) { return Elevate::Script->new; } @@ -379,8 +408,6 @@ sub run ( $pkg, @args ) { return $self->do_update(); } - $self->_parse_opt_upgrade_to(); - if ( $self->getopt('start') ) { die qq[Unsupported option with --start\n] if $self->getopt('continue') || $self->getopt('service'); return 1 if $self->start(); @@ -440,17 +467,37 @@ sub get_blocker ( $self, $name ) { # helper for tests sub _parse_opt_upgrade_to ($self) { - return unless my $flavor = $self->getopt('upgrade-to'); + my $flavor = $self->getopt('upgrade-to'); if ( !defined $self->getopt('start') && !defined $self->getopt('check') ) { die qq[--upgrade-to option is only supported with --start or --check\n]; } + + Elevate::Blockers::Distros::bail_out_on_inappropriate_distro(); + + $flavor ||= Elevate::OS::default_upgrade_to(); + $flavor = lc $flavor; - if ( $flavor !~ m{^(?:almalinux|alma|rocky)$} ) { - die qq[Invalid --upgrade_to value '$flavor'. Only 'almalinux' or 'rocky' are supported.\n]; + if ( !Elevate::OS::can_upgrade_to($flavor) ) { + my @available_flavors = Elevate::OS::available_upgrade_paths(); + my $af = join( "\n", @available_flavors ); + die qq[The current OS can only upgrade to the following flavors:\n\n$af\n]; } - $self->{upgrade_to} = $flavor eq 'rocky' ? UPGRADE_TO_ROCKY : UPGRADE_TO_ALMALINUX; + $self->_set_upgrade_to($flavor); + + return; +} + +sub _set_upgrade_to ( $self, $flavor ) { + my $upgrade_to = UPGRADE_TO_ROCKY if $flavor =~ m/^rocky/a; + + $upgrade_to = UPGRADE_TO_ALMALINUX if $flavor =~ m/^alma/a; + $upgrade_to = UPGRADE_TO_CLOUDLINUX if $flavor =~ m/^cloud/a; + + die qq['$flavor' is not a valid path to upgrade to\n] unless $upgrade_to; + + $self->{upgrade_to} = $upgrade_to; return; } @@ -486,16 +533,20 @@ sub do_update ($self) { } sub upgrade_to ($self) { # main helper to know the upgrade_to distro - return $self->{upgrade_to} if $self->{upgrade_to}; - return read_stage_file( 'upgrade_to', UPGRADE_TO_ALMALINUX ); # default to AlmaLinux + return $self->{upgrade_to} ? $self->{upgrade_to} : Elevate::OS::upgrade_to(); } sub upgrade_to_rocky ($self) { return $self->upgrade_to() eq UPGRADE_TO_ROCKY; } +sub upgrade_to_cloudlinux ($self) { + return $self->upgrade_to() eq UPGRADE_TO_CLOUDLINUX; +} + sub upgrade_to_pretty_name ($self) { # used by output messages return q[Rocky Linux 8] if $self->upgrade_to_rocky; + return q[CloudLinux 8] if $self->upgrade_to_cloudlinux; return q[AlmaLinux 8]; } @@ -523,7 +574,7 @@ sub monitor_upgrade ($self) { } sub start ($self) { - Elevate::Blockers::Distros::bail_out_on_inappropriate_distro(); + $self->_parse_opt_upgrade_to(); my $stage = get_stage(); if ( $stage != 0 ) { my $header; @@ -620,6 +671,13 @@ It is *highly* recommended that you have a full backup or snapshot of your server before proceeding. EOS + if ( Elevate::OS::is_experimental() ) { + say <getopt('non-interactive') ) { if ( !IO::Prompt::prompt( @@ -790,10 +848,11 @@ sub check_status ($self) { sub _notify_success ($self) { + my $upgrade_from_name = Elevate::OS::pretty_name(); my $pretty_distro_name = $self->upgrade_to_pretty_name(); my $msg = <<"EOS"; -The cPanel & WHM server has completed the elevation process from CentOS 7 to $pretty_distro_name. +The cPanel & WHM server has completed the elevation process from $upgrade_from_name to $pretty_distro_name. EOS if ( my $warnings = read_stage_file( 'final_notifications', [] ) ) { @@ -954,6 +1013,11 @@ sub run_stage_1 ($self) { # store 'upgrade_to' early so later stages can access it update_stage_file( { upgrade_to => $self->upgrade_to } ); + # store 'upgrade_from' early so that Elevate::OS can access it + # Elevate::OS is based on the OS we are upgrading from since the information + # that it provides is mostly based on what leapp can do when upgrading from an OS + update_stage_file( { upgrade_from => Elevate::OS::name() } ); + return $self->service->install(); } @@ -992,15 +1056,16 @@ sub run_stage_2 ($self) { =head2 run_stage_3 -Setup the AlmaLinux elevate-release-latest-el7 repo and install leapp packages. -Prepare the cPanel packages for the update. +Setup the elevate-release-latest-el7 repo from the appropriate fork and install +leapp Packages. Prepare the cPnael packages for the update. Remove some known conflicting packages. (Reinstall later). Provide answers to a few leapp questions. Attempt to perform the leapp upgrade itself. -In case of failure you probably want to reply to a few extra questions or remove some conflicting packages. +In case of failure you probably want to reply to a few extra questions or remove +some conflicting packages. =cut @@ -1012,8 +1077,13 @@ sub run_stage_3 ($self) { return $self->_request_to_upgrade_distro_manually(); } - $self->run_once('_install_leapp'); - $self->_do_leapp_upgrade(); + $self->run_once( + install_leapp => sub { + $self->leapp->install(); + }, + ); + + $self->leapp->upgrade(); WARN(<<~'EOS'); Rebooting for distro upgrade. This will take over 10 minutes to run. @@ -1025,27 +1095,6 @@ sub run_stage_3 ($self) { # This takes a while because on reboot it's installing 800 packages } -sub _install_leapp ($self) { - - unless ( Cpanel::Pkgr::is_installed('elevate-release') ) { - - # provides leapp-data-almalinux & leapp-data-rocky - $self->ssystem_and_die(qw{/usr/bin/yum install -y http://repo.almalinux.org/elevate/elevate-release-latest-el7.noarch.rpm}); - } - - my $leap_data_pkg = $self->upgrade_to_rocky() ? 'leapp-data-rocky' : 'leapp-data-almalinux'; - - unless ( Cpanel::Pkgr::is_installed('leapp-upgrade') && Cpanel::Pkgr::is_installed($leap_data_pkg) ) { - $self->ssystem_and_die( qw{/usr/bin/yum install -y leapp-upgrade }, $leap_data_pkg ); - } - - if ( Cpanel::Pkgr::is_installed('kernel-devel') ) { - $self->ssystem_and_die(qw{/usr/bin/yum -y remove kernel-devel}); - } - - return; -} - sub _request_to_upgrade_distro_manually ($self) { my $pause_file = PAUSE_ELEVATE_TOUCHFILE; @@ -1067,7 +1116,7 @@ EOS =head2 run_stage_4 -At this stage we should now run Alamalinux 8. +At this stage, we should now be run the upgraded OS (i.e. AlmaLinux 8). Update cPanel product for the new distro. Restore removed packages during the previous stage. @@ -1106,29 +1155,7 @@ sub run_stage_4 ($self) { $stash->{stage4} //= {}; # run once each blocks - $self->run_once( - sysup => sub { - Cpanel::Yum::Vars::install(); - $self->ssystem_and_die(qw{/usr/bin/dnf clean all}); - - # no failures once already installed: no need to check for the epel-release version - $self->ssystem_and_die(qw{/usr/bin/dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm}); - - $self->ssystem(qw{/usr/bin/dnf config-manager --enable powertools}); - $self->ssystem(qw{/usr/bin/dnf config-manager --enable epel}); - - # Break cpanel-perl (NOTE: This only works on perl 5.36) - $self->ssystem(qw{/usr/bin/rm -f /usr/local/cpanel/3rdparty/perl/536/cpanel-lib/X/Tiny.pm}); - { - local $ENV{'CPANEL_BASE_INSTALL'} = 1; # Don't fix more than perl itself. - $self->ssystem(qw{/usr/local/cpanel/scripts/fix-cpanel-perl}); - } - $self->ssystem(qw{/usr/bin/dnf -y --allowerasing --disablerepo cpanel-plugins update}); - $self->ssystem_and_die(qw{/usr/local/cpanel/scripts/sysup}); - - return; - } - ); + $self->run_component_once( 'RpmDB', => 'post_leapp' ); $self->run_once( restore_cpanel_services => sub { @@ -1162,7 +1189,7 @@ sub run_stage_4 ($self) { $self->post_leapp_update_restore(); my @known_modules_that_dont_convert = qw{libtermkey msgpack btrfs-progs elevate-release - leapp leapp-data-almalinux leapp-data-rocky leapp-upgrade-el7toel8 + leapp leapp-data-almalinux leapp-data-rocky leapp-data-cloudlinux leapp-upgrade-el7toel8 python2-leapp alt-pcre802 alt-pcre802-devel}; my @to_remove = grep { Cpanel::Pkgr::is_installed($_) } @known_modules_that_dont_convert; @@ -1187,7 +1214,7 @@ The elevate-cpanel service is now removed. =cut sub run_stage_5 ($self) { - return 1 if $self->post_upgrade_check(); + return 0 if $self->post_upgrade_check(); # we cannot stop the service ( ourself ) $self->service->disable; @@ -1251,14 +1278,6 @@ sub elevation_success_marker ($self) { return; } -=pod - -Example: - - $self->run_component_once( Imunify => 'backup' ); - -=cut - sub run_component_once ( $self, $name, $function ) { my $component = $self->component($name); @@ -1440,76 +1459,6 @@ sub disable_all_cpanel_services ($self) { return; } -sub _do_leapp_upgrade ($self) { - - return unless $self->should_run_leapp(); - - $self->run_once('setup_answer_file'); - - INFO("Running leapp upgrade"); - - my $ok = eval { - local $ENV{LEAPP_OVL_SIZE} = read_stage_file('env')->{'LEAPP_OVL_SIZE'} || 3000; - $self->ssystem_and_die( { keep_env => 1 }, qw{/usr/bin/leapp upgrade} ); - 1; - }; - - return 1 if $ok; - - my $msg = <<'EOS'; -The 'leapp upgrade' process failed. - -Please investigate, resolve then re-run the following command to continue the update: - - /scripts/elevate-cpanel --continue - -EOS - - my $leapp_json_report = LEAPP_REPORT_JSON; - if ( -e $leapp_json_report ) { - my $report = eval { Cpanel::JSON::LoadFile($leapp_json_report) } // {}; - - my $entries = $report->{entries}; - if ( ref $entries eq 'ARRAY' ) { - foreach my $e (@$entries) { - next unless ref $e && $e->{title} =~ qr{Missing.*answer}i; - - $msg .= $e->{summary} if $e->{summary}; - - if ( ref $e->{detail} ) { - my $d = $e->{detail}; - - if ( ref $d->{remediations} ) { - foreach my $remed ( $d->{remediations}->@* ) { - next unless $remed->{type} && $remed->{type} eq 'command'; - next unless ref $remed->{context}; - my @hint = $remed->{context}->@*; - next unless scalar @hint; - $hint[0] = q[/usr/bin/leapp] if $hint[0] && $hint[0] eq 'leapp'; - my $cmd = join( ' ', @hint ); - - $msg .= "\n\n"; - $msg .= <<"EOS"; -Consider running this command: - - $cmd -EOS - } - } - - } - - } - } - } - - if ( -e LEAPP_REPORT_TXT ) { - $msg .= qq[\nYou can read the full leapp report at: ] . LEAPP_REPORT_TXT; - } - - die qq[$msg\n]; -} - # remove and store sub run_final_components_pre_leapp ($self) { @@ -1664,12 +1613,12 @@ sub remove_rpms_from_repos ( $self, @repo_list ) { sub post_upgrade_check ($self) { - my $expect_distro = $self->upgrade_to_rocky() ? 'rocky' : 'almalinux'; + my $expect_distro = Elevate::OS::upgrade_to(); + $expect_distro = lc $expect_distro; unless ( Cpanel::OS::major() == 8 && Cpanel::OS::distro() eq $expect_distro ) { my $pretty_distro_name = $self->upgrade_to_pretty_name(); - FATAL("Your distro does not looks like $pretty_distro_name."); - return 1; + die "Your distro does not look like $pretty_distro_name.\n"; } # call a cpanel binary @@ -1678,40 +1627,6 @@ sub post_upgrade_check ($self) { return 0; } -=pod - - > cat /var/log/leapp/answerfile - [remove_pam_pkcs11_module_check] - confirm=True -=cut - -sub setup_answer_file { - my $leapp_dir = '/var/log/leapp'; - mkdir $leapp_dir unless -d $leapp_dir; - - my $answerfile_path = $leapp_dir . '/answerfile'; - system touch => $answerfile_path unless -e $answerfile_path; - - my $do_write; # no point in overwriting the file if nothing needs to change - - my $ini_obj = Config::Tiny->read( $answerfile_path, 'utf8' ); - LOGDIE( 'Failed to read leapp answerfile: ' . Config::Tiny->errstr ) unless $ini_obj; - - my $SECTION = 'remove_pam_pkcs11_module_check'; - - if ( not defined $ini_obj->{$SECTION}->{'confirm'} or $ini_obj->{$SECTION}->{'confirm'} ne 'True' ) { - $do_write = 1; - $ini_obj->{$SECTION}->{'confirm'} = 'True'; - } - - if ($do_write) { - $ini_obj->write( $answerfile_path, 'utf8' ) # - or LOGDIE( 'Failed to write leapp answerfile: ' . $ini_obj->errstr ); - } - - return; -} - # TODO: We're going to need to store reboot time so we know if the last reboot has happened when we re-run the script. sub should_run_leapp ($self) { diff --git a/t/blocker-Databases.t b/t/blocker-Databases.t index 07fcf36b..34f27bec 100644 --- a/t/blocker-Databases.t +++ b/t/blocker-Databases.t @@ -42,76 +42,82 @@ my $mock_elevate = Test::MockFile->file('/var/cpanel/elevate'); } { - is( - $db->_blocker_old_mysql('5.7'), - { - id => q[Elevate::Blockers::Databases::_blocker_old_mysql], - msg => <<~'EOS', + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + + my $expected_target_os = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; + + is( + $db->_blocker_old_mysql('5.7'), + { + id => q[Elevate::Blockers::Databases::_blocker_old_mysql], + msg => <<~"EOS", You are using MySQL 5.7 server. - This version is not available for AlmaLinux 8. + 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: /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=8.0 - Once the MySQL upgrade is finished, you can then retry to elevate to AlmaLinux 8. + Once the MySQL upgrade is finished, you can then retry to elevate to $expected_target_os. EOS - }, - 'MySQL 5.7 is a blocker.' - ); - - local $Cpanel::Version::Tiny::major_version = 108; - is( - $db->_blocker_old_mysql('10.1'), - { - id => q[Elevate::Blockers::Databases::_blocker_old_mysql], - msg => <<~'EOS', - You are using MariaDB server 10.1, this version is not available for AlmaLinux 8. + }, + 'MySQL 5.7 is a blocker.' + ); + + local $Cpanel::Version::Tiny::major_version = 108; + is( + $db->_blocker_old_mysql('10.1'), + { + id => q[Elevate::Blockers::Databases::_blocker_old_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.3 or later. You can update to version 10.3 using the following command: /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=10.3 - Once the MariaDB upgrade is finished, you can then retry to elevate to AlmaLinux 8. + Once the MariaDB upgrade is finished, you can then retry to elevate to $expected_target_os. EOS - }, - 'Maria 10.1 on 108 is a blocker.' - ); - - $Cpanel::Version::Tiny::major_version = 110; - is( - $db->_blocker_old_mysql('10.2'), - { - id => q[Elevate::Blockers::Databases::_blocker_old_mysql], - msg => <<~'EOS', - You are using MariaDB server 10.2, this version is not available for AlmaLinux 8. + }, + 'Maria 10.1 on 108 is a blocker.' + ); + + $Cpanel::Version::Tiny::major_version = 110; + is( + $db->_blocker_old_mysql('10.2'), + { + id => q[Elevate::Blockers::Databases::_blocker_old_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. You can update to version 10.5 using the following command: /usr/local/cpanel/bin/whmapi1 start_background_mysql_upgrade version=10.5 - Once the MariaDB upgrade is finished, you can then retry to elevate to AlmaLinux 8. + Once the MariaDB upgrade is finished, you can then retry to elevate to $expected_target_os. EOS - }, - 'Maria 10.2 on 110 is a blocker.' - ); - - is( - $db->_blocker_old_mysql('4.2'), - { - id => q[Elevate::Blockers::Databases::_blocker_old_mysql], - msg => <<~'EOS', - We do not know how to upgrade to AlmaLinux 8 with MySQL version 4.2. + }, + 'Maria 10.2 on 110 is a blocker.' + ); + + is( + $db->_blocker_old_mysql('4.2'), + { + id => q[Elevate::Blockers::Databases::_blocker_old_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 - }, - 'Maria 10.2 on 110 is a blocker.' - ); + }, + 'Maria 10.2 on 110 is a blocker.' + ); + } my $stash = undef; $cpev_mock->redefine( @@ -135,25 +141,31 @@ my $mock_elevate = Test::MockFile->file('/var/cpanel/elevate'); clear_messages_seen(); - my $pkgr_mock = Test::MockModule->new('Cpanel::Pkgr'); - my %installed = ( 'cpanel-ccs-calendarserver' => 9.2, 'postgresql-server' => 9.2 ); - $pkgr_mock->redefine( 'is_installed' => sub ($rpm) { return defined $installed{$rpm} ? 1 : 0 } ); - $pkgr_mock->redefine( 'get_package_version' => sub ($rpm) { return $installed{$rpm} } ); + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + + my $expected_target_os = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; + + my $pkgr_mock = Test::MockModule->new('Cpanel::Pkgr'); + my %installed = ( 'cpanel-ccs-calendarserver' => 9.2, 'postgresql-server' => 9.2 ); + $pkgr_mock->redefine( 'is_installed' => sub ($rpm) { return defined $installed{$rpm} ? 1 : 0 } ); + $pkgr_mock->redefine( 'get_package_version' => sub ($rpm) { return $installed{$rpm} } ); - is( $db->_warning_if_postgresql_installed, 2, "pg 9 is installed" ); - message_seen( 'WARN', "You have postgresql-server version 9.2 installed. This will be upgraded irreversibly to version 10.0 when you switch to AlmaLinux 8" ); + is( $db->_warning_if_postgresql_installed, 2, "pg 9 is installed" ); + message_seen( 'WARN', "You have postgresql-server version 9.2 installed. This will be upgraded irreversibly to version 10.0 when you switch to $expected_target_os" ); - $installed{'postgresql-server'} = '10.2'; - is( $db->_warning_if_postgresql_installed, 1, "pg 10 is installed so no warning" ); - no_messages_seen(); + $installed{'postgresql-server'} = '10.2'; + is( $db->_warning_if_postgresql_installed, 1, "pg 10 is installed so no warning" ); + no_messages_seen(); - $installed{'postgresql-server'} = 'an_unexpected_version'; - is( $db->_warning_if_postgresql_installed, 1, "unknown pg version is installed so no warning" ); - no_messages_seen(); + $installed{'postgresql-server'} = 'an_unexpected_version'; + is( $db->_warning_if_postgresql_installed, 1, "unknown pg version is installed so no warning" ); + no_messages_seen(); - delete $installed{'postgresql-server'}; - is( $db->_warning_if_postgresql_installed, 0, "pg is not installed so no warning" ); - no_messages_seen(); + delete $installed{'postgresql-server'}; + is( $db->_warning_if_postgresql_installed, 0, "pg is not installed so no warning" ); + no_messages_seen(); + } } diff --git a/t/blocker-DiskSpace.t b/t/blocker-DiskSpace.t index 34f38c51..16c07ac1 100644 --- a/t/blocker-DiskSpace.t +++ b/t/blocker-DiskSpace.t @@ -11,10 +11,9 @@ use Test2::Tools::Exception; use Test::MockModule qw/strict/; -BEGIN { - use FindBin; - require $FindBin::Bin . '/../elevate-cpanel'; -} +use FindBin; +use lib $FindBin::Bin . "/lib"; +use Test::Elevate; use Elevate::Blockers::DiskSpace (); diff --git a/t/blocker-Distros.t b/t/blocker-Distros.t index 6def708a..89dd6c87 100644 --- a/t/blocker-Distros.t +++ b/t/blocker-Distros.t @@ -26,47 +26,38 @@ my $distros = $cpev->get_blocker('Distros'); { note "Distro supported checks."; Cpanel::OS::clear_cache_after_cloudlinux_update(); + unmock_os(); my $f = Test::MockFile->symlink( 'linux|centos|6|9|2009', '/var/cpanel/caches/Cpanel-OS' ); my $osr = Test::MockFile->file( '/etc/os-release', '', { mtime => time - 100000 } ); my $rhr = Test::MockFile->file( '/etc/redhat-release', '', { mtime => time - 100000 } ); my $m_custom = Test::MockFile->file(q[/var/cpanel/caches/Cpanel-OS.custom]); - is( - $distros->check(), - { - id => q[Elevate::Blockers::Distros::_blocker_is_non_centos7], - msg => 'This script is only designed to upgrade CentOS 7 to AlmaLinux 8.', - }, + like( + dies { $distros->check() }, + qr/This script is only designed to upgrade the following OSs/, 'C6 is not supported.' ); undef $f; Cpanel::OS::clear_cache_after_cloudlinux_update(); + unmock_os(); $f = Test::MockFile->symlink( 'linux|centos|8|9|2009', '/var/cpanel/caches/Cpanel-OS' ); - is( - $distros->check(), - { - id => q[Elevate::Blockers::Distros::_blocker_is_non_centos7], - msg => 'This script is only designed to upgrade CentOS 7 to AlmaLinux 8.', - }, + like( + dies { $distros->check() }, + qr/This script is only designed to upgrade the following OSs/, 'C8 is not supported.' ); undef $f; Cpanel::OS::clear_cache_after_cloudlinux_update(); + unmock_os(); $f = Test::MockFile->symlink( 'linux|cloudlinux|7|9|2009', '/var/cpanel/caches/Cpanel-OS' ); - is( - $distros->check(), - { - id => q[Elevate::Blockers::Distros::_blocker_is_non_centos7], - msg => 'This script is only designed to upgrade CentOS 7 to AlmaLinux 8.', - }, - 'CL7 is not supported.' - ); + is( $distros->check(), 0, 'CL7 is supported.' ); undef $f; Cpanel::OS::clear_cache_after_cloudlinux_update(); + unmock_os(); $f = Test::MockFile->symlink( 'linux|centos|7|4|2009', '/var/cpanel/caches/Cpanel-OS' ); like( $distros->check(), @@ -79,6 +70,7 @@ my $distros = $cpev->get_blocker('Distros'); undef $f; Cpanel::OS::clear_cache_after_cloudlinux_update(); + unmock_os(); $f = Test::MockFile->symlink( 'linux|centos|7|9|2009', '/var/cpanel/caches/Cpanel-OS' ); $m_custom->contents(''); is( @@ -90,9 +82,9 @@ my $distros = $cpev->get_blocker('Distros'); 'Custom OS is not supported.' ); $m_custom->unlink; - is( $distros->_blocker_is_experimental_os(), 0, "if not experimental, we're ok" ); - is( $distros->_blocker_is_non_centos7(), 0, "now on a valid C7" ); - is( $distros->_blocker_is_old_centos7(), 0, "now on a up to date C7" ); + is( $distros->_blocker_is_experimental_os(), 0, "if not experimental, we're ok" ); + is( $distros->_blocker_os_is_not_supported(), 0, "now on a valid C7" ); + is( $distros->_blocker_is_old_centos7(), 0, "now on a up to date C7" ); #no_messages_seen(); } diff --git a/t/blocker-JetBackup.t b/t/blocker-JetBackup.t index a155ea2a..5e4a26f5 100644 --- a/t/blocker-JetBackup.t +++ b/t/blocker-JetBackup.t @@ -53,18 +53,24 @@ my $jb = $cpev->get_blocker('JetBackup'); my $jb_mock = Test::MockModule->new('Elevate::Blockers::JetBackup'); - $jb_mock->redefine( '_use_jetbackup4_or_earlier' => 1 ); - is( - $jb->_blocker_old_jetbackup(), - { - id => q[Elevate::Blockers::JetBackup::_blocker_old_jetbackup], - msg => "AlmaLinux 8 does not support JetBackup prior to version 5.\nPlease upgrade JetBackup before elevate.\n", - }, - q{Block if jetbackup 4 is installed.} - ); - - $jb_mock->redefine( '_use_jetbackup4_or_earlier' => 0 ); - is( $jb->_blocker_old_jetbackup(), 0, 'ok when jetbackup 4 or earlier is not installed.' ); + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + + my $expected_target_os = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; + + $jb_mock->redefine( '_use_jetbackup4_or_earlier' => 1 ); + is( + $jb->_blocker_old_jetbackup(), + { + id => q[Elevate::Blockers::JetBackup::_blocker_old_jetbackup], + msg => "$expected_target_os does not support JetBackup prior to version 5.\nPlease upgrade JetBackup before elevate.\n", + }, + q{Block if jetbackup 4 is installed.} + ); + + $jb_mock->redefine( '_use_jetbackup4_or_earlier' => 0 ); + is( $jb->_blocker_old_jetbackup(), 0, 'ok when jetbackup 4 or earlier is not installed.' ); + } } diff --git a/t/blocker-Python.t b/t/blocker-Python.t index f85c184e..867af173 100644 --- a/t/blocker-Python.t +++ b/t/blocker-Python.t @@ -12,6 +12,7 @@ use Test2::Plugin::NoWarnings; use Test::MockModule qw{strict}; use Elevate::Blockers::Python (); +use Elevate::OS (); my %mocks = map { $_ => Test::MockModule->new($_); } qw{ Cpanel::Pkgr @@ -40,3 +41,11 @@ my $expected = { is( $obj->check, $expected, "Got expected blocker returned when found" ); done_testing(); + +# ------------------------------ # + +package cpev; + +sub read_stage_file { + return 'CentOS7'; +} diff --git a/t/blocker-Repositories.t b/t/blocker-Repositories.t index 85efa99b..2b8515e0 100644 --- a/t/blocker-Repositories.t +++ b/t/blocker-Repositories.t @@ -57,9 +57,13 @@ my $mocked_yum_repos_d = Test::MockFile->dir($path_yum_repos_d); #mkdir $path_yum_repos_d; is $yum->_check_yum_repos(), undef, "no blockers when directory is empty"; -ok scalar Elevate::Blockers::Repositories::VETTED_YUM_REPO(), "VETTED_YUM_REPO populated"; +for my $os ( 'cent', 'cloud' ) { + set_os_to($os); -ok( grep( { 'MariaDB103' } Elevate::Blockers::Repositories::VETTED_YUM_REPO() ), 'MariaDB103 is a valid repo' ); + ok scalar Elevate::OS::vetted_yum_repo(), 'vetted_yum_repo populated'; + + ok( grep( { 'MariaDB103' } Elevate::OS::vetted_yum_repo() ), 'MariaDB103 is a valid repo' ); +} my $mock_vetted_repo = Test::MockFile->file( "$path_yum_repos_d/MariaDB103.repo" => q[MariaDB103] ); diff --git a/t/blocker-dns.t b/t/blocker-dns.t index 74179701..cd4fabad 100644 --- a/t/blocker-dns.t +++ b/t/blocker-dns.t @@ -24,33 +24,37 @@ my $cpev = cpev->new; my $dns = $cpev->get_blocker('DNS'); { - is( - $dns->_blocker_non_bind_powerdns('nsd'), - { - id => q[Elevate::Blockers::DNS::_blocker_non_bind_powerdns], - msg => <<~'EOS', - AlmaLinux 8 only supports bind or powerdns. We suggest you switch to powerdns. + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + my $expected_target_os = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; + is( + $dns->_blocker_non_bind_powerdns('nsd'), + { + id => q[Elevate::Blockers::DNS::_blocker_non_bind_powerdns], + msg => <<~"EOS", + $expected_target_os only supports bind or powerdns. We suggest you switch to powerdns. Before upgrading, we suggest you run: /scripts/setupnameserver powerdns. EOS - }, - 'nsd nameserver is a blocker.' - ); - - is( - $dns->_blocker_non_bind_powerdns('mydns'), - { - id => q[Elevate::Blockers::DNS::_blocker_non_bind_powerdns], - msg => <<~'EOS', - AlmaLinux 8 only supports bind or powerdns. We suggest you switch to powerdns. + }, + 'nsd nameserver is a blocker.' + ); + + is( + $dns->_blocker_non_bind_powerdns('mydns'), + { + id => q[Elevate::Blockers::DNS::_blocker_non_bind_powerdns], + msg => <<~"EOS", + $expected_target_os only supports bind or powerdns. We suggest you switch to powerdns. Before upgrading, we suggest you run: /scripts/setupnameserver powerdns. EOS - }, - 'mydns nameserver is a blocker.' - ); - - is( $dns->_blocker_non_bind_powerdns('bind'), 0, "if they use bind, we're ok" ); - is( $dns->_blocker_non_bind_powerdns('powerdns'), 0, "if they use powerdns, we're ok" ); - is( $dns->_blocker_non_bind_powerdns('disabled'), 0, "if they use no dns, we're ok" ); + }, + 'mydns nameserver is a blocker.' + ); + + is( $dns->_blocker_non_bind_powerdns('bind'), 0, "if they use bind, we're ok" ); + is( $dns->_blocker_non_bind_powerdns('powerdns'), 0, "if they use powerdns, we're ok" ); + is( $dns->_blocker_non_bind_powerdns('disabled'), 0, "if they use no dns, we're ok" ); + } } done_testing(); diff --git a/t/blocker-ea4.t b/t/blocker-ea4.t index 4c20fb14..eae9c733 100644 --- a/t/blocker-ea4.t +++ b/t/blocker-ea4.t @@ -47,19 +47,37 @@ my $mock_cpev = Test::MockModule->new('cpev'); # only testing the blocking case - like( - $ea4->_blocker_ea4_profile(), - { - id => q[Elevate::Blockers::EA4::_blocker_ea4_profile], - msg => <<~'EOS', - One or more EasyApache 4 package(s) are not compatible with AlmaLinux 8. + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + + my $expected_target_os = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; + like( + $ea4->_blocker_ea4_profile(), + { + id => q[Elevate::Blockers::EA4::_blocker_ea4_profile], + msg => <<~"EOS", + One or more EasyApache 4 package(s) are not compatible with $expected_target_os. Please remove these packages before continuing the update. - ea4-bad-pkg EOS - }, - 'blocks when EA4 has an incompatible package' - ); + }, + 'blocks when EA4 has an incompatible package' + ); + + my $target_os = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; + ea_info_check($target_os); + message_seen( WARN => <<"EOS"); +*** Elevation Blocker detected: *** +One or more EasyApache 4 package(s) are not compatible with $target_os. +Please remove these packages before continuing the update. +- ea4-bad-pkg + +EOS + + } + + no_messages_seen(); $mock_cpev->unmock_all; } @@ -67,63 +85,72 @@ my $mock_cpev = Test::MockModule->new('cpev'); { $mock_compoment_ea4->redefine( backup => sub { return; } ); - my $ea_info_check = sub { - message_seen( 'INFO' => "Checking EasyApache profile compatibility with AlmaLinux 8." ); - }; + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); - ok !$ea4->_blocker_ea4_profile(), "no ea4 blockers without an ea4 profile to backup"; - $ea_info_check->(); + my $target_os = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; - my $stage_file = Test::MockFile->file( cpev::ELEVATE_STAGE_FILE() ); + ok !$ea4->_blocker_ea4_profile(), "no ea4 blockers without an ea4 profile to backup"; + ea_info_check($target_os); - my $stage_ea4 = { - profile => '/some/file.not.used.there', - }; + my $stage_file = Test::MockFile->file( cpev::ELEVATE_STAGE_FILE() ); - $mock_cpev->redefine( - read_stage_file => sub { - return { ea4 => $stage_ea4 }; - } - ); + my $stage_ea4 = { + profile => '/some/file.not.used.there', + }; - clear_messages_seen(); + $mock_cpev->redefine( + _read_stage_file => sub { + return { ea4 => $stage_ea4 }; + } + ); - ok( !$ea4->_blocker_ea4_profile(), "no ea4 blockers: profile without any dropped_pkgs" ); + ok( !$ea4->_blocker_ea4_profile(), "no ea4 blockers: profile without any dropped_pkgs" ); - $ea_info_check->(); + ea_info_check($target_os); - $stage_ea4->{'dropped_pkgs'} = { - "ea-bar" => "exp", - "ea-baz" => "exp", - }; - ok( !$ea4->_blocker_ea4_profile(), "no ea4 blockers: profile with dropped_pkgs: exp only" ); - $ea_info_check->(); + $stage_ea4->{'dropped_pkgs'} = { + "ea-bar" => "exp", + "ea-baz" => "exp", + }; + ok( !$ea4->_blocker_ea4_profile(), "no ea4 blockers: profile with dropped_pkgs: exp only" ); + ea_info_check($target_os); - $stage_ea4->{'dropped_pkgs'} = { - "pkg1" => "reg", - "ea-baz" => "exp", - "pkg3" => "reg", - "pkg4" => "whatever", - }; + $stage_ea4->{'dropped_pkgs'} = { + "pkg1" => "reg", + "ea-baz" => "exp", + "pkg3" => "reg", + "pkg4" => "whatever", + }; - ok my $blocker = $ea4->_blocker_ea4_profile(), "_blocker_ea4_profile "; - $ea_info_check->(); + ok my $blocker = $ea4->_blocker_ea4_profile(), "_blocker_ea4_profile "; + ea_info_check($target_os); - message_seen( 'WARN' => qr[Elevation Blocker detected] ); + message_seen( 'WARN' => qr[Elevation Blocker detected] ); - like $blocker, object { - prop blessed => 'cpev::Blocker'; + like $blocker, object { + prop blessed => 'cpev::Blocker'; - field id => q[Elevate::Blockers::EA4::_blocker_ea4_profile]; - field msg => 'One or more EasyApache 4 package(s) are not compatible with AlmaLinux 8. + field id => q[Elevate::Blockers::EA4::_blocker_ea4_profile]; + field msg => qq[One or more EasyApache 4 package(s) are not compatible with $target_os. Please remove these packages before continuing the update. - pkg1 - pkg3 - pkg4 -'; +]; + + end(); + }, "blocker with expected error" or diag explain $blocker; + + $stage_ea4 = {}; + $mock_cpev->unmock_all; + } + + no_messages_seen(); +} - end(); - }, "blocker with expected error" or diag explain $blocker; +sub ea_info_check ($os) { + message_seen( 'INFO' => "Checking EasyApache profile compatibility with $os." ); } done_testing(); diff --git a/t/blocker-whm.t b/t/blocker-whm.t index 14590059..a8172e7b 100644 --- a/t/blocker-whm.t +++ b/t/blocker-whm.t @@ -82,23 +82,30 @@ my $whm = $cpev->get_blocker('WHM'); { note "cPanel & WHM minimum LTS."; - local $Cpanel::Version::Tiny::major_version = 100; - local $Cpanel::Version::Tiny::VERSION_BUILD = '11.109.0.9999'; - - like( - $whm->_blocker_is_newer_than_lts(), - { - id => q[Elevate::Blockers::WHM::_blocker_is_newer_than_lts], - msg => qr{ - \QThis version 11.109.0.9999 does not support upgrades to AlmaLinux 8.\E \s+ + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + my $cpev = cpev->new; + my $whm = $cpev->get_blocker('WHM'); + + local $Cpanel::Version::Tiny::major_version = 100; + local $Cpanel::Version::Tiny::VERSION_BUILD = '11.109.0.9999'; + + my $expected_target_os = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; + like( + $whm->_blocker_is_newer_than_lts(), + { + id => q[Elevate::Blockers::WHM::_blocker_is_newer_than_lts], + msg => qr{ + \QThis version 11.109.0.9999 does not support upgrades to $expected_target_os.\E \s+ \QPlease ensure the cPanel version is 110.\E }xms, - }, - q{cPanel version must be above the known LTS.} - ); + }, + q{cPanel version must be above the known LTS.} + ); - $Cpanel::Version::Tiny::major_version = Elevate::Constants::MINIMUM_LTS_SUPPORTED; - is( $whm->_blocker_is_newer_than_lts(), 0, 'Recent LTS version passes this test.' ); + $Cpanel::Version::Tiny::major_version = Elevate::Constants::MINIMUM_LTS_SUPPORTED; + is( $whm->_blocker_is_newer_than_lts(), 0, 'Recent LTS version passes this test.' ); + } } diff --git a/t/components-ea4.t b/t/components-ea4.t index 5a2b7c2a..ab3b0baa 100644 --- a/t/components-ea4.t +++ b/t/components-ea4.t @@ -102,29 +102,35 @@ sub test_backup_and_restore_not_using_ea4 : Test(7) ($self) { return; } -sub test_missing_ea4_profile : Test(3) ($self) { +sub test_missing_ea4_profile : Test(6) ($self) { - $self->{mock_saferun}->redefine( - saferunnoerror => sub { - note "saferunnoerror: no output"; - return; - }, - ); + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); - my $ea4 = cpev->new->component('EA4'); - like( - dies { $ea4->_backup_ea4_profile() }, - qr/Unable to backup EA4 profile/, - "Unable to backup EA4 profile - no profile file" - ); + $self->{mock_saferun}->redefine( + saferunnoerror => sub { + note "saferunnoerror: no output"; + return; + }, + ); + + my $ea4 = cpev->new->component('EA4'); + like( + dies { $ea4->_backup_ea4_profile() }, + qr/Unable to backup EA4 profile/, + "Unable to backup EA4 profile - no profile file" + ); - _message_run_ea_current_to_profile(); + _message_run_ea_current_to_profile($os); + } return; } sub test_get_ea4_profile : Test(10) ($self) { + set_os_to('cent'); + my $profile = PROFILE_FILE; my $output = qq[$profile\n]; @@ -140,7 +146,7 @@ sub test_get_ea4_profile : Test(10) ($self) { my $ea4 = cpev->new->component('EA4'); is( $ea4->_get_ea4_profile(), PROFILE_FILE, "_get_ea4_profile" ); - _message_run_ea_current_to_profile(1); + _message_run_ea_current_to_profile( 'cent', 1 ); $output = <<'EOS'; The following packages are not available on AlmaLinux_8 and have been removed from the profile @@ -176,41 +182,46 @@ EOS is( $ea4->_get_ea4_profile(), $f, "_get_ea4_profile with noise..." ); - _message_run_ea_current_to_profile($f); + _message_run_ea_current_to_profile( 'cent', $f ); return; } -sub test_get_ea4_profile_check_mode : Test(7) ($self) { +sub test_get_ea4_profile_check_mode : Test(14) ($self) { - my $output = qq[void\n]; + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); - my $cpev = cpev->new( _is_check_mode => 1 ); - ok -d $cpev->tmp_dir, "tmp_dir works"; + my $output = qq[void\n]; - my $mock_b = Test::MockModule->new('Elevate::Blockers') # - ->redefine( is_check_mode => 1 ); + my $cpev = cpev->new( _is_check_mode => 1 ); + ok -d $cpev->tmp_dir, "tmp_dir works"; - ok( Elevate::Blockers->is_check_mode(), 'Elevate::Blockers->is_check_mode()' ); + my $mock_b = Test::MockModule->new('Elevate::Blockers') # + ->redefine( is_check_mode => 1 ); - my $expected_profile = $cpev->tmp_dir() . '/ea_profile.json'; - { - open( my $fh, '>', $expected_profile ) or die; - print {$fh} "...\n"; - } + ok( Elevate::Blockers->is_check_mode(), 'Elevate::Blockers->is_check_mode()' ); - $self->{mock_saferun}->redefine( - saferunnoerror => sub { - note "saferunnoerror: ", $output; - return $output; - }, - ); + my $expected_profile = $cpev->tmp_dir() . '/ea_profile.json'; + { + open( my $fh, '>', $expected_profile ) or die; + print {$fh} "...\n"; + } - my $ea4 = $cpev->component('EA4'); - is( $ea4->_get_ea4_profile(), $expected_profile, "_get_ea4_profile uses a temporary file for the profile" ); + $self->{mock_saferun}->redefine( + saferunnoerror => sub { + note "saferunnoerror: ", $output; + return $output; + }, + ); + + my $ea4 = $cpev->component('EA4'); + is( $ea4->_get_ea4_profile(), $expected_profile, "_get_ea4_profile uses a temporary file for the profile" ); - message_seen( 'INFO' => "Running: /usr/local/bin/ea_current_to_profile --target-os=AlmaLinux_8 --output=$expected_profile" ); - message_seen( 'INFO' => "Backed up EA4 profile to $expected_profile" ); + my $expected_target = $os eq 'cent' ? 'CentOS_8' : 'CloudLinux_8'; + message_seen( 'INFO' => "Running: /usr/local/bin/ea_current_to_profile --target-os=$expected_target --output=$expected_profile" ); + message_seen( 'INFO' => "Backed up EA4 profile to $expected_profile" ); + } return; } @@ -229,27 +240,31 @@ sub test_tmp_dir : Test(3) ($self) { return; } -sub backup_non_existing_profile : Test(10) ($self) { +sub backup_non_existing_profile : Test(16) ($self) { my $ea4 = cpev->new->component('EA4'); - like( - dies { $ea4->_backup_ea4_profile() }, - qr/Unable to backup EA4 profile/, - "Unable to backup EA4 profile - non existing profile file" - ); + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); - _message_run_ea_current_to_profile(); + like( + dies { $ea4->_backup_ea4_profile() }, + qr/Unable to backup EA4 profile/, + "Unable to backup EA4 profile - non existing profile file" + ); - $self->{mock_profile}->contents(''); + _message_run_ea_current_to_profile($os); - like( - dies { $ea4->_backup_ea4_profile() }, - qr/Unable to backup EA4 profile/, - "Unable to backup EA4 profile - empty profile file" - ); + $self->{mock_profile}->contents(''); + + like( + dies { $ea4->_backup_ea4_profile() }, + qr/Unable to backup EA4 profile/, + "Unable to backup EA4 profile - empty profile file" + ); - _message_run_ea_current_to_profile(); + _message_run_ea_current_to_profile($os); + } is cpev::read_stage_file(), { ea4 => { enable => 1 } }, "stage file - ea4 is enabled but we failed"; is $ea4->_restore_ea4_profile(), undef, "restore_ea4_profile: nothing to restore"; @@ -259,7 +274,7 @@ sub backup_non_existing_profile : Test(10) ($self) { return; } -sub test_backup_and_restore_ea4_profile : Test(8) ($self) { +sub test_backup_and_restore_ea4_profile : Test(13) ($self) { my $ea4 = cpev->new->component('EA4'); @@ -267,8 +282,12 @@ sub test_backup_and_restore_ea4_profile : Test(8) ($self) { $self->_update_profile_file($profile); - is( $ea4->_backup_ea4_profile(), 1, "backup_ea4_profile - using ea4" ); - _message_run_ea_current_to_profile(1); + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + + is( $ea4->_backup_ea4_profile(), 1, "backup_ea4_profile - using ea4" ); + _message_run_ea_current_to_profile( $os, 1 ); + } is cpev::read_stage_file(), { ea4 => { enable => 1, profile => PROFILE_FILE } }, "stage file - ea4 is enabled / profile is backup"; @@ -279,102 +298,113 @@ sub test_backup_and_restore_ea4_profile : Test(8) ($self) { return; } -sub test_backup_and_restore_ea4_profile_dropped_packages : Test(14) ($self) { +sub test_backup_and_restore_ea4_profile_dropped_packages : Test(28) ($self) { my $ea4 = cpev->new->component('EA4'); - my $profile = { - "os_upgrade" => { - "source_os" => "", - "target_os" => "", - "target_obs_project" => "", - "dropped_pkgs" => { - "ea-bar" => "reg", - "ea-baz" => "exp" + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + + my $profile = { + "os_upgrade" => { + "source_os" => "", + "target_os" => "", + "target_obs_project" => "", + "dropped_pkgs" => { + "ea-bar" => "reg", + "ea-baz" => "exp" + } } - } - }; - $self->_update_profile_file($profile); + }; + $self->_update_profile_file($profile); - is $ea4->_backup_ea4_profile(), 1, "backup_ea4_profile - using ea4"; - _message_run_ea_current_to_profile(1); + is $ea4->_backup_ea4_profile(), 1, "backup_ea4_profile - using ea4"; + _message_run_ea_current_to_profile( $os, 1 ); - is cpev::read_stage_file(), { - ea4 => { - enable => 1, # - profile => PROFILE_FILE, # - dropped_pkgs => $profile->{os_upgrade}->{dropped_pkgs} # - } - }, - "stage file - ea4 is enabled / profile is backup with dropped_pkgs"; + is cpev::read_stage_file(), { + ea4 => { + enable => 1, # + profile => PROFILE_FILE, # + dropped_pkgs => $profile->{os_upgrade}->{dropped_pkgs} # + } + }, + "stage file - ea4 is enabled / profile is backup with dropped_pkgs"; - is $ea4->_restore_ea4_profile(), 1, "restore_ea4_profile: profile restored"; - is $self->{last_ssystem_call}, [qw{ /usr/local/bin/ea_install_profile --install /var/my.profile}], "call ea_install_profile to restore it" - or diag explain $self->{last_ssystem_call}; + is $ea4->_restore_ea4_profile(), 1, "restore_ea4_profile: profile restored"; + is $self->{last_ssystem_call}, [qw{ /usr/local/bin/ea_install_profile --install /var/my.profile}], "call ea_install_profile to restore it" + or diag explain $self->{last_ssystem_call}; - my $expect = <<'EOS'; + my $expect = <<'EOS'; One or more EasyApache 4 package(s) cannot be restored from your previous profile: - 'ea-bar' - 'ea-baz' ( package was Experimental in CentOS 7 ) EOS - chomp $expect; - foreach my $l ( split( "\n", $expect ) ) { - message_seen( 'WARN' => $l ); + chomp $expect; + foreach my $l ( split( "\n", $expect ) ) { + message_seen( 'WARN' => $l ); + } + + $stage_file->unlink; } return; } -sub test_backup_and_restore_ea4_profile_cleanup_dropped_packages : Test(14) ($self) { +sub test_backup_and_restore_ea4_profile_cleanup_dropped_packages : Test(28) ($self) { my $ea4 = cpev->new->component('EA4'); - my $profile = { - "os_upgrade" => { - "source_os" => "", - "target_os" => "", - "target_obs_project" => "", - "dropped_pkgs" => { - "ea-bar" => "reg", - "ea-baz" => "exp" + for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + + my $profile = { + "os_upgrade" => { + "source_os" => "", + "target_os" => "", + "target_obs_project" => "", + "dropped_pkgs" => { + "ea-bar" => "reg", + "ea-baz" => "exp" + } } - } - }; - $self->_update_profile_file($profile); + }; + $self->_update_profile_file($profile); - is $ea4->_backup_ea4_profile(), 1, "backup_ea4_profile - using ea4"; - _message_run_ea_current_to_profile(1); + is $ea4->_backup_ea4_profile(), 1, "backup_ea4_profile - using ea4"; + _message_run_ea_current_to_profile( $os, 1 ); - is cpev::read_stage_file(), { - ea4 => { - enable => 1, # - profile => PROFILE_FILE, # - dropped_pkgs => $profile->{os_upgrade}->{dropped_pkgs} # - } - }, - "stage file - ea4 is enabled / profile is backup with dropped_pkgs"; - - $profile = { - "os_upgrade" => { - "source_os" => "", - "target_os" => "", - "target_obs_project" => "", - } - }; - $self->_update_profile_file($profile); + is cpev::read_stage_file(), { + ea4 => { + enable => 1, # + profile => PROFILE_FILE, # + dropped_pkgs => $profile->{os_upgrade}->{dropped_pkgs} # + } + }, + "stage file - ea4 is enabled / profile is backup with dropped_pkgs"; + + $profile = { + "os_upgrade" => { + "source_os" => "", + "target_os" => "", + "target_obs_project" => "", + } + }; + $self->_update_profile_file($profile); - is $ea4->_backup_ea4_profile(), 1, "backup_ea4_profile - using ea4"; - _message_run_ea_current_to_profile(1); + is $ea4->_backup_ea4_profile(), 1, "backup_ea4_profile - using ea4"; + _message_run_ea_current_to_profile( $os, 1 ); - my $stage = cpev::read_stage_file(); - is $stage, { - ea4 => { - enable => 1, # - profile => PROFILE_FILE, # - } - }, - "stage file - ea4 is enabled / profile: clear the dropped_pkgs hash" - or diag explain $stage; + my $stage = cpev::read_stage_file(); + is $stage, { + ea4 => { + enable => 1, # + profile => PROFILE_FILE, # + } + }, + "stage file - ea4 is enabled / profile: clear the dropped_pkgs hash" + or diag explain $stage; + + } return; @@ -512,9 +542,11 @@ Please remove these packages before continuing the update. ## helpers -sub _message_run_ea_current_to_profile ( $success = 0 ) { +sub _message_run_ea_current_to_profile ( $os = 'cent', $success = 0 ) { + + my $target = $os eq 'cent' ? 'CentOS_8' : 'CloudLinux_8'; - message_seen( 'INFO' => q[Running: /usr/local/bin/ea_current_to_profile --target-os=AlmaLinux_8] ); + message_seen( 'INFO' => qq[Running: /usr/local/bin/ea_current_to_profile --target-os=$target] ); return unless $success; my $f = $success eq 1 ? PROFILE_FILE : $success; diff --git a/t/leapp_upgrade.t b/t/leapp_upgrade.t index b2f9aaaa..a025d4d3 100644 --- a/t/leapp_upgrade.t +++ b/t/leapp_upgrade.t @@ -22,23 +22,37 @@ use cPstrict; $INC{'scripts/ElevateCpanel.pm'} = '__TEST__'; +my $ssystem_cmd; my $mock_elevate = Test::MockModule->new('cpev'); $mock_elevate->redefine( ssystem_and_die => sub ( $, @args ) { - note "run: ", join( ' ', @args ); + shift @args; + $ssystem_cmd = join( ' ', @args ); + note "run: $ssystem_cmd"; return; }, + getopt => sub { return; }, +); + +my $mock_elevate_leapp = Test::MockModule->new('Elevate::Leapp'); +$mock_elevate_leapp->redefine( setup_answer_file => sub { # cannot use Test::MockFile with system touch... note "mocked setup_answer_file"; return; }, - getopt => sub { return; }, ); my $mock_elevate_file = Test::MockFile->file('/var/cpanel/elevate'); -ok( cpev->_do_leapp_upgrade(), '_do_leapp_upgrade succeeds' ); +for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + + my $expect_cmd = '/usr/bin/leapp upgrade'; + $expect_cmd .= ' --nowarn' if $os eq 'cloud'; + ok( cpev->leapp->upgrade(), 'leapp upgrade succeeds' ); + is( $ssystem_cmd, $expect_cmd ); +} $mock_elevate->redefine( ssystem_and_die => sub { @@ -46,14 +60,14 @@ $mock_elevate->redefine( } ); -ok cpev::LEAPP_REPORT_JSON(), 'LEAPP_REPORT_JSON defined'; -ok cpev::LEAPP_REPORT_TXT(), 'LEAPP_REPORT_TXT defined'; +ok Elevate::Leapp::LEAPP_REPORT_JSON(), 'LEAPP_REPORT_JSON defined'; +ok Elevate::Leapp::LEAPP_REPORT_TXT(), 'LEAPP_REPORT_TXT defined'; -my $mock_leap_report_json = Test::MockFile->file( cpev::LEAPP_REPORT_JSON() ); -my $mock_leap_report_txt = Test::MockFile->file( cpev::LEAPP_REPORT_TXT() ); +my $mock_leap_report_json = Test::MockFile->file( Elevate::Leapp::LEAPP_REPORT_JSON() ); +my $mock_leap_report_txt = Test::MockFile->file( Elevate::Leapp::LEAPP_REPORT_TXT() ); like( - dies { cpev->_do_leapp_upgrade() }, + dies { cpev->leapp->upgrade() }, qr{The 'leapp upgrade' process failed}, '_do_leapp_upgrade failed' ); @@ -131,7 +145,7 @@ $mock_leap_report_json->contents( <<'EOS' ); } EOS -my $error = dies { cpev->_do_leapp_upgrade() }; +my $error = dies { cpev->leapp->upgrade() }; note $error; @@ -162,7 +176,7 @@ unlike( ); $mock_leap_report_txt->contents('full report'); -$error = dies { cpev->_do_leapp_upgrade() }; +$error = dies { cpev->leapp->upgrade() }; note $error; diff --git a/t/lib/Test/Elevate.pm b/t/lib/Test/Elevate.pm index 226e8f5d..dffd4fd8 100644 --- a/t/lib/Test/Elevate.pm +++ b/t/lib/Test/Elevate.pm @@ -7,7 +7,7 @@ use Test::More; use Test::Deep; our @ISA = qw(Exporter); -our @EXPORT = qw(message_seen message_seen_lines clear_messages_seen no_messages_seen no_message_seen ); +our @EXPORT = qw(message_seen message_seen_lines clear_messages_seen no_messages_seen no_message_seen set_os_to_centos_7 set_os_to_cloudlinux_7 set_os_to unmock_os); our @EXPORT_OK = @EXPORT; use Log::Log4perl; @@ -67,6 +67,10 @@ sub init { mockLoggerFor($cat); } + # Default to CentOS 7 + note 'Mock Elevate::OS singleton to think this server is CentOS 7'; + $Elevate::OS::OS = bless {}, 'Elevate::OS::CentOS7'; + return; } @@ -125,4 +129,28 @@ sub no_messages_seen { # convenience sub no_message_seen { goto &no_messages_seen; } +sub set_os_to_centos_7 { + note 'Mock Elevate::OS singleton to think this server is CentOS 7'; + $Elevate::OS::OS = bless {}, 'Elevate::OS::CentOS7'; + return; +} + +sub set_os_to_cloudlinux_7 { + note 'Mock Elevate::OS singleton to think this server is CloudLinux 7'; + $Elevate::OS::OS = bless {}, 'Elevate::OS::CloudLinux7'; + return; +} + +sub set_os_to ($os) { + return set_os_to_centos_7() if $os =~ m/^cent/i; + return set_os_to_cloudlinux_7 if $os =~ m/^cloud/i; + + die "Unknown os: $os\n"; +} + +sub unmock_os { + note 'Elevate::OS is no longer mocked'; + $Elevate::OS::OS = undef; +} + 1; diff --git a/t/notify.t b/t/notify.t index 16982630..20b9e1c3 100644 --- a/t/notify.t +++ b/t/notify.t @@ -33,55 +33,72 @@ $mock_notify->redefine( } ); -my $stage_file = Test::MockFile->file( cpev::ELEVATE_STAGE_FILE() ); - my $cpev = bless {}, 'cpev'; -$cpev->_notify_success(); +for my $os ( 'cent', 'cloud' ) { + set_os_to($os); + + my $stage_file = Test::MockFile->file( cpev::ELEVATE_STAGE_FILE() ); + + my $expect_title = $os eq 'cent' ? 'Successfully updated to AlmaLinux 8' : 'Successfully updated to CloudLinux 8'; + my $expect_body = + $os eq 'cent' + ? qq[The cPanel & WHM server has completed the elevation process from CentOS 7 to AlmaLinux 8.\n] + : qq[The cPanel & WHM server has completed the elevation process from CloudLinux 7 to CloudLinux 8.\n]; + + $cpev->_notify_success(); -is $notification, { - title => 'Successfully updated to AlmaLinux 8', - body => qq[The cPanel & WHM server has completed the elevation process from CentOS 7 to AlmaLinux 8.\n], - opts => { is_success => 1 }, - }, - " _notify_success" - or diag explain $notification; + is $notification, { + title => $expect_title, + body => $expect_body, + opts => { is_success => 1 }, + }, + " _notify_success" + or diag explain $notification; -$notification = {}; + $notification = {}; -ok Elevate::Notify::add_final_notification("My First Notification"), 'add_final_notification'; + ok Elevate::Notify::add_final_notification("My First Notification"), 'add_final_notification'; -is cpev::read_stage_file(), { final_notifications => ['My First Notification'] }, "stage file - notifications"; + is cpev::read_stage_file(), { final_notifications => ['My First Notification'] }, "stage file - notifications"; -ok !Elevate::Notify::add_final_notification(undef), q[cannot add_final_notification(undef)]; -ok !Elevate::Notify::add_final_notification(''), q[cannot add_final_notification('')]; + ok !Elevate::Notify::add_final_notification(undef), q[cannot add_final_notification(undef)]; + ok !Elevate::Notify::add_final_notification(''), q[cannot add_final_notification('')]; -is cpev::read_stage_file(), { final_notifications => ['My First Notification'] }, "stage file - notifications"; + is cpev::read_stage_file(), { final_notifications => ['My First Notification'] }, "stage file - notifications"; -Elevate::Notify::add_final_notification("My Second Notification\nwith two lines."); + Elevate::Notify::add_final_notification("My Second Notification\nwith two lines."); -is cpev::read_stage_file(), { - final_notifications => [ - "My Second Notification\nwith two lines.", - 'My First Notification', - ] - }, - "stage file - notifications" - or diag explain cpev::read_stage_file(); + is cpev::read_stage_file(), { + final_notifications => [ + "My Second Notification\nwith two lines.", + 'My First Notification', + ] + }, + "stage file - notifications" + or diag explain cpev::read_stage_file(); -$cpev->_notify_success(); + $cpev->_notify_success(); -is $notification->{title}, 'Successfully updated to AlmaLinux 8', '_notify_success: title'; -is $notification->{body}, <{body}; -The cPanel & WHM server has completed the elevation process from CentOS 7 to AlmaLinux 8. + my $expect_upgrade_from = $os eq 'cent' ? 'CentOS 7' : 'CloudLinux 7'; + my $expect_upgrade_to = $os eq 'cent' ? 'AlmaLinux 8' : 'CloudLinux 8'; -The update to AlmaLinux 8 was successful but please note that one ore more notifications require your attention: + is $notification->{title}, "Successfully updated to $expect_upgrade_to", '_notify_success: title'; + is $notification->{body}, <{body}; +The cPanel & WHM server has completed the elevation process from $expect_upgrade_from to $expect_upgrade_to. + +The update to $expect_upgrade_to was successful but please note that one ore more notifications require your attention: * My First Notification * My Second Notification with two lines. EOS -no_messages_seen(); + no_messages_seen(); + + $notification = {}; + undef $stage_file; + +} done_testing(); diff --git a/t/usage.t b/t/usage.t index ef947259..2d8589ef 100644 --- a/t/usage.t +++ b/t/usage.t @@ -247,35 +247,76 @@ foreach my $test_hr (@TEST_DATA) { # Test out setting --upgrade-to to different values # -my $mock_cpev = Test::MockModule->new('cpev'); -$mock_cpev->redefine( - _read_stage_file => sub { - return {}; - } -); +{ + note 'Test as CentOS 7 server'; -my $cpev = cpev->new->_init( '--check', '--upgrade-to=OogaBoogaLinux' ); + set_os_to_centos_7(); -like( - dies { $cpev->_parse_opt_upgrade_to() }, - qr/Invalid --upgrade_to value/, - 'Exception thrown for invalid linux distro' -); + my $cpev = cpev->new->_init( '--check', '--upgrade-to=OogaBoogaLinux' ); + + like( + dies { $cpev->_parse_opt_upgrade_to() }, + qr/The current OS can only upgrade to the following flavors/, + 'Exception thrown for invalid linux distro' + ); + + $cpev = cpev->new->_init('--check'); + + ok lives { $cpev->_parse_opt_upgrade_to() }, 'No exception when upgrade-to not supplied'; + is $cpev->upgrade_to(), 'AlmaLinux', 'CentOS 7 defaults to AlmaLinux when upgrade-to not specified'; + + $cpev = cpev->new->_init( '--check', '--upgrade-to=almalinux' ); + + ok lives { $cpev->_parse_opt_upgrade_to() }, 'No exception when upgrade-to set to AlmaLinux'; + is $cpev->upgrade_to(), 'AlmaLinux', 'Set to use AlmaLinux when upgrade-to set to AlmaLinux'; + + $cpev = cpev->new->_init( '--check', '--upgrade-to=rocky' ); + + ok lives { $cpev->_parse_opt_upgrade_to() }, 'No exception when upgrade-to set to Rocky'; + is $cpev->upgrade_to(), 'Rocky', 'Set to use Rocky when upgrade-to set to Rocky'; -$cpev = cpev->new->_init('--check'); + $cpev = cpev->new->_init( '--check', '--upgrade-to=cloudlinux' ); -ok lives { $cpev->_parse_opt_upgrade_to() }, 'No exception when upgrade-to not supplied'; -is $cpev->upgrade_to(), 'AlmaLinux', 'Defaults to AlmaLinux when upgrade-to not specified'; + like( + dies { $cpev->_parse_opt_upgrade_to() }, + qr/The current OS can only upgrade to the following flavors/, + 'Exception thrown for CloudLinux when the OS is CentOS 7', + ); -$cpev = cpev->new->_init( '--check', '--upgrade-to=almalinux' ); +} + +{ + note 'Test as CloudLinux 7 server'; + + set_os_to_cloudlinux_7(); + + my $cpev = cpev->new->_init('--check'); + + ok lives { $cpev->_parse_opt_upgrade_to() }, 'No exception when upgrade-to not supplied'; + is $cpev->upgrade_to(), 'CloudLinux', 'CloudLinux 7 defaults to CloudLinux when upgrade-to not specified'; -ok lives { $cpev->_parse_opt_upgrade_to() }, 'No exception when upgrade-to set to AlmaLinux'; -is $cpev->upgrade_to(), 'AlmaLinux', 'Set to use AlmaLinux when upgrade-to set to AlmaLinux'; + $cpev = cpev->new->_init( '--check', '--upgrade-to=almalinux' ); -$cpev = cpev->new->_init( '--check', '--upgrade-to=rocky' ); + like( + dies { $cpev->_parse_opt_upgrade_to() }, + qr/The current OS can only upgrade to the following flavors/, + 'Exception thrown for AlmaLinux when the OS is CloudLinux 7', + ); -ok lives { $cpev->_parse_opt_upgrade_to() }, 'No exception when upgrade-to set to Rocky'; -is $cpev->upgrade_to(), 'Rocky', 'Set to use Rocky when upgrade-to set to Rocky'; + $cpev = cpev->new->_init( '--check', '--upgrade-to=rocky' ); + + like( + dies { $cpev->_parse_opt_upgrade_to() }, + qr/The current OS can only upgrade to the following flavors/, + 'Exception thrown for Rocky Linux when the OS is CloudLinux 7', + ); + + $cpev = cpev->new->_init( '--check', '--upgrade-to=cloudlinux' ); + + ok lives { $cpev->_parse_opt_upgrade_to() }, 'No exception when upgrade-to set to cloudlinux'; + is $cpev->upgrade_to(), 'CloudLinux', 'Set to use CloudLinux when upgrade-to set to CloudLinux'; + +} # # Test out the --non-interactive parameter @@ -291,7 +332,7 @@ $mock_io_prompt->redefine( } ); -$cpev = cpev->new->_init('--start'); +my $cpev = cpev->new->_init('--start'); $cpev->give_last_chance(); is $user_has_been_prompted, 1, 'IP::Prompt invoked without the non-interactive option';